Commit 3c5d127f authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'mtd/for-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux

Pull MTD updates from Miquel Raynal:
 "MTD:

   - The Carillo Ranch driver has been removed

   - Top level mtd bindings have received a couple of improvements
     (references, selects)

   - The ssfdc driver received few minor adjustments

   - The usual load of misc/small improvements and fixes

  Raw NAND:

   - The main series brought is an update of the Broadcom support to
     support all BCMBCA SoCs and their specificity (ECC, write
     protection, configuration straps), plus a few misc fixes and
     changes in the main driver. Device tree updates are also part of
     this PR, initially because of a misunderstanding on my side.

   - The STM32_FMC2 controller driver is also upgraded to properly
     support MP1 and MP25 SoCs.

   - A new compatible is added for an Atmel flavor.

   - Among all these feature changes, there is as well a load of
     continuous read related fixes, avoiding more corner conditions and
     clarifying the logic. Finally a few miscellaneous fixes are made to
     the core, the lpx32xx_mlc, fsl_lbc, Meson and Atmel controller
     driver, as well as final one in the Hynix vendor driver.

  SPI-NAND:

   - The ESMT support has been extended to match 5 bytes ID to avoid
     collisions. Winbond support on its side receives support for
     W25N04KV chips.

  SPI NOR:

   - SPI NOR gets the non uniform erase code cleaned. We stopped using
     bitmasks for erase types and flags, and instead introduced
     dedicated members. We then passed the SPI NOR erase map to MTD.
     Users can now determine the erase regions and make informed
     decisions on partitions size.

   - An optional interrupt property is now described in the bindings"

* tag 'mtd/for-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux: (50 commits)
  mtd: rawnand: Ensure continuous reads are well disabled
  mtd: rawnand: Constrain even more when continuous reads are enabled
  mtd: rawnand: brcmnand: Add support for getting ecc setting from strap
  mtd: rawnand: brcmnand: fix sparse warnings
  mtd: nand: raw: atmel: Fix comment in timings preparation
  mtd: rawnand: Ensure all continuous terms are always in sync
  mtd: rawnand: Add a helper for calculating a page index
  mtd: rawnand: Fix and simplify again the continuous read derivations
  mtd: rawnand: hynix: remove @nand_technology kernel-doc description
  dt-bindings: atmel-nand: add microchip,sam9x7-pmecc
  mtd: rawnand: brcmnand: Support write protection setting from dts
  mtd: rawnand: brcmnand: Add BCMBCA read data bus interface
  mtd: rawnand: brcmnand: Rename bcm63138 nand driver
  arm64: dts: broadcom: bcmbca: Update router boards
  arm64: dts: broadcom: bcmbca: Add NAND controller node
  ARM: dts: broadcom: bcmbca: Add NAND controller node
  mtd: spi-nor: core: correct type of i
  mtd: spi-nor: core: set mtd->eraseregions for non-uniform erase map
  mtd: spi-nor: core: get rid of SNOR_OVERLAID_REGION flag
  mtd: spi-nor: core: get rid of SNOR_LAST_REGION flag
  ...
