Skip to contentSkip to conversation

Marvell NAND controller rework with ->exec_op()

PATCH Superseded Miquel Raynal 6 participants 17 messages Dec 19, 2017 – Jan 7, 2018

Subsystems (8)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

v2 cover Marvell NAND controller rework with ->exec_op()

Hi,

After the addition of the NAND framework ->exec_op() interface (see [1] for the series preparing it and [2] for the last version of the core-side implementation of ->exec_op() itself), this series replaces the current Marvell NAND controller driver pxa3xx_nand.c with a rework called marvell_nand.c.

Aside the fact that it drops the big state machine, improves the overall speed and implements raw accesses, it is the first driver-side implementation of the ->exec_op() interface and may be used as reference for latter reworks of the same type.

One may find more detail about why a completely new driver is needed in the commit log of:

"mtd: nand: add reworked Marvell NAND controller driver"

Device tree NAND node definition for all platforms referring to the Marvell driver using the new bindings have already been accepted by the MVEBU DT maintainers and will be merged after the driver. They are more hierarchical and fit the real organization of the hardware, by having NAND partitions that are part of NAND chip nodes, themselves part of the NAND controller node.

These changes have been tested on:
- PXA3xx platform with a CM-X300 board (2kiB page NAND, 1b/512B strength, Hamming ECC engine) [32 bits]
- Armada 385 DB AP (4kiB page NAND, 4b/512B, BCH ECC engine) [32 bits]
- Armada 398 DB (4kiB page NAND, 8b/512B, BCH ECC engine using a layout with a last chunk different than the others) [32 bits]
- Armada 7040 DB and Armada 8040 DB (4kiB page NAND, 4b/512B, BCH ECC

     engine) [64 bits]

- Triax dvb-tc board (2kiB page NAND, 4b/512B, BCH ECC engine) [32 bits]

This version is probably one step before the last version as Robert's Zylonite still does not boot with it.

For people who would like to test it easily, a branch ready to be tested is available at [3]. It is based on nand/next and has all the changes brought by the previously mentionned series as well as this one.

Thank you,
Miqu?l

[1] https://www.spinics.net/lists/arm-kernel/msg619633.html
[2] http://lists.infradead.org/pipermail/linux-mtd/2017-December/077965.html
[3] https://github.com/miquelraynal/linux/tree/marvell/nand-next/nfc-rework

Changes since v1:
- Rewording
- Fixed BCH ->read/write_page() hooks for 2kiB pages NAND chips
- Removed license text, used SPDX tag instead
- Removed read_page_data()
- Enhanced the DT bindings document with the label property and the deprecated bindings.
- Simplified the read_chunk() helper (OOB always read).
- Simplified the ->bch_read_page() hook by removing the addition raw read to get ECC bytes.
- Fixed the ->correct() function that did not check for bitflips in ECC bytes in erased pages.

Miquel Raynal (5):
dt-bindings: mtd: add Marvell NAND controller documentation
mtd: nand: add reworked Marvell NAND controller driver
mtd: nand: replace pxa3xx_nand driver by its rework called
marvell_nand
dt-bindings: mtd: remove pxa3xx NAND controller documentation
mtd: nand: remove useless fields from pxa3xx NAND platform data

 .../devicetree/bindings/mtd/marvell-nand.txt       |  123 +
 .../devicetree/bindings/mtd/pxa3xx-nand.txt        |   50 -
 arch/arm/configs/cm_x300_defconfig                 |    2 +-
 arch/arm/configs/mvebu_v7_defconfig                |    2 +-
 arch/arm/configs/pxa3xx_defconfig                  |    3 +-
 arch/arm/configs/pxa_defconfig                     |    2 +-
 arch/arm/configs/raumfeld_defconfig                |    2 +-
 arch/arm/mach-mmp/ttc_dkb.c                        |    4 +-
 arch/arm/mach-pxa/cm-x300.c                        |    8 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c                 |    8 +-
 arch/arm/mach-pxa/colibri.h                        |    2 +-
 arch/arm/mach-pxa/littleton.c                      |   10 +-
 arch/arm/mach-pxa/mxm8x10.c                        |   10 +-
 arch/arm/mach-pxa/raumfeld.c                       |    6 +-
 arch/arm/mach-pxa/zylonite.c                       |   10 +-
 drivers/mtd/nand/Kconfig                           |   18 +-
 drivers/mtd/nand/Makefile                          |    2 +-
 drivers/mtd/nand/marvell_nand.c                    | 2941 ++++++++++++++++++++
 drivers/mtd/nand/pxa3xx_nand.c                     | 2104 --------------
 include/linux/platform_data/mtd-nand-pxa3xx.h      |   43 +-
 20 files changed, 3115 insertions(+), 2235 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/mtd/marvell-nand.txt
 delete mode 100644 Documentation/devicetree/bindings/mtd/pxa3xx-nand.txt
 create mode 100644 drivers/mtd/nand/marvell_nand.c
 delete mode 100644 drivers/mtd/nand/pxa3xx_nand.c
Signature · 1 line
2.11.0
#1 lore raw

PATCH v2 1/5 dt-bindings: mtd: add Marvell NAND controller documentation

Document the legacy and the new bindings for Marvell NAND controller.

The pxa3xx_nand.c driver does only support legacy bindings, which are incomplete and inaccurate. A rework of this controller (called marvell_nand.c) does support both.

---
 .../devicetree/bindings/mtd/marvell-nand.txt       | 123 +++++++++++++++++++++
 1 file changed, 123 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/marvell-nand.txt
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +123 -0
new file mode 100644
index 000000000000..aa6a1ed045b2
@@ -0,0 +1,123 @@
+Marvell NAND Flash Controller (NFC)
+
+Required properties:
+- compatible: can be one of the following:
+    * "marvell,armada-8k-nand-controller"
+    * "marvell,armada370-nand-controller"
+    * "marvell,pxa3xx-nand-controller"
+    * "marvell,armada-8k-nand" (deprecated)
+    * "marvell,armada370-nand" (deprecated)
+    * "marvell,pxa3xx-nand" (deprecated)
+  Compatibles marked deprecated support only the old bindings described
+  at the bottom.
+- reg: NAND flash controller memory area.
+- #address-cells: shall be set to 1. Encode the NAND CS.
+- #size-cells: shall be set to 0.
+- interrupts: shall define the NAND controller interrupt.
+- clocks: shall reference the NAND controller clock.
+- marvell,system-controller: Set to retrieve the syscon node that handles
+  NAND controller related registers (only required with the
+  "marvell,armada-8k-nand[-controller]" compatibles).
+
+Optional properties:
+- label: see partition.txt. New platforms shall omit this property.
+- dmas: shall reference DMA channel associated to the NAND controller.
+  This property is only used with "marvell,pxa3xx-nand[-controller]"
+  compatible strings.
+- dma-names: shall be "rxtx".
+  This property is only used with "marvell,pxa3xx-nand[-controller]"
+  compatible strings.
+
+Optional children nodes:
+Children nodes represent the available NAND chips.
+
+Required properties:
+- reg: shall contain the native Chip Select ids (0-3)
+- marvell,rb: shall contain the native Ready/Busy ids (0-1)
+
+Optional properties:
+- marvell,nand-keep-config: orders the driver not to take the timings
+  from the core and leaving them completely untouched. Bootloader
+  timings will then be used.
+- label: MTD name.
+- nand-on-flash-bbt: see nand.txt.
+- nand-ecc-mode: see nand.txt. Will use hardware ECC if not specified.
+- nand-ecc-algo: see nand.txt. This property may be added when using
+  hardware ECC for clarification but will be ignored by the driver
+  because ECC mode is chosen depending on the page size and the strength
+  required by the NAND chip. This value may be overwritten with
+  nand-ecc-strength property.
+- nand-ecc-strength: see nand.txt.
+- nand-ecc-step-size: see nand.txt. This has no effect and will be
+  ignored by the driver when using hardware ECC because Marvell's NAND
+  flash controller does use fixed strength (1-bit for Hamming, 16-bit
+  for BCH), so the step size will shrink or grow in order to fit the
+  required strength. Step sizes are not completely random for all and
+  follow certain patterns described in AN-379, "Marvell SoC NFC ECC".
+
+See Documentation/devicetree/bindings/mtd/nand.txt for more details on
+generic bindings.
+
+
+Example:
+nand_controller: nand-controller at d0000 {
+	compatible = "marvell,armada370-nand-controller";
+	reg = <0xd0000 0x54>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+	clocks = <&coredivclk 0>;
+
+	nand at 0 {
+		reg = <0>;
+		label = "main-storage";
+		marvell,rb = <0>;
+		nand-ecc-mode = "hw";
+		marvell,nand-keep-config;
+		nand-on-flash-bbt;
+		nand-ecc-strength = <4>;
+		nand-ecc-step-size = <512>;
+
+		partitions {
+			compatible = "fixed-partitions";
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			partition at 0 {
+				label = "Rootfs";
+				reg = <0x00000000 0x40000000>;
+			};
+		};
+	};
+};
+
+
+Note on legacy bindings: One can find, in not-updated device trees,
+bindings slightly differents than described above with other properties
+described below as well as the partitions node at the root of a so
+called "nand" node (without clear controller/chip separation).
+
+Legacy properties:
+- marvell,nand-enable-arbiter: To enable the arbiter, all boards blindly
+  used it, this bit was set by the bootloader for many boards and even if
+  it is marked reserved in several datasheets, it might be needed to set
+  it (otherwise it is harmless) so whether or not this property is set,
+  the bit is selected by the driver.
+- num-cs: Number of chip-select lines to use, all boards blindly set 1
+  to this and for a reason, other values would have failed. The value of
+  this property is ignored.
+
+Example:
+
+	nand0: nand at 43100000 {
+		compatible = "marvell,pxa3xx-nand";
+		reg = <0x43100000 90>;
+		interrupts = <45>;
+		dmas = <&pdma 97 0>;
+		dma-names = "rxtx";
+		#address-cells = <1>;
+		marvell,nand-keep-config;
+		marvell,nand-enable-arbiter;
+		num-cs = <1>;
+		/* Partitions (optional) */
+       };
Signature · 1 line
2.11.0
maintainer
#2 lore raw

PATCH v2 1/5 dt-bindings: mtd: add Marvell NAND controller documentation

Quoted 44 lines · Miquel Raynal

Document the legacy and the new bindings for Marvell NAND controller.

The pxa3xx_nand.c driver does only support legacy bindings, which are incomplete and inaccurate. A rework of this controller (called marvell_nand.c) does support both.

---
 .../devicetree/bindings/mtd/marvell-nand.txt       | 123 +++++++++++++++++++++
 1 file changed, 123 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/marvell-nand.txt
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +26 -0
new file mode 100644
index 000000000000..aa6a1ed045b2
@@ -0,0 +1,123 @@
+Marvell NAND Flash Controller (NFC)
+
+Required properties:
+- compatible: can be one of the following:
+    * "marvell,armada-8k-nand-controller"
+    * "marvell,armada370-nand-controller"
+    * "marvell,pxa3xx-nand-controller"
+    * "marvell,armada-8k-nand" (deprecated)
+    * "marvell,armada370-nand" (deprecated)
+    * "marvell,pxa3xx-nand" (deprecated)
+  Compatibles marked deprecated support only the old bindings described
+  at the bottom.
+- reg: NAND flash controller memory area.
+- #address-cells: shall be set to 1. Encode the NAND CS.
+- #size-cells: shall be set to 0.
+- interrupts: shall define the NAND controller interrupt.
+- clocks: shall reference the NAND controller clock.
+- marvell,system-controller: Set to retrieve the syscon node that handles
+  NAND controller related registers (only required with the
+  "marvell,armada-8k-nand[-controller]" compatibles).
+
+Optional properties:
+- label: see partition.txt. New platforms shall omit this property.
+- dmas: shall reference DMA channel associated to the NAND controller.
+  This property is only used with "marvell,pxa3xx-nand[-controller]"
+  compatible strings.

Documentation/devicetree/bindings/mtd/marvell-nand.txt line 36

Quoted 10 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +10 -0
+- dma-names: shall be "rxtx".
+  This property is only used with "marvell,pxa3xx-nand[-controller]"
+  compatible strings.
+
+Optional children nodes:
+Children nodes represent the available NAND chips.
+
+Required properties:
+- reg: shall contain the native Chip Select ids (0-3)
+- marvell,rb: shall contain the native Ready/Busy ids (0-1)

We already have at least 2 other <vendor>,rb properties. Let's not add a 3rd and make a common one instead.

Documentation/devicetree/bindings/mtd/marvell-nand.txt line 49

Quoted 13 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +13 -0
+
+Optional properties:
+- marvell,nand-keep-config: orders the driver not to take the timings
+  from the core and leaving them completely untouched. Bootloader
+  timings will then be used.
+- label: MTD name.
+- nand-on-flash-bbt: see nand.txt.
+- nand-ecc-mode: see nand.txt. Will use hardware ECC if not specified.
+- nand-ecc-algo: see nand.txt. This property may be added when using
+  hardware ECC for clarification but will be ignored by the driver
+  because ECC mode is chosen depending on the page size and the strength
+  required by the NAND chip. This value may be overwritten with
+  nand-ecc-strength property.

If not used, then drop it.

Documentation/devicetree/bindings/mtd/marvell-nand.txt line 56

Quoted 7 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +7 -0
+- nand-ecc-strength: see nand.txt.
+- nand-ecc-step-size: see nand.txt. This has no effect and will be
+  ignored by the driver when using hardware ECC because Marvell's NAND
+  flash controller does use fixed strength (1-bit for Hamming, 16-bit
+  for BCH), so the step size will shrink or grow in order to fit the
+  required strength. Step sizes are not completely random for all and
+  follow certain patterns described in AN-379, "Marvell SoC NFC ECC".

Same here.

Quoted 30 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +30 -0
+
+See Documentation/devicetree/bindings/mtd/nand.txt for more details on
+generic bindings.
+
+
+Example:
+nand_controller: nand-controller at d0000 {
+	compatible = "marvell,armada370-nand-controller";
+	reg = <0xd0000 0x54>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
+	clocks = <&coredivclk 0>;
+
+	nand at 0 {
+		reg = <0>;
+		label = "main-storage";
+		marvell,rb = <0>;
+		nand-ecc-mode = "hw";
+		marvell,nand-keep-config;
+		nand-on-flash-bbt;
+		nand-ecc-strength = <4>;
+		nand-ecc-step-size = <512>;
+
+		partitions {
+			compatible = "fixed-partitions";
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			partition at 0 {

Documentation/devicetree/bindings/mtd/marvell-nand.txt line 96

Quoted 10 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +10 -0
+				label = "Rootfs";
+				reg = <0x00000000 0x40000000>;
+			};
+		};
+	};
+};
+
+
+Note on legacy bindings: One can find, in not-updated device trees,
+bindings slightly differents than described above with other properties

s/differents/different/

Quoted 30 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +27 -0
+described below as well as the partitions node at the root of a so
+called "nand" node (without clear controller/chip separation).
+
+Legacy properties:
+- marvell,nand-enable-arbiter: To enable the arbiter, all boards blindly
+  used it, this bit was set by the bootloader for many boards and even if
+  it is marked reserved in several datasheets, it might be needed to set
+  it (otherwise it is harmless) so whether or not this property is set,
+  the bit is selected by the driver.
+- num-cs: Number of chip-select lines to use, all boards blindly set 1
+  to this and for a reason, other values would have failed. The value of
+  this property is ignored.
+
+Example:
+
+	nand0: nand at 43100000 {
+		compatible = "marvell,pxa3xx-nand";
+		reg = <0x43100000 90>;
+		interrupts = <45>;
+		dmas = <&pdma 97 0>;
+		dma-names = "rxtx";
+		#address-cells = <1>;
+		marvell,nand-keep-config;
+		marvell,nand-enable-arbiter;
+		num-cs = <1>;
+		/* Partitions (optional) */
+       };
Signature · 1 line
2.11.0
#3 lore raw

PATCH v2 1/5 dt-bindings: mtd: add Marvell NAND controller documentation

Hi Rob,

Quoted 57 lines · On Wed, 20 Dec 2017 15:05:11 -0600 Rob Herring <[email protected]> wrote:
Quoted 56 lines · Miquel Raynal

Document the legacy and the new bindings for Marvell NAND controller.

The pxa3xx_nand.c driver does only support legacy bindings, which are incomplete and inaccurate. A rework of this controller (called marvell_nand.c) does support both.

---
 .../devicetree/bindings/mtd/marvell-nand.txt       | 123
+++++++++++++++++++++ 1 file changed, 123 insertions(+)
 create mode 100644

Documentation/devicetree/bindings/mtd/marvell-nand.txt

(quoted diff) +0 -0

b/Documentation/devicetree/bindings/mtd/marvell-nand.txt new file mode 100644 index 000000000000..aa6a1ed045b2

Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +11 -0
@@ -0,0 +1,123 @@
+Marvell NAND Flash Controller (NFC)
+
+Required properties:
+- compatible: can be one of the following:
+    * "marvell,armada-8k-nand-controller"
+    * "marvell,armada370-nand-controller"
+    * "marvell,pxa3xx-nand-controller"
+    * "marvell,armada-8k-nand" (deprecated)
+    * "marvell,armada370-nand" (deprecated)
+    * "marvell,pxa3xx-nand" (deprecated)
+  Compatibles marked deprecated support only the old bindings

described
+ at the bottom.

+- reg: NAND flash controller memory area.
+- #address-cells: shall be set to 1. Encode the NAND CS.
+- #size-cells: shall be set to 0.
+- interrupts: shall define the NAND controller interrupt.
+- clocks: shall reference the NAND controller clock.
+- marvell,system-controller: Set to retrieve the syscon node that

handles

Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +6 -0
+  NAND controller related registers (only required with the
+  "marvell,armada-8k-nand[-controller]" compatibles).
+
+Optional properties:
+- label: see partition.txt. New platforms shall omit this property.
+- dmas: shall reference DMA channel associated to the NAND

controller.
+ This property is only used with
"marvell,pxa3xx-nand[-controller]"

Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +3 -0
+  compatible strings.
+- dma-names: shall be "rxtx".
+  This property is only used with

"marvell,pxa3xx-nand[-controller]"

Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +3 -0
+  compatible strings.
+
+Optional children nodes:
Quoted 8 lines

Documentation/devicetree/bindings/mtd/marvell-nand.txt line 36

Quoted 5 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +5 -0
+Children nodes represent the available NAND chips.
+
+Required properties:
+- reg: shall contain the native Chip Select ids (0-3)
+- marvell,rb: shall contain the native Ready/Busy ids (0-1)  

We already have at least 2 other <vendor>,rb properties. Let's not add a 3rd and make a common one instead.

I switched to "nand-rb", added this property in nand.txt (in another commit) and updated all the DT accordingly.

Quoted 11 lines
Quoted 11 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +3 -0
+
+Optional properties:
+- marvell,nand-keep-config: orders the driver not to take the

timings

Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +5 -0
+  from the core and leaving them completely untouched. Bootloader
+  timings will then be used.
+- label: MTD name.
+- nand-on-flash-bbt: see nand.txt.
+- nand-ecc-mode: see nand.txt. Will use hardware ECC if not

specified. +- nand-ecc-algo: see nand.txt. This property may be

Quoted 8 lines

Documentation/devicetree/bindings/mtd/marvell-nand.txt line 49

Quoted 6 lines

added when using

Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +2 -0
+  hardware ECC for clarification but will be ignored by the driver
+  because ECC mode is chosen depending on the page size and the

strength

Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +2 -0
+  required by the NAND chip. This value may be overwritten with
+  nand-ecc-strength property.  

If not used, then drop it.

Well, nand-ecc-algo will be useful when not using the ECC engine, so it cannot be removed. I tried to explain better the situation.

Quoted 14 lines
Quoted 4 lines
+- nand-ecc-strength: see nand.txt.
+- nand-ecc-step-size: see nand.txt. This has no effect and will be
+  ignored by the driver when using hardware ECC because Marvell's
Quoted 9 lines

NAND
+ flash controller does use fixed strength (1-bit for Hamming, 16-bit
+ for BCH), so the step size will shrink or grow in order to fit the
+ required strength. Step sizes are not completely random for all and
+ follow certain patterns described in AN-379, "Marvell SoC NFC ECC".

Quoted 1 line

Same here.

The comment about being ignored was not accurate, I simply removed that part.

Thanks,
Miqu?l

#4 lore raw

PATCH v2 2/5 mtd: nand: add reworked Marvell NAND controller driver

In reply to Miquel Raynal

Add marvell_nand driver which aims at replacing the existing pxa3xx_nand driver.

The new driver intends to be easier to understand and follows the brand new NAND framework rules by implementing hooks for every pattern the controller might support and referencing them inside a parser object that will be given to the core at each ->exec_op() call.

Raw accessors are implemented, useful to test/debug memory/filesystem corruptions. Userspace binaries contained in the mtd-utils package may now be used and their output trusted.

Timings may not be kept from the bootloader anymore, the timings used for instance in U-Boot were not optimal and it supposed to have NAND support (and initialized) in the bootloader.

Thanks to the improved timings, implementation of ONFI mode 5 support (with EDO managed by adding a delay on data sampling), merging the commands together and optimizing writes in the command registers, the new driver may achieve faster throughputs in both directions. Measurements show an improvement of about +23% read throughput and +24% write throughput. These measurements have been done with an Armada-385-DB-AP (4kiB NAND pages forced in 4-bit strength BCH ECC correction) using the userspace tool 'flash_speed' from the MTD test suite.

Besides these important topics, the new driver addresses several unsolved known issues in the old driver which:
- did not work with ECC soft neither with ECC none ;
- relied on naked read/write (which is unchanged) while the NFCv1 embedded in the pxa3xx platforms do not implement it, so several NAND commands did not actually ever work without any notice (like reading the ONFI PARAM_PAGE or SET/GET_FEATURES) ;
- wrote the OOB data correctly, but was not able to read it correctly past the first OOB data chunk ;
- did not displayed ECC bytes ;
- used device tree bindings that did not allow more than one NAND chip, and did not allow to choose the correct chip select if not incrementing from 0. Plus, the Ready/Busy line used had to be 0.

Old device tree bindings are still supported but deprecated. A more hierarchical view has to be used to keep the controller and the NAND chip structures clearly separated both inside the device tree and also in the driver code.

---
 drivers/mtd/nand/Kconfig        |   12 +
 drivers/mtd/nand/Makefile       |    1 +
 drivers/mtd/nand/marvell_nand.c | 2942 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 2955 insertions(+)
 create mode 100644 drivers/mtd/nand/marvell_nand.c
drivers/mtd/nand/Kconfig +12 -0
index 859eb7790c46..9e141e03f5c2 100644
@@ -323,6 +323,18 @@ config MTD_NAND_PXA3xx
 	  platforms (XP, 370, 375, 38x, 39x) and 64-bit Armada
 	  platforms (7K, 8K) (NFCv2).
 
+config MTD_NAND_MARVELL
+	tristate "NAND controller support on Marvell boards"
+	depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \
+		   COMPILE_TEST
+	depends on HAS_IOMEM
+	help
+	  This enables the NAND flash controller driver for Marvell boards,
+	  including:
+	  - PXA3xx processors (NFCv1)
+	  - 32-bit Armada platforms (XP, 37x, 38x, 39x) (NFCv2)
+	  - 64-bit Aramda platforms (7k, 8k) (NFCv2)
+
 config MTD_NAND_SLC_LPC32XX
 	tristate "NXP LPC32xx SLC Controller"
 	depends on ARCH_LPC32XX
drivers/mtd/nand/Makefile +1 -0
index 118a1349aad3..921634ba400c 100644
@@ -32,6 +32,7 @@ obj-$(CONFIG_MTD_NAND_OMAP2) 		+= omap2_nand.o
 obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD)	+= omap_elm.o
 obj-$(CONFIG_MTD_NAND_CM_X270)		+= cmx270_nand.o
 obj-$(CONFIG_MTD_NAND_PXA3xx)		+= pxa3xx_nand.o
+obj-$(CONFIG_MTD_NAND_MARVELL)		+= marvell_nand.o
 obj-$(CONFIG_MTD_NAND_TMIO)		+= tmio_nand.o
 obj-$(CONFIG_MTD_NAND_PLATFORM)		+= plat_nand.o
 obj-$(CONFIG_MTD_NAND_PASEMI)		+= pasemi_nand.o
drivers/mtd/nand/marvell_nand.c new file +2942 -0

2,942 lines not on this page · raw message

Signature · 1 line
2.11.0
#5 lore raw

PATCH v2 2/5 mtd: nand: add reworked Marvell NAND controller driver

Hi Miquel,

Quoted 8 lines · On Tue, 19 Dec 2017 14:29:39 +0100 Miquel Raynal…

Add marvell_nand driver which aims at replacing the existing pxa3xx_nand driver.

The new driver intends to be easier to understand and follows the brand new NAND framework rules by implementing hooks for every pattern the controller might support and referencing them inside a parser object that will be given to the core at each ->exec_op() call.

Quoted 7 lines

Raw accessors are implemented, useful to test/debug memory/filesystem corruptions. Userspace binaries contained in the mtd-utils package may now be used and their output trusted.

Timings may not be kept from the bootloader anymore, the timings used for instance in U-Boot were not optimal and it supposed to have NAND support (and initialized) in the bootloader.

Hm, AFAIR the old driver was able to dynamically adjust the timings when the NAND was ONFI compliant.

Quoted 11 lines

Thanks to the improved timings, implementation of ONFI mode 5 support (with EDO managed by adding a delay on data sampling), merging the commands together and optimizing writes in the command registers, the new driver may achieve faster throughputs in both directions. Measurements show an improvement of about +23% read throughput and +24% write throughput. These measurements have been done with an Armada-385-DB-AP (4kiB NAND pages forced in 4-bit strength BCH ECC correction) using the userspace tool 'flash_speed' from the MTD test suite.

Quoted 10 lines

Besides these important topics, the new driver addresses several unsolved known issues in the old driver which:
- did not work with ECC soft neither with ECC none ;
- relied on naked read/write (which is unchanged) while the NFCv1 embedded in the pxa3xx platforms do not implement it, so several NAND commands did not actually ever work without any notice (like reading the ONFI PARAM_PAGE or SET/GET_FEATURES) ;
- wrote the OOB data correctly, but was not able to read it correctly past the first OOB data chunk ;
- did not displayed ECC bytes ;

^display

and I'm not even sure display is the right term here. How about 'retrieve'.

Quoted 60 lines

- used device tree bindings that did not allow more than one NAND chip, and did not allow to choose the correct chip select if not incrementing from 0. Plus, the Ready/Busy line used had to be 0.

Old device tree bindings are still supported but deprecated. A more hierarchical view has to be used to keep the controller and the NAND chip structures clearly separated both inside the device tree and also in the driver code.

---
 drivers/mtd/nand/Kconfig        |   12 +
 drivers/mtd/nand/Makefile       |    1 +
 drivers/mtd/nand/marvell_nand.c | 2942 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 2955 insertions(+)
 create mode 100644 drivers/mtd/nand/marvell_nand.c
drivers/mtd/nand/Kconfig +12 -0
index 859eb7790c46..9e141e03f5c2 100644
@@ -323,6 +323,18 @@ config MTD_NAND_PXA3xx
 	  platforms (XP, 370, 375, 38x, 39x) and 64-bit Armada
 	  platforms (7K, 8K) (NFCv2).
 
+config MTD_NAND_MARVELL
+	tristate "NAND controller support on Marvell boards"
+	depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \
+		   COMPILE_TEST
+	depends on HAS_IOMEM
+	help
+	  This enables the NAND flash controller driver for Marvell boards,
+	  including:
+	  - PXA3xx processors (NFCv1)
+	  - 32-bit Armada platforms (XP, 37x, 38x, 39x) (NFCv2)
+	  - 64-bit Aramda platforms (7k, 8k) (NFCv2)
+
 config MTD_NAND_SLC_LPC32XX
 	tristate "NXP LPC32xx SLC Controller"
 	depends on ARCH_LPC32XX
drivers/mtd/nand/Makefile +1 -0
index 118a1349aad3..921634ba400c 100644
@@ -32,6 +32,7 @@ obj-$(CONFIG_MTD_NAND_OMAP2) 		+= omap2_nand.o
 obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD)	+= omap_elm.o
 obj-$(CONFIG_MTD_NAND_CM_X270)		+= cmx270_nand.o
 obj-$(CONFIG_MTD_NAND_PXA3xx)		+= pxa3xx_nand.o
+obj-$(CONFIG_MTD_NAND_MARVELL)		+= marvell_nand.o
 obj-$(CONFIG_MTD_NAND_TMIO)		+= tmio_nand.o
 obj-$(CONFIG_MTD_NAND_PLATFORM)		+= plat_nand.o
 obj-$(CONFIG_MTD_NAND_PASEMI)		+= pasemi_nand.o
drivers/mtd/nand/marvell_nand.c new file +0 -0
new file mode 100644
index 000000000000..0a5432bcda6a

drivers/mtd/nand/marvell_nand.c line 7

Quoted 7 lines
drivers/mtd/nand/marvell_nand.c new file +7 -0
@@ -0,0 +1,2942 @@
+/*
+ * Marvell NAND flash controller driver
+ *
+ * Copyright (C) 2017 Marvell
+ * Author: Miquel RAYNAL <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0

AFAIU (maybe I'm wrong), this should be a single line comment (C++-style) on the first line of the file:

// SPDX-License-Identifier: GPL-2.0
/*
* Marvell NAND flash controller driver
*
* Copyright (C) 2017 Marvell
* Author: Miquel RAYNAL <[email protected]> */

Quoted 57 lines
drivers/mtd/nand/marvell_nand.c new file +57 -0
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/of_platform.h>
+#include <linux/iopoll.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <asm/unaligned.h>
+
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma/pxa-dma.h>
+#include <linux/platform_data/mtd-nand-pxa3xx.h>
+
+/* Data FIFO granularity, FIFO reads/writes must be a multiple of this length */
+#define FIFO_DEPTH		8
+#define FIFO_REP(x)		(x / sizeof(u32))
+#define BCH_SEQ_READS		(32 / FIFO_DEPTH)
+/* NFC does not support transfers of larger chunks at a time */
+#define MAX_CHUNK_SIZE		2112
+/* NFCv1 cannot read more that 7 bytes of ID */
+#define NFCV1_READID_LEN	7
+/* Polling is done at a pace of POLL_PERIOD us until POLL_TIMEOUT is reached */
+#define POLL_PERIOD		0
+#define POLL_TIMEOUT		100000
+/* Interrupt maximum wait period in ms */
+#define IRQ_TIMEOUT		1000
+/* Latency in clock cycles between SoC pins and NFC logic */
+#define MIN_RD_DEL_CNT		3
+/* Maximum number of contiguous address cycles */
+#define MAX_ADDRESS_CYC_NFCV1	5
+#define MAX_ADDRESS_CYC_NFCV2	7
+/* System control registers/bits to enable the NAND controller on some SoCs */
+#define GENCONF_SOC_DEVICE_MUX	0x208
+#define GENCONF_SOC_DEVICE_MUX_NFC_EN BIT(0)
+#define GENCONF_SOC_DEVICE_MUX_ECC_CLK_RST BIT(20)
+#define GENCONF_SOC_DEVICE_MUX_ECC_CORE_RST BIT(21)
+#define GENCONF_SOC_DEVICE_MUX_NFC_INT_EN BIT(25)
+#define GENCONF_CLK_GATING_CTRL	0x220
+#define GENCONF_CLK_GATING_CTRL_ND_GATE BIT(2)
+#define GENCONF_ND_CLK_CTRL	0x700
+#define GENCONF_ND_CLK_CTRL_EN	BIT(0)
+
+/* NAND controller data flash control register */
+#define NDCR			0x00
+/* NAND interface timing parameter 0 register */
+#define NDTR0			0x04
+/* NAND interface timing parameter 1 register */
+#define NDTR1			0x0C
+/* NAND controller status register */
+#define NDSR			0x14
+/* NAND ECC control register */
+#define NDECCCTRL		0x28

drivers/mtd/nand/marvell_nand.c line 74

Quoted 10 lines
drivers/mtd/nand/marvell_nand.c new file +10 -0
+/* NAND controller data buffer register */
+#define NDDB			0x40
+/* NAND controller command buffer 0 register */
+#define NDCB0			0x48
+/* NAND controller command buffer 1 register */
+#define NDCB1			0x4C
+/* NAND controller command buffer 2 register */
+#define NDCB2			0x50
+/* NAND controller command buffer 3 register */
+#define NDCB3			0x54

Just nitpicking, but I find it clearer when register offsets and register fields are defined next to each other:

#define MY_REG 0xYYYY
#define MY_REG_FIELDX BIT(X)
...

Quoted 17 lines
drivers/mtd/nand/marvell_nand.c new file +17 -0
+
+/* Data flash control register bitfields */
+#define NDCR_ALL_INT		GENMASK(11, 0)
+#define NDCR_CS1_CMDDM		BIT(7)
+#define NDCR_CS0_CMDDM		BIT(8)
+#define NDCR_RDYM		BIT(11)
+#define NDCR_ND_ARB_EN		BIT(12)
+#define NDCR_RA_START		BIT(15)
+#define NDCR_RD_ID_CNT(x)	(min_t(unsigned int, x, 0x7) << 16)
+#define NDCR_PAGE_SZ(x)		(x >= 2048 ? BIT(24) : 0)
+#define NDCR_DWIDTH_M		BIT(26)
+#define NDCR_DWIDTH_C		BIT(27)
+#define NDCR_ND_RUN		BIT(28)
+#define NDCR_DMA_EN		BIT(29)
+#define NDCR_ECC_EN		BIT(30)
+#define NDCR_SPARE_EN		BIT(31)
+

[...]

drivers/mtd/nand/marvell_nand.c

Quoted 2 lines
drivers/mtd/nand/marvell_nand.c new file +2 -0
+
+/*

You seem to use the kerneldoc format, so you should probably start all your comments with:

/**

Quoted 2 lines

+ * Marvell ECC engine works differently than the others, in order to limit the
+ * size of the IP, hardware engineers choose to set a fixed strength at 16 bits

^chose

Quoted 8 lines

+ * per subpage, and depending on a the desired strength needed by the NAND chip,
+ * a particular layout mixing data/spare/ecc is defined, with a possible last
+ * chunk smaller that the others.
+ *
+ * @writesize: Full page size on which the layout applies
+ * @chunk: Desired ECC chunk size on which the layout applies
+ * @strength: Desired ECC strength (per chunk size bytes) on which the
+ * layout applies

Quoted 8 lines

+ * @full_chunk_cnt: Number of full-sized chunks, which is the number of
+ * repetitions of the pattern:
+ * (data_bytes + spare_bytes + ecc_bytes).
+ * @data_bytes: Number of data bytes per chunk
+ * @spare_bytes: Number of spare bytes per chunk
+ * @ecc_bytes: Number of ecc bytes per chunk
+ * @last_chunk_cnt: If there is a last chunk with a different size than
+ * the first ones, the next fields may not be empty

A boolean named has_last_chunk would be more appropriate since you can only have one last chunk.

Quoted 20 lines

+ * @last_data_bytes: Number of data bytes in the last chunk
+ * @last_spare_bytes: Number of spare bytes in the last chunk
+ * @last_ecc_bytes: Number of ecc bytes in the last chunk
+ */

drivers/mtd/nand/marvell_nand.c new file +16 -0
+struct marvell_hw_ecc_layout {
+	/* Constraints */
+	int writesize;
+	int chunk;
+	int strength;
+	/* Corresponding layout */
+	int full_chunk_cnt;
+	int data_bytes;
+	int spare_bytes;
+	int ecc_bytes;
+	int last_chunk_cnt;
+	int last_data_bytes;
+	int last_spare_bytes;
+	int last_ecc_bytes;
+};
+

[...]

Quoted 27 lines
drivers/mtd/nand/marvell_nand.c new file +27 -0
+/*
+ * NAND controller structure: stores Marvell NAND controller information
+ *
+ * @controller:		Base controller structure
+ * @dev:		Parent device (used to print error messages)
+ * @regs:		NAND controller registers
+ * @ecc_clk:		ECC block clock, two times the NAND controller clock
+ * @complete:		Completion object to wait for NAND controller events
+ * @assigned_cs:	Bitmask describing already assigned CS lines
+ * @chips:		List containing all the NAND chips attached to
+ *			this NAND controller
+ * @caps:		NAND controller capabilities for each compatible string
+ * @buf:		Controller local buffer to store a part of the read
+ *			buffer when the read operation was not 8 bytes aligned
+ *			as is the FIFO.
+ * @buf_pos:		Position in the 'buf' buffer
+ * @dma_chan:		DMA channel (NFCv1 only)
+ * @dma_buf:		32-bit aligned buffer for DMA transfers (NFCv1 only)
+ */
+struct marvell_nfc {
+	struct nand_hw_control controller;
+	struct device *dev;
+	void __iomem *regs;
+	struct clk *ecc_clk;
+	struct completion complete;
+	unsigned long assigned_cs;
+	struct list_head chips;

drivers/mtd/nand/marvell_nand.c line 341

Quoted 10 lines
drivers/mtd/nand/marvell_nand.c new file +10 -0
+	struct nand_chip *selected_chip;
+	const struct marvell_nfc_caps *caps;
+
+	/*
+	 * Buffer handling: @buf will be accessed byte-per-byter but also
+	 * int-per-int when exchanging data with the NAND controller FIFO,
+	 * 32-bit alignment is then required.
+	 */
+	u8 buf[FIFO_DEPTH] __aligned(sizeof(u32));
+	int buf_pos;

I'm almost sure you don't need this temporary buffer. Every time it's used locally, so just allocate an u32 buf[2] on the stack where you need it and you should be good.

Quoted 7 lines
drivers/mtd/nand/marvell_nand.c new file +7 -0
+
+	/* DMA (NFCv1 only) */
+	bool use_dma;
+	struct dma_chan *dma_chan;
+	u8 *dma_buf;
+};
+

[...]

Quoted 6 lines
drivers/mtd/nand/marvell_nand.c new file +6 -0
+/*
+ * Derives a duration in numbers of clock cycles.
+ *
+ * @ps: Duration in pico-seconds
+ * @period_ns:  Clock period in nano-seconds
+ *

drivers/mtd/nand/marvell_nand.c line 400

Quoted 10 lines
drivers/mtd/nand/marvell_nand.c new file +10 -0
+ * Convert the duration in nano-seconds, then divide by the period and
+ * return the number of clock periods.
+ */
+#define TO_CYCLES(ps, period_ns) (DIV_ROUND_UP(ps / 1000, period_ns))
+
+/*
+ * NAND driver structure filled during the parsing of the ->exec_op() subop
+ * subset of instructions.
+ *
+ * @ndcb:		Array for the values of the NDCBx registers

Array of values written to NDCBx registers

Quoted 16 lines

+ * @cle_ale_delay_ns: Optional delay after the last CMD or ADDR cycle
+ * @rdy_timeout_ms: Timeout for waits on Ready/Busy pin
+ * @rdy_delay_ns: Optional delay after waiting for the RB pin
+ * @data_delay_ns: Optional delay after the data xfer
+ * @data_instr_idx: Index of the data instruction in the subop
+ * @data_instr: Pointer to the data instruction in the subop
+ */

drivers/mtd/nand/marvell_nand.c new file +9 -0
+struct marvell_nfc_op {
+	u32 ndcb[4];
+	unsigned int cle_ale_delay_ns;
+	unsigned int rdy_timeout_ms;
+	unsigned int rdy_delay_ns;
+	unsigned int data_delay_ns;
+	unsigned int data_instr_idx;
+	const struct nand_op_instr *data_instr;
+};

[...]

drivers/mtd/nand/marvell_nand.c line 463

Quoted 4 lines
drivers/mtd/nand/marvell_nand.c new file +4 -0
+
+/*
+ * The core may ask the controller to use only 8-bit accesses while usually
+ * using 16-bit accesses. Later function may blindly call this one with a

^Later, callers of this function may ...

Quoted 1 line

+ * boolean to indicate if 8-bit accesses must be enabled of disabled without

							    ^or
Quoted 2 lines

+ * knowing if 16-bit accesses are actually in use.
+ */

I'd move this comment in the code...

drivers/mtd/nand/marvell_nand.c line 472

Quoted 6 lines
drivers/mtd/nand/marvell_nand.c new file +6 -0
+static void marvell_nfc_force_byte_access(struct nand_chip *chip,
+					  bool force_8bit)
+{
+	struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+	u32 ndcr;
+

... like here:

/*
* Callers of this function do not verify if the NAND is
* using a 16-bit or an 8-bit bus for normal operations, so we
* need to take care of that here by leaving the configuration
* unchanged if the NAND does not have the NAND_BUSWIDTH_16
* flag set.
*/

Quoted 12 lines
drivers/mtd/nand/marvell_nand.c new file +12 -0
+	if (!(chip->options & NAND_BUSWIDTH_16))
+		return;
+
+	ndcr = readl_relaxed(nfc->regs + NDCR);
+
+	if (force_8bit)
+		ndcr &= ~(NDCR_DWIDTH_M | NDCR_DWIDTH_C);
+	else
+		ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C;
+
+	writel_relaxed(ndcr, nfc->regs + NDCR);
+}

[...]

Quoted 4 lines
drivers/mtd/nand/marvell_nand.c new file +4 -0
+
+/*
+ * Any time a command has to be sent to the controller, the following sequence
+ * has to be followed:

drivers/mtd/nand/marvell_nand.c line 521

Quoted 10 lines
drivers/mtd/nand/marvell_nand.c new file +10 -0
+ * - call marvell_nfc_prepare_cmd()
+ *      -> activate the ND_RUN bit that will kind of 'start a job'
+ *      -> wait the signal indicating the NFC is waiting for a command
+ * - send the command (cmd and address cycles)
+ * - enventually send or receive the data
+ * - call marvell_nfc_end_cmd() with the corresponding flag
+ *      -> wait the flag to be triggered or cancel the job with a timeout
+ *
+ * The following functions are helpers to do this job and keep in the
+ * specialized functions the code that really does the operations.

I would rephrase it like that:
"
The following helpers are here to factorize the code a bit so that specialized functions responsible for executing the actual NAND operations do not have to replicate the same code blocks. "

Quoted 32 lines

+ */

drivers/mtd/nand/marvell_nand.c new file +31 -0
+static int marvell_nfc_prepare_cmd(struct nand_chip *chip)
+{
+	struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+	u32 ndcr, val;
+	int ret;
+
+	/* Poll ND_RUN and clear NDSR before issuing any command */
+	ret = marvell_nfc_wait_ndrun(chip);
+	if (ret) {
+		dev_err(nfc->dev, "Last operation did not suceed\n");
+		return ret;
+	}
+
+	ndcr = readl_relaxed(nfc->regs + NDCR);
+	writel_relaxed(readl_relaxed(nfc->regs + NDSR), nfc->regs + NDSR);
+
+	/* Assert ND_RUN bit and wait the NFC to be ready */
+	writel_relaxed(ndcr | NDCR_ND_RUN, nfc->regs + NDCR);
+	ret = readl_relaxed_poll_timeout(nfc->regs + NDSR, val,
+					 val & NDSR_WRCMDREQ,
+					 POLL_PERIOD, POLL_TIMEOUT);
+	if (ret) {
+		dev_err(nfc->dev, "Timeout on WRCMDRE\n");
+		return -ETIMEDOUT;
+	}
+
+	/* Command may be written, clear WRCMDREQ status bit */
+	writel_relaxed(NDSR_WRCMDREQ, nfc->regs + NDSR);
+
+	return 0;
+}

[...]

Quoted 29 lines
drivers/mtd/nand/marvell_nand.c new file +29 -0
+
+static void marvell_nfc_select_chip(struct mtd_info *mtd, int die_nr)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
+	struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+	u32 ndcr;
+
+	if (chip == nfc->selected_chip && die_nr == marvell_nand->selected_die)
+		return;
+
+	if (die_nr < 0 || die_nr >= marvell_nand->nsels) {
+		nfc->selected_chip = NULL;
+		marvell_nand->selected_die = -1;
+		return;
+	}
+
+	/*
+	 * Do not change the timing registers when using the DT property
+	 * marvell,nand-keep-config; in that case ->ndtr0 and ->ndtr1 from the
+	 * marvell_nand structure are supposedly empty.
+	 */
+	if (marvell_nand->ndtr0 && marvell_nand->ndtr1) {
+		writel_relaxed(marvell_nand->ndtr0, nfc->regs + NDTR0);
+		writel_relaxed(marvell_nand->ndtr1, nfc->regs + NDTR1);
+	}
+
+	ndcr = readl_relaxed(nfc->regs + NDCR);
+

drivers/mtd/nand/marvell_nand.c line 685

Quoted 10 lines
drivers/mtd/nand/marvell_nand.c new file +10 -0
+	/* Ensure controller is not blocked; also clear some fields */
+	ndcr &= ~(NDCR_ND_RUN | NDCR_DWIDTH_M | NDCR_DWIDTH_C |
+		  NDCR_PAGE_SZ(2048));
+
+	/* Adapt bus width */
+	if (chip->options & NAND_BUSWIDTH_16)
+		ndcr |= NDCR_DWIDTH_M | NDCR_DWIDTH_C;
+
+	/* Page size as seen by the controller, either 512B or 2kiB */
+	ndcr |= NDCR_PAGE_SZ(mtd->writesize);

I wonder if we couldn't prepare the value of ->ndcr at probe time (just like we prepare ->ndtrX) so that the only thing you'd have to do here is a

	writel_relaxed(ndcr, nfc->regs + NDCR);
Quoted 11 lines
drivers/mtd/nand/marvell_nand.c new file +11 -0
+
+	/* Update the control register */
+	writel_relaxed(ndcr,  nfc->regs + NDCR);
+
+	/* Also reset the interrupt status register */
+	marvell_nfc_clear_int(nfc, NDCR_ALL_INT);
+
+	nfc->selected_chip = chip;
+	marvell_nand->selected_die = die_nr;
+}
+

[...]

Quoted 48 lines
drivers/mtd/nand/marvell_nand.c new file +48 -0
+
+/* Read/write PIO/DMA accessors */
+static int marvell_nfc_xfer_data_dma(struct marvell_nfc *nfc,
+				     enum dma_data_direction direction,
+				     unsigned int len)
+{
+	unsigned int dma_len = min_t(int, ALIGN(len, 32), MAX_CHUNK_SIZE);
+	struct dma_async_tx_descriptor *tx;
+	struct scatterlist sg;
+	dma_cookie_t cookie;
+	int ret;
+
+	marvell_nfc_enable_dma(nfc);
+	/* Prepare the DMA transfer */
+	sg_init_one(&sg, nfc->dma_buf, dma_len);
+	dma_map_sg(nfc->dma_chan->device->dev, &sg, 1, direction);
+	tx = dmaengine_prep_slave_sg(nfc->dma_chan, &sg, 1,
+				     direction == DMA_FROM_DEVICE ?
+				     DMA_DEV_TO_MEM : DMA_MEM_TO_DEV,
+				     DMA_PREP_INTERRUPT);
+	if (!tx) {
+		dev_err(nfc->dev, "Could not prepare DMA S/G list\n");
+		return -ENXIO;
+	}
+
+	/* Do the task and wait for it to finish */
+	cookie = dmaengine_submit(tx);
+	ret = dma_submit_error(cookie);
+	if (ret)
+		return -EIO;
+
+	dma_async_issue_pending(nfc->dma_chan);
+	ret = marvell_nfc_wait_cmdd(nfc->selected_chip);
+	dma_unmap_sg(nfc->dma_chan->device->dev, &sg, 1, direction);
+	marvell_nfc_disable_dma(nfc);
+	if (ret) {
+		dev_err(nfc->dev, "Timeout waiting for DMA (status: %d)\n",
+			dmaengine_tx_status(nfc->dma_chan, cookie, NULL));
+		dmaengine_terminate_all(nfc->dma_chan);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int marvell_nfc_xfer_data_in_pio(struct marvell_nfc *nfc, u8 *in,
+					unsigned int len)
+{

drivers/mtd/nand/marvell_nand.c line 824

Quoted 10 lines
drivers/mtd/nand/marvell_nand.c new file +10 -0
+	unsigned int last_len = len % FIFO_DEPTH;
+	unsigned int last_full_offset = round_down(len, FIFO_DEPTH);
+	int i;
+
+	for (i = 0; i < last_full_offset; i += FIFO_DEPTH)
+		ioread32_rep(nfc->regs + NDDB, in + i, FIFO_REP(FIFO_DEPTH));
+
+	if (last_len) {
+		ioread32_rep(nfc->regs + NDDB, nfc->buf, FIFO_REP(FIFO_DEPTH));
+		memcpy(in + last_full_offset, nfc->buf, last_len);

This is where you should declare your temporary buffer:

		u32 tmpbuf[FIFO_REP(FIFO_DEPTH)];
		ioread32_rep(nfc->regs + NDDB, tmpbuf, sizeof(tmpbuf));
		memcpy(in + last_full_offset, tmpbuf, last_len);
Quoted 5 lines
drivers/mtd/nand/marvell_nand.c new file +5 -0
+	}
+
+	return 0;
+}
+

[...]

Quoted 20 lines
drivers/mtd/nand/marvell_nand.c new file +20 -0
+
+/*
+ * Check a chunk is correct or not according to hardware ECC engine.
+ * mtd->ecc_stats.corrected is updated, as well as max_bitflips, however
+ * mtd->ecc_stats.failure is not, the function will instead return a non-zero
+ * value indicating that a check on the emptyness of the subpage must be
+ * performed before declaring the subpage corrupted.
+ */
+static int marvell_nfc_hw_ecc_correct(struct nand_chip *chip,
+				      unsigned int *max_bitflips)
+{
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+	int bf = 0;
+	u32 ndsr;
+
+	ndsr = readl_relaxed(nfc->regs + NDSR);
+
+	/* Check uncorrectable error flag */
+	if (ndsr & NDSR_UNCERR) {

drivers/mtd/nand/marvell_nand.c line 909

Quoted 10 lines
drivers/mtd/nand/marvell_nand.c new file +10 -0
+		writel_relaxed(ndsr, nfc->regs + NDSR);
+
+		/*
+		 * Do not increment ->ecc_stats.failed now, instead, return a
+		 * non-zero value to indicate that this chunk was apparently
+		 * bad, and it should be check to see if it empty or not. If
+		 * the chunk (with ECC bytes) is not declared empty, the calling
+		 * function must increment the failure count.
+		 */
+		return -EIO;

-EBADMSG would be better, just to be consistent with the error code returned in case of ECC errors elsewhere.

Quoted 39 lines
drivers/mtd/nand/marvell_nand.c new file +39 -0
+	}
+
+	/* Check correctable error flag */
+	if (ndsr & NDSR_CORERR) {
+		writel_relaxed(ndsr, nfc->regs + NDSR);
+
+		if (chip->ecc.algo == NAND_ECC_BCH)
+			bf = NDSR_ERRCNT(ndsr);
+		else
+			bf = 1;
+	}
+
+	/* Update the stats and max_bitflips */
+	mtd->ecc_stats.corrected += bf;
+	*max_bitflips = max_t(unsigned int, *max_bitflips, bf);
+
+	return 0;
+}
+
+/* Hamming read helpers */
+static int marvell_nfc_hw_ecc_hmg_do_read_page(struct nand_chip *chip, u8 *buf,
+					       bool oob_required, bool raw,
+					       int page)
+{
+	struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
+	struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+	const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
+	struct marvell_nfc_op nfc_op = {
+		.ndcb[0] = NDCB0_CMD_TYPE(TYPE_READ) |
+			   NDCB0_ADDR_CYC(marvell_nand->addr_cyc) |
+			   NDCB0_DBC |
+			   NDCB0_CMD1(NAND_CMD_READ0) |
+			   NDCB0_CMD2(NAND_CMD_READSTART),
+		.ndcb[1] = NDCB1_ADDRS_PAGE(page),
+		.ndcb[2] = NDCB2_ADDR5_PAGE(page),
+	};
+	unsigned int oob_bytes = 0;
+	int ret;
+

drivers/mtd/nand/marvell_nand.c line 958

Quoted 10 lines
drivers/mtd/nand/marvell_nand.c new file +10 -0
+	/* NFCv2 needs more information about the operation being executed */
+	if (nfc->caps->is_nfcv2)
+		nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW);
+
+	ret = marvell_nfc_prepare_cmd(chip);
+	if (ret)
+		return ret;
+
+	if (oob_required) {
+		oob_bytes = lt->spare_bytes;

AFAIR, you have to read the spare bytes when raw is false, so this should be something like:

	if (oob_required || !raw)

This being said, we don't really care about performances when reading in raw mode, so you could just drop the oob_required argument and always read the OOB bytes.

Quoted 29 lines
drivers/mtd/nand/marvell_nand.c new file +29 -0
+		if (raw)
+			oob_bytes += lt->ecc_bytes;
+	}
+
+	marvell_nfc_send_cmd(chip, &nfc_op);
+	ret = marvell_nfc_end_cmd(chip, NDSR_RDDREQ,
+				  "RDDREQ while draining FIFO (data/oob)");
+	if (ret)
+		return ret;
+
+	/* Read the page then the OOB area */
+	if (nfc->use_dma) {
+		marvell_nfc_xfer_data_dma(nfc, DMA_FROM_DEVICE,
+					  lt->data_bytes + oob_bytes);
+		memcpy(buf, nfc->dma_buf, lt->data_bytes);
+		memcpy(chip->oob_poi + (raw ? 0 : lt->ecc_bytes),
+		       nfc->dma_buf + lt->data_bytes, oob_bytes);
+	} else {
+		marvell_nfc_xfer_data_in_pio(nfc, buf, lt->data_bytes);
+		marvell_nfc_xfer_data_in_pio(nfc, chip->oob_poi, oob_bytes);
+	}
+
+	ret = marvell_nfc_wait_cmdd(chip);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+

[...]

Quoted 8 lines
drivers/mtd/nand/marvell_nand.c new file +8 -0
+
+static int marvell_nfc_hw_ecc_hmg_read_page(struct mtd_info *mtd,
+					    struct nand_chip *chip,
+					    u8 *buf, int oob_required,
+					    int page)
+{
+	const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
+	int max_bitflips = 0, ret;

drivers/mtd/nand/marvell_nand.c line 1012

Quoted 10 lines
drivers/mtd/nand/marvell_nand.c new file +10 -0
+
+	/*
+	 * Reading/Writing a given page must always be performed with the same
+	 * configuration regarding the state of the SPARE_EN bit or ECC bytes
+	 * will not be present at the same location (writing only data, without
+	 * SPARE_EN will put the ECC bytes at the beginning of the OOB area,
+	 * while writing with the SPARE_EN bit (hence, also writing free OOB
+	 * bytes) will put first the spare bytes then, at the end of the OOB
+	 * area, the ECC bytes. Choices has been made to always read/write OOB
+	 * area (padding with 0xFF is handled by the core for writes).

IMHO, this comment should go next to the place where you actually set the SPARE_EN bit. This way you don't have to duplicate the same comment block in various places.

drivers/mtd/nand/marvell_nand.c

Quoted 2 lines
drivers/mtd/nand/marvell_nand.c new file +2 -0
+	 */
+

Drop this empty line.

Quoted 23 lines
drivers/mtd/nand/marvell_nand.c new file +23 -0
+	marvell_nfc_enable_hw_ecc(chip);
+	marvell_nfc_hw_ecc_hmg_do_read_page(chip, buf, true, false, page);
+	ret = marvell_nfc_hw_ecc_correct(chip, &max_bitflips);
+	marvell_nfc_disable_hw_ecc(chip);
+
+	if (!ret)
+		return max_bitflips;
+
+	/*
+	 * Re-read the OOB area in raw mode if ECC failures have been detected
+	 * to check for empty pages.
+	 */
+	nand_change_read_column_op(chip, lt->data_bytes + lt->spare_bytes,
+				   chip->oob_poi + lt->spare_bytes,
+				   lt->ecc_bytes, false);
+	marvell_nfc_check_empty_chunk(chip, buf, lt->data_bytes, chip->oob_poi,
+				      lt->spare_bytes, chip->oob_poi +
+				      lt->spare_bytes, lt->ecc_bytes,
+				      &max_bitflips);
+
+	return max_bitflips;
+}
+

[...]

Quoted 21 lines
drivers/mtd/nand/marvell_nand.c new file +21 -0
+
+/* Hamming write helpers */
+static int marvell_nfc_hw_ecc_hmg_do_write_page(struct nand_chip *chip,
+						const u8 *buf,
+						bool oob_required, bool raw,
+						int page)
+{
+	struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
+	struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+	const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
+	struct marvell_nfc_op nfc_op = {
+		.ndcb[0] = NDCB0_CMD_TYPE(TYPE_WRITE) |
+			   NDCB0_ADDR_CYC(marvell_nand->addr_cyc) |
+			   NDCB0_CMD1(NAND_CMD_SEQIN) |
+			   NDCB0_CMD2(NAND_CMD_PAGEPROG) |
+			   NDCB0_DBC,
+		.ndcb[1] = NDCB1_ADDRS_PAGE(page),
+		.ndcb[2] = NDCB2_ADDR5_PAGE(page),
+	};
+	int oob_bytes = 0;
+	int ret;

drivers/mtd/nand/marvell_nand.c line 1082

Quoted 10 lines
drivers/mtd/nand/marvell_nand.c new file +10 -0
+
+	/* NFCv2 needs more information about the operation being executed */
+	if (nfc->caps->is_nfcv2)
+		nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW);
+
+	ret = marvell_nfc_prepare_cmd(chip);
+	if (ret)
+		return ret;
+
+	if (oob_required) {

Same comment as for the marvell_nfc_hw_ecc_hmg_read_page() function:
just drop the oob_required argument to simplify the logic.

Quoted 35 lines
drivers/mtd/nand/marvell_nand.c new file +35 -0
+		oob_bytes = lt->spare_bytes;
+		if (raw)
+			oob_bytes += lt->ecc_bytes;
+	}
+
+	marvell_nfc_send_cmd(chip, &nfc_op);
+	ret = marvell_nfc_end_cmd(chip, NDSR_WRDREQ,
+				  "WRDREQ while loading FIFO (data)");
+	if (ret)
+		return ret;
+
+	/* Write the page then the OOB area */
+	if (nfc->use_dma) {
+		memcpy(nfc->dma_buf, buf, lt->data_bytes);
+		if (oob_required)
+			memcpy(nfc->dma_buf + lt->data_bytes, chip->oob_poi,
+			       oob_bytes);
+		marvell_nfc_xfer_data_dma(nfc, DMA_TO_DEVICE, lt->data_bytes +
+					  lt->ecc_bytes + lt->spare_bytes);
+	} else {
+		marvell_nfc_xfer_data_out_pio(nfc, buf, lt->data_bytes);
+		if (oob_required)
+			marvell_nfc_xfer_data_out_pio(nfc, chip->oob_poi,
+						      oob_bytes);
+	}
+
+	ret = marvell_nfc_wait_cmdd(chip);
+	if (ret)
+		return ret;
+
+	ret = marvell_nfc_wait_op(chip,
+				  chip->data_interface.timings.sdr.tPROG_max);
+	return ret;
+}
+

[...]

Quoted 7 lines
drivers/mtd/nand/marvell_nand.c new file +7 -0
+static int marvell_nfc_hw_ecc_hmg_write_page(struct mtd_info *mtd,
+					     struct nand_chip *chip,
+					     const u8 *buf,
+					     int oob_required, int page)
+{
+	int ret;
+

drivers/mtd/nand/marvell_nand.c line 1143

Quoted 10 lines
drivers/mtd/nand/marvell_nand.c new file +10 -0
+	/*
+	 * Reading/Writing a given page must always be performed with the same
+	 * configuration regarding the state of the SPARE_EN bit or ECC bytes
+	 * will not be present at the same location (writing only data, without
+	 * SPARE_EN will put the ECC bytes at the beginning of the OOB area,
+	 * while writing with the SPARE_EN bit (hence, also writing free OOB
+	 * bytes) will put first the spare bytes then, at the end of the OOB
+	 * area, the ECC bytes. Choices has been made to always read/write OOB
+	 * area (padding with 0xFF is handled by the core for writes).
+	 */

Same as above: drop the comment.

Quoted 9 lines
drivers/mtd/nand/marvell_nand.c new file +9 -0
+
+	marvell_nfc_enable_hw_ecc(chip);
+	ret = marvell_nfc_hw_ecc_hmg_do_write_page(chip, buf, true, false,
+						   page);
+	marvell_nfc_disable_hw_ecc(chip);
+
+	return ret;
+}
+

[...]

Quoted 41 lines
drivers/mtd/nand/marvell_nand.c new file +41 -0
+
+static void marvell_nfc_hw_ecc_bch_read_chunk(struct nand_chip *chip, int chunk,
+					      u8 *data, unsigned int data_len,
+					      u8 *spare, unsigned int spare_len,
+					      int page)
+{
+	struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
+	struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+	const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
+	int nchunks = lt->full_chunk_cnt + lt->last_chunk_cnt;
+	int i, ret;
+	struct marvell_nfc_op nfc_op = {
+		.ndcb[0] = NDCB0_CMD_TYPE(TYPE_READ) |
+			   NDCB0_ADDR_CYC(marvell_nand->addr_cyc) |
+			   NDCB0_LEN_OVRD,
+		.ndcb[1] = NDCB1_ADDRS_PAGE(page),
+		.ndcb[2] = NDCB2_ADDR5_PAGE(page),
+		.ndcb[3] = data_len + spare_len,
+	};
+
+	ret = marvell_nfc_prepare_cmd(chip);
+	if (ret)
+		return;
+
+	if (chunk == 0)
+		nfc_op.ndcb[0] |= NDCB0_DBC |
+				  NDCB0_CMD1(NAND_CMD_READ0) |
+				  NDCB0_CMD2(NAND_CMD_READSTART);
+
+	/*
+	 * Trigger the naked read operation only on the last chunk.
+	 * Otherwise, use monolithic read.
+	 */
+	if (nchunks == 1 || (chunk < nchunks - 1))
+		nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_MONOLITHIC_RW);
+	else
+		nfc_op.ndcb[0] |= NDCB0_CMD_XTYPE(XTYPE_LAST_NAKED_RW);
+
+	marvell_nfc_send_cmd(chip, &nfc_op);
+
+	/*

drivers/mtd/nand/marvell_nand.c line 1267

Quoted 10 lines
drivers/mtd/nand/marvell_nand.c new file +10 -0
+	 * According to the datasheet, when reading from NDDB
+	 * with BCH enabled, after each 32 bytes reads, we
+	 * have to make sure that the NDSR.RDDREQ bit is set.
+	 *
+	 * Drain the FIFO, 8 32-bit reads at a time, and skip
+	 * the polling on the last read.
+	 *
+	 * Length is a multiple of 32 bytes, hence it is a multiple of 8 too.
+	 */
+

Drop the empty line.

Quoted 63 lines
drivers/mtd/nand/marvell_nand.c new file +63 -0
+	for (i = 0; i < data_len; i += FIFO_DEPTH * BCH_SEQ_READS) {
+		marvell_nfc_end_cmd(chip, NDSR_RDDREQ,
+				    "RDDREQ while draining FIFO (data)");
+		marvell_nfc_xfer_data_in_pio(nfc, data,
+					     FIFO_DEPTH * BCH_SEQ_READS);
+		data += FIFO_DEPTH * BCH_SEQ_READS;
+	}
+
+	for (i = 0; i < spare_len; i += FIFO_DEPTH * BCH_SEQ_READS) {
+		marvell_nfc_end_cmd(chip, NDSR_RDDREQ,
+				    "RDDREQ while draining FIFO (OOB)");
+		marvell_nfc_xfer_data_in_pio(nfc, spare,
+					     FIFO_DEPTH * BCH_SEQ_READS);
+		spare += FIFO_DEPTH * BCH_SEQ_READS;
+	}
+}
+
+static int marvell_nfc_hw_ecc_bch_read_page(struct mtd_info *mtd,
+					    struct nand_chip *chip,
+					    u8 *buf, int oob_required,
+					    int page)
+{
+	const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
+	int nchunks = lt->full_chunk_cnt + lt->last_chunk_cnt;
+	int data_len = lt->data_bytes, spare_len = lt->spare_bytes, ecc_len;
+	u8 *data = buf, *spare = chip->oob_poi, *ecc;
+	int max_bitflips = 0;
+	u32 failure_mask = 0;
+	int chunk, ecc_offset_in_page, ret;
+
+	/*
+	 * With BCH, OOB is not fully used (and thus not read entirely), not
+	 * expected bytes could show up at the end of the OOB buffer if not
+	 * explicitly erased.
+	 */
+	if (oob_required)
+		memset(chip->oob_poi, 0xFF, mtd->oobsize);
+
+	marvell_nfc_enable_hw_ecc(chip);
+
+	for (chunk = 0; chunk < nchunks; chunk++) {
+		/* Update length for the last chunk */
+		if (chunk >= lt->full_chunk_cnt) {
+			data_len = lt->last_data_bytes;
+			spare_len = lt->last_spare_bytes;
+		}
+
+		/* Read the chunk and detect number of bitflips */
+		marvell_nfc_hw_ecc_bch_read_chunk(chip, chunk, data, data_len,
+						  spare, spare_len, page);
+		ret = marvell_nfc_hw_ecc_correct(chip, &max_bitflips);
+		if (ret)
+			failure_mask |= BIT(chunk);
+
+		data += data_len;
+		spare += spare_len;
+	}
+
+	marvell_nfc_disable_hw_ecc(chip);
+
+	if (!failure_mask)
+		return max_bitflips;
+

drivers/mtd/nand/marvell_nand.c line 1340

Quoted 10 lines
drivers/mtd/nand/marvell_nand.c new file +10 -0
+	/*
+	 * Please note that dumping the ECC bytes during a normal read with OOB
+	 * area would add a significant overhead as ECC bytes are "consumed" by
+	 * the controller in normal mode and must be re-read in raw mode. To
+	 * avoid dropping the performances, we prefer not to include them. The
+	 * user should re-read the page in raw mode if ECC bytes are required.
+	 *
+	 * However, for any subpage read error reported by ->correct(), the ECC
+	 * bytes must be read in raw mode and the full subpage must be checked
+	 * to see if it is entirely empty of if there was an actual error.

Nice explanation!

drivers/mtd/nand/marvell_nand.c

Quoted 2 lines
drivers/mtd/nand/marvell_nand.c new file +2 -0
+	 */
+

Extra empty

Quoted 41 lines
drivers/mtd/nand/marvell_nand.c new file +41 -0
+	for (chunk = 0; chunk < nchunks; chunk++) {
+		/* No failure reported for this chunk, move to the next one */
+		if (!(failure_mask & BIT(chunk)))
+			continue;
+
+		/* Derive ECC bytes positions (in page/buffer) and length */
+		ecc = chip->oob_poi +
+			(lt->full_chunk_cnt * lt->spare_bytes) +
+			(lt->last_chunk_cnt * lt->last_spare_bytes) +
+			(chunk * ALIGN(lt->ecc_bytes, 32));
+		ecc_offset_in_page =
+			(chunk * (lt->data_bytes + lt->spare_bytes +
+				  lt->ecc_bytes)) +
+			(chunk < lt->full_chunk_cnt ?
+			 lt->data_bytes + lt->spare_bytes :
+			 lt->last_data_bytes + lt->last_spare_bytes);
+		ecc_len = chunk < lt->full_chunk_cnt ?
+			lt->ecc_bytes : lt->last_ecc_bytes;
+
+		/* Do the actual raw read of the ECC bytes */
+		nand_change_read_column_op(chip, ecc_offset_in_page,
+					   ecc, ecc_len, false);
+
+		/* Derive data/spare bytes positions (in buffer) and length */
+		data = buf + (chunk * lt->data_bytes);
+		data_len = chunk < lt->full_chunk_cnt ?
+			lt->data_bytes : lt->last_data_bytes;
+		spare = chip->oob_poi + (chunk * (lt->spare_bytes +
+						  lt->ecc_bytes));
+		spare_len = chunk < lt->full_chunk_cnt ?
+			lt->spare_bytes : lt->last_spare_bytes;
+
+		/* Check the entire chunk (data + spare + ecc) for emptyness */
+		marvell_nfc_check_empty_chunk(chip, data, data_len, spare,
+					      spare_len, ecc, ecc_len,
+					      &max_bitflips);
+	}
+
+	return max_bitflips;
+}
+

[...]

Quoted 8 lines
drivers/mtd/nand/marvell_nand.c new file +8 -0
+
+/*
+ * HW ECC layouts, identical to old pxa3xx_nand driver,
+ * to be fully backward compatible.
+ */
+static int marvell_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
+				      struct mtd_oob_region *oobregion)
+{

drivers/mtd/nand/marvell_nand.c line 2087

Quoted 10 lines
drivers/mtd/nand/marvell_nand.c new file +10 -0
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
+	int nchunks = lt->full_chunk_cnt;
+
+	if (section >= nchunks)
+		return -ERANGE;
+
+	oobregion->offset = ((lt->spare_bytes + lt->ecc_bytes) * section) +
+		lt->spare_bytes;
+	oobregion->length = lt->ecc_bytes;

We discussed about fixing the layouts to expose all free bytes at the beginning of the OOB area and all ECC bytes at the end. It doesn't seem to be the case here.

Quoted 41 lines
drivers/mtd/nand/marvell_nand.c new file +41 -0
+
+	return 0;
+}
+
+static int marvell_nand_ooblayout_free(struct mtd_info *mtd, int section,
+				       struct mtd_oob_region *oobregion)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	const struct marvell_hw_ecc_layout *lt = to_marvell_nand(chip)->layout;
+	int nchunks = lt->full_chunk_cnt;
+
+	if (section >= nchunks)
+		return -ERANGE;
+
+	if (!lt->spare_bytes)
+		return 0;
+
+	oobregion->offset = section * (lt->spare_bytes + lt->ecc_bytes);
+	oobregion->length = lt->spare_bytes;
+	if (!section) {
+		/*
+		 * Bootrom looks in bytes 0 & 5 for bad blocks for the
+		 * 4KB page / 4bit BCH combination.
+		 */
+		if (mtd->writesize == SZ_4K && lt->data_bytes == SZ_2K) {
+			oobregion->offset += 6;
+			oobregion->length -= 6;
+		} else {
+			oobregion->offset += 2;
+			oobregion->length -= 2;
+		}
+	}
+
+	return 0;
+}
+
+static const struct mtd_ooblayout_ops marvell_nand_ooblayout_ops = {
+	.ecc = marvell_nand_ooblayout_ecc,
+	.free = marvell_nand_ooblayout_free,
+};
+

[...]

Quoted 18 lines
drivers/mtd/nand/marvell_nand.c new file +18 -0
+
+static int marvell_nand_ecc_init(struct mtd_info *mtd,
+				 struct nand_ecc_ctrl *ecc)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+	int ret;
+
+	if ((ecc->mode != NAND_ECC_NONE) && (!ecc->size || !ecc->strength)) {
+		if (chip->ecc_step_ds && chip->ecc_strength_ds) {
+			ecc->size = chip->ecc_step_ds;
+			ecc->strength = chip->ecc_strength_ds;
+		} else {
+			dev_info(nfc->dev,
+				 "No minimum ECC strength, using 1b/512B\n");
+			ecc->size = 512;
+			ecc->strength = 1;
+		}

drivers/mtd/nand/marvell_nand.c line 2219

Quoted 10 lines
drivers/mtd/nand/marvell_nand.c new file +10 -0
+	}
+
+	switch (ecc->mode) {
+	case NAND_ECC_HW:
+		ret = marvell_nand_hw_ecc_ctrl_init(mtd, ecc);
+		if (ret)
+			return ret;
+		break;
+	case NAND_ECC_NONE:
+		chip->ecc.algo = 0;

No need to set ecc->algo to 0 here.

drivers/mtd/nand/marvell_nand.c line 2220

Quoted 1 line
drivers/mtd/nand/marvell_nand.c new file +1 -0
+	case NAND_ECC_SOFT:

You should probably do a check on ->writesize, especially for NFCv1 when used in ECC_NONE of ECC_SOFT, since you can't write/read arbitrary size.

Quoted 8 lines
drivers/mtd/nand/marvell_nand.c new file +8 -0
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+

[...]

Quoted 183 lines
drivers/mtd/nand/marvell_nand.c new file +183 -0
+static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
+				  struct device_node *np)
+{
+	struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(dev);
+	struct marvell_nand_chip *marvell_nand;
+	struct mtd_info *mtd;
+	struct nand_chip *chip;
+	int nsels, ret, i;
+	u32 cs, rb;
+
+	/*
+	 * The legacy "num-cs" property indicates the number of CS on the only
+	 * chip connected to the controller (legacy bindings does not support
+	 * more than one chip). CS are only incremented one by one while the RB
+	 * pin is always the #0.
+	 *
+	 * When not using legacy bindings, a couple of "reg" and "marvell,rb"
+	 * properties must be filled. For each chip, expressed as a subnode,
+	 * "reg" points to the CS lines and "marvell,rb" to the RB line.
+	 */
+	if (pdata) {
+		nsels = 1;
+	} else if (nfc->caps->legacy_of_bindings) {
+		if (!of_get_property(np, "num-cs", &nsels)) {
+			dev_err(dev, "missing num-cs property\n");
+			return -EINVAL;
+		}
+	} else {
+		if (!of_get_property(np, "reg", &nsels)) {
+			dev_err(dev, "missing reg property\n");
+			return -EINVAL;
+		}
+	}
+
+	if (!pdata)
+		nsels /= sizeof(u32);
+	if (!nsels) {
+		dev_err(dev, "invalid reg property size\n");
+		return -EINVAL;
+	}
+
+	/* Alloc the nand chip structure */
+	marvell_nand = devm_kzalloc(dev, sizeof(*marvell_nand) +
+				    (nsels *
+				     sizeof(struct marvell_nand_chip_sel)),
+				    GFP_KERNEL);
+	if (!marvell_nand) {
+		dev_err(dev, "could not allocate chip structure\n");
+		return -ENOMEM;
+	}
+
+	marvell_nand->nsels = nsels;
+	marvell_nand->selected_die = -1;
+
+	for (i = 0; i < nsels; i++) {
+		if (pdata || nfc->caps->legacy_of_bindings) {
+			/*
+			 * Legacy bindings use the CS lines in natural
+			 * order (0, 1, ...)
+			 */
+			cs = i;
+		} else {
+			/* Retrieve CS id */
+			ret = of_property_read_u32_index(np, "reg", i, &cs);
+			if (ret) {
+				dev_err(dev, "could not retrieve reg property: %d\n",
+					ret);
+				return ret;
+			}
+		}
+
+		if (cs >= nfc->caps->max_cs_nb) {
+			dev_err(dev, "invalid reg value: %u (max CS = %d)\n",
+				cs, nfc->caps->max_cs_nb);
+			return -EINVAL;
+		}
+
+		if (test_and_set_bit(cs, &nfc->assigned_cs)) {
+			dev_err(dev, "CS %d already assigned\n", cs);
+			return -EINVAL;
+		}
+
+		/*
+		 * The cs variable represents the chip select id, which must be
+		 * converted in bit fields for NDCB0 and NDCB2 to select the
+		 * right chip. Unfortunately, due to a lack of information on
+		 * the subject and incoherent documentation, the user should not
+		 * use CS1 and CS3 at all as asserting them is not supported in
+		 * a reliable way (due to multiplexing inside ADDR5 field).
+		 */
+		marvell_nand->sels[i].cs = cs;
+		switch (cs) {
+		case 0:
+		case 2:
+			marvell_nand->sels[i].ndcb0_csel = 0;
+			break;
+		case 1:
+		case 3:
+			marvell_nand->sels[i].ndcb0_csel = NDCB0_CSEL;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		/* Retrieve RB id */
+		if (pdata || nfc->caps->legacy_of_bindings) {
+			/* Legacy bindings always use RB #0 */
+			rb = 0;
+		} else {
+			ret = of_property_read_u32_index(np, "marvell,rb", i,
+							 &rb);
+			if (ret) {
+				dev_err(dev,
+					"could not retrieve RB property: %d\n",
+					ret);
+				return ret;
+			}
+		}
+
+		if (rb >= nfc->caps->max_rb_nb) {
+			dev_err(dev, "invalid reg value: %u (max RB = %d)\n",
+				rb, nfc->caps->max_rb_nb);
+			return -EINVAL;
+		}
+
+		marvell_nand->sels[i].rb = rb;
+	}
+
+	chip = &marvell_nand->chip;
+	chip->controller = &nfc->controller;
+	nand_set_flash_node(chip, np);
+
+	chip->exec_op = marvell_nfc_exec_op;
+	chip->select_chip = marvell_nfc_select_chip;
+	if (nfc->caps->is_nfcv2 &&
+	    !of_property_read_bool(np, "marvell,nand-keep-config"))
+		chip->setup_data_interface = marvell_nfc_setup_data_interface;
+
+	mtd = nand_to_mtd(chip);
+	mtd->dev.parent = dev;
+
+	/*
+	 * Default to HW ECC engine mode. If the nand-ecc-mode property is given
+	 * in the DT node, this entry will be overwritten in nand_scan_ident().
+	 */
+	chip->ecc.mode = NAND_ECC_HW;
+
+	ret = nand_scan_ident(mtd, marvell_nand->nsels, NULL);
+	if (ret) {
+		dev_err(dev, "could not identify the nand chip\n");
+		return ret;
+	}
+
+	if (pdata && pdata->flash_bbt)
+		chip->bbt_options |= NAND_BBT_USE_FLASH;
+
+	if (chip->bbt_options & NAND_BBT_USE_FLASH) {
+		/*
+		 * We'll use a bad block table stored in-flash and don't
+		 * allow writing the bad block marker to the flash.
+		 */
+		chip->bbt_options |= NAND_BBT_NO_OOB_BBM;
+		chip->bbt_td = &bbt_main_descr;
+		chip->bbt_md = &bbt_mirror_descr;
+	}
+
+	/*
+	 * With RA_START bit set in NDCR, columns takes two address cycles. This
+	 * means addressing a chip with more than 256 pages needs a fifth
+	 * address cycle. Addressing a chip using CS 2 or 3 should also needs
+	 * this additional cycle but due to insistance in the documentation and
+	 * lack of hardware to test this situation, this case has been dropped
+	 * and is not supported by this driver.
+	 */
+	marvell_nand->addr_cyc = 4;
+	if (chip->options & NAND_ROW_ADDR_3)
+		marvell_nand->addr_cyc = 5;
+
+	if (pdata) {
+		chip->ecc.size = pdata->ecc_step_size;
+		chip->ecc.strength = pdata->ecc_strength;
+	}
+

drivers/mtd/nand/marvell_nand.c line 2529

Quoted 10 lines
drivers/mtd/nand/marvell_nand.c new file +10 -0
+	ret = marvell_nand_ecc_init(mtd, &chip->ecc);
+	if (ret) {
+		dev_err(dev, "ECC init failed: %d\n", ret);
+		return ret;
+	}
+
+	if (chip->ecc.mode == NAND_ECC_HW) {
+		/*
+		 * Subpage write not available with hardware ECC, prohibit also
+		 * subpage read as in userspace subpage acces would still be

^ access

Quoted 69 lines
drivers/mtd/nand/marvell_nand.c new file +69 -0
+		 * allowed and subpage write, if used, would lead to numerous
+		 * uncorrectable ECC errors.
+		 */
+		chip->options |= NAND_NO_SUBPAGE_WRITE;
+	}
+
+	if (pdata || nfc->caps->legacy_of_bindings) {
+		/*
+		 * We keep the MTD name unchanged to avoid breaking platforms
+		 * where the MTD cmdline parser is used and the bootloader
+		 * has not been updated to use the new naming scheme.
+		 */
+		mtd->name = "pxa3xx_nand-0";
+	} else if (!mtd->name) {
+		/*
+		 * If the new bindings are used and the bootloader has not been
+		 * updated to pass a new mtdparts parameter on the cmdline, you
+		 * should define the following property in your NAND node, ie:
+		 *
+		 *	label = "main-storage";
+		 *
+		 * This way, mtd->name will be set by the core when
+		 * nand_set_flash_node() is called.
+		 */
+		mtd->name = devm_kasprintf(nfc->dev, GFP_KERNEL,
+					   "%s:nand.%d", dev_name(nfc->dev),
+					   marvell_nand->sels[0].cs);
+		if (!mtd->name) {
+			dev_err(nfc->dev, "Failed to allocate mtd->name\n");
+			return -ENOMEM;
+		}
+	}
+
+	ret = nand_scan_tail(mtd);
+	if (ret) {
+		dev_err(dev, "nand_scan_tail failed: %d\n", ret);
+		return ret;
+	}
+
+	if (pdata)
+		/* Legacy bindings support only one chip */
+		ret = mtd_device_register(mtd, pdata->parts[0],
+					  pdata->nr_parts[0]);
+	else
+		ret = mtd_device_register(mtd, NULL, 0);
+	if (ret) {
+		dev_err(dev, "failed to register mtd device: %d\n", ret);
+		nand_release(mtd);
+		return ret;
+	}
+
+	list_add_tail(&marvell_nand->node, &nfc->chips);
+
+	return 0;
+}
+
+static int marvell_nand_chips_init(struct device *dev, struct marvell_nfc *nfc)
+{
+	struct device_node *np = dev->of_node;
+	struct device_node *nand_np;
+	int max_cs = nfc->caps->max_cs_nb;
+	int nchips;
+	int ret;
+
+	if (!np)
+		nchips = 1;
+	else
+		nchips = of_get_child_count(np);
+

drivers/mtd/nand/marvell_nand.c line 2608

Quoted 10 lines
drivers/mtd/nand/marvell_nand.c new file +10 -0
+	if (nchips > max_cs) {
+		dev_err(dev, "too many NAND chips: %d (max = %d CS)\n", nchips,
+			max_cs);
+		return -EINVAL;
+	}
+
+	/*
+	 * Legacy bindings do not use child nodes to exhibit NAND chip
+	 * properties and layout. Instead, NAND properties are mixed with the
+	 * controller's and a single subnode presents the memory layout.

"
controller ones, and partitions are defined as direct subnodes of the NAND controller node.
"

Quoted 7 lines
drivers/mtd/nand/marvell_nand.c new file +7 -0
+	 */
+	if (nfc->caps->legacy_of_bindings) {
+		ret = marvell_nand_chip_init(dev, nfc, np);
+		return ret;
+	}
+
+	for_each_child_of_node(np, nand_np) {

drivers/mtd/nand/marvell_nand.c line 2625

Quoted 10 lines
drivers/mtd/nand/marvell_nand.c new file +10 -0
+		ret = marvell_nand_chip_init(dev, nfc, nand_np);
+		if (ret) {
+			of_node_put(nand_np);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+

That's all I see for now.

Really nice rework you've done here.

Thanks,

Boris

#6 lore raw

PATCH v2 2/5 mtd: nand: add reworked Marvell NAND controller driver

Hi Boris,

Quoted 47 lines · On Thu, 21 Dec 2017 11:14:28 +0100 Boris Brezillon…

Hi Miquel,

Quoted 9 lines · On Tue, 19 Dec 2017 14:29:39 +0100 Miquel Raynal…

Add marvell_nand driver which aims at replacing the existing pxa3xx_nand driver.

The new driver intends to be easier to understand and follows the brand new NAND framework rules by implementing hooks for every pattern the controller might support and referencing them inside a parser object that will be given to the core at each ->exec_op() call.

Quoted 7 lines

Raw accessors are implemented, useful to test/debug memory/filesystem corruptions. Userspace binaries contained in the mtd-utils package may now be used and their output trusted.

Timings may not be kept from the bootloader anymore, the timings used for instance in U-Boot were not optimal and it supposed to have NAND support (and initialized) in the bootloader.

Hm, AFAIR the old driver was able to dynamically adjust the timings when the NAND was ONFI compliant.

Quoted 11 lines

Thanks to the improved timings, implementation of ONFI mode 5 support (with EDO managed by adding a delay on data sampling), merging the commands together and optimizing writes in the command registers, the new driver may achieve faster throughputs in both directions. Measurements show an improvement of about +23% read throughput and +24% write throughput. These measurements have been done with an Armada-385-DB-AP (4kiB NAND pages forced in 4-bit strength BCH ECC correction) using the userspace tool 'flash_speed' from the MTD test suite.

Quoted 10 lines

Besides these important topics, the new driver addresses several unsolved known issues in the old driver which:
- did not work with ECC soft neither with ECC none ;
- relied on naked read/write (which is unchanged) while the NFCv1 embedded in the pxa3xx platforms do not implement it, so several NAND commands did not actually ever work without any notice (like reading the ONFI PARAM_PAGE or SET/GET_FEATURES) ;
- wrote the OOB data correctly, but was not able to read it correctly past the first OOB data chunk ;
- did not displayed ECC bytes ;

Quoted 18 lines

^display

and I'm not even sure display is the right term here. How about 'retrieve'.

Quoted 13 lines

- used device tree bindings that did not allow more than one NAND chip, and did not allow to choose the correct chip select if not incrementing from 0. Plus, the Ready/Busy line used had to be 0.

Old device tree bindings are still supported but deprecated. A more hierarchical view has to be used to keep the controller and the NAND chip structures clearly separated both inside the device tree and also in the driver code.

---

I made all the changes you requested, let me the time to do further tests and I will send a v3.

Thanks,
Miqu?l

#7 lore raw

PATCH v2 3/5 mtd: nand: replace pxa3xx_nand driver by its rework called marvell_nand

In reply to Miquel Raynal

Actually remove pxa3xx_nand.c to let marvell_nand.c be compiled instead. Also change the defconfig files using it as well as some board files depending on CONFIG_MTD_NAND_PXA3xx.

---
 arch/arm/configs/cm_x300_defconfig  |    2 +-
 arch/arm/configs/mvebu_v7_defconfig |    2 +-
 arch/arm/configs/pxa3xx_defconfig   |    3 +-
 arch/arm/configs/pxa_defconfig      |    2 +-
 arch/arm/configs/raumfeld_defconfig |    2 +-
 arch/arm/mach-mmp/ttc_dkb.c         |    4 +-
 arch/arm/mach-pxa/cm-x300.c         |    2 +-
 arch/arm/mach-pxa/colibri-pxa3xx.c  |    2 +-
 arch/arm/mach-pxa/colibri.h         |    2 +-
 arch/arm/mach-pxa/littleton.c       |    4 +-
 arch/arm/mach-pxa/mxm8x10.c         |    4 +-
 arch/arm/mach-pxa/zylonite.c        |    4 +-
 drivers/mtd/nand/Kconfig            |   10 -
 drivers/mtd/nand/Makefile           |    1 -
 drivers/mtd/nand/pxa3xx_nand.c      | 2104 -----------------------------------
 15 files changed, 16 insertions(+), 2132 deletions(-)
 delete mode 100644 drivers/mtd/nand/pxa3xx_nand.c
arch/arm/configs/cm_x300_defconfig +1 -1
index c0418e03d180..5e349c625b71 100644
@@ -49,7 +49,7 @@ CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 CONFIG_MTD=y
 CONFIG_MTD_BLOCK=y
 CONFIG_MTD_NAND=y
-CONFIG_MTD_NAND_PXA3xx=y
+CONFIG_MTD_NAND_MARVELL=y
 CONFIG_MTD_UBI=y
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_BLK_DEV_RAM=y
arch/arm/configs/mvebu_v7_defconfig +1 -1
index 69553704f2dc..4b6e4fd47e5d 100644
@@ -57,7 +57,7 @@ CONFIG_MTD_CFI_STAA=y
 CONFIG_MTD_PHYSMAP_OF=y
 CONFIG_MTD_M25P80=y
 CONFIG_MTD_NAND=y
-CONFIG_MTD_NAND_PXA3xx=y
+CONFIG_MTD_NAND_MARVELL=y
 CONFIG_MTD_SPI_NOR=y
 CONFIG_SRAM=y
 CONFIG_MTD_UBI=y
arch/arm/configs/pxa3xx_defconfig +1 -2
index bfea6874b0a1..3e0de035ab77 100644
@@ -32,8 +32,7 @@ CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 CONFIG_MTD=y
 CONFIG_MTD_BLOCK=y
 CONFIG_MTD_NAND=y
-CONFIG_MTD_NAND_PXA3xx=y
-CONFIG_MTD_NAND_PXA3xx_BUILTIN=y
+CONFIG_MTD_NAND_MARVELL=y
 CONFIG_MTD_ONENAND=y
 CONFIG_MTD_ONENAND_VERIFY_WRITE=y
 CONFIG_MTD_ONENAND_GENERIC=y
arch/arm/configs/pxa_defconfig +1 -1
index 830e817a028a..934af8601f7d 100644
@@ -198,7 +198,7 @@ CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS=0x4000000
 CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH=y
 CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE=y
 CONFIG_MTD_NAND_SHARPSL=m
-CONFIG_MTD_NAND_PXA3xx=m
+CONFIG_MTD_NAND_MARVELL=m
 CONFIG_MTD_NAND_CM_X270=m
 CONFIG_MTD_NAND_TMIO=m
 CONFIG_MTD_NAND_BRCMNAND=m
arch/arm/configs/raumfeld_defconfig +1 -1
index 77a56c23c6ef..2dd56e9a484e 100644
@@ -33,7 +33,7 @@ CONFIG_NFTL=y
 CONFIG_NFTL_RW=y
 CONFIG_MTD_BLOCK2MTD=y
 CONFIG_MTD_NAND=y
-CONFIG_MTD_NAND_PXA3xx=y
+CONFIG_MTD_NAND_MARVELL=y
 CONFIG_MTD_UBI=y
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_ISL29003=y
arch/arm/mach-mmp/ttc_dkb.c +2 -2
index d90c74fa614d..e0b6073c61a7 100644
@@ -178,7 +178,7 @@ static struct mv_usb_platform_data ttc_usb_pdata = {
 #endif
 #endif
 
-#if IS_ENABLED(CONFIG_MTD_NAND_PXA3xx)
+#if IS_ENABLED(CONFIG_MTD_NAND_MARVELL)
 static struct pxa3xx_nand_platform_data dkb_nand_info = {
 	.enable_arbiter = 1,
 	.num_cs = 1,
@@ -275,7 +275,7 @@ static void __init ttc_dkb_init(void)
 
 	/* on-chip devices */
 	pxa910_add_uart(1);
-#if IS_ENABLED(CONFIG_MTD_NAND_PXA3xx)
+#if IS_ENABLED(CONFIG_MTD_NAND_MARVELL)
 	pxa910_add_nand(&dkb_nand_info);
 #endif
 
arch/arm/mach-pxa/cm-x300.c +1 -1
index 868448d2cd82..89326378dabf 100644
@@ -391,7 +391,7 @@ static void __init cm_x300_init_ac97(void)
 static inline void cm_x300_init_ac97(void) {}
 #endif
 
-#if defined(CONFIG_MTD_NAND_PXA3xx) || defined(CONFIG_MTD_NAND_PXA3xx_MODULE)
+#if defined(CONFIG_MTD_NAND_MARVELL) || defined(CONFIG_MTD_NAND_MARVELL_MODULE)
 static struct mtd_partition cm_x300_nand_partitions[] = {
 	[0] = {
 		.name        = "OBM",
arch/arm/mach-pxa/colibri-pxa3xx.c +1 -1
index b04431bb4ba7..c9c36f707555 100644
@@ -110,7 +110,7 @@ void __init colibri_pxa3xx_init_lcd(int bl_pin)
 }
 #endif
 
-#if defined(CONFIG_MTD_NAND_PXA3xx) || defined(CONFIG_MTD_NAND_PXA3xx_MODULE)
+#if defined(CONFIG_MTD_NAND_MARVELL) || defined(CONFIG_MTD_NAND_MARVELL_MODULE)
 static struct mtd_partition colibri_nand_partitions[] = {
 	{
 		.name        = "bootloader",
arch/arm/mach-pxa/colibri.h +1 -1
index 673a131da875..d5ba74831916 100644
@@ -46,7 +46,7 @@ static inline void colibri_pxa3xx_init_lcd(int bl_pin) {}
 extern void colibri_pxa3xx_init_eth(struct ax_plat_data *plat_data);
 #endif
 
-#if defined(CONFIG_MTD_NAND_PXA3xx) || defined(CONFIG_MTD_NAND_PXA3xx_MODULE)
+#if defined(CONFIG_MTD_NAND_MARVELL) || defined(CONFIG_MTD_NAND_MARVELL_MODULE)
 extern void colibri_pxa3xx_init_nand(void);
 #else
 static inline void colibri_pxa3xx_init_nand(void) {}
arch/arm/mach-pxa/littleton.c +2 -2
index fae38fdc8d8e..898961be36db 100644
@@ -290,7 +290,7 @@ static void __init littleton_init_mmc(void)
 static inline void littleton_init_mmc(void) {}
 #endif
 
-#if defined(CONFIG_MTD_NAND_PXA3xx) || defined(CONFIG_MTD_NAND_PXA3xx_MODULE)
+#if defined(CONFIG_MTD_NAND_MARVELL) || defined(CONFIG_MTD_NAND_MARVELL_MODULE)
 static struct mtd_partition littleton_nand_partitions[] = {
 	[0] = {
 		.name        = "Bootloader",
@@ -340,7 +340,7 @@ static void __init littleton_init_nand(void)
 }
 #else
 static inline void littleton_init_nand(void) {}
-#endif /* CONFIG_MTD_NAND_PXA3xx || CONFIG_MTD_NAND_PXA3xx_MODULE */
+#endif /* CONFIG_MTD_NAND_MARVELL || CONFIG_MTD_NAND_MARVELL_MODULE */
 
 #if defined(CONFIG_I2C_PXA) || defined(CONFIG_I2C_PXA_MODULE)
 static struct led_info littleton_da9034_leds[] = {
arch/arm/mach-pxa/mxm8x10.c +2 -2
index 9a22ae0ad8c9..7d7aab20b70a 100644
@@ -359,7 +359,7 @@ void __init mxm_8x10_ac97_init(void)
 }
 
 /* NAND flash Support */
-#if defined(CONFIG_MTD_NAND_PXA3xx) || defined(CONFIG_MTD_NAND_PXA3xx_MODULE)
+#if defined(CONFIG_MTD_NAND_MARVELL) || defined(CONFIG_MTD_NAND_MARVELL_MODULE)
 #define NAND_BLOCK_SIZE SZ_128K
 #define NB(x)           (NAND_BLOCK_SIZE * (x))
 static struct mtd_partition mxm_8x10_nand_partitions[] = {
@@ -402,7 +402,7 @@ static void __init mxm_8x10_nand_init(void)
 }
 #else
 static inline void mxm_8x10_nand_init(void) {}
-#endif /* CONFIG_MTD_NAND_PXA3xx || CONFIG_MTD_NAND_PXA3xx_MODULE */
+#endif /* CONFIG_MTD_NAND_MARVELL || CONFIG_MTD_NAND_MARVELL_MODULE */
 
 /* Ethernet support: Davicom DM9000 */
 static struct resource dm9k_resources[] = {
arch/arm/mach-pxa/zylonite.c +2 -2
index 4268552d600d..a4577118d518 100644
@@ -338,7 +338,7 @@ static void __init zylonite_init_keypad(void)
 static inline void zylonite_init_keypad(void) {}
 #endif
 
-#if defined(CONFIG_MTD_NAND_PXA3xx) || defined(CONFIG_MTD_NAND_PXA3xx_MODULE)
+#if defined(CONFIG_MTD_NAND_MARVELL) || defined(CONFIG_MTD_NAND_MARVELL_MODULE)
 static struct mtd_partition zylonite_nand_partitions[] = {
 	[0] = {
 		.name        = "Bootloader",
@@ -388,7 +388,7 @@ static void __init zylonite_init_nand(void)
 }
 #else
 static inline void zylonite_init_nand(void) {}
-#endif /* CONFIG_MTD_NAND_PXA3xx || CONFIG_MTD_NAND_PXA3xx_MODULE */
+#endif /* CONFIG_MTD_NAND_MARVELL || CONFIG_MTD_NAND_MARVELL_MODULE */
 
 #if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
 static struct pxaohci_platform_data zylonite_ohci_info = {
drivers/mtd/nand/Kconfig +0 -10
index 9e141e03f5c2..2c6ecb7ae753 100644
@@ -313,16 +313,6 @@ config MTD_NAND_ATMEL
 	  Enables support for NAND Flash / Smart Media Card interface
 	  on Atmel AT91 processors.
 
-config MTD_NAND_PXA3xx
-	tristate "NAND support on PXA3xx and Armada 370/XP"
-	depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU
-	help
-
-	  This enables the driver for the NAND flash device found on
-	  PXA3xx processors (NFCv1) and also on 32-bit Armada
-	  platforms (XP, 370, 375, 38x, 39x) and 64-bit Armada
-	  platforms (7K, 8K) (NFCv2).
-
 config MTD_NAND_MARVELL
 	tristate "NAND controller support on Marvell boards"
 	depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \
drivers/mtd/nand/Makefile +0 -1
index 921634ba400c..c882d5ef192a 100644
@@ -31,7 +31,6 @@ omap2_nand-objs := omap2.o
 obj-$(CONFIG_MTD_NAND_OMAP2) 		+= omap2_nand.o
 obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD)	+= omap_elm.o
 obj-$(CONFIG_MTD_NAND_CM_X270)		+= cmx270_nand.o
-obj-$(CONFIG_MTD_NAND_PXA3xx)		+= pxa3xx_nand.o
 obj-$(CONFIG_MTD_NAND_MARVELL)		+= marvell_nand.o
 obj-$(CONFIG_MTD_NAND_TMIO)		+= tmio_nand.o
 obj-$(CONFIG_MTD_NAND_PLATFORM)		+= plat_nand.o
drivers/mtd/nand/pxa3xx_nand.c deleted +0 -2104

2,104 lines not on this page · raw message

Signature · 1 line
2.11.0
maintainer
#8 lore raw

PATCH v2 3/5 mtd: nand: replace pxa3xx_nand driver by its rework called marvell_nand

Quoted 5 lines · Miquel Raynal

Actually remove pxa3xx_nand.c to let marvell_nand.c be compiled instead. Also change the defconfig files using it as well as some board files depending on CONFIG_MTD_NAND_PXA3xx.

Actually that approach doesn't quite suit me.
I'm not convinced at all a new rewrite of the driver will be stable right out of the box as the old one is.

What I would propose instead is :
- keep both the new marvell nand driver and the old pxa3xx_nand driver
- switch pxa_defconfig to compile them both
- continue testing (with my help)

Once I see the marvell_nand is working properly, we can remove pca3xx_nand in a second stage, keeping the pxa platform functional all along.

Would you agree to that path ?

As a side not, this patch should be split into 2 parts :
- one for arch/arm -> switch to the new driver
- one for drivers/mtd -> removal of the old driver

Cheers.

--
Robert

#9 lore raw

PATCH v2 3/5 mtd: nand: replace pxa3xx_nand driver by its rework called marvell_nand

Quoted 11 lines · On Fri, 22 Dec 2017 21:47:18 +0100 Robert Jarzmik <[email protected]>…
Quoted 5 lines · Miquel Raynal

Actually remove pxa3xx_nand.c to let marvell_nand.c be compiled instead. Also change the defconfig files using it as well as some board files depending on CONFIG_MTD_NAND_PXA3xx.

Actually that approach doesn't quite suit me.
I'm not convinced at all a new rewrite of the driver will be stable right out of the box as the old one is.

I would not qualify the old driver as stable, given that we regularly have bug reports, but I understand your point and kind of understand your concerns about stability.

Quoted 10 lines

What I would propose instead is :
- keep both the new marvell nand driver and the old pxa3xx_nand driver
- switch pxa_defconfig to compile them both
- continue testing (with my help)

Once I see the marvell_nand is working properly, we can remove pca3xx_nand in a second stage, keeping the pxa platform functional all along.

Would you agree to that path ?

Hm, I tried this approach already (with Marvell's crypto engine) and it doesn't quite solve the problem, just delay it a bit until someone decides to remove the old driver. In my experience, people only start testing the new driver when they're forced to do it (IOW, when the old driver is removed and defconfigs are patched to use the new one). So let's find a way to fix the remaining issues you have instead of delaying the inevitable.
Note that Miquel has done a lot of testing on various Marvell boards (mainly mvebu ones since we only have one pxa board). Of course he couldn't test all possible combinations of NFC <-> NAND, but I'm quite confident the remaining issues can be fixed quickly with your help.

Quoted 4 lines

As a side not, this patch should be split into 2 parts :
- one for arch/arm -> switch to the new driver
- one for drivers/mtd -> removal of the old driver

I agree on that part.

Quoted 5 lines

Cheers.

--
Robert

#10 lore raw

PATCH v2 3/5 mtd: nand: replace pxa3xx_nand driver by its rework called marvell_nand

Quoted 12 lines · Boris Brezillon
Quoted 9 lines · On Fri, 22 Dec 2017 21:47:18 +0100 Robert Jarzmik <[email protected]>…

What I would propose instead is :
- keep both the new marvell nand driver and the old pxa3xx_nand driver
- switch pxa_defconfig to compile them both
- continue testing (with my help)

Once I see the marvell_nand is working properly, we can remove pca3xx_nand in a second stage, keeping the pxa platform functional all along.

Would you agree to that path ?

Quoted 7 lines

Hm, I tried this approach already (with Marvell's crypto engine) and it doesn't quite solve the problem, just delay it a bit until someone decides to remove the old driver. In my experience, people only start testing the new driver when they're forced to do it (IOW, when the old driver is removed and defconfigs are patched to use the new one). So let's find a way to fix the remaining issues you have instead of delaying the inevitable.

That also reminds me of the old vs new PATA/IDE drivers a long time ago. A number of us would stay on the old ones because we were satisfied, never really making the effort to migrate until it we were really forced.

For the NAND, I think that if the old driver had to coexist with the new one, at the very least the config option name must change so that it's not selected by default by make oldconfig, and it's clearly written that it is not maintained anymore, may be broken and that any one having a good reason to use it must absolutely contact the maintainer to get the other one fixed.

Just my two cents,
Willy

#11 lore raw

PATCH v2 3/5 mtd: nand: replace pxa3xx_nand driver by its rework called marvell_nand

In reply to Robert Jarzmik
Quoted 8 lines · On Fri, 22 Dec 2017 21:47:18 +0100 Robert Jarzmik <[email protected]>…
Quoted 5 lines · Miquel Raynal

Actually remove pxa3xx_nand.c to let marvell_nand.c be compiled instead. Also change the defconfig files using it as well as some board files depending on CONFIG_MTD_NAND_PXA3xx.

Quoted 7 lines

Actually that approach doesn't quite suit me.
I'm not convinced at all a new rewrite of the driver will be stable right out of the box as the old one is.

What I would propose instead is :
- keep both the new marvell nand driver and the old pxa3xx_nand driver
- switch pxa_defconfig to compile them both

Didn't notice you were suggesting to compile both, which doesn't work because both drivers match the same devices, and only one of them can actually claim the device (likely the first one to register to the device model). So, to make it safe you need to have a

depends on !MTD_NAND_PXA3xx

in your MTD_NAND_MARVELL entry, which means only one driver can be compiled.

Quoted 15 lines

- continue testing (with my help)

Once I see the marvell_nand is working properly, we can remove pca3xx_nand in a second stage, keeping the pxa platform functional all along.

Would you agree to that path ?

As a side not, this patch should be split into 2 parts :
- one for arch/arm -> switch to the new driver
- one for drivers/mtd -> removal of the old driver

Cheers.

--
Robert

maintainer
#12 lore raw

PATCH v2 3/5 mtd: nand: replace pxa3xx_nand driver by its rework called marvell_nand

Quoted 9 lines · Boris Brezillon
Quoted 3 lines

What I would propose instead is :
- keep both the new marvell nand driver and the old pxa3xx_nand driver
- switch pxa_defconfig to compile them both

Didn't notice you were suggesting to compile both, which doesn't work because both drivers match the same devices, and only one of them can actually claim the device (likely the first one to register to the device model). So, to make it safe you need to have a

Quoted 4 lines

depends on !MTD_NAND_PXA3xx

in your MTD_NAND_MARVELL entry, which means only one driver can be compiled.

Mmm... that is I didn't explain to you what pxa_defconfig is designed for. This defconfig is not for any board actually, it rather is a build coverage tool.

Quoted 2 lines

So let's find a way to fix the remaining issues you have instead of delaying the inevitable.

It's up to you of course, as long as my boards don't break, and nothing breaking them is merged, I'm fine with it. It's just not the approach I usually choose, I'm rather a 2-step guy, ie. merge the new one, then merge the switch (which can be reverted easilly).

Cheers.

Signature · 1 line
Robert
#13 lore raw

PATCH v2 3/5 mtd: nand: replace pxa3xx_nand driver by its rework called marvell_nand

Quoted 19 lines · Robert Jarzmik
Quoted 9 lines · Boris Brezillon
Quoted 3 lines

What I would propose instead is :
- keep both the new marvell nand driver and the old pxa3xx_nand driver
- switch pxa_defconfig to compile them both

Didn't notice you were suggesting to compile both, which doesn't work because both drivers match the same devices, and only one of them can actually claim the device (likely the first one to register to the device model). So, to make it safe you need to have a

Quoted 4 lines

depends on !MTD_NAND_PXA3xx

in your MTD_NAND_MARVELL entry, which means only one driver can be compiled.

Mmm... that is I didn't explain to you what pxa_defconfig is designed for. This defconfig is not for any board actually, it rather is a build coverage tool.

Quoted 7 lines
Quoted 2 lines

So let's find a way to fix the remaining issues you have instead of delaying the inevitable.

It's up to you of course, as long as my boards don't break, and nothing breaking them is merged, I'm fine with it. It's just not the approach I usually choose, I'm rather a 2-step guy, ie. merge the new one, then merge the switch (which can be reverted easilly).

I agree with Boris on this. Let's get this new driver tested as much as possible; then, once everyone is fairly happy with it, merge it.

It won't be completely stable on day-one, but that is why we have -rc cycles. Plus, distributions and appliances use -stable.

Signature · 2 lines
Ezequiel Garc?a, VanguardiaSur
www.vanguardiasur.com.ar
#14 lore raw

PATCH v2 4/5 dt-bindings: mtd: remove pxa3xx NAND controller documentation

In reply to Miquel Raynal

The deprecated pxa3xx_nand.c driver does not exist anymore, it has been replaced by marvell_nand.c which has its own up-to-date documentation.

---
 .../devicetree/bindings/mtd/pxa3xx-nand.txt        | 50 ----------------------
 1 file changed, 50 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/mtd/pxa3xx-nand.txt
Documentation/devicetree/bindings/mtd/pxa3xx-nand.txt deleted +0 -50
deleted file mode 100644
index d4ee4da58463..000000000000
@@ -1,50 +0,0 @@
-PXA3xx NAND DT bindings
-
-Required properties:
-
- - compatible:		Should be set to one of the following:
-			marvell,pxa3xx-nand
-			marvell,armada370-nand
-			marvell,armada-8k-nand
- - reg: 		The register base for the controller
- - interrupts:		The interrupt to map
- - #address-cells:	Set to <1> if the node includes partitions
- - marvell,system-controller: Set to retrieve the syscon node that handles
-			NAND controller related registers (only required
-			with marvell,armada-8k-nand compatible).
-
-Optional properties:
-
- - dmas:			dma data channel, see dma.txt binding doc
- - marvell,nand-enable-arbiter:	Set to enable the bus arbiter
- - marvell,nand-keep-config:	Set to keep the NAND controller config as set
-				by the bootloader
- - num-cs:			Number of chipselect lines to use
- - nand-on-flash-bbt: 		boolean to enable on flash bbt option if
-				not present false
- - nand-ecc-strength:           number of bits to correct per ECC step
- - nand-ecc-step-size:          number of data bytes covered by a single ECC step
-
-The following ECC strength and step size are currently supported:
-
- - nand-ecc-strength = <1>, nand-ecc-step-size = <512>
- - nand-ecc-strength = <4>, nand-ecc-step-size = <512>
- - nand-ecc-strength = <8>, nand-ecc-step-size = <512>
-
-Example:
-
-	nand0: nand at 43100000 {
-		compatible = "marvell,pxa3xx-nand";
-		reg = <0x43100000 90>;
-		interrupts = <45>;
-		dmas = <&pdma 97 0>;
-		dma-names = "data";
-		#address-cells = <1>;
-
-		marvell,nand-enable-arbiter;
-		marvell,nand-keep-config;
-		num-cs = <1>;
-
-		/* partitions (optional) */
-	};
-
Signature · 1 line
2.11.0
maintainer
#15 lore raw

PATCH v2 4/5 dt-bindings: mtd: remove pxa3xx NAND controller documentation

Quoted 8 lines · Miquel Raynal

The deprecated pxa3xx_nand.c driver does not exist anymore, it has been replaced by marvell_nand.c which has its own up-to-date documentation.

---
 .../devicetree/bindings/mtd/pxa3xx-nand.txt        | 50 ----------------------
 1 file changed, 50 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/mtd/pxa3xx-nand.txt
#16 lore raw

PATCH v2 5/5 mtd: nand: remove useless fields from pxa3xx NAND platform data

In reply to Miquel Raynal

The "enable arbiter" bit is available only for pxa3xx based platforms but it was experimentally shown that even if this bit is reserved, some Marvell platforms (64-bit) actually need it to be set. The driver always set this bit regardless of this property, which is harmless. Then this property is not needed.

The "num_cs" field is always 1 and for a good reason, the old driver (pxa3xx_nand.c) could only handle one. The new driver that replaces it (marvell_nand.c) can handle more, but better use device tree for such description. As there is only one available chip select, there is no need for an array of partitions neither an array of partition numbers.

---
 arch/arm/mach-pxa/cm-x300.c                   |  6 ++--
 arch/arm/mach-pxa/colibri-pxa3xx.c            |  6 ++--
 arch/arm/mach-pxa/littleton.c                 |  6 ++--
 arch/arm/mach-pxa/mxm8x10.c                   |  6 ++--
 arch/arm/mach-pxa/raumfeld.c                  |  6 ++--
 arch/arm/mach-pxa/zylonite.c                  |  6 ++--
 drivers/mtd/nand/marvell_nand.c               |  3 +-
 include/linux/platform_data/mtd-nand-pxa3xx.h | 43 ++++++++-------------------
 8 files changed, 25 insertions(+), 57 deletions(-)
arch/arm/mach-pxa/cm-x300.c +2 -4
index 89326378dabf..23bb273f3ff3 100644
@@ -429,11 +429,9 @@ static struct mtd_partition cm_x300_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data cm_x300_nand_info = {
-	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.num_cs		= 1,
-	.parts[0]	= cm_x300_nand_partitions,
-	.nr_parts[0]	= ARRAY_SIZE(cm_x300_nand_partitions),
+	.parts		= cm_x300_nand_partitions,
+	.nr_parts	= ARRAY_SIZE(cm_x300_nand_partitions),
 };
 
 static void __init cm_x300_init_nand(void)
arch/arm/mach-pxa/colibri-pxa3xx.c +2 -4
index c9c36f707555..adec6e013fe6 100644
@@ -138,11 +138,9 @@ static struct mtd_partition colibri_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data colibri_nand_info = {
-	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.num_cs		= 1,
-	.parts[0]	= colibri_nand_partitions,
-	.nr_parts[0]	= ARRAY_SIZE(colibri_nand_partitions),
+	.parts		= colibri_nand_partitions,
+	.nr_parts	= ARRAY_SIZE(colibri_nand_partitions),
 };
 
 void __init colibri_pxa3xx_init_nand(void)
arch/arm/mach-pxa/littleton.c +2 -4
index 898961be36db..7e120e884165 100644
@@ -328,10 +328,8 @@ static struct mtd_partition littleton_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data littleton_nand_info = {
-	.enable_arbiter	= 1,
-	.num_cs		= 1,
-	.parts[0]	= littleton_nand_partitions,
-	.nr_parts[0]	= ARRAY_SIZE(littleton_nand_partitions),
+	.parts		= littleton_nand_partitions,
+	.nr_parts	= ARRAY_SIZE(littleton_nand_partitions),
 };
 
 static void __init littleton_init_nand(void)
arch/arm/mach-pxa/mxm8x10.c +2 -4
index 7d7aab20b70a..328689052668 100644
@@ -389,11 +389,9 @@ static struct mtd_partition mxm_8x10_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data mxm_8x10_nand_info = {
-	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.num_cs		= 1,
-	.parts[0]	= mxm_8x10_nand_partitions,
-	.nr_parts[0]	= ARRAY_SIZE(mxm_8x10_nand_partitions)
+	.parts		= mxm_8x10_nand_partitions,
+	.nr_parts	= ARRAY_SIZE(mxm_8x10_nand_partitions)
 };
 
 static void __init mxm_8x10_nand_init(void)
arch/arm/mach-pxa/raumfeld.c +2 -4
index 9d662fed03ec..af72e79a7ffa 100644
@@ -346,11 +346,9 @@ static struct mtd_partition raumfeld_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data raumfeld_nand_info = {
-	.enable_arbiter	= 1,
 	.keep_config	= 1,
-	.num_cs		= 1,
-	.parts[0]	= raumfeld_nand_partitions,
-	.nr_parts[0]	= ARRAY_SIZE(raumfeld_nand_partitions),
+	.parts		= raumfeld_nand_partitions,
+	.nr_parts	= ARRAY_SIZE(raumfeld_nand_partitions),
 };
 
 /**
arch/arm/mach-pxa/zylonite.c +2 -4
index a4577118d518..0534949d63f6 100644
@@ -376,10 +376,8 @@ static struct mtd_partition zylonite_nand_partitions[] = {
 };
 
 static struct pxa3xx_nand_platform_data zylonite_nand_info = {
-	.enable_arbiter	= 1,
-	.num_cs		= 1,
-	.parts[0]	= zylonite_nand_partitions,
-	.nr_parts[0]	= ARRAY_SIZE(zylonite_nand_partitions),
+	.parts		= zylonite_nand_partitions,
+	.nr_parts	= ARRAY_SIZE(zylonite_nand_partitions),
 };
 
 static void __init zylonite_init_nand(void)
drivers/mtd/nand/marvell_nand.c +1 -2
index 0a5432bcda6a..c618ccb22a61 100644
@@ -2568,8 +2568,7 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
 
 	if (pdata)
 		/* Legacy bindings support only one chip */
-		ret = mtd_device_register(mtd, pdata->parts[0],
-					  pdata->nr_parts[0]);
+		ret = mtd_device_register(mtd, pdata->parts, pdata->nr_parts);
 	else
 		ret = mtd_device_register(mtd, NULL, 0);
 	if (ret) {
include/linux/platform_data/mtd-nand-pxa3xx.h +12 -31

53 lines not on this page · raw message

Signature · 1 line
2.11.0