Merge 3c5d127fb5 ("Merge tag 'mtd/for-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux") into android-mainline
Steps on the way to v6.9-rc1 Signed-off-by: Lee Jones <joneslee@google.com> Change-Id: I68fda1c24128ba9e8e383a030ce1db99cdc8e2a1
This commit is contained in:
@@ -56,6 +56,7 @@ Required properties:
|
||||
"atmel,sama5d4-pmecc"
|
||||
"atmel,sama5d2-pmecc"
|
||||
"microchip,sam9x60-pmecc"
|
||||
"microchip,sam9x7-pmecc", "atmel,at91sam9g45-pmecc"
|
||||
- reg: should contain 2 register ranges. The first one is pointing to the PMECC
|
||||
block, and the second one to the PMECC_ERRLOC block.
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ title: Broadcom STB NAND Controller
|
||||
maintainers:
|
||||
- Brian Norris <computersforpeace@gmail.com>
|
||||
- Kamal Dasu <kdasu.kdev@gmail.com>
|
||||
- William Zhang <william.zhang@broadcom.com>
|
||||
|
||||
description: |
|
||||
The Broadcom Set-Top Box NAND controller supports low-level access to raw NAND
|
||||
@@ -18,9 +19,10 @@ description: |
|
||||
supports basic PROGRAM and READ functions, among other features.
|
||||
|
||||
This controller was originally designed for STB SoCs (BCM7xxx) but is now
|
||||
available on a variety of Broadcom SoCs, including some BCM3xxx, BCM63xx, and
|
||||
iProc/Cygnus. Its history includes several similar (but not fully register
|
||||
compatible) versions.
|
||||
available on a variety of Broadcom SoCs, including some BCM3xxx, MIPS based
|
||||
Broadband SoC (BCM63xx), ARM based Broadband SoC (BCMBCA) and iProc/Cygnus.
|
||||
Its history includes several similar (but not fully register compatible)
|
||||
versions.
|
||||
|
||||
-- Additional SoC-specific NAND controller properties --
|
||||
|
||||
@@ -53,7 +55,7 @@ properties:
|
||||
- brcm,brcmnand-v7.2
|
||||
- brcm,brcmnand-v7.3
|
||||
- const: brcm,brcmnand
|
||||
- description: BCM63138 SoC-specific NAND controller
|
||||
- description: BCMBCA SoC-specific NAND controller
|
||||
items:
|
||||
- const: brcm,nand-bcm63138
|
||||
- enum:
|
||||
@@ -111,6 +113,13 @@ properties:
|
||||
earlier versions of this core that include WP
|
||||
type: boolean
|
||||
|
||||
brcm,wp-not-connected:
|
||||
description:
|
||||
Use this property when WP pin is not physically wired to the NAND chip.
|
||||
Write protection feature cannot be used. By default, controller assumes
|
||||
the pin is connected and feature is used.
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
|
||||
patternProperties:
|
||||
"^nand@[a-f0-9]$":
|
||||
type: object
|
||||
@@ -137,6 +146,15 @@ patternProperties:
|
||||
layout.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
brcm,nand-ecc-use-strap:
|
||||
description:
|
||||
This property requires the host system to get the ECC related
|
||||
settings from the SoC NAND boot strap configuration instead of
|
||||
the generic NAND ECC settings. This is a common hardware design
|
||||
on BCMBCA based boards. This strap ECC option and generic NAND
|
||||
ECC option can not be specified at the same time.
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
allOf:
|
||||
@@ -177,6 +195,8 @@ allOf:
|
||||
- const: iproc-idm
|
||||
- const: iproc-ext
|
||||
- if:
|
||||
required:
|
||||
- interrupts
|
||||
properties:
|
||||
interrupts:
|
||||
minItems: 2
|
||||
@@ -184,12 +204,26 @@ allOf:
|
||||
required:
|
||||
- interrupt-names
|
||||
|
||||
- if:
|
||||
patternProperties:
|
||||
"^nand@[a-f0-9]$":
|
||||
required:
|
||||
- brcm,nand-ecc-use-strap
|
||||
then:
|
||||
patternProperties:
|
||||
"^nand@[a-f0-9]$":
|
||||
properties:
|
||||
nand-ecc-strength: false
|
||||
nand-ecc-step-size: false
|
||||
nand-ecc-maximize: false
|
||||
nand-ecc-algo: false
|
||||
brcm,nand-oob-sector-size: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- reg
|
||||
- reg-names
|
||||
- interrupts
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
||||
@@ -68,7 +68,7 @@ Deprecated properties:
|
||||
false.
|
||||
|
||||
Nand device bindings may contain additional sub-nodes describing partitions of
|
||||
the address space. See partition.txt for more detail. The NAND Flash timing
|
||||
the address space. See mtd.yaml for more detail. The NAND Flash timing
|
||||
values must be programmed in the chip select’s node of AEMIF
|
||||
memory-controller (see Documentation/devicetree/bindings/memory-controllers/
|
||||
davinci-aemif.txt).
|
||||
|
||||
@@ -15,7 +15,7 @@ The DMA fields are not used yet in the driver but are listed here for
|
||||
completing the bindings.
|
||||
|
||||
The device tree may optionally contain sub-nodes describing partitions of the
|
||||
address space. See partition.txt for more detail.
|
||||
address space. See mtd.yaml for more detail.
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ Deprecated properties:
|
||||
(R/B# pins not connected).
|
||||
|
||||
Each flash chip described may optionally contain additional sub-nodes
|
||||
describing partitions of the address space. See partition.txt for more
|
||||
describing partitions of the address space. See mtd.yaml for more
|
||||
detail.
|
||||
|
||||
Examples:
|
||||
|
||||
@@ -26,7 +26,7 @@ Optional properties:
|
||||
read to ensure that the GPIO accesses have completed.
|
||||
|
||||
The device tree may optionally contain sub-nodes describing partitions of the
|
||||
address space. See partition.txt for more detail.
|
||||
address space. See mtd.yaml for more detail.
|
||||
|
||||
Examples:
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ maintainers:
|
||||
description: |
|
||||
The GPMI nand controller provides an interface to control the NAND
|
||||
flash chips. The device tree may optionally contain sub-nodes
|
||||
describing partitions of the address space. See partition.txt for
|
||||
describing partitions of the address space. See mtd.yaml for
|
||||
more detail.
|
||||
|
||||
properties:
|
||||
|
||||
@@ -22,7 +22,7 @@ The following ECC strength and step size are currently supported:
|
||||
- nand-ecc-strength = <16>, nand-ecc-step-size = <1024>
|
||||
|
||||
Flash chip may optionally contain additional sub-nodes describing partitions of
|
||||
the address space. See partition.txt for more detail.
|
||||
the address space. See mtd.yaml for more detail.
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
@@ -52,6 +52,9 @@ properties:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
m25p,fast-read:
|
||||
type: boolean
|
||||
description:
|
||||
|
||||
@@ -10,6 +10,8 @@ maintainers:
|
||||
- Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
- Richard Weinberger <richard@nod.at>
|
||||
|
||||
select: false
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^(flash|.*sram|nand)(@.*)?$"
|
||||
|
||||
@@ -39,7 +39,7 @@ Optional children node properties:
|
||||
- wp-gpios: GPIO specifier for the write protect pin.
|
||||
|
||||
Optional child node of NAND chip nodes:
|
||||
Partitions: see partition.txt
|
||||
Partitions: see mtd.yaml
|
||||
|
||||
Example:
|
||||
nand-controller@70008000 {
|
||||
|
||||
@@ -13,7 +13,7 @@ Optional properties:
|
||||
registers in usecs
|
||||
|
||||
The device tree may optionally contain sub-nodes describing partitions of the
|
||||
address space. See partition.txt for more detail.
|
||||
address space. See mtd.yaml for more detail.
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ Optional child properties:
|
||||
|
||||
Each child device node may optionally contain a 'partitions' sub-node,
|
||||
which further contains sub-nodes describing the flash partition mapping.
|
||||
See partition.txt for more detail.
|
||||
See mtd.yaml for more detail.
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
@@ -14,10 +14,11 @@ properties:
|
||||
enum:
|
||||
- st,stm32mp15-fmc2
|
||||
- st,stm32mp1-fmc2-nfc
|
||||
- st,stm32mp25-fmc2-nfc
|
||||
|
||||
reg:
|
||||
minItems: 6
|
||||
maxItems: 7
|
||||
maxItems: 12
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
@@ -92,6 +93,28 @@ allOf:
|
||||
- description: Chip select 1 command
|
||||
- description: Chip select 1 address space
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: st,stm32mp25-fmc2-nfc
|
||||
then:
|
||||
properties:
|
||||
reg:
|
||||
items:
|
||||
- description: Chip select 0 data
|
||||
- description: Chip select 0 command
|
||||
- description: Chip select 0 address space
|
||||
- description: Chip select 1 data
|
||||
- description: Chip select 1 command
|
||||
- description: Chip select 1 address space
|
||||
- description: Chip select 2 data
|
||||
- description: Chip select 2 command
|
||||
- description: Chip select 2 address space
|
||||
- description: Chip select 3 data
|
||||
- description: Chip select 3 command
|
||||
- description: Chip select 3 address space
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
@@ -138,6 +138,20 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
nand_controller: nand-controller@1800 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
|
||||
reg = <0x1800 0x600>, <0x2000 0x10>;
|
||||
reg-names = "nand", "nand-int-base";
|
||||
status = "disabled";
|
||||
|
||||
nandcs: nand@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
uart0: serial@12000 {
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
reg = <0x12000 0x1000>;
|
||||
|
||||
@@ -229,7 +229,12 @@
|
||||
reg-names = "nand", "nand-int-base";
|
||||
status = "disabled";
|
||||
interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "nand";
|
||||
interrupt-names = "nand_ctlrdy";
|
||||
|
||||
nandcs: nand@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
serial@4400 {
|
||||
|
||||
@@ -119,5 +119,19 @@
|
||||
num-cs = <8>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
nand_controller: nand-controller@2000 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
|
||||
reg = <0x2000 0x600>, <0xf0 0x10>;
|
||||
reg-names = "nand", "nand-int-base";
|
||||
status = "disabled";
|
||||
|
||||
nandcs: nand@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -129,6 +129,20 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
nand_controller: nand-controller@1800 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
|
||||
reg = <0x1800 0x600>, <0x2000 0x10>;
|
||||
reg-names = "nand", "nand-int-base";
|
||||
status = "disabled";
|
||||
|
||||
nandcs: nand@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
uart0: serial@12000 {
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
reg = <0x12000 0x1000>;
|
||||
|
||||
@@ -139,6 +139,20 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
nand_controller: nand-controller@1800 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
|
||||
reg = <0x1800 0x600>, <0x2000 0x10>;
|
||||
reg-names = "nand", "nand-int-base";
|
||||
status = "disabled";
|
||||
|
||||
nandcs: nand@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
uart0: serial@12000 {
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
reg = <0x12000 0x1000>;
|
||||
|
||||
@@ -119,5 +119,19 @@
|
||||
num-cs = <8>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
nand_controller: nand-controller@1800 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
|
||||
reg = <0x1800 0x600>, <0x2000 0x10>;
|
||||
reg-names = "nand", "nand-int-base";
|
||||
status = "disabled";
|
||||
|
||||
nandcs: nand@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -129,6 +129,20 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
nand_controller: nand-controller@1800 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
|
||||
reg = <0x1800 0x600>, <0x2000 0x10>;
|
||||
reg-names = "nand", "nand-int-base";
|
||||
status = "disabled";
|
||||
|
||||
nandcs: nand@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
uart0: serial@12000 {
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
reg = <0x12000 0x1000>;
|
||||
|
||||
@@ -120,6 +120,20 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
nand_controller: nand-controller@1800 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
|
||||
reg = <0x1800 0x600>, <0x2000 0x10>;
|
||||
reg-names = "nand", "nand-int-base";
|
||||
status = "disabled";
|
||||
|
||||
nandcs: nand@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
uart0: serial@12000 {
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
reg = <0x12000 0x1000>;
|
||||
|
||||
@@ -32,3 +32,13 @@
|
||||
&hsspi {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nand_controller {
|
||||
brcm,wp-not-connected;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nandcs {
|
||||
nand-on-flash-bbt;
|
||||
brcm,nand-ecc-use-strap;
|
||||
};
|
||||
|
||||
@@ -29,3 +29,13 @@
|
||||
&hsspi {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nand_controller {
|
||||
brcm,wp-not-connected;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nandcs {
|
||||
nand-on-flash-bbt;
|
||||
brcm,nand-ecc-use-strap;
|
||||
};
|
||||
|
||||
@@ -32,15 +32,15 @@
|
||||
};
|
||||
|
||||
&nand_controller {
|
||||
brcm,wp-not-connected;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
nand@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <0>;
|
||||
nand-ecc-strength = <4>;
|
||||
nand-ecc-step-size = <512>;
|
||||
brcm,nand-oob-sectors-size = <16>;
|
||||
};
|
||||
&nandcs {
|
||||
nand-ecc-strength = <4>;
|
||||
nand-ecc-step-size = <512>;
|
||||
brcm,nand-oob-sector-size = <16>;
|
||||
nand-on-flash-bbt;
|
||||
};
|
||||
|
||||
&ahci {
|
||||
|
||||
@@ -32,3 +32,13 @@
|
||||
&hsspi {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nand_controller {
|
||||
brcm,wp-not-connected;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nandcs {
|
||||
nand-on-flash-bbt;
|
||||
brcm,nand-ecc-use-strap;
|
||||
};
|
||||
|
||||
@@ -32,3 +32,13 @@
|
||||
&hsspi {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nand_controller {
|
||||
brcm,wp-not-connected;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nandcs {
|
||||
nand-on-flash-bbt;
|
||||
brcm,nand-ecc-use-strap;
|
||||
};
|
||||
|
||||
@@ -32,3 +32,13 @@
|
||||
&hsspi {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nand_controller {
|
||||
brcm,wp-not-connected;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nandcs {
|
||||
nand-on-flash-bbt;
|
||||
brcm,nand-ecc-use-strap;
|
||||
};
|
||||
|
||||
@@ -32,3 +32,13 @@
|
||||
&hsspi {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nand_controller {
|
||||
brcm,wp-not-connected;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nandcs {
|
||||
nand-on-flash-bbt;
|
||||
brcm,nand-ecc-use-strap;
|
||||
};
|
||||
|
||||
@@ -32,3 +32,13 @@
|
||||
&hsspi {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nand_controller {
|
||||
brcm,wp-not-connected;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nandcs {
|
||||
nand-on-flash-bbt;
|
||||
brcm,nand-ecc-use-strap;
|
||||
};
|
||||
|
||||
@@ -32,3 +32,13 @@
|
||||
&hsspi {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nand_controller {
|
||||
brcm,wp-not-connected;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nandcs {
|
||||
nand-on-flash-bbt;
|
||||
brcm,nand-ecc-use-strap;
|
||||
};
|
||||
|
||||
@@ -125,6 +125,11 @@
|
||||
};
|
||||
};
|
||||
|
||||
&nand_controller {
|
||||
brcm,wp-not-connected;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nandcs {
|
||||
nand-ecc-strength = <4>;
|
||||
nand-ecc-step-size = <512>;
|
||||
|
||||
@@ -155,6 +155,11 @@
|
||||
};
|
||||
};
|
||||
|
||||
&nand_controller {
|
||||
brcm,wp-not-connected;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nandcs {
|
||||
nand-ecc-strength = <4>;
|
||||
nand-ecc-step-size = <512>;
|
||||
|
||||
@@ -166,11 +166,15 @@
|
||||
};
|
||||
};
|
||||
|
||||
&nand_controller {
|
||||
brcm,wp-not-connected;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nandcs {
|
||||
nand-ecc-strength = <4>;
|
||||
nand-ecc-step-size = <512>;
|
||||
nand-on-flash-bbt;
|
||||
brcm,nand-has-wp;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
@@ -589,7 +589,7 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
nand-controller@1800 {
|
||||
nand_controller: nand-controller@1800 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
|
||||
@@ -597,7 +597,7 @@
|
||||
reg-names = "nand", "nand-int-base";
|
||||
interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "nand_ctlrdy";
|
||||
status = "okay";
|
||||
status = "disabled";
|
||||
|
||||
nandcs: nand@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
|
||||
@@ -138,6 +138,20 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
nand_controller: nand-controller@1800 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
|
||||
reg = <0x1800 0x600>, <0x2000 0x10>;
|
||||
reg-names = "nand", "nand-int-base";
|
||||
status = "disabled";
|
||||
|
||||
nandcs: nand@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
uart0: serial@12000 {
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
reg = <0x12000 0x1000>;
|
||||
|
||||
@@ -119,6 +119,20 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
nand_controller: nand-controller@1800 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
|
||||
reg = <0x1800 0x600>, <0x2000 0x10>;
|
||||
reg-names = "nand", "nand-int-base";
|
||||
status = "disabled";
|
||||
|
||||
nandcs: nand@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
uart0: serial@12000 {
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
reg = <0x12000 0x1000>;
|
||||
|
||||
@@ -137,6 +137,20 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
nand_controller: nand-controller@1800 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
|
||||
reg = <0x1800 0x600>, <0x2000 0x10>;
|
||||
reg-names = "nand", "nand-int-base";
|
||||
status = "disabled";
|
||||
|
||||
nandcs: nand@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
uart0: serial@12000 {
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
reg = <0x12000 0x1000>;
|
||||
|
||||
@@ -138,6 +138,20 @@
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
nand_controller: nand-controller@1800 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
|
||||
reg = <0x1800 0x600>, <0x2000 0x10>;
|
||||
reg-names = "nand", "nand-int-base";
|
||||
status = "disabled";
|
||||
|
||||
nandcs: nand@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
||||
uart0: serial@12000 {
|
||||
compatible = "arm,pl011", "arm,primecell";
|
||||
reg = <0x12000 0x1000>;
|
||||
|
||||
@@ -119,5 +119,19 @@
|
||||
num-cs = <8>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
nand_controller: nand-controller@1800 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
|
||||
reg = <0x1800 0x600>, <0x2000 0x10>;
|
||||
reg-names = "nand", "nand-int-base";
|
||||
status = "disabled";
|
||||
|
||||
nandcs: nand@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -156,5 +156,19 @@
|
||||
num-cs = <8>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
nand_controller: nand-controller@1800 {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
compatible = "brcm,nand-bcm63138", "brcm,brcmnand-v7.1", "brcm,brcmnand";
|
||||
reg = <0x1800 0x600>, <0x2000 0x10>;
|
||||
reg-names = "nand", "nand-int-base";
|
||||
status = "disabled";
|
||||
|
||||
nandcs: nand@0 {
|
||||
compatible = "brcm,nandcs";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -32,3 +32,13 @@
|
||||
&hsspi {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nand_controller {
|
||||
brcm,wp-not-connected;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nandcs {
|
||||
nand-on-flash-bbt;
|
||||
brcm,nand-ecc-use-strap;
|
||||
};
|
||||
|
||||
@@ -32,3 +32,13 @@
|
||||
&hsspi {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nand_controller {
|
||||
brcm,wp-not-connected;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nandcs {
|
||||
nand-on-flash-bbt;
|
||||
brcm,nand-ecc-use-strap;
|
||||
};
|
||||
|
||||
@@ -32,3 +32,13 @@
|
||||
&hsspi {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nand_controller {
|
||||
brcm,wp-not-connected;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nandcs {
|
||||
nand-on-flash-bbt;
|
||||
brcm,nand-ecc-use-strap;
|
||||
};
|
||||
|
||||
@@ -32,3 +32,13 @@
|
||||
&hsspi {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nand_controller {
|
||||
brcm,wp-not-connected;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nandcs {
|
||||
nand-on-flash-bbt;
|
||||
brcm,nand-ecc-use-strap;
|
||||
};
|
||||
|
||||
@@ -32,3 +32,13 @@
|
||||
&hsspi {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nand_controller {
|
||||
brcm,wp-not-connected;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nandcs {
|
||||
nand-on-flash-bbt;
|
||||
brcm,nand-ecc-use-strap;
|
||||
};
|
||||
|
||||
@@ -32,3 +32,13 @@
|
||||
&hsspi {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nand_controller {
|
||||
brcm,wp-not-connected;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nandcs {
|
||||
nand-on-flash-bbt;
|
||||
brcm,nand-ecc-use-strap;
|
||||
};
|
||||
|
||||
@@ -32,3 +32,13 @@
|
||||
&hsspi {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nand_controller {
|
||||
brcm,wp-not-connected;
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&nandcs {
|
||||
nand-on-flash-bbt;
|
||||
brcm,nand-ecc-use-strap;
|
||||
};
|
||||
|
||||
@@ -2411,7 +2411,7 @@ static int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
unsigned long timeo = jiffies + HZ;
|
||||
unsigned long timeo;
|
||||
unsigned long int adr;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
int ret;
|
||||
@@ -2512,7 +2512,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
|
||||
static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk)
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
unsigned long timeo = jiffies + HZ;
|
||||
unsigned long timeo;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
int ret;
|
||||
int retry_cnt = 0;
|
||||
|
||||
@@ -341,13 +341,6 @@ config MTD_UCLINUX
|
||||
help
|
||||
Map driver to support image based filesystems for uClinux.
|
||||
|
||||
config MTD_INTEL_VR_NOR
|
||||
tristate "NOR flash on Intel Vermilion Range Expansion Bus CS0"
|
||||
depends on PCI
|
||||
help
|
||||
Map driver for a NOR flash bank located on the Expansion Bus of the
|
||||
Intel Vermilion Range chipset.
|
||||
|
||||
config MTD_PLATRAM
|
||||
tristate "Map driver for platform device RAM (mtd-ram)"
|
||||
select MTD_RAM
|
||||
|
||||
@@ -40,6 +40,5 @@ obj-$(CONFIG_MTD_UCLINUX) += uclinux.o
|
||||
obj-$(CONFIG_MTD_NETtel) += nettel.o
|
||||
obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o
|
||||
obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
|
||||
obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
|
||||
obj-$(CONFIG_MTD_VMU) += vmu-flash.o
|
||||
obj-$(CONFIG_MTD_LANTIQ) += lantiq-flash.o
|
||||
|
||||
@@ -1,265 +0,0 @@
|
||||
/*
|
||||
* drivers/mtd/maps/intel_vr_nor.c
|
||||
*
|
||||
* An MTD map driver for a NOR flash bank on the Expansion Bus of the Intel
|
||||
* Vermilion Range chipset.
|
||||
*
|
||||
* The Vermilion Range Expansion Bus supports four chip selects, each of which
|
||||
* has 64MiB of address space. The 2nd BAR of the Expansion Bus PCI Device
|
||||
* is a 256MiB memory region containing the address spaces for all four of the
|
||||
* chip selects, with start addresses hardcoded on 64MiB boundaries.
|
||||
*
|
||||
* This map driver only supports NOR flash on chip select 0. The buswidth
|
||||
* (either 8 bits or 16 bits) is determined by reading the Expansion Bus Timing
|
||||
* and Control Register for Chip Select 0 (EXP_TIMING_CS0). This driver does
|
||||
* not modify the value in the EXP_TIMING_CS0 register except to enable writing
|
||||
* and disable boot acceleration. The timing parameters in the register are
|
||||
* assumed to have been properly initialized by the BIOS. The reset default
|
||||
* timing parameters are maximally conservative (slow), so access to the flash
|
||||
* will be slower than it should be if the BIOS has not initialized the timing
|
||||
* parameters.
|
||||
*
|
||||
* Author: Andy Lowe <alowe@mvista.com>
|
||||
*
|
||||
* 2006 (c) MontaVista Software, Inc. This file is licensed under
|
||||
* the terms of the GNU General Public License version 2. This program
|
||||
* is licensed "as is" without any warranty of any kind, whether express
|
||||
* or implied.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/cfi.h>
|
||||
#include <linux/mtd/flashchip.h>
|
||||
|
||||
#define DRV_NAME "vr_nor"
|
||||
|
||||
struct vr_nor_mtd {
|
||||
void __iomem *csr_base;
|
||||
struct map_info map;
|
||||
struct mtd_info *info;
|
||||
struct pci_dev *dev;
|
||||
};
|
||||
|
||||
/* Expansion Bus Configuration and Status Registers are in BAR 0 */
|
||||
#define EXP_CSR_MBAR 0
|
||||
/* Expansion Bus Memory Window is BAR 1 */
|
||||
#define EXP_WIN_MBAR 1
|
||||
/* Maximum address space for Chip Select 0 is 64MiB */
|
||||
#define CS0_SIZE 0x04000000
|
||||
/* Chip Select 0 is at offset 0 in the Memory Window */
|
||||
#define CS0_START 0x0
|
||||
/* Chip Select 0 Timing Register is at offset 0 in CSR */
|
||||
#define EXP_TIMING_CS0 0x00
|
||||
#define TIMING_CS_EN (1 << 31) /* Chip Select Enable */
|
||||
#define TIMING_BOOT_ACCEL_DIS (1 << 8) /* Boot Acceleration Disable */
|
||||
#define TIMING_WR_EN (1 << 1) /* Write Enable */
|
||||
#define TIMING_BYTE_EN (1 << 0) /* 8-bit vs 16-bit bus */
|
||||
#define TIMING_MASK 0x3FFF0000
|
||||
|
||||
static void vr_nor_destroy_partitions(struct vr_nor_mtd *p)
|
||||
{
|
||||
mtd_device_unregister(p->info);
|
||||
}
|
||||
|
||||
static int vr_nor_init_partitions(struct vr_nor_mtd *p)
|
||||
{
|
||||
/* register the flash bank */
|
||||
/* partition the flash bank */
|
||||
return mtd_device_register(p->info, NULL, 0);
|
||||
}
|
||||
|
||||
static void vr_nor_destroy_mtd_setup(struct vr_nor_mtd *p)
|
||||
{
|
||||
map_destroy(p->info);
|
||||
}
|
||||
|
||||
static int vr_nor_mtd_setup(struct vr_nor_mtd *p)
|
||||
{
|
||||
static const char * const probe_types[] =
|
||||
{ "cfi_probe", "jedec_probe", NULL };
|
||||
const char * const *type;
|
||||
|
||||
for (type = probe_types; !p->info && *type; type++)
|
||||
p->info = do_map_probe(*type, &p->map);
|
||||
if (!p->info)
|
||||
return -ENODEV;
|
||||
|
||||
p->info->dev.parent = &p->dev->dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vr_nor_destroy_maps(struct vr_nor_mtd *p)
|
||||
{
|
||||
unsigned int exp_timing_cs0;
|
||||
|
||||
/* write-protect the flash bank */
|
||||
exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
|
||||
exp_timing_cs0 &= ~TIMING_WR_EN;
|
||||
writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
|
||||
|
||||
/* unmap the flash window */
|
||||
iounmap(p->map.virt);
|
||||
|
||||
/* unmap the csr window */
|
||||
iounmap(p->csr_base);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the map_info structure and map the flash.
|
||||
* Returns 0 on success, nonzero otherwise.
|
||||
*/
|
||||
static int vr_nor_init_maps(struct vr_nor_mtd *p)
|
||||
{
|
||||
unsigned long csr_phys, csr_len;
|
||||
unsigned long win_phys, win_len;
|
||||
unsigned int exp_timing_cs0;
|
||||
int err;
|
||||
|
||||
csr_phys = pci_resource_start(p->dev, EXP_CSR_MBAR);
|
||||
csr_len = pci_resource_len(p->dev, EXP_CSR_MBAR);
|
||||
win_phys = pci_resource_start(p->dev, EXP_WIN_MBAR);
|
||||
win_len = pci_resource_len(p->dev, EXP_WIN_MBAR);
|
||||
|
||||
if (!csr_phys || !csr_len || !win_phys || !win_len)
|
||||
return -ENODEV;
|
||||
|
||||
if (win_len < (CS0_START + CS0_SIZE))
|
||||
return -ENXIO;
|
||||
|
||||
p->csr_base = ioremap(csr_phys, csr_len);
|
||||
if (!p->csr_base)
|
||||
return -ENOMEM;
|
||||
|
||||
exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
|
||||
if (!(exp_timing_cs0 & TIMING_CS_EN)) {
|
||||
dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 "
|
||||
"is disabled.\n");
|
||||
err = -ENODEV;
|
||||
goto release;
|
||||
}
|
||||
if ((exp_timing_cs0 & TIMING_MASK) == TIMING_MASK) {
|
||||
dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 "
|
||||
"is configured for maximally slow access times.\n");
|
||||
}
|
||||
p->map.name = DRV_NAME;
|
||||
p->map.bankwidth = (exp_timing_cs0 & TIMING_BYTE_EN) ? 1 : 2;
|
||||
p->map.phys = win_phys + CS0_START;
|
||||
p->map.size = CS0_SIZE;
|
||||
p->map.virt = ioremap(p->map.phys, p->map.size);
|
||||
if (!p->map.virt) {
|
||||
err = -ENOMEM;
|
||||
goto release;
|
||||
}
|
||||
simple_map_init(&p->map);
|
||||
|
||||
/* Enable writes to flash bank */
|
||||
exp_timing_cs0 |= TIMING_BOOT_ACCEL_DIS | TIMING_WR_EN;
|
||||
writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
|
||||
|
||||
return 0;
|
||||
|
||||
release:
|
||||
iounmap(p->csr_base);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct pci_device_id vr_nor_pci_ids[] = {
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x500D)},
|
||||
{0,}
|
||||
};
|
||||
|
||||
static void vr_nor_pci_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct vr_nor_mtd *p = pci_get_drvdata(dev);
|
||||
|
||||
vr_nor_destroy_partitions(p);
|
||||
vr_nor_destroy_mtd_setup(p);
|
||||
vr_nor_destroy_maps(p);
|
||||
kfree(p);
|
||||
pci_release_regions(dev);
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
|
||||
static int vr_nor_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
struct vr_nor_mtd *p = NULL;
|
||||
unsigned int exp_timing_cs0;
|
||||
int err;
|
||||
|
||||
err = pci_enable_device(dev);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = pci_request_regions(dev, DRV_NAME);
|
||||
if (err)
|
||||
goto disable_dev;
|
||||
|
||||
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||
err = -ENOMEM;
|
||||
if (!p)
|
||||
goto release;
|
||||
|
||||
p->dev = dev;
|
||||
|
||||
err = vr_nor_init_maps(p);
|
||||
if (err)
|
||||
goto release;
|
||||
|
||||
err = vr_nor_mtd_setup(p);
|
||||
if (err)
|
||||
goto destroy_maps;
|
||||
|
||||
err = vr_nor_init_partitions(p);
|
||||
if (err)
|
||||
goto destroy_mtd_setup;
|
||||
|
||||
pci_set_drvdata(dev, p);
|
||||
|
||||
return 0;
|
||||
|
||||
destroy_mtd_setup:
|
||||
map_destroy(p->info);
|
||||
|
||||
destroy_maps:
|
||||
/* write-protect the flash bank */
|
||||
exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
|
||||
exp_timing_cs0 &= ~TIMING_WR_EN;
|
||||
writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
|
||||
|
||||
/* unmap the flash window */
|
||||
iounmap(p->map.virt);
|
||||
|
||||
/* unmap the csr window */
|
||||
iounmap(p->csr_base);
|
||||
|
||||
release:
|
||||
kfree(p);
|
||||
pci_release_regions(dev);
|
||||
|
||||
disable_dev:
|
||||
pci_disable_device(dev);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct pci_driver vr_nor_pci_driver = {
|
||||
.name = DRV_NAME,
|
||||
.probe = vr_nor_pci_probe,
|
||||
.remove = vr_nor_pci_remove,
|
||||
.id_table = vr_nor_pci_ids,
|
||||
};
|
||||
|
||||
module_pci_driver(vr_nor_pci_driver);
|
||||
|
||||
MODULE_AUTHOR("Andy Lowe");
|
||||
MODULE_DESCRIPTION("MTD map driver for NOR flash on Intel Vermilion Range");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DEVICE_TABLE(pci, vr_nor_pci_ids);
|
||||
@@ -518,7 +518,7 @@ static int physmap_flash_probe(struct platform_device *dev)
|
||||
if (!info->maps[i].phys)
|
||||
info->maps[i].phys = res->start;
|
||||
|
||||
info->win_order = get_bitmask_order(resource_size(res)) - 1;
|
||||
info->win_order = fls64(resource_size(res)) - 1;
|
||||
info->maps[i].size = BIT(info->win_order +
|
||||
(info->gpios ?
|
||||
info->gpios->ndescs : 0));
|
||||
|
||||
@@ -1378,7 +1378,7 @@ static int atmel_smc_nand_prepare_smcconf(struct atmel_nand *nand,
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* The write cycle timing is directly matching tWC, but is also
|
||||
* The read cycle timing is directly matching tRC, but is also
|
||||
* dependent on the setup and hold timings we calculated earlier,
|
||||
* which gives:
|
||||
*
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# link order matters; don't link the more generic brcmstb_nand.o before the
|
||||
# more specific iproc_nand.o, for instance
|
||||
obj-$(CONFIG_MTD_NAND_BRCMNAND_IPROC) += iproc_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_BRCMNAND_BCMBCA) += bcm63138_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_BRCMNAND_BCMBCA) += bcmbca_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_BRCMNAND_BCM63XX) += bcm6368_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_BRCMNAND_BRCMSTB) += brcmstb_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_BRCMNAND) += brcmnand.o
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright © 2015 Broadcom Corporation
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "brcmnand.h"
|
||||
|
||||
struct bcm63138_nand_soc {
|
||||
struct brcmnand_soc soc;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
#define BCM63138_NAND_INT_STATUS 0x00
|
||||
#define BCM63138_NAND_INT_EN 0x04
|
||||
|
||||
enum {
|
||||
BCM63138_CTLRDY = BIT(4),
|
||||
};
|
||||
|
||||
static bool bcm63138_nand_intc_ack(struct brcmnand_soc *soc)
|
||||
{
|
||||
struct bcm63138_nand_soc *priv =
|
||||
container_of(soc, struct bcm63138_nand_soc, soc);
|
||||
void __iomem *mmio = priv->base + BCM63138_NAND_INT_STATUS;
|
||||
u32 val = brcmnand_readl(mmio);
|
||||
|
||||
if (val & BCM63138_CTLRDY) {
|
||||
brcmnand_writel(val & ~BCM63138_CTLRDY, mmio);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void bcm63138_nand_intc_set(struct brcmnand_soc *soc, bool en)
|
||||
{
|
||||
struct bcm63138_nand_soc *priv =
|
||||
container_of(soc, struct bcm63138_nand_soc, soc);
|
||||
void __iomem *mmio = priv->base + BCM63138_NAND_INT_EN;
|
||||
u32 val = brcmnand_readl(mmio);
|
||||
|
||||
if (en)
|
||||
val |= BCM63138_CTLRDY;
|
||||
else
|
||||
val &= ~BCM63138_CTLRDY;
|
||||
|
||||
brcmnand_writel(val, mmio);
|
||||
}
|
||||
|
||||
static int bcm63138_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct bcm63138_nand_soc *priv;
|
||||
struct brcmnand_soc *soc;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
soc = &priv->soc;
|
||||
|
||||
priv->base = devm_platform_ioremap_resource_byname(pdev, "nand-int-base");
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
soc->ctlrdy_ack = bcm63138_nand_intc_ack;
|
||||
soc->ctlrdy_set_enabled = bcm63138_nand_intc_set;
|
||||
|
||||
return brcmnand_probe(pdev, soc);
|
||||
}
|
||||
|
||||
static const struct of_device_id bcm63138_nand_of_match[] = {
|
||||
{ .compatible = "brcm,nand-bcm63138" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bcm63138_nand_of_match);
|
||||
|
||||
static struct platform_driver bcm63138_nand_driver = {
|
||||
.probe = bcm63138_nand_probe,
|
||||
.remove_new = brcmnand_remove,
|
||||
.driver = {
|
||||
.name = "bcm63138_nand",
|
||||
.pm = &brcmnand_pm_ops,
|
||||
.of_match_table = bcm63138_nand_of_match,
|
||||
}
|
||||
};
|
||||
module_platform_driver(bcm63138_nand_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Brian Norris");
|
||||
MODULE_DESCRIPTION("NAND driver for BCM63138");
|
||||
@@ -0,0 +1,126 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright © 2015 Broadcom Corporation
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "brcmnand.h"
|
||||
|
||||
struct bcmbca_nand_soc {
|
||||
struct brcmnand_soc soc;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
#define BCMBCA_NAND_INT_STATUS 0x00
|
||||
#define BCMBCA_NAND_INT_EN 0x04
|
||||
|
||||
enum {
|
||||
BCMBCA_CTLRDY = BIT(4),
|
||||
};
|
||||
|
||||
#if defined(CONFIG_ARM64)
|
||||
#define ALIGN_REQ 8
|
||||
#else
|
||||
#define ALIGN_REQ 4
|
||||
#endif
|
||||
|
||||
static inline bool bcmbca_nand_is_buf_aligned(void *flash_cache, void *buffer)
|
||||
{
|
||||
return IS_ALIGNED((uintptr_t)buffer, ALIGN_REQ) &&
|
||||
IS_ALIGNED((uintptr_t)flash_cache, ALIGN_REQ);
|
||||
}
|
||||
|
||||
static bool bcmbca_nand_intc_ack(struct brcmnand_soc *soc)
|
||||
{
|
||||
struct bcmbca_nand_soc *priv =
|
||||
container_of(soc, struct bcmbca_nand_soc, soc);
|
||||
void __iomem *mmio = priv->base + BCMBCA_NAND_INT_STATUS;
|
||||
u32 val = brcmnand_readl(mmio);
|
||||
|
||||
if (val & BCMBCA_CTLRDY) {
|
||||
brcmnand_writel(val & ~BCMBCA_CTLRDY, mmio);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void bcmbca_nand_intc_set(struct brcmnand_soc *soc, bool en)
|
||||
{
|
||||
struct bcmbca_nand_soc *priv =
|
||||
container_of(soc, struct bcmbca_nand_soc, soc);
|
||||
void __iomem *mmio = priv->base + BCMBCA_NAND_INT_EN;
|
||||
u32 val = brcmnand_readl(mmio);
|
||||
|
||||
if (en)
|
||||
val |= BCMBCA_CTLRDY;
|
||||
else
|
||||
val &= ~BCMBCA_CTLRDY;
|
||||
|
||||
brcmnand_writel(val, mmio);
|
||||
}
|
||||
|
||||
static void bcmbca_read_data_bus(struct brcmnand_soc *soc,
|
||||
void __iomem *flash_cache, u32 *buffer, int fc_words)
|
||||
{
|
||||
/*
|
||||
* memcpy can do unaligned aligned access depending on source
|
||||
* and dest address, which is incompatible with nand cache. Fallback
|
||||
* to the memcpy_fromio in such case
|
||||
*/
|
||||
if (bcmbca_nand_is_buf_aligned((void __force *)flash_cache, buffer))
|
||||
memcpy((void *)buffer, (void __force *)flash_cache, fc_words * 4);
|
||||
else
|
||||
memcpy_fromio((void *)buffer, flash_cache, fc_words * 4);
|
||||
}
|
||||
|
||||
static int bcmbca_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct bcmbca_nand_soc *priv;
|
||||
struct brcmnand_soc *soc;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
soc = &priv->soc;
|
||||
|
||||
priv->base = devm_platform_ioremap_resource_byname(pdev, "nand-int-base");
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
soc->ctlrdy_ack = bcmbca_nand_intc_ack;
|
||||
soc->ctlrdy_set_enabled = bcmbca_nand_intc_set;
|
||||
soc->read_data_bus = bcmbca_read_data_bus;
|
||||
|
||||
return brcmnand_probe(pdev, soc);
|
||||
}
|
||||
|
||||
static const struct of_device_id bcmbca_nand_of_match[] = {
|
||||
{ .compatible = "brcm,nand-bcm63138" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bcmbca_nand_of_match);
|
||||
|
||||
static struct platform_driver bcmbca_nand_driver = {
|
||||
.probe = bcmbca_nand_probe,
|
||||
.remove_new = brcmnand_remove,
|
||||
.driver = {
|
||||
.name = "bcmbca_nand",
|
||||
.pm = &brcmnand_pm_ops,
|
||||
.of_match_table = bcmbca_nand_of_match,
|
||||
}
|
||||
};
|
||||
module_platform_driver(bcmbca_nand_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Brian Norris");
|
||||
MODULE_DESCRIPTION("NAND driver for BCMBCA");
|
||||
@@ -625,7 +625,7 @@ enum {
|
||||
/* Only for v7.2 */
|
||||
#define ACC_CONTROL_ECC_EXT_SHIFT 13
|
||||
|
||||
static u8 brcmnand_status(struct brcmnand_host *host);
|
||||
static int brcmnand_status(struct brcmnand_host *host);
|
||||
|
||||
static inline bool brcmnand_non_mmio_ops(struct brcmnand_controller *ctrl)
|
||||
{
|
||||
@@ -851,6 +851,20 @@ static inline u32 edu_readl(struct brcmnand_controller *ctrl,
|
||||
return brcmnand_readl(ctrl->edu_base + offs);
|
||||
}
|
||||
|
||||
static inline void brcmnand_read_data_bus(struct brcmnand_controller *ctrl,
|
||||
void __iomem *flash_cache, u32 *buffer, int fc_words)
|
||||
{
|
||||
struct brcmnand_soc *soc = ctrl->soc;
|
||||
int i;
|
||||
|
||||
if (soc->read_data_bus) {
|
||||
soc->read_data_bus(soc, flash_cache, buffer, fc_words);
|
||||
} else {
|
||||
for (i = 0; i < fc_words; i++)
|
||||
buffer[i] = brcmnand_read_fc(ctrl, i);
|
||||
}
|
||||
}
|
||||
|
||||
static void brcmnand_clear_ecc_addr(struct brcmnand_controller *ctrl)
|
||||
{
|
||||
|
||||
@@ -1024,6 +1038,22 @@ static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool brcmnand_get_sector_size_1k(struct brcmnand_host *host)
|
||||
{
|
||||
struct brcmnand_controller *ctrl = host->ctrl;
|
||||
int sector_size_bit = brcmnand_sector_1k_shift(ctrl);
|
||||
u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
|
||||
BRCMNAND_CS_ACC_CONTROL);
|
||||
u32 acc_control;
|
||||
|
||||
if (sector_size_bit < 0)
|
||||
return false;
|
||||
|
||||
acc_control = nand_readreg(ctrl, acc_control_offs);
|
||||
|
||||
return ((acc_control & BIT(sector_size_bit)) != 0);
|
||||
}
|
||||
|
||||
static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val)
|
||||
{
|
||||
struct brcmnand_controller *ctrl = host->ctrl;
|
||||
@@ -1041,6 +1071,43 @@ static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val)
|
||||
nand_writereg(ctrl, acc_control_offs, tmp);
|
||||
}
|
||||
|
||||
static int brcmnand_get_spare_size(struct brcmnand_host *host)
|
||||
{
|
||||
struct brcmnand_controller *ctrl = host->ctrl;
|
||||
u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
|
||||
BRCMNAND_CS_ACC_CONTROL);
|
||||
u32 acc = nand_readreg(ctrl, acc_control_offs);
|
||||
|
||||
return (acc & brcmnand_spare_area_mask(ctrl));
|
||||
}
|
||||
|
||||
static void brcmnand_get_ecc_settings(struct brcmnand_host *host, struct nand_chip *chip)
|
||||
{
|
||||
struct brcmnand_controller *ctrl = host->ctrl;
|
||||
u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
|
||||
BRCMNAND_CS_ACC_CONTROL);
|
||||
bool sector_size_1k = brcmnand_get_sector_size_1k(host);
|
||||
int spare_area_size, ecc_level;
|
||||
u32 acc;
|
||||
|
||||
spare_area_size = brcmnand_get_spare_size(host);
|
||||
acc = nand_readreg(ctrl, acc_control_offs);
|
||||
ecc_level = (acc & brcmnand_ecc_level_mask(ctrl)) >> ctrl->ecc_level_shift;
|
||||
if (sector_size_1k)
|
||||
chip->ecc.strength = ecc_level * 2;
|
||||
else if (spare_area_size == 16 && ecc_level == 15)
|
||||
chip->ecc.strength = 1; /* hamming */
|
||||
else
|
||||
chip->ecc.strength = ecc_level;
|
||||
|
||||
if (chip->ecc.size == 0) {
|
||||
if (sector_size_1k)
|
||||
chip->ecc.size = 1024;
|
||||
else
|
||||
chip->ecc.size = 512;
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* CS_NAND_SELECT
|
||||
***********************************************************************/
|
||||
@@ -1084,8 +1151,8 @@ static int bcmnand_ctrl_poll_status(struct brcmnand_host *host,
|
||||
if ((val & mask) == expected_val)
|
||||
return 0;
|
||||
|
||||
dev_warn(ctrl->dev, "timeout on status poll (expected %x got %x)\n",
|
||||
expected_val, val & mask);
|
||||
dev_err(ctrl->dev, "timeout on status poll (expected %x got %x)\n",
|
||||
expected_val, val & mask);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
@@ -1690,7 +1757,7 @@ static int brcmnand_waitfunc(struct nand_chip *chip)
|
||||
INTFC_FLASH_STATUS;
|
||||
}
|
||||
|
||||
static u8 brcmnand_status(struct brcmnand_host *host)
|
||||
static int brcmnand_status(struct brcmnand_host *host)
|
||||
{
|
||||
struct nand_chip *chip = &host->chip;
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
@@ -1701,7 +1768,7 @@ static u8 brcmnand_status(struct brcmnand_host *host)
|
||||
return brcmnand_waitfunc(chip);
|
||||
}
|
||||
|
||||
static u8 brcmnand_reset(struct brcmnand_host *host)
|
||||
static int brcmnand_reset(struct brcmnand_host *host)
|
||||
{
|
||||
struct nand_chip *chip = &host->chip;
|
||||
|
||||
@@ -1975,7 +2042,7 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
{
|
||||
struct brcmnand_host *host = nand_get_controller_data(chip);
|
||||
struct brcmnand_controller *ctrl = host->ctrl;
|
||||
int i, j, ret = 0;
|
||||
int i, ret = 0;
|
||||
|
||||
brcmnand_clear_ecc_addr(ctrl);
|
||||
|
||||
@@ -1988,8 +2055,8 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
|
||||
if (likely(buf)) {
|
||||
brcmnand_soc_data_bus_prepare(ctrl->soc, false);
|
||||
|
||||
for (j = 0; j < FC_WORDS; j++, buf++)
|
||||
*buf = brcmnand_read_fc(ctrl, j);
|
||||
brcmnand_read_data_bus(ctrl, ctrl->nand_fc, buf, FC_WORDS);
|
||||
buf += FC_WORDS;
|
||||
|
||||
brcmnand_soc_data_bus_unprepare(ctrl->soc, false);
|
||||
}
|
||||
@@ -2137,7 +2204,7 @@ try_dmaread:
|
||||
return err;
|
||||
}
|
||||
|
||||
dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n",
|
||||
dev_err(ctrl->dev, "uncorrectable error at 0x%llx\n",
|
||||
(unsigned long long)err_addr);
|
||||
mtd->ecc_stats.failed++;
|
||||
/* NAND layer expects zero on ECC errors */
|
||||
@@ -2339,7 +2406,7 @@ static int brcmnand_write_oob_raw(struct nand_chip *chip, int page)
|
||||
}
|
||||
|
||||
static int brcmnand_exec_instr(struct brcmnand_host *host, int i,
|
||||
const struct nand_operation *op)
|
||||
const struct nand_operation *op)
|
||||
{
|
||||
const struct nand_op_instr *instr = &op->instrs[i];
|
||||
struct brcmnand_controller *ctrl = host->ctrl;
|
||||
@@ -2353,7 +2420,7 @@ static int brcmnand_exec_instr(struct brcmnand_host *host, int i,
|
||||
* (WAITRDY excepted).
|
||||
*/
|
||||
last_op = ((i == (op->ninstrs - 1)) && (instr->type != NAND_OP_WAITRDY_INSTR)) ||
|
||||
((i == (op->ninstrs - 2)) && (op->instrs[i+1].type == NAND_OP_WAITRDY_INSTR));
|
||||
((i == (op->ninstrs - 2)) && (op->instrs[i + 1].type == NAND_OP_WAITRDY_INSTR));
|
||||
|
||||
switch (instr->type) {
|
||||
case NAND_OP_CMD_INSTR:
|
||||
@@ -2398,10 +2465,10 @@ static int brcmnand_exec_instr(struct brcmnand_host *host, int i,
|
||||
|
||||
static int brcmnand_op_is_status(const struct nand_operation *op)
|
||||
{
|
||||
if ((op->ninstrs == 2) &&
|
||||
(op->instrs[0].type == NAND_OP_CMD_INSTR) &&
|
||||
(op->instrs[0].ctx.cmd.opcode == NAND_CMD_STATUS) &&
|
||||
(op->instrs[1].type == NAND_OP_DATA_IN_INSTR))
|
||||
if (op->ninstrs == 2 &&
|
||||
op->instrs[0].type == NAND_OP_CMD_INSTR &&
|
||||
op->instrs[0].ctx.cmd.opcode == NAND_CMD_STATUS &&
|
||||
op->instrs[1].type == NAND_OP_DATA_IN_INSTR)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
@@ -2409,10 +2476,10 @@ static int brcmnand_op_is_status(const struct nand_operation *op)
|
||||
|
||||
static int brcmnand_op_is_reset(const struct nand_operation *op)
|
||||
{
|
||||
if ((op->ninstrs == 2) &&
|
||||
(op->instrs[0].type == NAND_OP_CMD_INSTR) &&
|
||||
(op->instrs[0].ctx.cmd.opcode == NAND_CMD_RESET) &&
|
||||
(op->instrs[1].type == NAND_OP_WAITRDY_INSTR))
|
||||
if (op->ninstrs == 2 &&
|
||||
op->instrs[0].type == NAND_OP_CMD_INSTR &&
|
||||
op->instrs[0].ctx.cmd.opcode == NAND_CMD_RESET &&
|
||||
op->instrs[1].type == NAND_OP_WAITRDY_INSTR)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
@@ -2433,11 +2500,14 @@ static int brcmnand_exec_op(struct nand_chip *chip,
|
||||
|
||||
if (brcmnand_op_is_status(op)) {
|
||||
status = op->instrs[1].ctx.data.buf.in;
|
||||
*status = brcmnand_status(host);
|
||||
ret = brcmnand_status(host);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*status = ret & 0xFF;
|
||||
|
||||
return 0;
|
||||
}
|
||||
else if (brcmnand_op_is_reset(op)) {
|
||||
} else if (brcmnand_op_is_reset(op)) {
|
||||
ret = brcmnand_reset(host);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@@ -2608,19 +2678,37 @@ static int brcmnand_setup_dev(struct brcmnand_host *host)
|
||||
nanddev_get_memorg(&chip->base);
|
||||
struct brcmnand_controller *ctrl = host->ctrl;
|
||||
struct brcmnand_cfg *cfg = &host->hwcfg;
|
||||
char msg[128];
|
||||
struct device_node *np = nand_get_flash_node(chip);
|
||||
u32 offs, tmp, oob_sector;
|
||||
bool use_strap = false;
|
||||
char msg[128];
|
||||
int ret;
|
||||
|
||||
memset(cfg, 0, sizeof(*cfg));
|
||||
use_strap = of_property_read_bool(np, "brcm,nand-ecc-use-strap");
|
||||
|
||||
ret = of_property_read_u32(nand_get_flash_node(chip),
|
||||
"brcm,nand-oob-sector-size",
|
||||
/*
|
||||
* Either nand-ecc-xxx or brcm,nand-ecc-use-strap can be set. Error out
|
||||
* if both exist.
|
||||
*/
|
||||
if (chip->ecc.strength && use_strap) {
|
||||
dev_err(ctrl->dev,
|
||||
"ECC strap and DT ECC configuration properties are mutually exclusive\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (use_strap)
|
||||
brcmnand_get_ecc_settings(host, chip);
|
||||
|
||||
ret = of_property_read_u32(np, "brcm,nand-oob-sector-size",
|
||||
&oob_sector);
|
||||
if (ret) {
|
||||
/* Use detected size */
|
||||
cfg->spare_area_size = mtd->oobsize /
|
||||
(mtd->writesize >> FC_SHIFT);
|
||||
if (use_strap)
|
||||
cfg->spare_area_size = brcmnand_get_spare_size(host);
|
||||
else
|
||||
/* Use detected size */
|
||||
cfg->spare_area_size = mtd->oobsize /
|
||||
(mtd->writesize >> FC_SHIFT);
|
||||
} else {
|
||||
cfg->spare_area_size = oob_sector;
|
||||
}
|
||||
@@ -3135,6 +3223,10 @@ int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
|
||||
/* Disable XOR addressing */
|
||||
brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0);
|
||||
|
||||
/* Check if the board connects the WP pin */
|
||||
if (of_property_read_bool(dn, "brcm,wp-not-connected"))
|
||||
wp_on = 0;
|
||||
|
||||
if (ctrl->features & BRCMNAND_HAS_WP) {
|
||||
/* Permanently disable write protection */
|
||||
if (wp_on == 2)
|
||||
|
||||
@@ -24,6 +24,8 @@ struct brcmnand_soc {
|
||||
void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en);
|
||||
void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare,
|
||||
bool is_param);
|
||||
void (*read_data_bus)(struct brcmnand_soc *soc, void __iomem *flash_cache,
|
||||
u32 *buffer, int fc_words);
|
||||
const struct brcmnand_io_ops *ops;
|
||||
};
|
||||
|
||||
|
||||
@@ -869,7 +869,8 @@ static int fsl_elbc_nand_probe(struct platform_device *pdev)
|
||||
struct mtd_info *mtd;
|
||||
|
||||
if (!fsl_lbc_ctrl_dev || !fsl_lbc_ctrl_dev->regs)
|
||||
return -ENODEV;
|
||||
return dev_err_probe(&pdev->dev, -EPROBE_DEFER, "lbc_ctrl_dev missing\n");
|
||||
|
||||
lbc = fsl_lbc_ctrl_dev->regs;
|
||||
dev = fsl_lbc_ctrl_dev->dev;
|
||||
|
||||
|
||||
@@ -303,8 +303,9 @@ static int lpc32xx_nand_device_ready(struct nand_chip *nand_chip)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t lpc3xxx_nand_irq(int irq, struct lpc32xx_nand_host *host)
|
||||
static irqreturn_t lpc3xxx_nand_irq(int irq, void *data)
|
||||
{
|
||||
struct lpc32xx_nand_host *host = data;
|
||||
uint8_t sr;
|
||||
|
||||
/* Clear interrupt flag by reading status */
|
||||
@@ -780,7 +781,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
|
||||
goto release_dma_chan;
|
||||
}
|
||||
|
||||
if (request_irq(host->irq, (irq_handler_t)&lpc3xxx_nand_irq,
|
||||
if (request_irq(host->irq, &lpc3xxx_nand_irq,
|
||||
IRQF_TRIGGER_HIGH, DRV_NAME, host)) {
|
||||
dev_err(&pdev->dev, "Error requesting NAND IRQ\n");
|
||||
res = -ENXIO;
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
#define CMDRWGEN(cmd_dir, ran, bch, short_mode, page_size, pages) \
|
||||
( \
|
||||
(cmd_dir) | \
|
||||
((ran) << 19) | \
|
||||
(ran) | \
|
||||
((bch) << 14) | \
|
||||
((short_mode) << 13) | \
|
||||
(((page_size) & 0x7f) << 6) | \
|
||||
|
||||
@@ -1356,7 +1356,7 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chip = devm_kzalloc(dev, sizeof(*chip) + nsels * sizeof(u8),
|
||||
chip = devm_kzalloc(dev, struct_size(chip, sels, nsels),
|
||||
GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -1211,21 +1211,36 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
|
||||
return nand_exec_op(chip, &op);
|
||||
}
|
||||
|
||||
static unsigned int rawnand_last_page_of_lun(unsigned int pages_per_lun, unsigned int lun)
|
||||
{
|
||||
/* lun is expected to be very small */
|
||||
return (lun * pages_per_lun) + pages_per_lun - 1;
|
||||
}
|
||||
|
||||
static void rawnand_cap_cont_reads(struct nand_chip *chip)
|
||||
{
|
||||
struct nand_memory_organization *memorg;
|
||||
unsigned int pages_per_lun, first_lun, last_lun;
|
||||
unsigned int ppl, first_lun, last_lun;
|
||||
|
||||
memorg = nanddev_get_memorg(&chip->base);
|
||||
pages_per_lun = memorg->pages_per_eraseblock * memorg->eraseblocks_per_lun;
|
||||
first_lun = chip->cont_read.first_page / pages_per_lun;
|
||||
last_lun = chip->cont_read.last_page / pages_per_lun;
|
||||
ppl = memorg->pages_per_eraseblock * memorg->eraseblocks_per_lun;
|
||||
first_lun = chip->cont_read.first_page / ppl;
|
||||
last_lun = chip->cont_read.last_page / ppl;
|
||||
|
||||
/* Prevent sequential cache reads across LUN boundaries */
|
||||
if (first_lun != last_lun)
|
||||
chip->cont_read.pause_page = first_lun * pages_per_lun + pages_per_lun - 1;
|
||||
chip->cont_read.pause_page = rawnand_last_page_of_lun(ppl, first_lun);
|
||||
else
|
||||
chip->cont_read.pause_page = chip->cont_read.last_page;
|
||||
|
||||
if (chip->cont_read.first_page == chip->cont_read.pause_page) {
|
||||
chip->cont_read.first_page++;
|
||||
chip->cont_read.pause_page = min(chip->cont_read.last_page,
|
||||
rawnand_last_page_of_lun(ppl, first_lun + 1));
|
||||
}
|
||||
|
||||
if (chip->cont_read.first_page >= chip->cont_read.last_page)
|
||||
chip->cont_read.ongoing = false;
|
||||
}
|
||||
|
||||
static int nand_lp_exec_cont_read_page_op(struct nand_chip *chip, unsigned int page,
|
||||
@@ -1292,12 +1307,11 @@ static int nand_lp_exec_cont_read_page_op(struct nand_chip *chip, unsigned int p
|
||||
if (!chip->cont_read.ongoing)
|
||||
return 0;
|
||||
|
||||
if (page == chip->cont_read.pause_page &&
|
||||
page != chip->cont_read.last_page) {
|
||||
chip->cont_read.first_page = chip->cont_read.pause_page + 1;
|
||||
rawnand_cap_cont_reads(chip);
|
||||
} else if (page == chip->cont_read.last_page) {
|
||||
if (page == chip->cont_read.last_page) {
|
||||
chip->cont_read.ongoing = false;
|
||||
} else if (page == chip->cont_read.pause_page) {
|
||||
chip->cont_read.first_page++;
|
||||
rawnand_cap_cont_reads(chip);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -3466,30 +3480,36 @@ static void rawnand_enable_cont_reads(struct nand_chip *chip, unsigned int page,
|
||||
u32 readlen, int col)
|
||||
{
|
||||
struct mtd_info *mtd = nand_to_mtd(chip);
|
||||
unsigned int end_page, end_col;
|
||||
unsigned int first_page, last_page;
|
||||
|
||||
chip->cont_read.ongoing = false;
|
||||
|
||||
if (!chip->controller->supported_op.cont_read)
|
||||
return;
|
||||
|
||||
end_page = DIV_ROUND_UP(col + readlen, mtd->writesize);
|
||||
end_col = (col + readlen) % mtd->writesize;
|
||||
|
||||
if (col)
|
||||
page++;
|
||||
|
||||
if (end_col && end_page)
|
||||
end_page--;
|
||||
|
||||
if (page + 1 > end_page)
|
||||
/*
|
||||
* Don't bother making any calculations if the length is too small.
|
||||
* Side effect: avoids possible integer underflows below.
|
||||
*/
|
||||
if (readlen < (2 * mtd->writesize))
|
||||
return;
|
||||
|
||||
chip->cont_read.first_page = page;
|
||||
chip->cont_read.last_page = end_page;
|
||||
chip->cont_read.ongoing = true;
|
||||
/* Derive the page where continuous read should start (the first full page read) */
|
||||
first_page = page;
|
||||
if (col)
|
||||
first_page++;
|
||||
|
||||
rawnand_cap_cont_reads(chip);
|
||||
/* Derive the page where continuous read should stop (the last full page read) */
|
||||
last_page = page + ((col + readlen) / mtd->writesize) - 1;
|
||||
|
||||
/* Configure and enable continuous read when suitable */
|
||||
if (first_page < last_page) {
|
||||
chip->cont_read.first_page = first_page;
|
||||
chip->cont_read.last_page = last_page;
|
||||
chip->cont_read.ongoing = true;
|
||||
/* May reset the ongoing flag */
|
||||
rawnand_cap_cont_reads(chip);
|
||||
}
|
||||
}
|
||||
|
||||
static void rawnand_cont_read_skip_first_page(struct nand_chip *chip, unsigned int page)
|
||||
@@ -3498,10 +3518,7 @@ static void rawnand_cont_read_skip_first_page(struct nand_chip *chip, unsigned i
|
||||
return;
|
||||
|
||||
chip->cont_read.first_page++;
|
||||
if (chip->cont_read.first_page == chip->cont_read.pause_page)
|
||||
chip->cont_read.first_page++;
|
||||
if (chip->cont_read.first_page >= chip->cont_read.last_page)
|
||||
chip->cont_read.ongoing = false;
|
||||
rawnand_cap_cont_reads(chip);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3577,7 +3594,8 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
|
||||
oob = ops->oobbuf;
|
||||
oob_required = oob ? 1 : 0;
|
||||
|
||||
rawnand_enable_cont_reads(chip, page, readlen, col);
|
||||
if (likely(ops->mode != MTD_OPS_RAW))
|
||||
rawnand_enable_cont_reads(chip, page, readlen, col);
|
||||
|
||||
while (1) {
|
||||
struct mtd_ecc_stats ecc_stats = mtd->ecc_stats;
|
||||
@@ -3710,6 +3728,9 @@ read_retry:
|
||||
}
|
||||
nand_deselect_target(chip);
|
||||
|
||||
if (WARN_ON_ONCE(chip->cont_read.ongoing))
|
||||
chip->cont_read.ongoing = false;
|
||||
|
||||
ops->retlen = ops->len - (size_t) readlen;
|
||||
if (oob)
|
||||
ops->oobretlen = ops->ooblen - oobreadlen;
|
||||
@@ -5195,6 +5216,15 @@ static void rawnand_late_check_supported_ops(struct nand_chip *chip)
|
||||
if (!nand_has_exec_op(chip))
|
||||
return;
|
||||
|
||||
/*
|
||||
* For now, continuous reads can only be used with the core page helpers.
|
||||
* This can be extended later.
|
||||
*/
|
||||
if (!(chip->ecc.read_page == nand_read_page_hwecc ||
|
||||
chip->ecc.read_page == nand_read_page_syndrome ||
|
||||
chip->ecc.read_page == nand_read_page_swecc))
|
||||
return;
|
||||
|
||||
rawnand_check_cont_read_support(chip);
|
||||
}
|
||||
|
||||
|
||||
@@ -576,7 +576,6 @@ static int search_bbt(struct nand_chip *this, uint8_t *buf,
|
||||
startblock &= bbtblocks - 1;
|
||||
} else {
|
||||
chips = 1;
|
||||
bbtblocks = mtd->size >> this->bbt_erase_shift;
|
||||
}
|
||||
|
||||
for (i = 0; i < chips; i++) {
|
||||
|
||||
@@ -31,7 +31,6 @@ struct hynix_read_retry {
|
||||
|
||||
/**
|
||||
* struct hynix_nand - private Hynix NAND struct
|
||||
* @nand_technology: manufacturing process expressed in picometer
|
||||
* @read_retry: read-retry information
|
||||
*/
|
||||
struct hynix_nand {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/rawnand.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
@@ -37,7 +38,7 @@
|
||||
#define FMC2_MAX_SG 16
|
||||
|
||||
/* Max chip enable */
|
||||
#define FMC2_MAX_CE 2
|
||||
#define FMC2_MAX_CE 4
|
||||
|
||||
/* Max ECC buffer length */
|
||||
#define FMC2_MAX_ECC_BUF_LEN (FMC2_BCHDSRS_LEN * FMC2_MAX_SG)
|
||||
@@ -243,6 +244,13 @@ static inline struct stm32_fmc2_nand *to_fmc2_nand(struct nand_chip *chip)
|
||||
return container_of(chip, struct stm32_fmc2_nand, chip);
|
||||
}
|
||||
|
||||
struct stm32_fmc2_nfc;
|
||||
|
||||
struct stm32_fmc2_nfc_data {
|
||||
int max_ncs;
|
||||
int (*set_cdev)(struct stm32_fmc2_nfc *nfc);
|
||||
};
|
||||
|
||||
struct stm32_fmc2_nfc {
|
||||
struct nand_controller base;
|
||||
struct stm32_fmc2_nand nand;
|
||||
@@ -256,6 +264,7 @@ struct stm32_fmc2_nfc {
|
||||
phys_addr_t data_phys_addr[FMC2_MAX_CE];
|
||||
struct clk *clk;
|
||||
u8 irq_state;
|
||||
const struct stm32_fmc2_nfc_data *data;
|
||||
|
||||
struct dma_chan *dma_tx_ch;
|
||||
struct dma_chan *dma_rx_ch;
|
||||
@@ -264,6 +273,8 @@ struct stm32_fmc2_nfc {
|
||||
struct sg_table dma_ecc_sg;
|
||||
u8 *ecc_buf;
|
||||
int dma_ecc_len;
|
||||
u32 tx_dma_max_burst;
|
||||
u32 rx_dma_max_burst;
|
||||
|
||||
struct completion complete;
|
||||
struct completion dma_data_complete;
|
||||
@@ -347,20 +358,26 @@ static int stm32_fmc2_nfc_select_chip(struct nand_chip *chip, int chipnr)
|
||||
stm32_fmc2_nfc_setup(chip);
|
||||
stm32_fmc2_nfc_timings_init(chip);
|
||||
|
||||
if (nfc->dma_tx_ch && nfc->dma_rx_ch) {
|
||||
if (nfc->dma_tx_ch) {
|
||||
memset(&dma_cfg, 0, sizeof(dma_cfg));
|
||||
dma_cfg.src_addr = nfc->data_phys_addr[nfc->cs_sel];
|
||||
dma_cfg.dst_addr = nfc->data_phys_addr[nfc->cs_sel];
|
||||
dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
dma_cfg.src_maxburst = 32;
|
||||
dma_cfg.dst_maxburst = 32;
|
||||
dma_cfg.dst_maxburst = nfc->tx_dma_max_burst /
|
||||
dma_cfg.dst_addr_width;
|
||||
|
||||
ret = dmaengine_slave_config(nfc->dma_tx_ch, &dma_cfg);
|
||||
if (ret) {
|
||||
dev_err(nfc->dev, "tx DMA engine slave config failed\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (nfc->dma_rx_ch) {
|
||||
memset(&dma_cfg, 0, sizeof(dma_cfg));
|
||||
dma_cfg.src_addr = nfc->data_phys_addr[nfc->cs_sel];
|
||||
dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
dma_cfg.src_maxburst = nfc->rx_dma_max_burst /
|
||||
dma_cfg.src_addr_width;
|
||||
|
||||
ret = dmaengine_slave_config(nfc->dma_rx_ch, &dma_cfg);
|
||||
if (ret) {
|
||||
@@ -1545,6 +1562,7 @@ static int stm32_fmc2_nfc_setup_interface(struct nand_chip *chip, int chipnr,
|
||||
|
||||
static int stm32_fmc2_nfc_dma_setup(struct stm32_fmc2_nfc *nfc)
|
||||
{
|
||||
struct dma_slave_caps caps;
|
||||
int ret = 0;
|
||||
|
||||
nfc->dma_tx_ch = dma_request_chan(nfc->dev, "tx");
|
||||
@@ -1557,6 +1575,11 @@ static int stm32_fmc2_nfc_dma_setup(struct stm32_fmc2_nfc *nfc)
|
||||
goto err_dma;
|
||||
}
|
||||
|
||||
ret = dma_get_slave_caps(nfc->dma_tx_ch, &caps);
|
||||
if (ret)
|
||||
return ret;
|
||||
nfc->tx_dma_max_burst = caps.max_burst;
|
||||
|
||||
nfc->dma_rx_ch = dma_request_chan(nfc->dev, "rx");
|
||||
if (IS_ERR(nfc->dma_rx_ch)) {
|
||||
ret = PTR_ERR(nfc->dma_rx_ch);
|
||||
@@ -1567,6 +1590,11 @@ static int stm32_fmc2_nfc_dma_setup(struct stm32_fmc2_nfc *nfc)
|
||||
goto err_dma;
|
||||
}
|
||||
|
||||
ret = dma_get_slave_caps(nfc->dma_rx_ch, &caps);
|
||||
if (ret)
|
||||
return ret;
|
||||
nfc->rx_dma_max_burst = caps.max_burst;
|
||||
|
||||
nfc->dma_ecc_ch = dma_request_chan(nfc->dev, "ecc");
|
||||
if (IS_ERR(nfc->dma_ecc_ch)) {
|
||||
ret = PTR_ERR(nfc->dma_ecc_ch);
|
||||
@@ -1790,7 +1818,7 @@ static int stm32_fmc2_nfc_parse_child(struct stm32_fmc2_nfc *nfc,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (cs >= FMC2_MAX_CE) {
|
||||
if (cs >= nfc->data->max_ncs) {
|
||||
dev_err(nfc->dev, "invalid reg value: %d\n", cs);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -1896,9 +1924,17 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev)
|
||||
nand_controller_init(&nfc->base);
|
||||
nfc->base.ops = &stm32_fmc2_nfc_controller_ops;
|
||||
|
||||
ret = stm32_fmc2_nfc_set_cdev(nfc);
|
||||
if (ret)
|
||||
return ret;
|
||||
nfc->data = of_device_get_match_data(dev);
|
||||
if (!nfc->data)
|
||||
return -EINVAL;
|
||||
|
||||
if (nfc->data->set_cdev) {
|
||||
ret = nfc->data->set_cdev(nfc);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
nfc->cdev = dev->parent;
|
||||
}
|
||||
|
||||
ret = stm32_fmc2_nfc_parse_dt(nfc);
|
||||
if (ret)
|
||||
@@ -1917,7 +1953,7 @@ static int stm32_fmc2_nfc_probe(struct platform_device *pdev)
|
||||
if (nfc->dev == nfc->cdev)
|
||||
start_region = 1;
|
||||
|
||||
for (chip_cs = 0, mem_region = start_region; chip_cs < FMC2_MAX_CE;
|
||||
for (chip_cs = 0, mem_region = start_region; chip_cs < nfc->data->max_ncs;
|
||||
chip_cs++, mem_region += 3) {
|
||||
if (!(nfc->cs_assigned & BIT(chip_cs)))
|
||||
continue;
|
||||
@@ -2073,7 +2109,7 @@ static int __maybe_unused stm32_fmc2_nfc_resume(struct device *dev)
|
||||
|
||||
stm32_fmc2_nfc_wp_disable(nand);
|
||||
|
||||
for (chip_cs = 0; chip_cs < FMC2_MAX_CE; chip_cs++) {
|
||||
for (chip_cs = 0; chip_cs < nfc->data->max_ncs; chip_cs++) {
|
||||
if (!(nfc->cs_assigned & BIT(chip_cs)))
|
||||
continue;
|
||||
|
||||
@@ -2086,9 +2122,28 @@ static int __maybe_unused stm32_fmc2_nfc_resume(struct device *dev)
|
||||
static SIMPLE_DEV_PM_OPS(stm32_fmc2_nfc_pm_ops, stm32_fmc2_nfc_suspend,
|
||||
stm32_fmc2_nfc_resume);
|
||||
|
||||
static const struct stm32_fmc2_nfc_data stm32_fmc2_nfc_mp1_data = {
|
||||
.max_ncs = 2,
|
||||
.set_cdev = stm32_fmc2_nfc_set_cdev,
|
||||
};
|
||||
|
||||
static const struct stm32_fmc2_nfc_data stm32_fmc2_nfc_mp25_data = {
|
||||
.max_ncs = 4,
|
||||
};
|
||||
|
||||
static const struct of_device_id stm32_fmc2_nfc_match[] = {
|
||||
{.compatible = "st,stm32mp15-fmc2"},
|
||||
{.compatible = "st,stm32mp1-fmc2-nfc"},
|
||||
{
|
||||
.compatible = "st,stm32mp15-fmc2",
|
||||
.data = &stm32_fmc2_nfc_mp1_data,
|
||||
},
|
||||
{
|
||||
.compatible = "st,stm32mp1-fmc2-nfc",
|
||||
.data = &stm32_fmc2_nfc_mp1_data,
|
||||
},
|
||||
{
|
||||
.compatible = "st,stm32mp25-fmc2-nfc",
|
||||
.data = &stm32_fmc2_nfc_mp25_data,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stm32_fmc2_nfc_match);
|
||||
|
||||
@@ -104,7 +104,8 @@ static const struct mtd_ooblayout_ops f50l1g41lb_ooblayout = {
|
||||
|
||||
static const struct spinand_info esmt_c8_spinand_table[] = {
|
||||
SPINAND_INFO("F50L1G41LB",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01),
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x01, 0x7f,
|
||||
0x7f, 0x7f),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(1, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
@@ -113,7 +114,8 @@ static const struct spinand_info esmt_c8_spinand_table[] = {
|
||||
0,
|
||||
SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)),
|
||||
SPINAND_INFO("F50D1G41LB",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x11),
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x11, 0x7f,
|
||||
0x7f, 0x7f),
|
||||
NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
|
||||
NAND_ECCREQ(1, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
@@ -122,7 +124,8 @@ static const struct spinand_info esmt_c8_spinand_table[] = {
|
||||
0,
|
||||
SPINAND_ECCINFO(&f50l1g41lb_ooblayout, NULL)),
|
||||
SPINAND_INFO("F50D2G41KA",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x51),
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x51, 0x7f,
|
||||
0x7f, 0x7f),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
#define WINBOND_CFG_BUF_READ BIT(3)
|
||||
|
||||
#define W25N04KV_STATUS_ECC_5_8_BITFLIPS (3 << 4)
|
||||
|
||||
static SPINAND_OP_VARIANTS(read_cache_variants,
|
||||
SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
|
||||
SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
|
||||
@@ -118,6 +120,7 @@ static int w25n02kv_ecc_get_status(struct spinand_device *spinand,
|
||||
return -EBADMSG;
|
||||
|
||||
case STATUS_ECC_HAS_BITFLIPS:
|
||||
case W25N04KV_STATUS_ECC_5_8_BITFLIPS:
|
||||
/*
|
||||
* Let's try to retrieve the real maximum number of bitflips
|
||||
* in order to avoid forcing the wear-leveling layer to move
|
||||
@@ -214,6 +217,15 @@ static const struct spinand_info winbond_spinand_table[] = {
|
||||
&update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(&w25m02gv_ooblayout, w25n02kv_ecc_get_status)),
|
||||
SPINAND_INFO("W25N04KV",
|
||||
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x23),
|
||||
NAND_MEMORG(1, 2048, 128, 64, 4096, 40, 2, 1, 1),
|
||||
NAND_ECCREQ(8, 512),
|
||||
SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
|
||||
&write_cache_variants,
|
||||
&update_cache_variants),
|
||||
0,
|
||||
SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
|
||||
};
|
||||
|
||||
static int winbond_spinand_init(struct spinand_device *spinand)
|
||||
|
||||
+88
-99
@@ -1158,7 +1158,7 @@ static u8 spi_nor_convert_3to4_erase(u8 opcode)
|
||||
|
||||
static bool spi_nor_has_uniform_erase(const struct spi_nor *nor)
|
||||
{
|
||||
return !!nor->params->erase_map.uniform_erase_type;
|
||||
return !!nor->params->erase_map.uniform_region.erase_mask;
|
||||
}
|
||||
|
||||
static void spi_nor_set_4byte_opcodes(struct spi_nor *nor)
|
||||
@@ -1542,7 +1542,6 @@ spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map,
|
||||
const struct spi_nor_erase_type *erase;
|
||||
u32 rem;
|
||||
int i;
|
||||
u8 erase_mask = region->offset & SNOR_ERASE_TYPE_MASK;
|
||||
|
||||
/*
|
||||
* Erase types are ordered by size, with the smallest erase type at
|
||||
@@ -1550,7 +1549,7 @@ spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map,
|
||||
*/
|
||||
for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
|
||||
/* Does the erase region support the tested erase type? */
|
||||
if (!(erase_mask & BIT(i)))
|
||||
if (!(region->erase_mask & BIT(i)))
|
||||
continue;
|
||||
|
||||
erase = &map->erase_type[i];
|
||||
@@ -1558,8 +1557,7 @@ spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map,
|
||||
continue;
|
||||
|
||||
/* Alignment is not mandatory for overlaid regions */
|
||||
if (region->offset & SNOR_OVERLAID_REGION &&
|
||||
region->size <= len)
|
||||
if (region->overlaid && region->size <= len)
|
||||
return erase;
|
||||
|
||||
/* Don't erase more than what the user has asked for. */
|
||||
@@ -1574,59 +1572,6 @@ spi_nor_find_best_erase_type(const struct spi_nor_erase_map *map,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static u64 spi_nor_region_is_last(const struct spi_nor_erase_region *region)
|
||||
{
|
||||
return region->offset & SNOR_LAST_REGION;
|
||||
}
|
||||
|
||||
static u64 spi_nor_region_end(const struct spi_nor_erase_region *region)
|
||||
{
|
||||
return (region->offset & ~SNOR_ERASE_FLAGS_MASK) + region->size;
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_region_next() - get the next spi nor region
|
||||
* @region: pointer to a structure that describes a SPI NOR erase region
|
||||
*
|
||||
* Return: the next spi nor region or NULL if last region.
|
||||
*/
|
||||
struct spi_nor_erase_region *
|
||||
spi_nor_region_next(struct spi_nor_erase_region *region)
|
||||
{
|
||||
if (spi_nor_region_is_last(region))
|
||||
return NULL;
|
||||
region++;
|
||||
return region;
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_find_erase_region() - find the region of the serial flash memory in
|
||||
* which the offset fits
|
||||
* @map: the erase map of the SPI NOR
|
||||
* @addr: offset in the serial flash memory
|
||||
*
|
||||
* Return: a pointer to the spi_nor_erase_region struct, ERR_PTR(-errno)
|
||||
* otherwise.
|
||||
*/
|
||||
static struct spi_nor_erase_region *
|
||||
spi_nor_find_erase_region(const struct spi_nor_erase_map *map, u64 addr)
|
||||
{
|
||||
struct spi_nor_erase_region *region = map->regions;
|
||||
u64 region_start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
|
||||
u64 region_end = region_start + region->size;
|
||||
|
||||
while (addr < region_start || addr >= region_end) {
|
||||
region = spi_nor_region_next(region);
|
||||
if (!region)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
region_start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
|
||||
region_end = region_start + region->size;
|
||||
}
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_init_erase_cmd() - initialize an erase command
|
||||
* @region: pointer to a structure that describes a SPI NOR erase region
|
||||
@@ -1649,7 +1594,7 @@ spi_nor_init_erase_cmd(const struct spi_nor_erase_region *region,
|
||||
cmd->opcode = erase->opcode;
|
||||
cmd->count = 1;
|
||||
|
||||
if (region->offset & SNOR_OVERLAID_REGION)
|
||||
if (region->overlaid)
|
||||
cmd->size = region->size;
|
||||
else
|
||||
cmd->size = erase->size;
|
||||
@@ -1693,44 +1638,36 @@ static int spi_nor_init_erase_cmd_list(struct spi_nor *nor,
|
||||
struct spi_nor_erase_region *region;
|
||||
struct spi_nor_erase_command *cmd = NULL;
|
||||
u64 region_end;
|
||||
unsigned int i;
|
||||
int ret = -EINVAL;
|
||||
|
||||
region = spi_nor_find_erase_region(map, addr);
|
||||
if (IS_ERR(region))
|
||||
return PTR_ERR(region);
|
||||
for (i = 0; i < map->n_regions && len; i++) {
|
||||
region = &map->regions[i];
|
||||
region_end = region->offset + region->size;
|
||||
|
||||
region_end = spi_nor_region_end(region);
|
||||
|
||||
while (len) {
|
||||
erase = spi_nor_find_best_erase_type(map, region, addr, len);
|
||||
if (!erase)
|
||||
goto destroy_erase_cmd_list;
|
||||
|
||||
if (prev_erase != erase ||
|
||||
erase->size != cmd->size ||
|
||||
region->offset & SNOR_OVERLAID_REGION) {
|
||||
cmd = spi_nor_init_erase_cmd(region, erase);
|
||||
if (IS_ERR(cmd)) {
|
||||
ret = PTR_ERR(cmd);
|
||||
while (len && addr >= region->offset && addr < region_end) {
|
||||
erase = spi_nor_find_best_erase_type(map, region, addr,
|
||||
len);
|
||||
if (!erase)
|
||||
goto destroy_erase_cmd_list;
|
||||
|
||||
if (prev_erase != erase || erase->size != cmd->size ||
|
||||
region->overlaid) {
|
||||
cmd = spi_nor_init_erase_cmd(region, erase);
|
||||
if (IS_ERR(cmd)) {
|
||||
ret = PTR_ERR(cmd);
|
||||
goto destroy_erase_cmd_list;
|
||||
}
|
||||
|
||||
list_add_tail(&cmd->list, erase_list);
|
||||
} else {
|
||||
cmd->count++;
|
||||
}
|
||||
|
||||
list_add_tail(&cmd->list, erase_list);
|
||||
} else {
|
||||
cmd->count++;
|
||||
len -= cmd->size;
|
||||
addr += cmd->size;
|
||||
prev_erase = erase;
|
||||
}
|
||||
|
||||
addr += cmd->size;
|
||||
len -= cmd->size;
|
||||
|
||||
if (len && addr >= region_end) {
|
||||
region = spi_nor_region_next(region);
|
||||
if (!region)
|
||||
goto destroy_erase_cmd_list;
|
||||
region_end = spi_nor_region_end(region);
|
||||
}
|
||||
|
||||
prev_erase = erase;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -2468,12 +2405,11 @@ void spi_nor_mask_erase_type(struct spi_nor_erase_type *erase)
|
||||
void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map,
|
||||
u8 erase_mask, u64 flash_size)
|
||||
{
|
||||
/* Offset 0 with erase_mask and SNOR_LAST_REGION bit set */
|
||||
map->uniform_region.offset = (erase_mask & SNOR_ERASE_TYPE_MASK) |
|
||||
SNOR_LAST_REGION;
|
||||
map->uniform_region.offset = 0;
|
||||
map->uniform_region.size = flash_size;
|
||||
map->uniform_region.erase_mask = erase_mask;
|
||||
map->regions = &map->uniform_region;
|
||||
map->uniform_erase_type = erase_mask;
|
||||
map->n_regions = 1;
|
||||
}
|
||||
|
||||
int spi_nor_post_bfpt_fixups(struct spi_nor *nor,
|
||||
@@ -2560,7 +2496,7 @@ spi_nor_select_uniform_erase(struct spi_nor_erase_map *map)
|
||||
{
|
||||
const struct spi_nor_erase_type *tested_erase, *erase = NULL;
|
||||
int i;
|
||||
u8 uniform_erase_type = map->uniform_erase_type;
|
||||
u8 uniform_erase_type = map->uniform_region.erase_mask;
|
||||
|
||||
/*
|
||||
* Search for the biggest erase size, except for when compiled
|
||||
@@ -2599,8 +2535,7 @@ spi_nor_select_uniform_erase(struct spi_nor_erase_map *map)
|
||||
return NULL;
|
||||
|
||||
/* Disable all other Sector Erase commands. */
|
||||
map->uniform_erase_type &= ~SNOR_ERASE_TYPE_MASK;
|
||||
map->uniform_erase_type |= BIT(erase - map->erase_type);
|
||||
map->uniform_region.erase_mask = BIT(erase - map->erase_type);
|
||||
return erase;
|
||||
}
|
||||
|
||||
@@ -3434,7 +3369,54 @@ static const struct flash_info *spi_nor_get_flash_info(struct spi_nor *nor,
|
||||
return info;
|
||||
}
|
||||
|
||||
static void spi_nor_set_mtd_info(struct spi_nor *nor)
|
||||
static u32
|
||||
spi_nor_get_region_erasesize(const struct spi_nor_erase_region *region,
|
||||
const struct spi_nor_erase_type *erase_type)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (region->overlaid)
|
||||
return region->size;
|
||||
|
||||
for (i = SNOR_ERASE_TYPE_MAX - 1; i >= 0; i--) {
|
||||
if (region->erase_mask & BIT(i))
|
||||
return erase_type[i].size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_nor_set_mtd_eraseregions(struct spi_nor *nor)
|
||||
{
|
||||
const struct spi_nor_erase_map *map = &nor->params->erase_map;
|
||||
const struct spi_nor_erase_region *region = map->regions;
|
||||
struct mtd_erase_region_info *mtd_region;
|
||||
struct mtd_info *mtd = &nor->mtd;
|
||||
u32 erasesize, i;
|
||||
|
||||
mtd_region = devm_kcalloc(nor->dev, map->n_regions, sizeof(*mtd_region),
|
||||
GFP_KERNEL);
|
||||
if (!mtd_region)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < map->n_regions; i++) {
|
||||
erasesize = spi_nor_get_region_erasesize(®ion[i],
|
||||
map->erase_type);
|
||||
if (!erasesize)
|
||||
return -EINVAL;
|
||||
|
||||
mtd_region[i].erasesize = erasesize;
|
||||
mtd_region[i].numblocks = div64_ul(region[i].size, erasesize);
|
||||
mtd_region[i].offset = region[i].offset;
|
||||
}
|
||||
|
||||
mtd->numeraseregions = map->n_regions;
|
||||
mtd->eraseregions = mtd_region;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_nor_set_mtd_info(struct spi_nor *nor)
|
||||
{
|
||||
struct mtd_info *mtd = &nor->mtd;
|
||||
struct device *dev = nor->dev;
|
||||
@@ -3465,6 +3447,11 @@ static void spi_nor_set_mtd_info(struct spi_nor *nor)
|
||||
mtd->_resume = spi_nor_resume;
|
||||
mtd->_get_device = spi_nor_get_device;
|
||||
mtd->_put_device = spi_nor_put_device;
|
||||
|
||||
if (!spi_nor_has_uniform_erase(nor))
|
||||
return spi_nor_set_mtd_eraseregions(nor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_nor_hw_reset(struct spi_nor *nor)
|
||||
@@ -3555,7 +3542,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name,
|
||||
return ret;
|
||||
|
||||
/* No mtd_info fields should be used up to this point. */
|
||||
spi_nor_set_mtd_info(nor);
|
||||
ret = spi_nor_set_mtd_info(nor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(dev, "Manufacturer and device ID: %*phN\n",
|
||||
SPI_NOR_MAX_ID_LEN, nor->id);
|
||||
|
||||
@@ -240,27 +240,21 @@ struct spi_nor_erase_command {
|
||||
/**
|
||||
* struct spi_nor_erase_region - Structure to describe a SPI NOR erase region
|
||||
* @offset: the offset in the data array of erase region start.
|
||||
* LSB bits are used as a bitmask encoding flags to
|
||||
* determine if this region is overlaid, if this region is
|
||||
* the last in the SPI NOR flash memory and to indicate
|
||||
* all the supported erase commands inside this region.
|
||||
* The erase types are sorted in ascending order with the
|
||||
* smallest Erase Type size being at BIT(0).
|
||||
* @size: the size of the region in bytes.
|
||||
* @erase_mask: bitmask to indicate all the supported erase commands
|
||||
* inside this region. The erase types are sorted in
|
||||
* ascending order with the smallest Erase Type size being
|
||||
* at BIT(0).
|
||||
* @overlaid: determine if this region is overlaid.
|
||||
*/
|
||||
struct spi_nor_erase_region {
|
||||
u64 offset;
|
||||
u64 size;
|
||||
u8 erase_mask;
|
||||
bool overlaid;
|
||||
};
|
||||
|
||||
#define SNOR_ERASE_TYPE_MAX 4
|
||||
#define SNOR_ERASE_TYPE_MASK GENMASK_ULL(SNOR_ERASE_TYPE_MAX - 1, 0)
|
||||
|
||||
#define SNOR_LAST_REGION BIT(4)
|
||||
#define SNOR_OVERLAID_REGION BIT(5)
|
||||
|
||||
#define SNOR_ERASE_FLAGS_MAX 6
|
||||
#define SNOR_ERASE_FLAGS_MASK GENMASK_ULL(SNOR_ERASE_FLAGS_MAX - 1, 0)
|
||||
|
||||
/**
|
||||
* struct spi_nor_erase_map - Structure to describe the SPI NOR erase map
|
||||
@@ -273,17 +267,13 @@ struct spi_nor_erase_region {
|
||||
* The erase types are sorted in ascending order, with the
|
||||
* smallest Erase Type size being the first member in the
|
||||
* erase_type array.
|
||||
* @uniform_erase_type: bitmask encoding erase types that can erase the
|
||||
* entire memory. This member is completed at init by
|
||||
* uniform and non-uniform SPI NOR flash memories if they
|
||||
* support at least one erase type that can erase the
|
||||
* entire memory.
|
||||
* @n_regions: number of erase regions.
|
||||
*/
|
||||
struct spi_nor_erase_map {
|
||||
struct spi_nor_erase_region *regions;
|
||||
struct spi_nor_erase_region uniform_region;
|
||||
struct spi_nor_erase_type erase_type[SNOR_ERASE_TYPE_MAX];
|
||||
u8 uniform_erase_type;
|
||||
unsigned int n_regions;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -675,8 +665,6 @@ void spi_nor_set_pp_settings(struct spi_nor_pp_command *pp, u8 opcode,
|
||||
void spi_nor_set_erase_type(struct spi_nor_erase_type *erase, u32 size,
|
||||
u8 opcode);
|
||||
void spi_nor_mask_erase_type(struct spi_nor_erase_type *erase);
|
||||
struct spi_nor_erase_region *
|
||||
spi_nor_region_next(struct spi_nor_erase_region *region);
|
||||
void spi_nor_init_uniform_erase_map(struct spi_nor_erase_map *map,
|
||||
u8 erase_mask, u64 flash_size);
|
||||
|
||||
|
||||
@@ -78,10 +78,10 @@ static int spi_nor_params_show(struct seq_file *s, void *data)
|
||||
struct spi_nor *nor = s->private;
|
||||
struct spi_nor_flash_parameter *params = nor->params;
|
||||
struct spi_nor_erase_map *erase_map = ¶ms->erase_map;
|
||||
struct spi_nor_erase_region *region;
|
||||
struct spi_nor_erase_region *region = erase_map->regions;
|
||||
const struct flash_info *info = nor->info;
|
||||
char buf[16], *str;
|
||||
int i;
|
||||
unsigned int i;
|
||||
|
||||
seq_printf(s, "name\t\t%s\n", info->name);
|
||||
seq_printf(s, "id\t\t%*ph\n", SPI_NOR_MAX_ID_LEN, nor->id);
|
||||
@@ -142,22 +142,20 @@ static int spi_nor_params_show(struct seq_file *s, void *data)
|
||||
}
|
||||
|
||||
seq_puts(s, "\nsector map\n");
|
||||
seq_puts(s, " region (in hex) | erase mask | flags\n");
|
||||
seq_puts(s, " region (in hex) | erase mask | overlaid\n");
|
||||
seq_puts(s, " ------------------+------------+----------\n");
|
||||
for (region = erase_map->regions;
|
||||
region;
|
||||
region = spi_nor_region_next(region)) {
|
||||
u64 start = region->offset & ~SNOR_ERASE_FLAGS_MASK;
|
||||
u64 flags = region->offset & SNOR_ERASE_FLAGS_MASK;
|
||||
u64 end = start + region->size - 1;
|
||||
for (i = 0; i < erase_map->n_regions; i++) {
|
||||
u64 start = region[i].offset;
|
||||
u64 end = start + region[i].size - 1;
|
||||
u8 erase_mask = region[i].erase_mask;
|
||||
|
||||
seq_printf(s, " %08llx-%08llx | [%c%c%c%c] | %s\n",
|
||||
start, end,
|
||||
flags & BIT(0) ? '0' : ' ',
|
||||
flags & BIT(1) ? '1' : ' ',
|
||||
flags & BIT(2) ? '2' : ' ',
|
||||
flags & BIT(3) ? '3' : ' ',
|
||||
flags & SNOR_OVERLAID_REGION ? "overlaid" : "");
|
||||
erase_mask & BIT(0) ? '0' : ' ',
|
||||
erase_mask & BIT(1) ? '1' : ' ',
|
||||
erase_mask & BIT(2) ? '2' : ' ',
|
||||
erase_mask & BIT(3) ? '3' : ' ',
|
||||
region[i].overlaid ? "yes" : "no");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
+16
-31
@@ -389,19 +389,15 @@ static u8 spi_nor_sort_erase_mask(struct spi_nor_erase_map *map, u8 erase_mask)
|
||||
static void spi_nor_regions_sort_erase_types(struct spi_nor_erase_map *map)
|
||||
{
|
||||
struct spi_nor_erase_region *region = map->regions;
|
||||
u8 region_erase_mask, sorted_erase_mask;
|
||||
u8 sorted_erase_mask;
|
||||
unsigned int i;
|
||||
|
||||
while (region) {
|
||||
region_erase_mask = region->offset & SNOR_ERASE_TYPE_MASK;
|
||||
|
||||
sorted_erase_mask = spi_nor_sort_erase_mask(map,
|
||||
region_erase_mask);
|
||||
for (i = 0; i < map->n_regions; i++) {
|
||||
sorted_erase_mask =
|
||||
spi_nor_sort_erase_mask(map, region[i].erase_mask);
|
||||
|
||||
/* Overwrite erase mask. */
|
||||
region->offset = (region->offset & ~SNOR_ERASE_TYPE_MASK) |
|
||||
sorted_erase_mask;
|
||||
|
||||
region = spi_nor_region_next(region);
|
||||
region[i].erase_mask = sorted_erase_mask;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -554,8 +550,6 @@ static int spi_nor_parse_bfpt(struct spi_nor *nor,
|
||||
* selecting the uniform erase.
|
||||
*/
|
||||
spi_nor_regions_sort_erase_types(map);
|
||||
map->uniform_erase_type = map->uniform_region.offset &
|
||||
SNOR_ERASE_TYPE_MASK;
|
||||
|
||||
/* Stop here if not JESD216 rev A or later. */
|
||||
if (bfpt_header->length == BFPT_DWORD_MAX_JESD216)
|
||||
@@ -806,16 +800,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void spi_nor_region_mark_end(struct spi_nor_erase_region *region)
|
||||
{
|
||||
region->offset |= SNOR_LAST_REGION;
|
||||
}
|
||||
|
||||
static void spi_nor_region_mark_overlay(struct spi_nor_erase_region *region)
|
||||
{
|
||||
region->offset |= SNOR_OVERLAID_REGION;
|
||||
}
|
||||
|
||||
/**
|
||||
* spi_nor_region_check_overlay() - set overlay bit when the region is overlaid
|
||||
* @region: pointer to a structure that describes a SPI NOR erase region
|
||||
@@ -833,7 +817,7 @@ spi_nor_region_check_overlay(struct spi_nor_erase_region *region,
|
||||
if (!(erase[i].size && erase_type & BIT(erase[i].idx)))
|
||||
continue;
|
||||
if (region->size & erase[i].size_mask) {
|
||||
spi_nor_region_mark_overlay(region);
|
||||
region->overlaid = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -868,6 +852,7 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor,
|
||||
if (!region)
|
||||
return -ENOMEM;
|
||||
map->regions = region;
|
||||
map->n_regions = region_count;
|
||||
|
||||
uniform_erase_type = 0xff;
|
||||
regions_erase_type = 0;
|
||||
@@ -875,9 +860,10 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor,
|
||||
/* Populate regions. */
|
||||
for (i = 0; i < region_count; i++) {
|
||||
j = i + 1; /* index for the region dword */
|
||||
region[i].offset = offset;
|
||||
region[i].size = SMPT_MAP_REGION_SIZE(smpt[j]);
|
||||
erase_type = SMPT_MAP_REGION_ERASE_TYPE(smpt[j]);
|
||||
region[i].offset = offset | erase_type;
|
||||
region[i].erase_mask = erase_type;
|
||||
|
||||
spi_nor_region_check_overlay(®ion[i], erase, erase_type);
|
||||
|
||||
@@ -893,21 +879,20 @@ static int spi_nor_init_non_uniform_erase_map(struct spi_nor *nor,
|
||||
*/
|
||||
regions_erase_type |= erase_type;
|
||||
|
||||
offset = (region[i].offset & ~SNOR_ERASE_FLAGS_MASK) +
|
||||
region[i].size;
|
||||
offset = region[i].offset + region[i].size;
|
||||
}
|
||||
spi_nor_region_mark_end(®ion[i - 1]);
|
||||
|
||||
save_uniform_erase_type = map->uniform_erase_type;
|
||||
map->uniform_erase_type = spi_nor_sort_erase_mask(map,
|
||||
uniform_erase_type);
|
||||
save_uniform_erase_type = map->uniform_region.erase_mask;
|
||||
map->uniform_region.erase_mask =
|
||||
spi_nor_sort_erase_mask(map,
|
||||
uniform_erase_type);
|
||||
|
||||
if (!regions_erase_type) {
|
||||
/*
|
||||
* Roll back to the previous uniform_erase_type mask, SMPT is
|
||||
* broken.
|
||||
*/
|
||||
map->uniform_erase_type = save_uniform_erase_type;
|
||||
map->uniform_region.erase_mask = save_uniform_erase_type;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -295,7 +295,7 @@ static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
|
||||
if (cis_sector == -1)
|
||||
return;
|
||||
|
||||
ssfdc = kzalloc(sizeof(struct ssfdcr_record), GFP_KERNEL);
|
||||
ssfdc = kzalloc(sizeof(*ssfdc), GFP_KERNEL);
|
||||
if (!ssfdc)
|
||||
return;
|
||||
|
||||
@@ -332,7 +332,7 @@ static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
|
||||
kmalloc_array(ssfdc->map_len,
|
||||
sizeof(ssfdc->logic_block_map[0]), GFP_KERNEL);
|
||||
if (!ssfdc->logic_block_map)
|
||||
goto out_err;
|
||||
goto out_free_ssfdc;
|
||||
memset(ssfdc->logic_block_map, 0xff, sizeof(ssfdc->logic_block_map[0]) *
|
||||
ssfdc->map_len);
|
||||
|
||||
@@ -350,7 +350,8 @@ static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
|
||||
|
||||
out_err:
|
||||
kfree(ssfdc->logic_block_map);
|
||||
kfree(ssfdc);
|
||||
out_free_ssfdc:
|
||||
kfree(ssfdc);
|
||||
}
|
||||
|
||||
static void ssfdcr_remove_dev(struct mtd_blktrans_dev *dev)
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
*/
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
typedef enum {
|
||||
FL_READY,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <linux/dmaengine.h>
|
||||
|
||||
struct lpc32xx_mlc_platform_data {
|
||||
bool (*dma_filter)(struct dma_chan *chan, void *filter_param);
|
||||
dma_filter_fn dma_filter;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_MTD_LPC32XX_MLC_H */
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <linux/dmaengine.h>
|
||||
|
||||
struct lpc32xx_slc_platform_data {
|
||||
bool (*dma_filter)(struct dma_chan *chan, void *filter_param);
|
||||
dma_filter_fn dma_filter;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_MTD_LPC32XX_SLC_H */
|
||||
|
||||
@@ -223,7 +223,7 @@ struct mtd_part {
|
||||
* @partitions_lock: lock protecting accesses to the partition list. Protects
|
||||
* not only the master partition list, but also all
|
||||
* sub-partitions.
|
||||
* @suspended: et to 1 when the device is suspended, 0 otherwise
|
||||
* @suspended: set to 1 when the device is suspended, 0 otherwise
|
||||
*
|
||||
* This struct is embedded in mtd_info and contains master-specific
|
||||
* properties/fields. The master is the root MTD device from the MTD partition
|
||||
|
||||
@@ -169,7 +169,7 @@
|
||||
struct spinand_op;
|
||||
struct spinand_device;
|
||||
|
||||
#define SPINAND_MAX_ID_LEN 4
|
||||
#define SPINAND_MAX_ID_LEN 5
|
||||
/*
|
||||
* For erase, write and read operation, we got the following timings :
|
||||
* tBERS (erase) 1ms to 4ms
|
||||
|
||||
Reference in New Issue
Block a user