parents 2b3a4192 09888e97
......@@ -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 @@ hsspi: spi@1000 {
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 @@ nand_controller: nand-controller@2000 {
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 @@ hsspi: spi@1000 {
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 @@ hsspi: spi@1000 {
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 @@ hsspi: spi@1000 {
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 @@ hsspi: spi@1000 {
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 @@ hsspi: spi@1000 {
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 @@ hsspi: spi@1000 {
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 @@ &uart0 {
&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 @@ &serial0 {
&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 @@ &serial1 {
};
&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 @@ &uart0 {
&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 @@ &uart0 {
&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 @@ &uart0 {
&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 @@ &uart0 {
&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 @@ &uart0 {
&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 @@ &uart0 {
&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 @@ port@7 {
};
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-ecc-strength = <4>;
nand-ecc-step-size = <512>;
......
......@@ -155,6 +155,11 @@ port@7 {
};
};
&nand_controller {
brcm,wp-not-connected;
status = "okay";
};
&nandcs {
nand-ecc-strength = <4>;
nand-ecc-step-size = <512>;
......
......@@ -166,11 +166,15 @@ led@19 {
};
};
&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 @@ hsspi: spi@1000 {
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 @@ nand-controller@1800 {
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 @@ hsspi: spi@1000 {
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 @@ hsspi: spi@1000 {
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 @@ hsspi: spi@1000 {
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 @@ hsspi: spi@1000 {
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 @@ hsspi: spi@1000 {
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 @@ hsspi: spi@1000 {
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 @@ &uart0 {
&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 @@ &uart0 {
&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 @@ &uart0 {
&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 @@ &uart0 {
&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 @@ &uart0 {
&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 @@ &uart0 {
&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 @@ &uart0 {
&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
/*
* 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
......
......@@ -14,52 +14,78 @@
#include "brcmnand.h"
struct bcm63138_nand_soc {
struct bcmbca_nand_soc {
struct brcmnand_soc soc;
void __iomem *base;
};
#define BCM63138_NAND_INT_STATUS 0x00
#define BCM63138_NAND_INT_EN 0x04
#define BCMBCA_NAND_INT_STATUS 0x00
#define BCMBCA_NAND_INT_EN 0x04
enum {
BCM63138_CTLRDY = BIT(4),
BCMBCA_CTLRDY = BIT(4),
};
static bool bcm63138_nand_intc_ack(struct brcmnand_soc *soc)
#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 bcm63138_nand_soc *priv =
container_of(soc, struct bcm63138_nand_soc, soc);
void __iomem *mmio = priv->base + BCM63138_NAND_INT_STATUS;
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 & BCM63138_CTLRDY) {
brcmnand_writel(val & ~BCM63138_CTLRDY, mmio);
if (val & BCMBCA_CTLRDY) {
brcmnand_writel(val & ~BCMBCA_CTLRDY, mmio);
return true;
}
return false;
}
static void bcm63138_nand_intc_set(struct brcmnand_soc *soc, bool en)
static void bcmbca_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;
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 |= BCM63138_CTLRDY;
val |= BCMBCA_CTLRDY;
else
val &= ~BCM63138_CTLRDY;
val &= ~BCMBCA_CTLRDY;
brcmnand_writel(val, mmio);
}
static int bcm63138_nand_probe(struct platform_device *pdev)
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 bcm63138_nand_soc *priv;
struct bcmbca_nand_soc *priv;
struct brcmnand_soc *soc;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
......@@ -71,29 +97,30 @@ static int bcm63138_nand_probe(struct platform_device *pdev)
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;
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 bcm63138_nand_of_match[] = {
static const struct of_device_id bcmbca_nand_of_match[] = {
{ .compatible = "brcm,nand-bcm63138" },
{},
};
MODULE_DEVICE_TABLE(of, bcm63138_nand_of_match);
MODULE_DEVICE_TABLE(of, bcmbca_nand_of_match);
static struct platform_driver bcm63138_nand_driver = {
.probe = bcm63138_nand_probe,
static struct platform_driver bcmbca_nand_driver = {
.probe = bcmbca_nand_probe,
.remove_new = brcmnand_remove,
.driver = {
.name = "bcm63138_nand",
.name = "bcmbca_nand",
.pm = &brcmnand_pm_ops,
.of_match_table = bcm63138_nand_of_match,
.of_match_table = bcmbca_nand_of_match,
}
};
module_platform_driver(bcm63138_nand_driver);
module_platform_driver(bcmbca_nand_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Brian Norris");
MODULE_DESCRIPTION("NAND driver for BCM63138");
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 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
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");
/*
* 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(nand_get_flash_node(chip),
"brcm,nand-oob-sector-size",
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;
/*
* 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;
/* Derive the page where continuous read should start (the first full page read) */
first_page = page;
if (col)
page++;
first_page++;
if (end_col && end_page)
end_page--;
/* Derive the page where continuous read should stop (the last full page read) */
last_page = page + ((col + readlen) / mtd->writesize) - 1;
if (page + 1 > end_page)
return;
chip->cont_read.first_page = page;
chip->cont_read.last_page = end_page;
chip->cont_read.ongoing = true;
rawnand_cap_cont_reads(chip);
/* 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 @@ static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
}
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)
......
......@@ -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);
region_end = spi_nor_region_end(region);
for (i = 0; i < map->n_regions && len; i++) {
region = &map->regions[i];
region_end = region->offset + region->size;
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;
}
list_add_tail(&cmd->list, erase_list);
} else {
cmd->count++;
}
addr += cmd->size;
len -= cmd->size;
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++;
}
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);
len -= cmd->size;
addr += cmd->size;
prev_erase = erase;
}
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(&region[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 = &params->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;
......
......@@ -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 @@ static const u32 *spi_nor_get_map_in_use(struct spi_nor *nor, const u32 *smpt,
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(&region[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(&region[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;
}
......
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment