linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] mtd: rawnand: add STM32 FMC2 NAND flash controller driver
@ 2018-09-17 15:47 christophe.kerello
  2018-09-17 15:47 ` [PATCH 1/3] dt-bindings: mtd: stm32_fmc2: add STM32 FMC2 NAND controller documentation christophe.kerello
                   ` (2 more replies)
  0 siblings, 3 replies; 18+ messages in thread
From: christophe.kerello @ 2018-09-17 15:47 UTC (permalink / raw)
  To: boris.brezillon, miquel.raynal, richard, dwmw2,
	computersforpeace, marek.vasut, robh+dt, mark.rutland
  Cc: linux-mtd, linux-kernel, devicetree, linux-stm32, Christophe Kerello

From: Christophe Kerello <christophe.kerello@st.com>

This patchset adds the support for the STMicroelectronics FMC2 NAND flash
controller found on STM32MP SOCs.

This patchset supports:
  - the command sequencer feature, a hardware accelerator for read/write
    within a page
  - the manual mode feature, useful for debug purpose
  - a maximum 8k page size
  - following ECC strength and step size
	- nand-ecc-strength = <8>, nand-ecc-step-size = <512> (BCH8)
	- nand-ecc-strength = <4>, nand-ecc-step-size = <512> (BCH4)
	- nand-ecc-strength = <1>, nand-ecc-step-size = <512> (Extended ecc
	  based on HAMMING)
    
This patchset has been tested on Micron MT29F8G08ABACAH4 (8-bit SLC NAND)
and MT29F8G16ABACAH4 (16-bit SLC NAND)

Christophe Kerello (3):
  dt-bindings: mtd: stm32_fmc2: add STM32 FMC2 NAND controller
    documentation
  mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver
  mtd: rawnand: stm32_fmc2: add manual mode

 .../devicetree/bindings/mtd/stm32-fmc2-nand.txt    |   90 +
 drivers/mtd/nand/raw/Kconfig                       |    9 +
 drivers/mtd/nand/raw/Makefile                      |    1 +
 drivers/mtd/nand/raw/stm32_fmc2_nand.c             | 1980 ++++++++++++++++++++
 4 files changed, 2080 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt
 create mode 100644 drivers/mtd/nand/raw/stm32_fmc2_nand.c

-- 
1.9.1


^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH 1/3] dt-bindings: mtd: stm32_fmc2: add STM32 FMC2 NAND controller documentation
  2018-09-17 15:47 [PATCH 0/3] mtd: rawnand: add STM32 FMC2 NAND flash controller driver christophe.kerello
@ 2018-09-17 15:47 ` christophe.kerello
  2018-09-22  8:34   ` Miquel Raynal
  2018-09-17 15:47 ` [PATCH 2/3] mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver christophe.kerello
  2018-09-17 15:47 ` [PATCH 3/3] mtd: rawnand: stm32_fmc2: add manual mode christophe.kerello
  2 siblings, 1 reply; 18+ messages in thread
From: christophe.kerello @ 2018-09-17 15:47 UTC (permalink / raw)
  To: boris.brezillon, miquel.raynal, richard, dwmw2,
	computersforpeace, marek.vasut, robh+dt, mark.rutland
  Cc: linux-mtd, linux-kernel, devicetree, linux-stm32, Christophe Kerello

From: Christophe Kerello <christophe.kerello@st.com>

This patch adds the documentation of the device tree bindings for the STM32
FMC2 NAND controller.

Signed-off-by: Christophe Kerello <christophe.kerello@st.com>
---
 .../devicetree/bindings/mtd/stm32-fmc2-nand.txt    | 90 ++++++++++++++++++++++
 1 file changed, 90 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt

diff --git a/Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt b/Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt
new file mode 100644
index 0000000..93eaf11
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt
@@ -0,0 +1,90 @@
+STMicroelectronics Flexible Memory Controller 2 (FMC2)
+NAND Interface
+
+Required properties:
+- compatible: "st,stm32mp15-fmc2"
+- reg: the first contains the register location and length
+       the second contains the data common space used for cs0 and length
+       the third contains the cmd attribute space used for cs0 and length
+       the fourth contains the addr attribute space used for cs0 and length
+       the fifth contains the data common space used for cs1 and length
+       the sixth contains the cmd attribute space used for cs1 and length
+       the seventh contains the addr attribute space used for cs1 and length
+- interrupts: The interrupt number
+- pinctrl-0: Standard Pinctrl phandle (see: pinctrl/pinctrl-bindings.txt)
+- clocks: Use common clock framework
+
+Optional properties:
+- resets: Reference to a reset controller asserting the FMC controller
+- dmas: DMA specifiers (see: dma/stm32-mdma.txt)
+- dma-names: Must be "tx", "rx" and "ecc"
+
+Optional children nodes:
+Children nodes represent the available nand chips.
+
+Optional properties:
+- nand-on-flash-bbt: see nand.txt
+- nand-ecc-strength: see nand.txt
+- nand-ecc-step-size: see nand.txt
+- st,fmc2_timings: array of 8 bytes for NAND timings. The meanings of
+  these bytes are:
+  byte 0 TCLR      : CLE to RE delay in number of AHB clock cycles, only 4 bits
+                     are valid. Zero means one clock cycle, 15 means 16 clock
+                     cycles.
+  byte 1 TAR       : ALE to RE delay, 4 bits are valid. Same format as TCLR.
+  byte 2 THIZ      : number of HCLK clock cycles during which the data bus is
+                     kept in Hi-Z (tristate) after the start of a write access.
+                     Only valid for write transactions. Zero means 1 cycle,
+                     255 means 256 cycles.
+  byte 3 TWAIT     : number of HCLK clock cycles to assert the command to the
+                     NAND flash in response to SMWAITn. Zero means 1 cycle,
+                     255 means 256 cycles.
+  byte 4 THOLD_MEM : common memory space timing
+                     number of HCLK clock cycles to hold the address (and data
+                     when writing) after the command deassertion. Zero means
+                     1 cycle, 255 means 256 cycles.
+  byte 5 TSET_MEM  : common memory space timing
+                     number of HCLK clock cycles to assert the address before
+                     the command is asserted. Zero means 1 cycle, 255 means 256
+                     cycles.
+  byte 6 THOLD_ATT : attribute memory space timing
+                     number of HCLK clock cycles to hold the address (and data
+                     when writing) after the command deassertion. Zero means
+                     1 cycle, 255 means 256 cycles.
+  byte 7 TSET_ATT  : attribute memory space timing
+                     number of HCLK clock cycles to assert the address before
+                     the command is asserted. Zero means 1 cycle, 255 means 256
+                     cycles.
+
+The following ECC strength and step size are currently supported:
+ - nand-ecc-strength = <1>, nand-ecc-step-size = <512> (HAMMING)
+ - nand-ecc-strength = <4>, nand-ecc-step-size = <512> (BCH4)
+ - nand-ecc-strength = <8>, nand-ecc-step-size = <512> (BCH8) (default)
+
+Example:
+
+	fmc2_nand: fmc2_nand@58002000 {
+		compatible = "st,stm32mp15-fmc2";
+		reg = <0x58002000 0x1000>,
+		      <0x80000000 0x1000>,
+		      <0x88010000 0x1000>,
+		      <0x88020000 0x1000>,
+		      <0x81000000 0x1000>,
+		      <0x89010000 0x1000>,
+		      <0x89020000 0x1000>;
+		interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&rcc FMC_K>;
+		resets = <&rcc FMC_R>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&fmc2_pins_a>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		nand@0 {
+			reg = <0>;
+			nand-on-flash-bbt;
+			st,fmc2_timings = /bits/ 8 <2 2 1 7 1 0 104 25>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+		};
+	};
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 2/3] mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver
  2018-09-17 15:47 [PATCH 0/3] mtd: rawnand: add STM32 FMC2 NAND flash controller driver christophe.kerello
  2018-09-17 15:47 ` [PATCH 1/3] dt-bindings: mtd: stm32_fmc2: add STM32 FMC2 NAND controller documentation christophe.kerello
@ 2018-09-17 15:47 ` christophe.kerello
  2018-09-17 17:05   ` kbuild test robot
                     ` (4 more replies)
  2018-09-17 15:47 ` [PATCH 3/3] mtd: rawnand: stm32_fmc2: add manual mode christophe.kerello
  2 siblings, 5 replies; 18+ messages in thread
From: christophe.kerello @ 2018-09-17 15:47 UTC (permalink / raw)
  To: boris.brezillon, miquel.raynal, richard, dwmw2,
	computersforpeace, marek.vasut, robh+dt, mark.rutland
  Cc: linux-mtd, linux-kernel, devicetree, linux-stm32, Christophe Kerello

From: Christophe Kerello <christophe.kerello@st.com>

The driver adds the support for the STMicroelectronics FMC2 NAND
Controller found on STM32MP SOCs.

This patch is based on FMC2 command sequencer.
The purpose of the command sequencer is to facilitate the programming
and the reading of NAND flash pages with the ECC and to free the CPU
of sequencing tasks.
It requires one DMA channel for write and two DMA channels for read
operations.

Only NAND_ECC_HW mode is actually supported.
The driver supports a maximum 8k page size.
The following ECC strength and step size are currently supported:
 - nand-ecc-strength = <8>, nand-ecc-step-size = <512> (BCH8)
 - nand-ecc-strength = <4>, nand-ecc-step-size = <512> (BCH4)
 - nand-ecc-strength = <1>, nand-ecc-step-size = <512> (Extended ecc
   based on HAMMING)

This patch has been tested on Micron MT29F8G08ABACAH4 and
MT29F8G16ABACAH4

Signed-off-by: Christophe Kerello <christophe.kerello@st.com>
---
 drivers/mtd/nand/raw/Kconfig           |    9 +
 drivers/mtd/nand/raw/Makefile          |    1 +
 drivers/mtd/nand/raw/stm32_fmc2_nand.c | 1729 ++++++++++++++++++++++++++++++++
 3 files changed, 1739 insertions(+)
 create mode 100644 drivers/mtd/nand/raw/stm32_fmc2_nand.c

diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index c7efc31..863662c 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -541,4 +541,13 @@ config MTD_NAND_TEGRA
 	  is supported. Extra OOB bytes when using HW ECC are currently
 	  not supported.
 
+config MTD_NAND_STM32_FMC2
+	tristate "Support for NAND controller on STM32MP SoCs"
+	depends on MACH_STM32MP157 || COMPILE_TEST
+	help
+	  Enables support for NAND Flash chips on SoCs containing the FMC2
+	  NAND controller. This controller is found on STM32MP SoCs.
+	  The driver supports a maximum 8k page size. HW ECC is enabled and
+	  supports a maximum 8-bit correction error per sector of 512 bytes.
+
 endif # MTD_NAND
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index a6ef067..8c437e3 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -56,6 +56,7 @@ obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand/
 obj-$(CONFIG_MTD_NAND_QCOM)		+= qcom_nandc.o
 obj-$(CONFIG_MTD_NAND_MTK)		+= mtk_ecc.o mtk_nand.o
 obj-$(CONFIG_MTD_NAND_TEGRA)		+= tegra_nand.o
+obj-$(CONFIG_MTD_NAND_STM32_FMC2)	+= stm32_fmc2_nand.o
 
 nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o
 nand-objs += nand_amd.o
diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c
new file mode 100644
index 0000000..dd5762a
--- /dev/null
+++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c
@@ -0,0 +1,1729 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Christophe Kerello <christophe.kerello@st.com> for STMicroelectronics
+ */
+
+#include <linux/clk.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+/* Bad block marker length */
+#define FMC2_BBM_LEN			2
+
+/* ECC step size */
+#define FMC2_ECC_STEP_SIZE		512
+
+/* BCHDSRx registers length */
+#define FMC2_BCHDSRS_LEN		20
+
+/* HECCR length */
+#define FMC2_HECCR_LEN			4
+
+/* Max requests done for a 8k nand page size */
+#define FMC2_MAX_SG_COUNT		16
+
+/* Max chip enable */
+#define FMC2_MAX_CE			2
+
+/* Timings */
+#define FMC2_THIZ			1
+#define FMC2_TIO			8000
+#define FMC2_TSYNC			3000
+#define FMC2_PCR_TIMING_MASK		0xf
+#define FMC2_PMEM_PATT_TIMING_MASK	0xff
+
+/* FMC2 Controller Registers */
+#define FMC2_BCR1			0x0
+#define FMC2_PCR			0x80
+#define FMC2_SR				0x84
+#define FMC2_PMEM			0x88
+#define FMC2_PATT			0x8c
+#define FMC2_HECCR			0x94
+#define FMC2_CSQCR			0x200
+#define FMC2_CSQCFGR1			0x204
+#define FMC2_CSQCFGR2			0x208
+#define FMC2_CSQCFGR3			0x20c
+#define FMC2_CSQAR1			0x210
+#define FMC2_CSQAR2			0x214
+#define FMC2_CSQIER			0x220
+#define FMC2_CSQISR			0x224
+#define FMC2_CSQICR			0x228
+#define FMC2_CSQEMSR			0x230
+#define FMC2_BCHIER			0x250
+#define FMC2_BCHISR			0x254
+#define FMC2_BCHICR			0x258
+#define FMC2_BCHPBR1			0x260
+#define FMC2_BCHPBR2			0x264
+#define FMC2_BCHPBR3			0x268
+#define FMC2_BCHPBR4			0x26c
+#define FMC2_BCHDSR0			0x27c
+#define FMC2_BCHDSR1			0x280
+#define FMC2_BCHDSR2			0x284
+#define FMC2_BCHDSR3			0x288
+#define FMC2_BCHDSR4			0x28c
+
+/* Register: FMC2_BCR1 */
+#define FMC2_BCR1_FMC2EN		BIT(31)
+
+/* Register: FMC2_PCR */
+#define FMC2_PCR_PWAITEN		BIT(1)
+#define FMC2_PCR_PBKEN			BIT(2)
+#define FMC2_PCR_PWID_MASK		GENMASK(5, 4)
+#define FMC2_PCR_PWID(x)		(((x) & 0x3) << 4)
+#define FMC2_PCR_PWID_BUSWIDTH_8	0
+#define FMC2_PCR_PWID_BUSWIDTH_16	1
+#define FMC2_PCR_ECCEN			BIT(6)
+#define FMC2_PCR_ECCALG			BIT(8)
+#define FMC2_PCR_TCLR_MASK		GENMASK(12, 9)
+#define FMC2_PCR_TCLR(x)		(((x) & 0xf) << 9)
+#define FMC2_PCR_TCLR_DEFAULT		0xf
+#define FMC2_PCR_TAR_MASK		GENMASK(16, 13)
+#define FMC2_PCR_TAR(x)			(((x) & 0xf) << 13)
+#define FMC2_PCR_TAR_DEFAULT		0xf
+#define FMC2_PCR_ECCSS_MASK		GENMASK(19, 17)
+#define FMC2_PCR_ECCSS(x)		(((x) & 0x7) << 17)
+#define FMC2_PCR_ECCSS_512		1
+#define FMC2_PCR_ECCSS_2048		3
+#define FMC2_PCR_BCHECC			BIT(24)
+#define FMC2_PCR_WEN			BIT(25)
+
+/* Register: FMC2_SR */
+#define FMC2_SR_NWRF			BIT(6)
+
+/* Register: FMC2_PMEM */
+#define FMC2_PMEM_MEMSET(x)		(((x) & 0xff) << 0)
+#define FMC2_PMEM_MEMWAIT(x)		(((x) & 0xff) << 8)
+#define FMC2_PMEM_MEMHOLD(x)		(((x) & 0xff) << 16)
+#define FMC2_PMEM_MEMHIZ(x)		(((x) & 0xff) << 24)
+#define FMC2_PMEM_DEFAULT		0x0a0a0a0a
+
+/* Register: FMC2_PATT */
+#define FMC2_PATT_ATTSET(x)		(((x) & 0xff) << 0)
+#define FMC2_PATT_ATTWAIT(x)		(((x) & 0xff) << 8)
+#define FMC2_PATT_ATTHOLD(x)		(((x) & 0xff) << 16)
+#define FMC2_PATT_ATTHIZ(x)		(((x) & 0xff) << 24)
+#define FMC2_PATT_DEFAULT		0x0a0a0a0a
+
+/* Register: FMC2_CSQCR */
+#define FMC2_CSQCR_CSQSTART		BIT(0)
+
+/* Register: FMC2_CSQCFGR1 */
+#define FMC2_CSQCFGR1_CMD2EN		BIT(1)
+#define FMC2_CSQCFGR1_DMADEN		BIT(2)
+#define FMC2_CSQCFGR1_ACYNBR(x)		(((x) & 0x7) << 4)
+#define FMC2_CSQCFGR1_CMD1(x)		(((x) & 0xff) << 8)
+#define FMC2_CSQCFGR1_CMD2(x)		(((x) & 0xff) << 16)
+#define FMC2_CSQCFGR1_CMD1T		BIT(24)
+#define FMC2_CSQCFGR1_CMD2T		BIT(25)
+
+/* Register: FMC2_CSQCFGR2 */
+#define FMC2_CSQCFGR2_SQSDTEN		BIT(0)
+#define FMC2_CSQCFGR2_RCMD2EN		BIT(1)
+#define FMC2_CSQCFGR2_DMASEN		BIT(2)
+#define FMC2_CSQCFGR2_RCMD1(x)		(((x) & 0xff) << 8)
+#define FMC2_CSQCFGR2_RCMD2(x)		(((x) & 0xff) << 16)
+#define FMC2_CSQCFGR2_RCMD1T		BIT(24)
+#define FMC2_CSQCFGR2_RCMD2T		BIT(25)
+
+/* Register: FMC2_CSQCFGR3 */
+#define FMC2_CSQCFGR3_SNBR(x)		(((x) & 0x1f) << 8)
+#define FMC2_CSQCFGR3_AC1T		BIT(16)
+#define FMC2_CSQCFGR3_AC2T		BIT(17)
+#define FMC2_CSQCFGR3_AC3T		BIT(18)
+#define FMC2_CSQCFGR3_AC4T		BIT(19)
+#define FMC2_CSQCFGR3_AC5T		BIT(20)
+#define FMC2_CSQCFGR3_SDT		BIT(21)
+#define FMC2_CSQCFGR3_RAC1T		BIT(22)
+#define FMC2_CSQCFGR3_RAC2T		BIT(23)
+
+/* Register: FMC2_CSQCAR1 */
+#define FMC2_CSQCAR1_ADDC1(x)		(((x) & 0xff) << 0)
+#define FMC2_CSQCAR1_ADDC2(x)		(((x) & 0xff) << 8)
+#define FMC2_CSQCAR1_ADDC3(x)		(((x) & 0xff) << 16)
+#define FMC2_CSQCAR1_ADDC4(x)		(((x) & 0xff) << 24)
+
+/* Register: FMC2_CSQCAR2 */
+#define FMC2_CSQCAR2_ADDC5(x)		(((x) & 0xff) << 0)
+#define FMC2_CSQCAR2_NANDCEN(x)		(((x) & 0x3) << 10)
+#define FMC2_CSQCAR2_SAO(x)		(((x) & 0xffff) << 16)
+
+/* Register: FMC2_CSQIER */
+#define FMC2_CSQIER_TCIE		BIT(0)
+
+/* Register: FMC2_CSQICR */
+#define FMC2_CSQICR_CLEAR_IRQ		GENMASK(4, 0)
+
+/* Register: FMC2_CSQEMSR */
+#define FMC2_CSQEMSR_SEM		GENMASK(15, 0)
+
+/* Register: FMC2_BCHIER */
+#define FMC2_BCHIER_DERIE		BIT(1)
+#define FMC2_BCHIER_EPBRIE		BIT(4)
+
+/* Register: FMC2_BCHICR */
+#define FMC2_BCHICR_CLEAR_IRQ		GENMASK(4, 0)
+
+/* Register: FMC2_BCHDSR0 */
+#define FMC2_BCHDSR0_DUE		BIT(0)
+#define FMC2_BCHDSR0_DEF		BIT(1)
+#define FMC2_BCHDSR0_DEN_MASK		GENMASK(7, 4)
+#define FMC2_BCHDSR0_DEN_SHIFT		4
+
+/* Register: FMC2_BCHDSR1 */
+#define FMC2_BCHDSR1_EBP1_MASK		GENMASK(12, 0)
+#define FMC2_BCHDSR1_EBP2_MASK		GENMASK(28, 16)
+#define FMC2_BCHDSR1_EBP2_SHIFT		16
+
+/* Register: FMC2_BCHDSR2 */
+#define FMC2_BCHDSR2_EBP3_MASK		GENMASK(12, 0)
+#define FMC2_BCHDSR2_EBP4_MASK		GENMASK(28, 16)
+#define FMC2_BCHDSR2_EBP4_SHIFT		16
+
+/* Register: FMC2_BCHDSR3 */
+#define FMC2_BCHDSR3_EBP5_MASK		GENMASK(12, 0)
+#define FMC2_BCHDSR3_EBP6_MASK		GENMASK(28, 16)
+#define FMC2_BCHDSR3_EBP6_SHIFT		16
+
+/* Register: FMC2_BCHDSR4 */
+#define FMC2_BCHDSR4_EBP7_MASK		GENMASK(12, 0)
+#define FMC2_BCHDSR4_EBP8_MASK		GENMASK(28, 16)
+#define FMC2_BCHDSR4_EBP8_SHIFT		16
+
+enum stm32_fmc2_ecc {
+	FMC2_ECC_HAM = 1,
+	FMC2_ECC_BCH4 = 4,
+	FMC2_ECC_BCH8 = 8
+};
+
+struct stm32_fmc2_timings {
+	u8 tclr;
+	u8 tar;
+	u8 thiz;
+	u8 twait;
+	u8 thold_mem;
+	u8 tset_mem;
+	u8 thold_att;
+	u8 tset_att;
+};
+
+struct stm32_fmc2 {
+	struct nand_chip chip;
+	struct device *dev;
+	void __iomem *io_base;
+	void __iomem *data_base[FMC2_MAX_CE];
+	void __iomem *cmd_base[FMC2_MAX_CE];
+	void __iomem *addr_base[FMC2_MAX_CE];
+	phys_addr_t io_phys_addr;
+	phys_addr_t data_phys_addr[FMC2_MAX_CE];
+	struct clk *clk;
+
+	struct dma_chan *dma_tx_ch;
+	struct dma_chan *dma_rx_ch;
+	struct dma_chan *dma_ecc_ch;
+	struct sg_table dma_data_sg;
+	struct sg_table dma_ecc_sg;
+	u8 *ecc_buf;
+	int dma_ecc_len;
+
+	struct completion complete;
+	struct completion dma_data_complete;
+	struct completion dma_ecc_complete;
+
+	struct stm32_fmc2_timings timings;
+	u8 cs_assigned;
+	int cs_sel;
+	int ncs;
+	int cs_used[FMC2_MAX_CE];
+};
+
+/* Enable irq sources in case of the sequencer is used */
+static inline void stm32_fmc2_enable_seq_irq(struct stm32_fmc2 *fmc2)
+{
+	u32 csqier = readl_relaxed(fmc2->io_base + FMC2_CSQIER);
+
+	csqier |= FMC2_CSQIER_TCIE;
+
+	writel_relaxed(csqier, fmc2->io_base + FMC2_CSQIER);
+}
+
+/* Disable irq sources in case of the sequencer is used */
+static inline void stm32_fmc2_disable_seq_irq(struct stm32_fmc2 *fmc2)
+{
+	u32 csqier = readl_relaxed(fmc2->io_base + FMC2_CSQIER);
+
+	csqier &= ~FMC2_CSQIER_TCIE;
+
+	writel_relaxed(csqier, fmc2->io_base + FMC2_CSQIER);
+}
+
+/* Clear irq sources in case of the sequencer is used */
+static inline void stm32_fmc2_clear_seq_irq(struct stm32_fmc2 *fmc2)
+{
+	writel_relaxed(FMC2_CSQICR_CLEAR_IRQ, fmc2->io_base + FMC2_CSQICR);
+}
+
+/* Select function */
+static void stm32_fmc2_select_chip(struct nand_chip *chip, int chipnr)
+{
+	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
+	struct dma_slave_config dma_cfg;
+
+	if (chipnr < 0 || chipnr >= fmc2->ncs)
+		return;
+
+	if (fmc2->cs_used[chipnr] == fmc2->cs_sel)
+		return;
+
+	fmc2->cs_sel = fmc2->cs_used[chipnr];
+
+	if (fmc2->dma_tx_ch && fmc2->dma_rx_ch) {
+		memset(&dma_cfg, 0, sizeof(dma_cfg));
+		dma_cfg.src_addr = fmc2->data_phys_addr[fmc2->cs_sel];
+		dma_cfg.dst_addr = fmc2->data_phys_addr[fmc2->cs_sel];
+		dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		dma_cfg.src_maxburst = 32;
+		dma_cfg.dst_maxburst = 32;
+
+		if (dmaengine_slave_config(fmc2->dma_tx_ch, &dma_cfg))
+			dev_warn(fmc2->dev, "tx DMA engine slave config failed\n");
+
+		if (dmaengine_slave_config(fmc2->dma_rx_ch, &dma_cfg))
+			dev_warn(fmc2->dev, "rx DMA engine slave config failed\n");
+	}
+}
+
+/*
+ * ECC HAMMING calculation
+ * ECC is 3 bytes for 512 bytes of data (supports error correction up to
+ * max of 1-bit)
+ */
+static inline void stm32_fmc2_ham_set_ecc(const u32 ecc_sta, u8 *ecc)
+{
+	ecc[0] = ecc_sta;
+	ecc[1] = ecc_sta >> 8;
+	ecc[2] = ecc_sta >> 16;
+}
+
+static int stm32_fmc2_ham_correct(struct nand_chip *chip, uint8_t *dat,
+				  uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+	u8 bit_position = 0, b0, b1, b2;
+	u32 byte_addr = 0, b;
+	u32 i, shifting = 1;
+
+	/* Indicate which bit and byte is faulty (if any) */
+	b0 = read_ecc[0] ^ calc_ecc[0];
+	b1 = read_ecc[1] ^ calc_ecc[1];
+	b2 = read_ecc[2] ^ calc_ecc[2];
+	b = b0 | (b1 << 8) | (b2 << 16);
+
+	/* No errors */
+	if (likely(!b))
+		return 0;
+
+	/* Calculate bit position */
+	for (i = 0; i < 3; i++) {
+		switch (b % 4) {
+		case 2:
+			bit_position += shifting;
+		case 1:
+			break;
+		default:
+			return -EBADMSG;
+		}
+		shifting <<= 1;
+		b >>= 2;
+	}
+
+	/* Calculate byte position */
+	shifting = 1;
+	for (i = 0; i < 9; i++) {
+		switch (b % 4) {
+		case 2:
+			byte_addr += shifting;
+		case 1:
+			break;
+		default:
+			return -EBADMSG;
+		}
+		shifting <<= 1;
+		b >>= 2;
+	}
+
+	/* Flip the bit */
+	dat[byte_addr] ^= (1 << bit_position);
+
+	return 1;
+}
+
+/* BCH algorithm correction */
+static int stm32_fmc2_bch_decode(int eccsize, u8 *dat, u32 *ecc_sta)
+{
+	u32 bchdsr0 = ecc_sta[0];
+	u32 bchdsr1 = ecc_sta[1];
+	u32 bchdsr2 = ecc_sta[2];
+	u32 bchdsr3 = ecc_sta[3];
+	u32 bchdsr4 = ecc_sta[4];
+	u16 pos[8];
+	int i, den;
+	unsigned int nb_errs = 0;
+
+	/* No errors found */
+	if (likely(!(bchdsr0 & FMC2_BCHDSR0_DEF)))
+		return 0;
+
+	/* Too many errors detected */
+	if (unlikely(bchdsr0 & FMC2_BCHDSR0_DUE))
+		return -EBADMSG;
+
+	pos[0] = bchdsr1 & FMC2_BCHDSR1_EBP1_MASK;
+	pos[1] = (bchdsr1 & FMC2_BCHDSR1_EBP2_MASK) >> FMC2_BCHDSR1_EBP2_SHIFT;
+	pos[2] = bchdsr2 & FMC2_BCHDSR2_EBP3_MASK;
+	pos[3] = (bchdsr2 & FMC2_BCHDSR2_EBP4_MASK) >> FMC2_BCHDSR2_EBP4_SHIFT;
+	pos[4] = bchdsr3 & FMC2_BCHDSR3_EBP5_MASK;
+	pos[5] = (bchdsr3 & FMC2_BCHDSR3_EBP6_MASK) >> FMC2_BCHDSR3_EBP6_SHIFT;
+	pos[6] = bchdsr4 & FMC2_BCHDSR4_EBP7_MASK;
+	pos[7] = (bchdsr4 & FMC2_BCHDSR4_EBP8_MASK) >> FMC2_BCHDSR4_EBP8_SHIFT;
+
+	den = (bchdsr0 & FMC2_BCHDSR0_DEN_MASK) >> FMC2_BCHDSR0_DEN_SHIFT;
+	for (i = 0; i < den; i++) {
+		if (pos[i] < eccsize * 8) {
+			change_bit(pos[i], (unsigned long *)dat);
+			nb_errs++;
+		}
+	}
+
+	return nb_errs;
+}
+
+/* Sequencer read/write configuration */
+static void stm32_fmc2_rw_page_init(struct stm32_fmc2 *fmc2, int page,
+				    int raw, bool write_data)
+{
+	struct nand_chip *chip = &fmc2->chip;
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	u32 csqcfgr1, csqcfgr2, csqcfgr3;
+	u32 csqar1, csqar2;
+	u32 ecc_offset = mtd->writesize + FMC2_BBM_LEN;
+	u32 pcr = readl_relaxed(fmc2->io_base + FMC2_PCR);
+
+	if (write_data)
+		pcr |= FMC2_PCR_WEN;
+	else
+		pcr &= ~FMC2_PCR_WEN;
+	writel_relaxed(pcr, fmc2->io_base + FMC2_PCR);
+
+	/*
+	 * - Set Program Page/Page Read command
+	 * - Enable DMA request data
+	 * - Set timings
+	 */
+	csqcfgr1 = FMC2_CSQCFGR1_DMADEN | FMC2_CSQCFGR1_CMD1T;
+	if (write_data)
+		csqcfgr1 |= FMC2_CSQCFGR1_CMD1(NAND_CMD_SEQIN);
+	else
+		csqcfgr1 |= FMC2_CSQCFGR1_CMD1(NAND_CMD_READ0) |
+			    FMC2_CSQCFGR1_CMD2EN |
+			    FMC2_CSQCFGR1_CMD2(NAND_CMD_READSTART) |
+			    FMC2_CSQCFGR1_CMD2T;
+
+	/*
+	 * - Set Random Data Input/Random Data Read command
+	 * - Enable the sequencer to access the Spare data area
+	 * - Enable  DMA request status decoding for read
+	 * - Set timings
+	 */
+	if (write_data)
+		csqcfgr2 = FMC2_CSQCFGR2_RCMD1(NAND_CMD_RNDIN);
+	else
+		csqcfgr2 = FMC2_CSQCFGR2_RCMD1(NAND_CMD_RNDOUT) |
+			   FMC2_CSQCFGR2_RCMD2EN |
+			   FMC2_CSQCFGR2_RCMD2(NAND_CMD_RNDOUTSTART) |
+			   FMC2_CSQCFGR2_RCMD1T |
+			   FMC2_CSQCFGR2_RCMD2T;
+	if (!raw) {
+		csqcfgr2 |= write_data ? 0 : FMC2_CSQCFGR2_DMASEN;
+		csqcfgr2 |= FMC2_CSQCFGR2_SQSDTEN;
+	}
+
+	/*
+	 * - Set the number of sectors to be written
+	 * - Set timings
+	 */
+	csqcfgr3 = FMC2_CSQCFGR3_SNBR(chip->ecc.steps - 1);
+	if (write_data) {
+		csqcfgr3 |= FMC2_CSQCFGR3_RAC2T;
+		if (chip->chipsize > SZ_128M)
+			csqcfgr3 |= FMC2_CSQCFGR3_AC5T;
+		else
+			csqcfgr3 |= FMC2_CSQCFGR3_AC4T;
+	}
+
+	/*
+	 * Set the fourth first address cycles
+	 * Byte 1 and byte 2 => column, we start at 0x0
+	 * Byte 3 and byte 4 => page
+	 */
+	csqar1 = FMC2_CSQCAR1_ADDC3(page);
+	csqar1 |= FMC2_CSQCAR1_ADDC4(page >> 8);
+
+	/*
+	 * - Set chip enable number
+	 * - Set ecc byte offset in the spare area
+	 * - Calculate the number of address cycles to be issued
+	 * - Set byte 5 of address cycle if needed
+	 */
+	csqar2 = FMC2_CSQCAR2_NANDCEN(fmc2->cs_sel);
+	if (chip->options & NAND_BUSWIDTH_16)
+		csqar2 |= FMC2_CSQCAR2_SAO(ecc_offset >> 1);
+	else
+		csqar2 |= FMC2_CSQCAR2_SAO(ecc_offset);
+	if (chip->chipsize > SZ_128M) {
+		csqcfgr1 |= FMC2_CSQCFGR1_ACYNBR(5);
+		csqar2 |= FMC2_CSQCAR2_ADDC5(page >> 16);
+	} else {
+		csqcfgr1 |= FMC2_CSQCFGR1_ACYNBR(4);
+	}
+
+	writel_relaxed(csqcfgr1, fmc2->io_base + FMC2_CSQCFGR1);
+	writel_relaxed(csqcfgr2, fmc2->io_base + FMC2_CSQCFGR2);
+	writel_relaxed(csqcfgr3, fmc2->io_base + FMC2_CSQCFGR3);
+	writel_relaxed(csqar1, fmc2->io_base + FMC2_CSQAR1);
+	writel_relaxed(csqar2, fmc2->io_base + FMC2_CSQAR2);
+}
+
+static void stm32_fmc2_dma_callback(void *arg)
+{
+	complete((struct completion *)arg);
+}
+
+/* Read/write data from/to a page */
+static int stm32_fmc2_xfer(struct stm32_fmc2 *fmc2, const u8 *buf,
+			   int raw, bool write_data)
+{
+	struct nand_chip *chip = &fmc2->chip;
+	struct dma_async_tx_descriptor *desc_data, *desc_ecc;
+	struct scatterlist *sg;
+	struct dma_chan *dma_ch = fmc2->dma_rx_ch;
+	enum dma_data_direction dma_data_dir = DMA_FROM_DEVICE;
+	enum dma_transfer_direction dma_transfer_dir = DMA_DEV_TO_MEM;
+	u32 csqcr = readl_relaxed(fmc2->io_base + FMC2_CSQCR);
+	int eccsteps = chip->ecc.steps;
+	int eccsize = chip->ecc.size;
+	const u8 *p = buf;
+	int s, ret;
+
+	/* Configure DMA data */
+	if (write_data) {
+		dma_data_dir = DMA_TO_DEVICE;
+		dma_transfer_dir = DMA_MEM_TO_DEV;
+		dma_ch = fmc2->dma_tx_ch;
+	}
+
+	for_each_sg(fmc2->dma_data_sg.sgl, sg, eccsteps, s) {
+		sg_set_buf(sg, p, eccsize);
+		p += eccsize;
+	}
+
+	ret = dma_map_sg(fmc2->dev, fmc2->dma_data_sg.sgl,
+			 eccsteps, dma_data_dir);
+	if (ret < 0)
+		return ret;
+
+	desc_data = dmaengine_prep_slave_sg(dma_ch, fmc2->dma_data_sg.sgl,
+					    eccsteps, dma_transfer_dir,
+					    DMA_PREP_INTERRUPT);
+	if (!desc_data) {
+		ret = -ENOMEM;
+		goto err_unmap_data;
+	}
+
+	reinit_completion(&fmc2->dma_data_complete);
+	reinit_completion(&fmc2->complete);
+	desc_data->callback = stm32_fmc2_dma_callback;
+	desc_data->callback_param = &fmc2->dma_data_complete;
+	ret = dma_submit_error(dmaengine_submit(desc_data));
+	if (ret)
+		goto err_unmap_data;
+
+	dma_async_issue_pending(dma_ch);
+
+	if (!write_data && !raw) {
+		/* Configure DMA ecc status */
+		p = fmc2->ecc_buf;
+		for_each_sg(fmc2->dma_ecc_sg.sgl, sg, eccsteps, s) {
+			sg_set_buf(sg, p, fmc2->dma_ecc_len);
+			p += fmc2->dma_ecc_len;
+		}
+
+		ret = dma_map_sg(fmc2->dev, fmc2->dma_ecc_sg.sgl,
+				 eccsteps, dma_data_dir);
+		if (ret < 0)
+			goto err_unmap_data;
+
+		desc_ecc = dmaengine_prep_slave_sg(fmc2->dma_ecc_ch,
+						   fmc2->dma_ecc_sg.sgl,
+						   eccsteps, dma_transfer_dir,
+						   DMA_PREP_INTERRUPT);
+		if (!desc_ecc) {
+			ret = -ENOMEM;
+			goto err_unmap_ecc;
+		}
+
+		reinit_completion(&fmc2->dma_ecc_complete);
+		desc_ecc->callback = stm32_fmc2_dma_callback;
+		desc_ecc->callback_param = &fmc2->dma_ecc_complete;
+		ret = dma_submit_error(dmaengine_submit(desc_ecc));
+		if (ret)
+			goto err_unmap_ecc;
+
+		dma_async_issue_pending(fmc2->dma_ecc_ch);
+	}
+
+	stm32_fmc2_clear_seq_irq(fmc2);
+	stm32_fmc2_enable_seq_irq(fmc2);
+
+	/* Start the transfer */
+	csqcr |= FMC2_CSQCR_CSQSTART;
+	writel_relaxed(csqcr, fmc2->io_base + FMC2_CSQCR);
+
+	/* Wait end of sequencer transfer */
+	if (!wait_for_completion_timeout(&fmc2->complete,
+					 msecs_to_jiffies(1000))) {
+		dev_err(fmc2->dev, "seq timeout\n");
+		stm32_fmc2_disable_seq_irq(fmc2);
+		dmaengine_terminate_all(dma_ch);
+		if (!write_data && !raw)
+			dmaengine_terminate_all(fmc2->dma_ecc_ch);
+		ret = -ETIMEDOUT;
+		goto err_unmap_ecc;
+	}
+
+	/* Wait DMA data transfer completion */
+	if (!wait_for_completion_timeout(&fmc2->dma_data_complete,
+					 msecs_to_jiffies(100))) {
+		dev_err(fmc2->dev, "data DMA timeout\n");
+		dmaengine_terminate_all(dma_ch);
+		ret = -ETIMEDOUT;
+	}
+
+	/* Wait DMA ecc transfer completion */
+	if (!write_data && !raw) {
+		if (!wait_for_completion_timeout(&fmc2->dma_ecc_complete,
+						 msecs_to_jiffies(100))) {
+			dev_err(fmc2->dev, "ecc DMA timeout\n");
+			dmaengine_terminate_all(fmc2->dma_ecc_ch);
+			ret = -ETIMEDOUT;
+		}
+	}
+
+err_unmap_ecc:
+	if (!write_data && !raw)
+		dma_unmap_sg(fmc2->dev, fmc2->dma_ecc_sg.sgl,
+			     eccsteps, dma_data_dir);
+
+err_unmap_data:
+	dma_unmap_sg(fmc2->dev, fmc2->dma_data_sg.sgl, eccsteps, dma_data_dir);
+
+	return ret;
+}
+
+static int stm32_fmc2_sequencer_write(struct nand_chip *chip,
+				      const u8 *buf, int oob_required,
+				      int page, int raw)
+{
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
+	int ret;
+
+	/* Configure the sequencer */
+	stm32_fmc2_rw_page_init(fmc2, page, raw, true);
+
+	/* Write the page */
+	ret = stm32_fmc2_xfer(fmc2, buf, raw, true);
+	if (ret)
+		return ret;
+
+	/* Write oob */
+	if (oob_required) {
+		ret = nand_change_write_column_op(chip, mtd->writesize,
+						  chip->oob_poi, mtd->oobsize,
+						  false);
+		if (ret)
+			return ret;
+	}
+
+	return nand_prog_page_end_op(chip);
+}
+
+static int stm32_fmc2_sequencer_write_page(struct nand_chip *chip,
+					   const uint8_t *buf,
+					   int oob_required,
+					   int page)
+{
+	return stm32_fmc2_sequencer_write(chip, buf, oob_required, page, false);
+}
+
+static int stm32_fmc2_sequencer_write_page_raw(struct nand_chip *chip,
+					       const uint8_t *buf,
+					       int oob_required,
+					       int page)
+{
+	return stm32_fmc2_sequencer_write(chip, buf, oob_required, page, true);
+}
+
+/*
+ * Get a status indicating which sectors have errors
+ * Only available when the sequencer is used (BCH only)
+ */
+static inline u16 stm32_fmc2_get_mapping_status(struct stm32_fmc2 *fmc2)
+{
+	u32 csqemsr = readl_relaxed(fmc2->io_base + FMC2_CSQEMSR);
+
+	return csqemsr & FMC2_CSQEMSR_SEM;
+}
+
+static int stm32_fmc2_sequencer_read_page(struct nand_chip *chip,
+					  uint8_t *buf,
+					  int oob_required,
+					  int page)
+{
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
+	int i, s, ret, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	int eccstrength = chip->ecc.strength;
+	u8 *p = buf;
+	u8 *ecc_calc = chip->ecc.calc_buf;
+	u8 *ecc_code = chip->ecc.code_buf;
+	u32 *ecc_sta = (u32 *)fmc2->ecc_buf;
+	u16 sta_map;
+	unsigned int max_bitflips = 0;
+
+	/* Configure the sequencer */
+	stm32_fmc2_rw_page_init(fmc2, page, 0, false);
+
+	/* Read the page */
+	ret = stm32_fmc2_xfer(fmc2, buf, 0, false);
+	if (ret)
+		return ret;
+
+	sta_map = stm32_fmc2_get_mapping_status(fmc2);
+
+	/* Check if errors happen */
+	if (likely(!sta_map)) {
+		if (oob_required)
+			return nand_change_read_column_op(chip, mtd->writesize,
+							  chip->oob_poi,
+							  mtd->oobsize, false);
+
+		return 0;
+	}
+
+	/* Read oob */
+	ret = nand_change_read_column_op(chip, mtd->writesize,
+					 chip->oob_poi, mtd->oobsize, false);
+	if (ret)
+		return ret;
+
+	ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+					 chip->ecc.total);
+	if (ret)
+		return ret;
+
+	/* Correct data */
+	for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) {
+		int stat = 0;
+
+		if (eccstrength == FMC2_ECC_HAM) {
+			/* Ecc_sta = FMC2_HECCR */
+			if (sta_map & BIT(s)) {
+				stm32_fmc2_ham_set_ecc(*ecc_sta, &ecc_calc[i]);
+				stat = chip->ecc.correct(chip, p, &ecc_code[i],
+							 &ecc_calc[i]);
+			}
+			ecc_sta++;
+		} else {
+			/*
+			 * Ecc_sta[0] = FMC2_BCHDSR0
+			 * Ecc_sta[1] = FMC2_BCHDSR1
+			 * Ecc_sta[2] = FMC2_BCHDSR2
+			 * Ecc_sta[3] = FMC2_BCHDSR3
+			 * Ecc_sta[4] = FMC2_BCHDSR4
+			 */
+			if (sta_map & BIT(s))
+				stat = stm32_fmc2_bch_decode(chip->ecc.size,
+							     p, ecc_sta);
+			ecc_sta += 5;
+		}
+
+		if (stat == -EBADMSG)
+			/* Check for empty pages with bitflips */
+			stat = nand_check_erased_ecc_chunk(p, eccsize,
+							   &ecc_code[i],
+							   eccbytes,
+							   NULL, 0,
+							   eccstrength);
+
+		if (stat < 0) {
+			mtd->ecc_stats.failed++;
+		} else {
+			mtd->ecc_stats.corrected += stat;
+			max_bitflips = max_t(unsigned int, max_bitflips, stat);
+		}
+	}
+
+	return max_bitflips;
+}
+
+static int stm32_fmc2_sequencer_read_page_raw(struct nand_chip *chip,
+					      uint8_t *buf,
+					      int oob_required,
+					      int page)
+{
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
+	int ret;
+
+	/* Configure the sequencer */
+	stm32_fmc2_rw_page_init(fmc2, page, 1, false);
+
+	/* Read the page */
+	ret = stm32_fmc2_xfer(fmc2, buf, 1, false);
+	if (ret)
+		return ret;
+
+	/* Read oob */
+	if (oob_required)
+		return nand_change_read_column_op(chip, mtd->writesize,
+						  chip->oob_poi, mtd->oobsize,
+						  false);
+
+	return 0;
+}
+
+static irqreturn_t stm32_fmc2_irq(int irq, void *dev_id)
+{
+	struct stm32_fmc2 *fmc2 = (struct stm32_fmc2 *)dev_id;
+
+	stm32_fmc2_disable_seq_irq(fmc2);
+
+	complete(&fmc2->complete);
+
+	return IRQ_HANDLED;
+}
+
+void stm32_fmc2_read_data(struct nand_chip *chip, void *buf,
+			  unsigned int len, bool force_8bit)
+{
+	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
+	void __iomem *io_addr_r = fmc2->data_base[fmc2->cs_sel];
+	u8 *p = buf;
+	unsigned int i;
+
+	if (force_8bit)
+		goto read_8bit;
+
+	if (IS_ALIGNED((u32)buf, sizeof(u32)) && IS_ALIGNED(len, sizeof(u32))) {
+		u32 *p = buf;
+
+		len >>= 2;
+		for (i = 0; i < len; i++)
+			p[i] = readl_relaxed(io_addr_r);
+		return;
+	}
+
+	if (chip->options & NAND_BUSWIDTH_16) {
+		u16 *p = buf;
+
+		len >>= 1;
+		for (i = 0; i < len; i++)
+			p[i] = readw_relaxed(io_addr_r);
+		return;
+	}
+
+read_8bit:
+	for (i = 0; i < len; i++)
+		p[i] = readb_relaxed(io_addr_r);
+}
+
+void stm32_fmc2_write_data(struct nand_chip *chip, const void *buf,
+			   unsigned int len, bool force_8bit)
+{
+	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
+	void __iomem *io_addr_w = fmc2->data_base[fmc2->cs_sel];
+	const u8 *p = buf;
+	unsigned int i;
+
+	if (force_8bit)
+		goto write_8bit;
+
+	if (IS_ALIGNED((u32)buf, sizeof(u32)) && IS_ALIGNED(len, sizeof(u32))) {
+		const u32 *p = buf;
+
+		len >>= 2;
+		for (i = 0; i < len; i++)
+			writel_relaxed(p[i], io_addr_w);
+		return;
+	}
+
+	if (chip->options & NAND_BUSWIDTH_16) {
+		const u16 *p = buf;
+
+		len >>= 1;
+		for (i = 0; i < len; i++)
+			writew_relaxed(p[i], io_addr_w);
+		return;
+	}
+
+write_8bit:
+	for (i = 0; i < len; i++)
+		writeb_relaxed(p[i], io_addr_w);
+}
+
+static int stm32_fmc2_exec_op(struct nand_chip *chip,
+			      const struct nand_operation *op,
+			      bool check_only)
+{
+	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
+	const struct nand_op_instr *instr = NULL;
+	unsigned int op_id, i;
+	int ret = 0;
+
+	for (op_id = 0; op_id < op->ninstrs; op_id++) {
+		instr = &op->instrs[op_id];
+
+		switch (instr->type) {
+		case NAND_OP_CMD_INSTR:
+			writeb_relaxed(instr->ctx.cmd.opcode,
+				       fmc2->cmd_base[fmc2->cs_sel]);
+			break;
+
+		case NAND_OP_ADDR_INSTR:
+			for (i = 0; i < instr->ctx.addr.naddrs; i++)
+				writeb_relaxed(instr->ctx.addr.addrs[i],
+					       fmc2->addr_base[fmc2->cs_sel]);
+			break;
+
+		case NAND_OP_DATA_IN_INSTR:
+			stm32_fmc2_read_data(chip, instr->ctx.data.buf.in,
+					     instr->ctx.data.len,
+					     instr->ctx.data.force_8bit);
+			break;
+
+		case NAND_OP_DATA_OUT_INSTR:
+			stm32_fmc2_write_data(chip, instr->ctx.data.buf.out,
+					      instr->ctx.data.len,
+					      instr->ctx.data.force_8bit);
+			break;
+
+		case NAND_OP_WAITRDY_INSTR:
+			ret = nand_soft_waitrdy(chip,
+						instr->ctx.waitrdy.timeout_ms);
+			break;
+		}
+	}
+
+	return ret;
+}
+
+/* Timings configuration */
+static void stm32_fmc2_timings_init(struct stm32_fmc2 *fmc2)
+{
+	struct stm32_fmc2_timings *timings = &fmc2->timings;
+	u32 pcr = readl_relaxed(fmc2->io_base + FMC2_PCR);
+	u32 pmem, patt;
+
+	/* Set tclr/tar timings */
+	pcr &= ~FMC2_PCR_TCLR_MASK;
+	pcr |= FMC2_PCR_TCLR(timings->tclr);
+	pcr &= ~FMC2_PCR_TAR_MASK;
+	pcr |= FMC2_PCR_TAR(timings->tar);
+
+	/* Set tset/twait/thold/thiz timings in common bank */
+	pmem = FMC2_PMEM_MEMSET(timings->tset_mem);
+	pmem |= FMC2_PMEM_MEMWAIT(timings->twait);
+	pmem |= FMC2_PMEM_MEMHOLD(timings->thold_mem);
+	pmem |= FMC2_PMEM_MEMHIZ(timings->thiz);
+
+	/* Set tset/twait/thold/thiz timings in attribut bank */
+	patt = FMC2_PATT_ATTSET(timings->tset_att);
+	patt |= FMC2_PATT_ATTWAIT(timings->twait);
+	patt |= FMC2_PATT_ATTHOLD(timings->thold_att);
+	patt |= FMC2_PATT_ATTHIZ(timings->thiz);
+
+	writel_relaxed(pcr, fmc2->io_base + FMC2_PCR);
+	writel_relaxed(pmem, fmc2->io_base + FMC2_PMEM);
+	writel_relaxed(patt, fmc2->io_base + FMC2_PATT);
+}
+
+/* Controller initialization */
+static void stm32_fmc2_init(struct stm32_fmc2 *fmc2)
+{
+	u32 pcr = readl_relaxed(fmc2->io_base + FMC2_PCR);
+	u32 bcr1 = readl_relaxed(fmc2->io_base + FMC2_BCR1);
+
+	/* Enable wait feature and nand flash memory bank */
+	pcr |= FMC2_PCR_PWAITEN;
+	pcr |= FMC2_PCR_PBKEN;
+
+	/* Set buswidth to 8 bits mode for identification */
+	pcr &= ~FMC2_PCR_PWID_MASK;
+
+	/* Ecc logic is disabled */
+	pcr &= ~FMC2_PCR_ECCEN;
+
+	/* Default mode */
+	pcr &= ~FMC2_PCR_ECCALG;
+	pcr &= ~FMC2_PCR_BCHECC;
+	pcr &= ~FMC2_PCR_WEN;
+
+	/* Set default ecc sector size */
+	pcr &= ~FMC2_PCR_ECCSS_MASK;
+	pcr |= FMC2_PCR_ECCSS(FMC2_PCR_ECCSS_2048);
+
+	/* Set default tclr/tar timings */
+	pcr &= ~FMC2_PCR_TCLR_MASK;
+	pcr |= FMC2_PCR_TCLR(FMC2_PCR_TCLR_DEFAULT);
+	pcr &= ~FMC2_PCR_TAR_MASK;
+	pcr |= FMC2_PCR_TAR(FMC2_PCR_TAR_DEFAULT);
+
+	/* Enable FMC2 controller */
+	bcr1 |= FMC2_BCR1_FMC2EN;
+
+	writel_relaxed(bcr1, fmc2->io_base + FMC2_BCR1);
+	writel_relaxed(pcr, fmc2->io_base + FMC2_PCR);
+	writel_relaxed(FMC2_PMEM_DEFAULT, fmc2->io_base + FMC2_PMEM);
+	writel_relaxed(FMC2_PATT_DEFAULT, fmc2->io_base + FMC2_PATT);
+}
+
+/* Controller configuration */
+static void stm32_fmc2_setup(struct stm32_fmc2 *fmc2)
+{
+	struct nand_chip *chip = &fmc2->chip;
+	u32 pcr = readl_relaxed(fmc2->io_base + FMC2_PCR);
+
+	/* Configure in HAMMING by default */
+	if (chip->ecc.strength == FMC2_ECC_BCH8) {
+		pcr |= FMC2_PCR_ECCALG;
+		pcr |= FMC2_PCR_BCHECC;
+	} else if (chip->ecc.strength == FMC2_ECC_BCH4) {
+		pcr |= FMC2_PCR_ECCALG;
+	}
+
+	/* Set buswidth */
+	if (chip->options & NAND_BUSWIDTH_16)
+		pcr |= FMC2_PCR_PWID(FMC2_PCR_PWID_BUSWIDTH_16);
+
+	/* Set ecc sector size */
+	pcr &= ~FMC2_PCR_ECCSS_MASK;
+	pcr |= FMC2_PCR_ECCSS(FMC2_PCR_ECCSS_512);
+
+	writel_relaxed(pcr, fmc2->io_base + FMC2_PCR);
+}
+
+/* Controller timings */
+static void stm32_fmc2_calc_timings(struct stm32_fmc2 *fmc2,
+				    const struct nand_sdr_timings *sdrt,
+				    struct stm32_fmc2_timings *tims)
+{
+	unsigned long hclk = clk_get_rate(fmc2->clk);
+	unsigned long hclkp = NSEC_PER_SEC / (hclk / 1000);
+	int tar, tclr, thiz, twait, tset_mem, tset_att, thold_mem, thold_att;
+
+	tar = hclkp;
+	if (tar < sdrt->tAR_min)
+		tar = sdrt->tAR_min;
+	tims->tar = DIV_ROUND_UP(tar, hclkp) - 1;
+	if (tims->tar > FMC2_PCR_TIMING_MASK)
+		tims->tar = FMC2_PCR_TIMING_MASK;
+
+	tclr = hclkp;
+	if (tclr < sdrt->tCLR_min)
+		tclr = sdrt->tCLR_min;
+	tims->tclr = DIV_ROUND_UP(tclr, hclkp) - 1;
+	if (tims->tclr > FMC2_PCR_TIMING_MASK)
+		tims->tclr = FMC2_PCR_TIMING_MASK;
+
+	tims->thiz = FMC2_THIZ;
+	thiz = (tims->thiz + 1) * hclkp;
+
+	/*
+	 * tWAIT > tRP
+	 * tWAIT > tWP
+	 * tWAIT > tREA + tIO
+	 */
+	twait = hclkp;
+	if (twait < sdrt->tRP_min)
+		twait = sdrt->tRP_min;
+	if (twait < sdrt->tWP_min)
+		twait = sdrt->tWP_min;
+	if (twait < sdrt->tREA_max + FMC2_TIO)
+		twait = sdrt->tREA_max + FMC2_TIO;
+	tims->twait = DIV_ROUND_UP(twait, hclkp);
+	if (tims->twait == 0)
+		tims->twait = 1;
+	else if (tims->twait > FMC2_PMEM_PATT_TIMING_MASK)
+		tims->twait = FMC2_PMEM_PATT_TIMING_MASK;
+
+	/*
+	 * tSETUP_MEM > tCS - tWAIT
+	 * tSETUP_MEM > tALS - tWAIT
+	 * tSETUP_MEM > tDS - (tWAIT - tHIZ)
+	 */
+	tset_mem = hclkp;
+	if (sdrt->tCS_min > twait && (tset_mem < sdrt->tCS_min - twait))
+		tset_mem = sdrt->tCS_min - twait;
+	if (sdrt->tALS_min > twait && (tset_mem < sdrt->tALS_min - twait))
+		tset_mem = sdrt->tALS_min - twait;
+	if (twait > thiz && (sdrt->tDS_min > twait - thiz) &&
+	    (tset_mem < sdrt->tDS_min - (twait - thiz)))
+		tset_mem = sdrt->tDS_min - (twait - thiz);
+	tims->tset_mem = DIV_ROUND_UP(tset_mem, hclkp);
+	if (tims->tset_mem == 0)
+		tims->tset_mem = 1;
+	else if (tims->tset_mem > FMC2_PMEM_PATT_TIMING_MASK)
+		tims->tset_mem = FMC2_PMEM_PATT_TIMING_MASK;
+
+	/*
+	 * tHOLD_MEM > tCH
+	 * tHOLD_MEM > tREH - tSETUP_MEM
+	 * tHOLD_MEM > max(tRC, tWC) - (tSETUP_MEM + tWAIT)
+	 */
+	thold_mem = hclkp;
+	if (thold_mem < sdrt->tCH_min)
+		thold_mem = sdrt->tCH_min;
+	if (sdrt->tREH_min > tset_mem &&
+	    (thold_mem < sdrt->tREH_min - tset_mem))
+		thold_mem = sdrt->tREH_min - tset_mem;
+	if ((sdrt->tRC_min > tset_mem + twait) &&
+	    (thold_mem < sdrt->tRC_min - (tset_mem + twait)))
+		thold_mem = sdrt->tRC_min - (tset_mem + twait);
+	if ((sdrt->tWC_min > tset_mem + twait) &&
+	    (thold_mem < sdrt->tWC_min - (tset_mem + twait)))
+		thold_mem = sdrt->tWC_min - (tset_mem + twait);
+	tims->thold_mem = DIV_ROUND_UP(thold_mem, hclkp);
+	if (tims->thold_mem == 0)
+		tims->thold_mem = 1;
+	else if (tims->thold_mem > FMC2_PMEM_PATT_TIMING_MASK)
+		tims->thold_mem = FMC2_PMEM_PATT_TIMING_MASK;
+
+	/*
+	 * tSETUP_ATT > tCS - tWAIT
+	 * tSETUP_ATT > tCLS - tWAIT
+	 * tSETUP_ATT > tALS - tWAIT
+	 * tSETUP_ATT > tRHW - tHOLD_MEM
+	 * tSETUP_ATT > tDS - (tWAIT - tHIZ)
+	 */
+	tset_att = hclkp;
+	if (sdrt->tCS_min > twait && (tset_att < sdrt->tCS_min - twait))
+		tset_att = sdrt->tCS_min - twait;
+	if (sdrt->tCLS_min > twait && (tset_att < sdrt->tCLS_min - twait))
+		tset_att = sdrt->tCLS_min - twait;
+	if (sdrt->tALS_min > twait && (tset_att < sdrt->tALS_min - twait))
+		tset_att = sdrt->tALS_min - twait;
+	if (sdrt->tRHW_min > thold_mem &&
+	    (tset_att < sdrt->tRHW_min - thold_mem))
+		tset_att = sdrt->tRHW_min - thold_mem;
+	if (twait > thiz && (sdrt->tDS_min > twait - thiz) &&
+	    (tset_att < sdrt->tDS_min - (twait - thiz)))
+		tset_att = sdrt->tDS_min - (twait - thiz);
+	tims->tset_att = DIV_ROUND_UP(tset_att, hclkp);
+	if (tims->tset_att == 0)
+		tims->tset_att = 1;
+	else if (tims->tset_att > FMC2_PMEM_PATT_TIMING_MASK)
+		tims->tset_att = FMC2_PMEM_PATT_TIMING_MASK;
+
+	/*
+	 * tHOLD_ATT > tALH
+	 * tHOLD_ATT > tCH
+	 * tHOLD_ATT > tCLH
+	 * tHOLD_ATT > tCOH
+	 * tHOLD_ATT > tDH
+	 * tHOLD_ATT > tWB + tIO + tSYNC - tSETUP_MEM
+	 * tHOLD_ATT > tADL - tSETUP_MEM
+	 * tHOLD_ATT > tWH - tSETUP_MEM
+	 * tHOLD_ATT > tWHR - tSETUP_MEM
+	 * tHOLD_ATT > tRC - (tSETUP_ATT + tWAIT)
+	 * tHOLD_ATT > tWC - (tSETUP_ATT + tWAIT)
+	 */
+	thold_att = hclkp;
+	if (thold_att < sdrt->tALH_min)
+		thold_att = sdrt->tALH_min;
+	if (thold_att < sdrt->tCH_min)
+		thold_att = sdrt->tCH_min;
+	if (thold_att < sdrt->tCLH_min)
+		thold_att = sdrt->tCLH_min;
+	if (thold_att < sdrt->tCOH_min)
+		thold_att = sdrt->tCOH_min;
+	if (thold_att < sdrt->tDH_min)
+		thold_att = sdrt->tDH_min;
+	if ((sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC > tset_mem) &&
+	    (thold_att < sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem))
+		thold_att = sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem;
+	if (sdrt->tADL_min > tset_mem &&
+	    (thold_att < sdrt->tADL_min - tset_mem))
+		thold_att = sdrt->tADL_min - tset_mem;
+	if (sdrt->tWH_min > tset_mem &&
+	    (thold_att < sdrt->tWH_min - tset_mem))
+		thold_att = sdrt->tWH_min - tset_mem;
+	if (sdrt->tWHR_min > tset_mem &&
+	    (thold_att < sdrt->tWHR_min - tset_mem))
+		thold_att = sdrt->tWHR_min - tset_mem;
+	if ((sdrt->tRC_min > tset_att + twait) &&
+	    (thold_att < sdrt->tRC_min - (tset_att + twait)))
+		thold_att = sdrt->tRC_min - (tset_att + twait);
+	if ((sdrt->tWC_min > tset_att + twait) &&
+	    (thold_att < sdrt->tWC_min - (tset_att + twait)))
+		thold_att = sdrt->tWC_min - (tset_att + twait);
+	tims->thold_att = DIV_ROUND_UP(thold_att, hclkp);
+	if (tims->thold_att == 0)
+		tims->thold_att = 1;
+	else if (tims->thold_att > FMC2_PMEM_PATT_TIMING_MASK)
+		tims->thold_att = FMC2_PMEM_PATT_TIMING_MASK;
+}
+
+static int stm32_fmc2_setup_interface(struct nand_chip *chip, int chipnr,
+				      const struct nand_data_interface *conf)
+{
+	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
+	struct stm32_fmc2_timings tims;
+	const struct nand_sdr_timings *sdrt;
+
+	sdrt = nand_get_sdr_timings(conf);
+	if (IS_ERR(sdrt))
+		return PTR_ERR(sdrt);
+
+	stm32_fmc2_calc_timings(fmc2, sdrt, &tims);
+
+	if (chipnr == NAND_DATA_IFACE_CHECK_ONLY)
+		return 0;
+
+	/* Save and apply timings */
+	memcpy(&fmc2->timings, &tims, sizeof(tims));
+	stm32_fmc2_timings_init(fmc2);
+
+	return 0;
+}
+
+/* DMA configuration */
+static int stm32_fmc2_dma_setup(struct stm32_fmc2 *fmc2, u8 nbsect)
+{
+	struct nand_chip *chip = &fmc2->chip;
+	struct dma_slave_config dma_cfg;
+	int ret;
+
+	memset(&dma_cfg, 0, sizeof(dma_cfg));
+	dma_cfg.src_addr = fmc2->data_phys_addr[fmc2->cs_sel];
+	dma_cfg.dst_addr = fmc2->data_phys_addr[fmc2->cs_sel];
+	dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	dma_cfg.src_maxburst = 32;
+	dma_cfg.dst_maxburst = 32;
+
+	fmc2->dma_tx_ch = dma_request_slave_channel(fmc2->dev, "tx");
+	if (fmc2->dma_tx_ch) {
+		ret = dmaengine_slave_config(fmc2->dma_tx_ch, &dma_cfg);
+		if (ret) {
+			dev_err(fmc2->dev, "data tx DMA engine slave config failed\n");
+			return ret;
+		}
+	}
+
+	fmc2->dma_rx_ch = dma_request_slave_channel(fmc2->dev, "rx");
+	if (fmc2->dma_rx_ch) {
+		ret = dmaengine_slave_config(fmc2->dma_rx_ch, &dma_cfg);
+		if (ret) {
+			dev_err(fmc2->dev, "data rx DMA engine slave config failed\n");
+			return ret;
+		}
+
+		fmc2->dma_ecc_ch = dma_request_slave_channel(fmc2->dev, "ecc");
+		if (fmc2->dma_ecc_ch) {
+			/*
+			 * HAMMING: we read HECCR register
+			 * BCH4/BCH8: we read BCHDSRSx registers
+			 */
+			memset(&dma_cfg, 0, sizeof(dma_cfg));
+			dma_cfg.src_addr = fmc2->io_phys_addr;
+			dma_cfg.src_addr += chip->ecc.strength == FMC2_ECC_HAM ?
+					    FMC2_HECCR : FMC2_BCHDSR0;
+			dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+			ret = dmaengine_slave_config(fmc2->dma_ecc_ch,
+						     &dma_cfg);
+			if (ret) {
+				dev_err(fmc2->dev, "ecc DMA engine slave config failed\n");
+				return ret;
+			}
+
+			ret = sg_alloc_table(&fmc2->dma_ecc_sg,
+					     nbsect, GFP_KERNEL);
+			if (ret)
+				return ret;
+
+			/* Calculate ecc length needed for one sector */
+			fmc2->dma_ecc_len = chip->ecc.strength == FMC2_ECC_HAM ?
+					    FMC2_HECCR_LEN : FMC2_BCHDSRS_LEN;
+
+			/* Allocate a buffer to store ecc status registers */
+			fmc2->ecc_buf = devm_kzalloc(fmc2->dev,
+						     fmc2->dma_ecc_len * nbsect,
+						     GFP_KERNEL);
+			if (!fmc2->ecc_buf)
+				return -ENOMEM;
+		} else {
+			dev_err(fmc2->dev, "ecc DMA not defined in the device tree\n");
+			return -ENOENT;
+		}
+	}
+
+	if (fmc2->dma_tx_ch && fmc2->dma_rx_ch) {
+		ret = sg_alloc_table(&fmc2->dma_data_sg, nbsect, GFP_KERNEL);
+		if (ret)
+			return ret;
+
+		init_completion(&fmc2->dma_data_complete);
+		init_completion(&fmc2->dma_ecc_complete);
+	} else {
+		dev_err(fmc2->dev, "rx/tx DMA not defined in the device tree\n");
+		return -ENOENT;
+	}
+
+	return 0;
+}
+
+/* NAND callbacks setup */
+static void stm32_fmc2_nand_callbacks_setup(struct stm32_fmc2 *fmc2)
+{
+	struct nand_chip *chip = &fmc2->chip;
+
+	/* Specific callbacks to read/write a page */
+	chip->ecc.correct = stm32_fmc2_ham_correct;
+	chip->ecc.write_page = stm32_fmc2_sequencer_write_page;
+	chip->ecc.read_page = stm32_fmc2_sequencer_read_page;
+	chip->ecc.write_page_raw = stm32_fmc2_sequencer_write_page_raw;
+	chip->ecc.read_page_raw = stm32_fmc2_sequencer_read_page_raw;
+	chip->options |= NAND_USE_BOUNCE_BUFFER;
+
+	/* Specific configurations depending on the algo used */
+	if (chip->ecc.strength == FMC2_ECC_HAM)
+		chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 4 : 3;
+	else if (chip->ecc.strength == FMC2_ECC_BCH8)
+		chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 14 : 13;
+	else
+		chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 8 : 7;
+}
+
+/* FMC2 layout */
+static int stm32_fmc2_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
+					 struct mtd_oob_region *oobregion)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+	if (section)
+		return -ERANGE;
+
+	oobregion->length = ecc->total;
+	oobregion->offset = FMC2_BBM_LEN;
+
+	return 0;
+}
+
+static int stm32_fmc2_nand_ooblayout_free(struct mtd_info *mtd, int section,
+					  struct mtd_oob_region *oobregion)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+
+	if (section)
+		return -ERANGE;
+
+	oobregion->length = mtd->oobsize - ecc->total - FMC2_BBM_LEN;
+	oobregion->offset = ecc->total + FMC2_BBM_LEN;
+
+	return 0;
+}
+
+const struct mtd_ooblayout_ops stm32_fmc2_nand_ooblayout_ops = {
+	.ecc = stm32_fmc2_nand_ooblayout_ecc,
+	.free = stm32_fmc2_nand_ooblayout_free,
+};
+
+/* FMC2 caps */
+static int stm32_fmc2_calc_ecc_bytes(int step_size, int strength)
+{
+	/* Hamming */
+	if (strength == FMC2_ECC_HAM)
+		return 4;
+
+	/* BCH8 */
+	if (strength == FMC2_ECC_BCH8)
+		return 14;
+
+	/* BCH4 */
+	return 8;
+}
+
+NAND_ECC_CAPS_SINGLE(stm32_fmc2_ecc_caps, stm32_fmc2_calc_ecc_bytes,
+		     FMC2_ECC_STEP_SIZE,
+		     FMC2_ECC_HAM, FMC2_ECC_BCH4, FMC2_ECC_BCH8);
+
+/* FMC2 controller ops */
+static int stm32_fmc2_attach_chip(struct nand_chip *chip)
+{
+	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	u8 nbsect;
+	int ret;
+
+	/*
+	 * Only NAND_ECC_HW mode is actually supported
+	 * HAMMING => ecc.strength = 1
+	 * BCH4 => ecc.strength = 4
+	 * BCH8 => ecc.strength = 8
+	 * ecc sector size = 512
+	 */
+	if (chip->ecc.mode != NAND_ECC_HW) {
+		dev_err(fmc2->dev, "nand_ecc_mode is not well defined in the DT\n");
+		return -EINVAL;
+	}
+
+	ret = nand_ecc_choose_conf(chip, &stm32_fmc2_ecc_caps,
+				   mtd->oobsize - FMC2_BBM_LEN);
+	if (ret) {
+		dev_err(fmc2->dev, "no valid ECC settings set\n");
+		return ret;
+	}
+
+	nbsect = mtd->writesize / chip->ecc.size;
+	if (nbsect > FMC2_MAX_SG_COUNT) {
+		dev_err(fmc2->dev, "nand page size is not supported\n");
+		return -EINVAL;
+	}
+
+	if (chip->bbt_options & NAND_BBT_USE_FLASH)
+		chip->bbt_options |= NAND_BBT_NO_OOB;
+
+	/* FMC2 setup routine */
+	stm32_fmc2_setup(fmc2);
+
+	/* DMA setup */
+	ret = stm32_fmc2_dma_setup(fmc2, nbsect);
+	if (ret)
+		return ret;
+
+	/* NAND callbacks setup */
+	stm32_fmc2_nand_callbacks_setup(fmc2);
+
+	/* Define ECC layout */
+	mtd_set_ooblayout(mtd, &stm32_fmc2_nand_ooblayout_ops);
+
+	return 0;
+}
+
+static const struct nand_controller_ops stm32_fmc2_nand_controller_ops = {
+	.attach_chip = stm32_fmc2_attach_chip,
+};
+
+/* FMC2 probe */
+static int stm32_fmc2_parse_child(struct device *dev,
+				  struct stm32_fmc2 *fmc2,
+				  struct device_node *dn)
+{
+	u32 cs;
+	int ret, i;
+
+	if (!of_get_property(dn, "reg", &fmc2->ncs))
+		return -EINVAL;
+
+	fmc2->ncs /= sizeof(u32);
+	if (!fmc2->ncs) {
+		dev_err(dev, "invalid reg property size\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < fmc2->ncs; i++) {
+		ret = of_property_read_u32_index(dn, "reg", i, &cs);
+		if (ret) {
+			dev_err(dev, "could not retrieve reg property: %d\n",
+				ret);
+			return ret;
+		}
+
+		if (cs > FMC2_MAX_CE) {
+			dev_err(dev, "invalid reg value: %d\n", cs);
+			return -EINVAL;
+		}
+
+		if (fmc2->cs_assigned & BIT(cs)) {
+			dev_err(dev, "cs already assigned: %d\n", cs);
+			return -EINVAL;
+		}
+
+		fmc2->cs_assigned |= BIT(cs);
+		fmc2->cs_used[i] = cs;
+	}
+
+	/* Default cs used */
+	fmc2->cs_sel = fmc2->cs_used[0];
+
+	/* Timings */
+	ret = of_property_read_u8_array(dn, "st,fmc2_timings",
+					(u8 *)&fmc2->timings,
+					sizeof(fmc2->timings));
+	return ret ? 0 : 1;
+}
+
+static int stm32_fmc2_parse_dt(struct device *dev,
+			       struct stm32_fmc2 *fmc2)
+{
+	struct device_node *dn = dev->of_node;
+	struct device_node *child;
+	int nchips = of_get_child_count(dn);
+	int ret = 0;
+
+	if (!nchips) {
+		dev_err(dev, "NAND chip not defined\n");
+		return -EINVAL;
+	}
+
+	if (nchips > 1) {
+		dev_err(dev, "too many NAND chips defined\n");
+		return -EINVAL;
+	}
+
+	for_each_child_of_node(dn, child) {
+		ret = stm32_fmc2_parse_child(dev, fmc2, child);
+		if (ret < 0) {
+			of_node_put(child);
+			return ret;
+		}
+
+		nand_set_flash_node(&fmc2->chip, child);
+	}
+
+	return ret;
+}
+
+static int stm32_fmc2_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct reset_control *rstc;
+	struct stm32_fmc2 *fmc2;
+	struct resource *res;
+	struct mtd_info *mtd;
+	struct nand_chip *chip;
+	int i, j, ret, irq, timings_def;
+
+	fmc2 = devm_kzalloc(dev, sizeof(*fmc2), GFP_KERNEL);
+	if (!fmc2)
+		return -ENOMEM;
+
+	timings_def = stm32_fmc2_parse_dt(dev, fmc2);
+	if (timings_def < 0)
+		return timings_def;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	fmc2->io_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(fmc2->io_base))
+		return PTR_ERR(fmc2->io_base);
+
+	fmc2->io_phys_addr = res->start;
+
+	for (i = 0, j = 1; i < FMC2_MAX_CE; i++, j += 3) {
+		if (!(fmc2->cs_assigned & BIT(i)))
+			continue;
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM, j);
+		fmc2->data_base[i] = devm_ioremap_resource(dev, res);
+		if (IS_ERR(fmc2->data_base[i]))
+			return PTR_ERR(fmc2->data_base[i]);
+
+		fmc2->data_phys_addr[i] = res->start;
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM, j + 1);
+		fmc2->cmd_base[i] = devm_ioremap_resource(dev, res);
+		if (IS_ERR(fmc2->cmd_base[i]))
+			return PTR_ERR(fmc2->cmd_base[i]);
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM, j + 2);
+		fmc2->addr_base[i] = devm_ioremap_resource(dev, res);
+		if (IS_ERR(fmc2->addr_base[i]))
+			return PTR_ERR(fmc2->addr_base[i]);
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	ret = devm_request_irq(dev, irq, stm32_fmc2_irq, 0,
+			       dev_name(dev), fmc2);
+	if (ret) {
+		dev_err(dev, "failed to request irq\n");
+		return ret;
+	}
+
+	init_completion(&fmc2->complete);
+
+	fmc2->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(fmc2->clk))
+		return PTR_ERR(fmc2->clk);
+
+	ret = clk_prepare_enable(fmc2->clk);
+	if (ret) {
+		dev_err(dev, "can not enable the clock\n");
+		return ret;
+	}
+
+	rstc = devm_reset_control_get(dev, NULL);
+	if (!IS_ERR(rstc)) {
+		reset_control_assert(rstc);
+		reset_control_deassert(rstc);
+	}
+
+	fmc2->dev = dev;
+	mtd = nand_to_mtd(&fmc2->chip);
+	chip = &fmc2->chip;
+	nand_set_controller_data(chip, fmc2);
+	mtd->dev.parent = dev;
+
+	chip->exec_op = stm32_fmc2_exec_op;
+	chip->select_chip = stm32_fmc2_select_chip;
+	chip->options |= NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE;
+
+	/* FMC2 init routine */
+	stm32_fmc2_init(fmc2);
+	if (timings_def)
+		stm32_fmc2_timings_init(fmc2);
+	else
+		chip->setup_data_interface = stm32_fmc2_setup_interface;
+
+	/* Default settings */
+	chip->ecc.mode = NAND_ECC_HW;
+	chip->ecc.size = FMC2_ECC_STEP_SIZE;
+	chip->ecc.strength = FMC2_ECC_BCH8;
+
+	/* Scan to find existence of the device */
+	chip->dummy_controller.ops = &stm32_fmc2_nand_controller_ops;
+	ret = nand_scan(chip, fmc2->ncs);
+	if (ret)
+		goto err_scan;
+
+	ret = mtd_device_register(mtd, NULL, 0);
+	if (ret)
+		goto err_device_register;
+
+	platform_set_drvdata(pdev, fmc2);
+
+	return 0;
+
+err_device_register:
+	nand_cleanup(chip);
+
+err_scan:
+	if (fmc2->dma_ecc_ch)
+		dma_release_channel(fmc2->dma_ecc_ch);
+	if (fmc2->dma_tx_ch)
+		dma_release_channel(fmc2->dma_tx_ch);
+	if (fmc2->dma_rx_ch)
+		dma_release_channel(fmc2->dma_rx_ch);
+
+	sg_free_table(&fmc2->dma_data_sg);
+	sg_free_table(&fmc2->dma_ecc_sg);
+
+	clk_disable_unprepare(fmc2->clk);
+
+	return ret;
+}
+
+static int stm32_fmc2_remove(struct platform_device *pdev)
+{
+	struct stm32_fmc2 *fmc2 = platform_get_drvdata(pdev);
+
+	nand_release(&fmc2->chip);
+
+	if (fmc2->dma_ecc_ch)
+		dma_release_channel(fmc2->dma_ecc_ch);
+	if (fmc2->dma_tx_ch)
+		dma_release_channel(fmc2->dma_tx_ch);
+	if (fmc2->dma_rx_ch)
+		dma_release_channel(fmc2->dma_rx_ch);
+
+	sg_free_table(&fmc2->dma_data_sg);
+	sg_free_table(&fmc2->dma_ecc_sg);
+
+	clk_disable_unprepare(fmc2->clk);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int stm32_fmc2_suspend(struct device *dev)
+{
+	struct stm32_fmc2 *fmc2 = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(fmc2->clk);
+
+	pinctrl_pm_select_sleep_state(dev);
+
+	return 0;
+}
+
+static int stm32_fmc2_resume(struct device *dev)
+{
+	struct stm32_fmc2 *fmc2 = dev_get_drvdata(dev);
+	int i, ret;
+
+	pinctrl_pm_select_default_state(dev);
+
+	ret = clk_prepare_enable(fmc2->clk);
+	if (ret) {
+		dev_err(dev, "can not enable the clock\n");
+		return ret;
+	}
+
+	stm32_fmc2_init(fmc2);
+	stm32_fmc2_timings_init(fmc2);
+	stm32_fmc2_setup(fmc2);
+
+	for (i = 0; i < fmc2->ncs; i++)
+		nand_reset(&fmc2->chip, i);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(stm32_fmc2_pm_ops, stm32_fmc2_suspend,
+			 stm32_fmc2_resume);
+
+static const struct of_device_id stm32_fmc2_match[] = {
+	{.compatible = "st,stm32mp15-fmc2"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, stm32_fmc2_match);
+
+static struct platform_driver stm32_fmc2_driver = {
+	.probe	= stm32_fmc2_probe,
+	.remove	= stm32_fmc2_remove,
+	.driver	= {
+		.name = "stm32-fmc2",
+		.of_match_table = stm32_fmc2_match,
+		.pm = &stm32_fmc2_pm_ops,
+	},
+};
+module_platform_driver(stm32_fmc2_driver);
+
+MODULE_ALIAS("platform:" DRIVER_NAME);
+MODULE_AUTHOR("Christophe Kerello <christophe.kerello@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 FMC2 nand driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 3/3] mtd: rawnand: stm32_fmc2: add manual mode
  2018-09-17 15:47 [PATCH 0/3] mtd: rawnand: add STM32 FMC2 NAND flash controller driver christophe.kerello
  2018-09-17 15:47 ` [PATCH 1/3] dt-bindings: mtd: stm32_fmc2: add STM32 FMC2 NAND controller documentation christophe.kerello
  2018-09-17 15:47 ` [PATCH 2/3] mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver christophe.kerello
@ 2018-09-17 15:47 ` christophe.kerello
  2 siblings, 0 replies; 18+ messages in thread
From: christophe.kerello @ 2018-09-17 15:47 UTC (permalink / raw)
  To: boris.brezillon, miquel.raynal, richard, dwmw2,
	computersforpeace, marek.vasut, robh+dt, mark.rutland
  Cc: linux-mtd, linux-kernel, devicetree, linux-stm32, Christophe Kerello

From: Christophe Kerello <christophe.kerello@st.com>

This patch adds the manual mode, a basic mode that do not need
any DMA channels. This mode is also useful for debug purpose.

Signed-off-by: Christophe Kerello <christophe.kerello@st.com>
---
 drivers/mtd/nand/raw/stm32_fmc2_nand.c | 271 +++++++++++++++++++++++++++++++--
 1 file changed, 261 insertions(+), 10 deletions(-)

diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c
index dd5762a..c74df81 100644
--- a/drivers/mtd/nand/raw/stm32_fmc2_nand.c
+++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c
@@ -204,6 +204,12 @@ enum stm32_fmc2_ecc {
 	FMC2_ECC_BCH8 = 8
 };
 
+enum stm32_fmc2_irq_state {
+	FMC2_IRQ_UNKNOWN = 0,
+	FMC2_IRQ_BCH,
+	FMC2_IRQ_SEQ
+};
+
 struct stm32_fmc2_timings {
 	u8 tclr;
 	u8 tar;
@@ -225,6 +231,7 @@ struct stm32_fmc2 {
 	phys_addr_t io_phys_addr;
 	phys_addr_t data_phys_addr[FMC2_MAX_CE];
 	struct clk *clk;
+	u8 irq_state;
 
 	struct dma_chan *dma_tx_ch;
 	struct dma_chan *dma_rx_ch;
@@ -252,6 +259,8 @@ static inline void stm32_fmc2_enable_seq_irq(struct stm32_fmc2 *fmc2)
 
 	csqier |= FMC2_CSQIER_TCIE;
 
+	fmc2->irq_state = FMC2_IRQ_SEQ;
+
 	writel_relaxed(csqier, fmc2->io_base + FMC2_CSQIER);
 }
 
@@ -263,6 +272,8 @@ static inline void stm32_fmc2_disable_seq_irq(struct stm32_fmc2 *fmc2)
 	csqier &= ~FMC2_CSQIER_TCIE;
 
 	writel_relaxed(csqier, fmc2->io_base + FMC2_CSQIER);
+
+	fmc2->irq_state = FMC2_IRQ_UNKNOWN;
 }
 
 /* Clear irq sources in case of the sequencer is used */
@@ -271,6 +282,40 @@ static inline void stm32_fmc2_clear_seq_irq(struct stm32_fmc2 *fmc2)
 	writel_relaxed(FMC2_CSQICR_CLEAR_IRQ, fmc2->io_base + FMC2_CSQICR);
 }
 
+/* Enable irq sources in case of bch is used */
+static inline void stm32_fmc2_enable_bch_irq(struct stm32_fmc2 *fmc2, int mode)
+{
+	u32 bchier = readl_relaxed(fmc2->io_base + FMC2_BCHIER);
+
+	if (mode == NAND_ECC_WRITE)
+		bchier |= FMC2_BCHIER_EPBRIE;
+	else
+		bchier |= FMC2_BCHIER_DERIE;
+
+	fmc2->irq_state = FMC2_IRQ_BCH;
+
+	writel_relaxed(bchier, fmc2->io_base + FMC2_BCHIER);
+}
+
+/* Disable irq sources in case of bch is used */
+static inline void stm32_fmc2_disable_bch_irq(struct stm32_fmc2 *fmc2)
+{
+	u32 bchier = readl_relaxed(fmc2->io_base + FMC2_BCHIER);
+
+	bchier &= ~FMC2_BCHIER_DERIE;
+	bchier &= ~FMC2_BCHIER_EPBRIE;
+
+	writel_relaxed(bchier, fmc2->io_base + FMC2_BCHIER);
+
+	fmc2->irq_state = FMC2_IRQ_UNKNOWN;
+}
+
+/* Clear irq sources in case of bch is used */
+static inline void stm32_fmc2_clear_bch_irq(struct stm32_fmc2 *fmc2)
+{
+	writel_relaxed(FMC2_BCHICR_CLEAR_IRQ, fmc2->io_base + FMC2_BCHICR);
+}
+
 /* Select function */
 static void stm32_fmc2_select_chip(struct nand_chip *chip, int chipnr)
 {
@@ -303,6 +348,35 @@ static void stm32_fmc2_select_chip(struct nand_chip *chip, int chipnr)
 }
 
 /*
+ * Enable ecc logic and reset syndrome/parity bits previously calculated
+ * Syndrome/parity bits is cleared by setting the ECCEN bit to 0
+ */
+static void stm32_fmc2_hwctl(struct nand_chip *chip, int mode)
+{
+	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
+	u32 pcr = readl_relaxed(fmc2->io_base + FMC2_PCR);
+
+	pcr &= ~FMC2_PCR_ECCEN;
+	writel_relaxed(pcr, fmc2->io_base + FMC2_PCR);
+
+	if (chip->ecc.strength != FMC2_ECC_HAM) {
+		if (mode == NAND_ECC_WRITE)
+			pcr |= FMC2_PCR_WEN;
+		else
+			pcr &= ~FMC2_PCR_WEN;
+		writel_relaxed(pcr, fmc2->io_base + FMC2_PCR);
+
+		reinit_completion(&fmc2->complete);
+		stm32_fmc2_clear_bch_irq(fmc2);
+		stm32_fmc2_enable_bch_irq(fmc2, mode);
+	}
+
+	pcr = readl_relaxed(fmc2->io_base + FMC2_PCR);
+	pcr |= FMC2_PCR_ECCEN;
+	writel_relaxed(pcr, fmc2->io_base + FMC2_PCR);
+}
+
+/*
  * ECC HAMMING calculation
  * ECC is 3 bytes for 512 bytes of data (supports error correction up to
  * max of 1-bit)
@@ -314,6 +388,27 @@ static inline void stm32_fmc2_ham_set_ecc(const u32 ecc_sta, u8 *ecc)
 	ecc[2] = ecc_sta >> 16;
 }
 
+static int stm32_fmc2_ham_calculate(struct nand_chip *chip, const uint8_t *data,
+				    uint8_t *ecc)
+{
+	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
+	u32 sr, heccr;
+	int ret;
+
+	ret = readl_relaxed_poll_timeout(fmc2->io_base + FMC2_SR,
+					 sr, sr & FMC2_SR_NWRF, 10, 1000);
+	if (ret) {
+		dev_err(fmc2->dev, "ham timeout\n");
+		return ret;
+	}
+
+	heccr = readl_relaxed(fmc2->io_base + FMC2_HECCR);
+
+	stm32_fmc2_ham_set_ecc(heccr, ecc);
+
+	return 0;
+}
+
 static int stm32_fmc2_ham_correct(struct nand_chip *chip, uint8_t *dat,
 				  uint8_t *read_ecc, uint8_t *calc_ecc)
 {
@@ -366,6 +461,53 @@ static int stm32_fmc2_ham_correct(struct nand_chip *chip, uint8_t *dat,
 	return 1;
 }
 
+/*
+ * ECC BCH calculation and correction
+ * ECC is 7/13 bytes for 512 bytes of data (supports error correction up to
+ * max of 4-bit/8-bit)
+ */
+static int stm32_fmc2_bch_calculate(struct nand_chip *chip, const uint8_t *data,
+				    uint8_t *ecc)
+{
+	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
+	u32 bchpbr;
+
+	/* Wait until the BCH code is ready */
+	if (!wait_for_completion_timeout(&fmc2->complete,
+					 msecs_to_jiffies(1000))) {
+		dev_err(fmc2->dev, "bch timeout\n");
+		stm32_fmc2_disable_bch_irq(fmc2);
+		return -ETIMEDOUT;
+	}
+
+	/* Read parity bits */
+	bchpbr = readl_relaxed(fmc2->io_base + FMC2_BCHPBR1);
+	ecc[0] = bchpbr;
+	ecc[1] = bchpbr >> 8;
+	ecc[2] = bchpbr >> 16;
+	ecc[3] = bchpbr >> 24;
+
+	bchpbr = readl_relaxed(fmc2->io_base + FMC2_BCHPBR2);
+	ecc[4] = bchpbr;
+	ecc[5] = bchpbr >> 8;
+	ecc[6] = bchpbr >> 16;
+
+	if (chip->ecc.strength == FMC2_ECC_BCH8) {
+		ecc[7] = bchpbr >> 24;
+
+		bchpbr = readl_relaxed(fmc2->io_base + FMC2_BCHPBR3);
+		ecc[8] = bchpbr;
+		ecc[9] = bchpbr >> 8;
+		ecc[10] = bchpbr >> 16;
+		ecc[11] = bchpbr >> 24;
+
+		bchpbr = readl_relaxed(fmc2->io_base + FMC2_BCHPBR4);
+		ecc[12] = bchpbr;
+	}
+
+	return 0;
+}
+
 /* BCH algorithm correction */
 static int stm32_fmc2_bch_decode(int eccsize, u8 *dat, u32 *ecc_sta)
 {
@@ -406,6 +548,91 @@ static int stm32_fmc2_bch_decode(int eccsize, u8 *dat, u32 *ecc_sta)
 	return nb_errs;
 }
 
+static int stm32_fmc2_bch_correct(struct nand_chip *chip, uint8_t *dat,
+				  uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
+	u32 ecc_sta[5];
+
+	/* Wait until the decoding error is ready */
+	if (!wait_for_completion_timeout(&fmc2->complete,
+					 msecs_to_jiffies(1000))) {
+		dev_err(fmc2->dev, "bch timeout\n");
+		stm32_fmc2_disable_bch_irq(fmc2);
+		return -ETIMEDOUT;
+	}
+
+	ecc_sta[0] = readl_relaxed(fmc2->io_base + FMC2_BCHDSR0);
+	ecc_sta[1] = readl_relaxed(fmc2->io_base + FMC2_BCHDSR1);
+	ecc_sta[2] = readl_relaxed(fmc2->io_base + FMC2_BCHDSR2);
+	ecc_sta[3] = readl_relaxed(fmc2->io_base + FMC2_BCHDSR3);
+	ecc_sta[4] = readl_relaxed(fmc2->io_base + FMC2_BCHDSR4);
+
+	return stm32_fmc2_bch_decode(chip->ecc.size, dat, ecc_sta);
+}
+
+static int stm32_fmc2_read_page(struct nand_chip *chip, uint8_t *buf,
+				int oob_required, int page)
+{
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	int ret, i, s, stat, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	int eccstrength = chip->ecc.strength;
+	u8 *p = buf;
+	u8 *ecc_calc = chip->ecc.calc_buf;
+	u8 *ecc_code = chip->ecc.code_buf;
+	unsigned int max_bitflips = 0;
+
+	ret = nand_read_page_op(chip, page, 0, NULL, 0);
+	if (ret)
+		return ret;
+
+	for (i = mtd->writesize + FMC2_BBM_LEN, s = 0; s < eccsteps;
+	     s++, i += eccbytes, p += eccsize) {
+		chip->ecc.hwctl(chip, NAND_ECC_READ);
+
+		/* Read the nand page sector (512 bytes) */
+		ret = nand_change_read_column_op(chip, s * eccsize, p,
+						 eccsize, false);
+		if (ret)
+			return ret;
+
+		/* Read the corresponding ecc bytes */
+		ret = nand_change_read_column_op(chip, i, ecc_code,
+						 eccbytes, false);
+		if (ret)
+			return ret;
+
+		/* Correct the data */
+		stat = chip->ecc.correct(chip, p, ecc_code, ecc_calc);
+		if (stat == -EBADMSG)
+			/* Check for empty pages with bitflips */
+			stat = nand_check_erased_ecc_chunk(p, eccsize,
+							   ecc_code, eccbytes,
+							   NULL, 0,
+							   eccstrength);
+
+		if (stat < 0) {
+			mtd->ecc_stats.failed++;
+		} else {
+			mtd->ecc_stats.corrected += stat;
+			max_bitflips = max_t(unsigned int, max_bitflips, stat);
+		}
+	}
+
+	/* Read oob */
+	if (oob_required) {
+		ret = nand_change_read_column_op(chip, mtd->writesize,
+						 chip->oob_poi, mtd->oobsize,
+						 false);
+		if (ret)
+			return ret;
+	}
+
+	return max_bitflips;
+}
+
 /* Sequencer read/write configuration */
 static void stm32_fmc2_rw_page_init(struct stm32_fmc2 *fmc2, int page,
 				    int raw, bool write_data)
@@ -817,7 +1044,12 @@ static irqreturn_t stm32_fmc2_irq(int irq, void *dev_id)
 {
 	struct stm32_fmc2 *fmc2 = (struct stm32_fmc2 *)dev_id;
 
-	stm32_fmc2_disable_seq_irq(fmc2);
+	if (fmc2->irq_state == FMC2_IRQ_SEQ)
+		/* Sequencer is used */
+		stm32_fmc2_disable_seq_irq(fmc2);
+	else if (fmc2->irq_state == FMC2_IRQ_BCH)
+		/* BCH is used */
+		stm32_fmc2_disable_bch_irq(fmc2);
 
 	complete(&fmc2->complete);
 
@@ -1297,8 +1529,7 @@ static int stm32_fmc2_dma_setup(struct stm32_fmc2 *fmc2, u8 nbsect)
 		init_completion(&fmc2->dma_data_complete);
 		init_completion(&fmc2->dma_ecc_complete);
 	} else {
-		dev_err(fmc2->dev, "rx/tx DMA not defined in the device tree\n");
-		return -ENOENT;
+		dev_warn(fmc2->dev, "DMA not defined in the device tree, manual mode is used\n");
 	}
 
 	return 0;
@@ -1309,13 +1540,33 @@ static void stm32_fmc2_nand_callbacks_setup(struct stm32_fmc2 *fmc2)
 {
 	struct nand_chip *chip = &fmc2->chip;
 
-	/* Specific callbacks to read/write a page */
-	chip->ecc.correct = stm32_fmc2_ham_correct;
-	chip->ecc.write_page = stm32_fmc2_sequencer_write_page;
-	chip->ecc.read_page = stm32_fmc2_sequencer_read_page;
-	chip->ecc.write_page_raw = stm32_fmc2_sequencer_write_page_raw;
-	chip->ecc.read_page_raw = stm32_fmc2_sequencer_read_page_raw;
-	chip->options |= NAND_USE_BOUNCE_BUFFER;
+	/*
+	 * Specific callbacks to read/write a page depending on
+	 * the mode (manual/sequencer) and the algo used (HAMMING, BCH).
+	 */
+	if (fmc2->dma_tx_ch && fmc2->dma_rx_ch) {
+		/* DMA => use sequencer mode callbacks */
+		chip->ecc.correct = stm32_fmc2_ham_correct;
+		chip->ecc.write_page = stm32_fmc2_sequencer_write_page;
+		chip->ecc.read_page = stm32_fmc2_sequencer_read_page;
+		chip->ecc.write_page_raw = stm32_fmc2_sequencer_write_page_raw;
+		chip->ecc.read_page_raw = stm32_fmc2_sequencer_read_page_raw;
+		chip->options |= NAND_USE_BOUNCE_BUFFER;
+	} else {
+		/* No DMA => use manual mode callbacks */
+		chip->ecc.hwctl = stm32_fmc2_hwctl;
+		if (chip->ecc.strength == FMC2_ECC_HAM) {
+			/* HAMMING is used */
+			chip->ecc.calculate = stm32_fmc2_ham_calculate;
+			chip->ecc.correct = stm32_fmc2_ham_correct;
+			chip->ecc.options |= NAND_ECC_GENERIC_ERASED_CHECK;
+		} else {
+			/* BCH is used */
+			chip->ecc.calculate = stm32_fmc2_bch_calculate;
+			chip->ecc.correct = stm32_fmc2_bch_correct;
+			chip->ecc.read_page = stm32_fmc2_read_page;
+		}
+	}
 
 	/* Specific configurations depending on the algo used */
 	if (chip->ecc.strength == FMC2_ECC_HAM)
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/3] mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver
  2018-09-17 15:47 ` [PATCH 2/3] mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver christophe.kerello
@ 2018-09-17 17:05   ` kbuild test robot
  2018-09-17 17:32   ` kbuild test robot
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 18+ messages in thread
From: kbuild test robot @ 2018-09-17 17:05 UTC (permalink / raw)
  To: christophe.kerello
  Cc: kbuild-all, boris.brezillon, miquel.raynal, richard, dwmw2,
	computersforpeace, marek.vasut, robh+dt, mark.rutland, linux-mtd,
	linux-kernel, devicetree, linux-stm32, Christophe Kerello

[-- Attachment #1: Type: text/plain, Size: 1951 bytes --]

Hi Christophe,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on mtd/nand/next]
[also build test ERROR on v4.19-rc4 next-20180913]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/christophe-kerello-st-com/mtd-rawnand-add-STM32-FMC2-NAND-flash-controller-driver/20180918-000016
base:   git://git.infradead.org/linux-mtd.git nand/next
config: i386-allmodconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

Note: the linux-review/christophe-kerello-st-com/mtd-rawnand-add-STM32-FMC2-NAND-flash-controller-driver/20180918-000016 HEAD 0dcec50ff1bb14c8b5e37b2bbd8c5f9744199293 builds fine.
      It only hurts bisectibility.

All error/warnings (new ones prefixed by >>):

   In file included from include/linux/module.h:18:0,
                    from drivers/mtd/nand/raw/stm32_fmc2_nand.c:13:
>> drivers/mtd/nand/raw/stm32_fmc2_nand.c:1726:26: error: expected ',' or ';' before 'DRIVER_NAME'
    MODULE_ALIAS("platform:" DRIVER_NAME);
                             ^
   include/linux/moduleparam.h:24:26: note: in definition of macro '__MODULE_INFO'
      = __stringify(tag) "=" info
                             ^~~~
   include/linux/module.h:164:30: note: in expansion of macro 'MODULE_INFO'
    #define MODULE_ALIAS(_alias) MODULE_INFO(alias, _alias)
                                 ^~~~~~~~~~~
>> drivers/mtd/nand/raw/stm32_fmc2_nand.c:1726:1: note: in expansion of macro 'MODULE_ALIAS'
    MODULE_ALIAS("platform:" DRIVER_NAME);
    ^~~~~~~~~~~~

vim +1726 drivers/mtd/nand/raw/stm32_fmc2_nand.c

  1725	
> 1726	MODULE_ALIAS("platform:" DRIVER_NAME);

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 65139 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/3] mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver
  2018-09-17 15:47 ` [PATCH 2/3] mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver christophe.kerello
  2018-09-17 17:05   ` kbuild test robot
@ 2018-09-17 17:32   ` kbuild test robot
  2018-09-22 13:48   ` Miquel Raynal
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 18+ messages in thread
From: kbuild test robot @ 2018-09-17 17:32 UTC (permalink / raw)
  To: christophe.kerello
  Cc: kbuild-all, boris.brezillon, miquel.raynal, richard, dwmw2,
	computersforpeace, marek.vasut, robh+dt, mark.rutland, linux-mtd,
	linux-kernel, devicetree, linux-stm32, Christophe Kerello

[-- Attachment #1: Type: text/plain, Size: 4777 bytes --]

Hi Christophe,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on mtd/nand/next]
[also build test WARNING on v4.19-rc4 next-20180913]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/christophe-kerello-st-com/mtd-rawnand-add-STM32-FMC2-NAND-flash-controller-driver/20180918-000016
base:   git://git.infradead.org/linux-mtd.git nand/next
config: ia64-allmodconfig (attached as .config)
compiler: ia64-linux-gcc (GCC) 8.1.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        GCC_VERSION=8.1.0 make.cross ARCH=ia64 

All warnings (new ones prefixed by >>):

   In file included from include/linux/clk.h:16,
                    from drivers/mtd/nand/raw/stm32_fmc2_nand.c:7:
   drivers/mtd/nand/raw/stm32_fmc2_nand.c: In function 'stm32_fmc2_read_data':
>> drivers/mtd/nand/raw/stm32_fmc2_nand.c:838:17: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
     if (IS_ALIGNED((u32)buf, sizeof(u32)) && IS_ALIGNED(len, sizeof(u32))) {
                    ^
   include/linux/kernel.h:62:30: note: in definition of macro 'IS_ALIGNED'
    #define IS_ALIGNED(x, a)  (((x) & ((typeof(x))(a) - 1)) == 0)
                                 ^
>> drivers/mtd/nand/raw/stm32_fmc2_nand.c:838:17: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
     if (IS_ALIGNED((u32)buf, sizeof(u32)) && IS_ALIGNED(len, sizeof(u32))) {
                    ^
   include/linux/kernel.h:62:44: note: in definition of macro 'IS_ALIGNED'
    #define IS_ALIGNED(x, a)  (((x) & ((typeof(x))(a) - 1)) == 0)
                                               ^
   drivers/mtd/nand/raw/stm32_fmc2_nand.c: In function 'stm32_fmc2_write_data':
   drivers/mtd/nand/raw/stm32_fmc2_nand.c:872:17: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
     if (IS_ALIGNED((u32)buf, sizeof(u32)) && IS_ALIGNED(len, sizeof(u32))) {
                    ^
   include/linux/kernel.h:62:30: note: in definition of macro 'IS_ALIGNED'
    #define IS_ALIGNED(x, a)  (((x) & ((typeof(x))(a) - 1)) == 0)
                                 ^
   drivers/mtd/nand/raw/stm32_fmc2_nand.c:872:17: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
     if (IS_ALIGNED((u32)buf, sizeof(u32)) && IS_ALIGNED(len, sizeof(u32))) {
                    ^
   include/linux/kernel.h:62:44: note: in definition of macro 'IS_ALIGNED'
    #define IS_ALIGNED(x, a)  (((x) & ((typeof(x))(a) - 1)) == 0)
                                               ^
   In file included from include/linux/module.h:18,
                    from drivers/mtd/nand/raw/stm32_fmc2_nand.c:13:
   drivers/mtd/nand/raw/stm32_fmc2_nand.c: At top level:
   drivers/mtd/nand/raw/stm32_fmc2_nand.c:1726:26: error: expected ',' or ';' before 'DRIVER_NAME'
    MODULE_ALIAS("platform:" DRIVER_NAME);
                             ^~~~~~~~~~~
   include/linux/moduleparam.h:24:26: note: in definition of macro '__MODULE_INFO'
      = __stringify(tag) "=" info
                             ^~~~
   include/linux/module.h:164:30: note: in expansion of macro 'MODULE_INFO'
    #define MODULE_ALIAS(_alias) MODULE_INFO(alias, _alias)
                                 ^~~~~~~~~~~
   drivers/mtd/nand/raw/stm32_fmc2_nand.c:1726:1: note: in expansion of macro 'MODULE_ALIAS'
    MODULE_ALIAS("platform:" DRIVER_NAME);
    ^~~~~~~~~~~~

vim +838 drivers/mtd/nand/raw/stm32_fmc2_nand.c

   826	
   827	void stm32_fmc2_read_data(struct nand_chip *chip, void *buf,
   828				  unsigned int len, bool force_8bit)
   829	{
   830		struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
   831		void __iomem *io_addr_r = fmc2->data_base[fmc2->cs_sel];
   832		u8 *p = buf;
   833		unsigned int i;
   834	
   835		if (force_8bit)
   836			goto read_8bit;
   837	
 > 838		if (IS_ALIGNED((u32)buf, sizeof(u32)) && IS_ALIGNED(len, sizeof(u32))) {
   839			u32 *p = buf;
   840	
   841			len >>= 2;
   842			for (i = 0; i < len; i++)
   843				p[i] = readl_relaxed(io_addr_r);
   844			return;
   845		}
   846	
   847		if (chip->options & NAND_BUSWIDTH_16) {
   848			u16 *p = buf;
   849	
   850			len >>= 1;
   851			for (i = 0; i < len; i++)
   852				p[i] = readw_relaxed(io_addr_r);
   853			return;
   854		}
   855	
   856	read_8bit:
   857		for (i = 0; i < len; i++)
   858			p[i] = readb_relaxed(io_addr_r);
   859	}
   860	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 51736 bytes --]

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 1/3] dt-bindings: mtd: stm32_fmc2: add STM32 FMC2 NAND controller documentation
  2018-09-17 15:47 ` [PATCH 1/3] dt-bindings: mtd: stm32_fmc2: add STM32 FMC2 NAND controller documentation christophe.kerello
@ 2018-09-22  8:34   ` Miquel Raynal
  2018-09-24 16:36     ` Christophe Kerello
  0 siblings, 1 reply; 18+ messages in thread
From: Miquel Raynal @ 2018-09-22  8:34 UTC (permalink / raw)
  To: christophe.kerello
  Cc: boris.brezillon, richard, dwmw2, computersforpeace, marek.vasut,
	robh+dt, mark.rutland, linux-mtd, linux-kernel, devicetree,
	linux-stm32

Hi Christophe,

<christophe.kerello@st.com> wrote on Mon, 17 Sep 2018 17:47:38 +0200:

> From: Christophe Kerello <christophe.kerello@st.com>
> 
> This patch adds the documentation of the device tree bindings for the STM32
> FMC2 NAND controller.
> 
> Signed-off-by: Christophe Kerello <christophe.kerello@st.com>
> ---
>  .../devicetree/bindings/mtd/stm32-fmc2-nand.txt    | 90 ++++++++++++++++++++++
>  1 file changed, 90 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt
> 
> diff --git a/Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt b/Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt
> new file mode 100644
> index 0000000..93eaf11
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt
> @@ -0,0 +1,90 @@
> +STMicroelectronics Flexible Memory Controller 2 (FMC2)
> +NAND Interface
> +
> +Required properties:
> +- compatible: "st,stm32mp15-fmc2"

I think this form is preferred:

"
- compatible: Should be one of:
              * st,stm32mp15-fmc2
"

> +- reg: the first contains the register location and length

the register location and length of...?

> +       the second contains the data common space used for cs0 and length
> +       the third contains the cmd attribute space used for cs0 and length
> +       the fourth contains the addr attribute space used for cs0 and length
> +       the fifth contains the data common space used for cs1 and length
> +       the sixth contains the cmd attribute space used for cs1 and length
> +       the seventh contains the addr attribute space used for cs1 and length

Maybe you could simplify a bit with something like:

-reg: NAND flash controller memory areas.
      First region ...
      Regions 2 to 4 respectively contain the data, command, and
      address space for CS0.
      Regions 5 to 7 contain the same areas for CS1.

> +- interrupts: The interrupt number
> +- pinctrl-0: Standard Pinctrl phandle (see: pinctrl/pinctrl-bindings.txt)
> +- clocks: Use common clock framework
> +
> +Optional properties:
> +- resets: Reference to a reset controller asserting the FMC controller
> +- dmas: DMA specifiers (see: dma/stm32-mdma.txt)
> +- dma-names: Must be "tx", "rx" and "ecc"
> +
> +Optional children nodes:
> +Children nodes represent the available nand chips.

Please s/nand/NAND/ in plain English.

> +
> +Optional properties:
> +- nand-on-flash-bbt: see nand.txt
> +- nand-ecc-strength: see nand.txt
> +- nand-ecc-step-size: see nand.txt
> +- st,fmc2_timings: array of 8 bytes for NAND timings. The meanings of
> +  these bytes are:
> +  byte 0 TCLR      : CLE to RE delay in number of AHB clock cycles, only 4 bits
> +                     are valid. Zero means one clock cycle, 15 means 16 clock
> +                     cycles.
> +  byte 1 TAR       : ALE to RE delay, 4 bits are valid. Same format as TCLR.
> +  byte 2 THIZ      : number of HCLK clock cycles during which the data bus is
> +                     kept in Hi-Z (tristate) after the start of a write access.
> +                     Only valid for write transactions. Zero means 1 cycle,
> +                     255 means 256 cycles.
> +  byte 3 TWAIT     : number of HCLK clock cycles to assert the command to the
> +                     NAND flash in response to SMWAITn. Zero means 1 cycle,
> +                     255 means 256 cycles.
> +  byte 4 THOLD_MEM : common memory space timing
> +                     number of HCLK clock cycles to hold the address (and data
> +                     when writing) after the command deassertion. Zero means
> +                     1 cycle, 255 means 256 cycles.
> +  byte 5 TSET_MEM  : common memory space timing
> +                     number of HCLK clock cycles to assert the address before
> +                     the command is asserted. Zero means 1 cycle, 255 means 256
> +                     cycles.
> +  byte 6 THOLD_ATT : attribute memory space timing
> +                     number of HCLK clock cycles to hold the address (and data
> +                     when writing) after the command deassertion. Zero means
> +                     1 cycle, 255 means 256 cycles.
> +  byte 7 TSET_ATT  : attribute memory space timing
> +                     number of HCLK clock cycles to assert the address before
> +                     the command is asserted. Zero means 1 cycle, 255 means 256
> +                     cycles.

Let me review the driver but this array of timings is really
suspicious. I am pretty sure you don't need it in the DT.

> +
> +The following ECC strength and step size are currently supported:
> + - nand-ecc-strength = <1>, nand-ecc-step-size = <512> (HAMMING)
> + - nand-ecc-strength = <4>, nand-ecc-step-size = <512> (BCH4)
> + - nand-ecc-strength = <8>, nand-ecc-step-size = <512> (BCH8) (default)
> +
> +Example:
> +
> +	fmc2_nand: fmc2_nand@58002000 {
> +		compatible = "st,stm32mp15-fmc2";
> +		reg = <0x58002000 0x1000>,
> +		      <0x80000000 0x1000>,
> +		      <0x88010000 0x1000>,
> +		      <0x88020000 0x1000>,
> +		      <0x81000000 0x1000>,
> +		      <0x89010000 0x1000>,
> +		      <0x89020000 0x1000>;
> +		interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
> +		clocks = <&rcc FMC_K>;
> +		resets = <&rcc FMC_R>;
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&fmc2_pins_a>;
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		nand@0 {
> +			reg = <0>;
> +			nand-on-flash-bbt;
> +			st,fmc2_timings = /bits/ 8 <2 2 1 7 1 0 104 25>;
> +			#address-cells = <1>;
> +			#size-cells = <1>;
> +		};
> +	};

Thanks,
Miquèl

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/3] mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver
  2018-09-17 15:47 ` [PATCH 2/3] mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver christophe.kerello
  2018-09-17 17:05   ` kbuild test robot
  2018-09-17 17:32   ` kbuild test robot
@ 2018-09-22 13:48   ` Miquel Raynal
  2018-09-24 16:36     ` Christophe Kerello
  2018-09-23 11:34   ` Miquel Raynal
  2018-09-24 17:23   ` Boris Brezillon
  4 siblings, 1 reply; 18+ messages in thread
From: Miquel Raynal @ 2018-09-22 13:48 UTC (permalink / raw)
  To: christophe.kerello
  Cc: boris.brezillon, richard, dwmw2, computersforpeace, marek.vasut,
	robh+dt, mark.rutland, linux-mtd, linux-kernel, devicetree,
	linux-stm32

Hi Christophe,

I suppose you received the kbuildrobot issues already, please have a
look at them.

The driver looks well, some comments below.

<christophe.kerello@st.com> wrote on Mon, 17 Sep 2018 17:47:39 +0200:

> From: Christophe Kerello <christophe.kerello@st.com>
> 
> The driver adds the support for the STMicroelectronics FMC2 NAND
> Controller found on STM32MP SOCs.
> 
> This patch is based on FMC2 command sequencer.
> The purpose of the command sequencer is to facilitate the programming
> and the reading of NAND flash pages with the ECC and to free the CPU
> of sequencing tasks.
> It requires one DMA channel for write and two DMA channels for read
> operations.
> 
> Only NAND_ECC_HW mode is actually supported.
> The driver supports a maximum 8k page size.
> The following ECC strength and step size are currently supported:
>  - nand-ecc-strength = <8>, nand-ecc-step-size = <512> (BCH8)
>  - nand-ecc-strength = <4>, nand-ecc-step-size = <512> (BCH4)
>  - nand-ecc-strength = <1>, nand-ecc-step-size = <512> (Extended ecc
>    based on HAMMING)
> 
> This patch has been tested on Micron MT29F8G08ABACAH4 and
> MT29F8G16ABACAH4
> 
> Signed-off-by: Christophe Kerello <christophe.kerello@st.com>
> ---
>  drivers/mtd/nand/raw/Kconfig           |    9 +
>  drivers/mtd/nand/raw/Makefile          |    1 +
>  drivers/mtd/nand/raw/stm32_fmc2_nand.c | 1729 ++++++++++++++++++++++++++++++++
>  3 files changed, 1739 insertions(+)
>  create mode 100644 drivers/mtd/nand/raw/stm32_fmc2_nand.c
> 
> diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
> index c7efc31..863662c 100644
> --- a/drivers/mtd/nand/raw/Kconfig
> +++ b/drivers/mtd/nand/raw/Kconfig
> @@ -541,4 +541,13 @@ config MTD_NAND_TEGRA
>  	  is supported. Extra OOB bytes when using HW ECC are currently
>  	  not supported.
>  
> +config MTD_NAND_STM32_FMC2
> +	tristate "Support for NAND controller on STM32MP SoCs"
> +	depends on MACH_STM32MP157 || COMPILE_TEST
> +	help
> +	  Enables support for NAND Flash chips on SoCs containing the FMC2
> +	  NAND controller. This controller is found on STM32MP SoCs.
> +	  The driver supports a maximum 8k page size. HW ECC is enabled and
> +	  supports a maximum 8-bit correction error per sector of 512 bytes.

HW ECC should not be enabled by default. 8-bit/512B of correctability
is good but not that high and people might want to use software ECC in
conjunction with raw accesses.

> +
>  endif # MTD_NAND
> diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
> index a6ef067..8c437e3 100644
> --- a/drivers/mtd/nand/raw/Makefile
> +++ b/drivers/mtd/nand/raw/Makefile
> @@ -56,6 +56,7 @@ obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand/
>  obj-$(CONFIG_MTD_NAND_QCOM)		+= qcom_nandc.o
>  obj-$(CONFIG_MTD_NAND_MTK)		+= mtk_ecc.o mtk_nand.o
>  obj-$(CONFIG_MTD_NAND_TEGRA)		+= tegra_nand.o
> +obj-$(CONFIG_MTD_NAND_STM32_FMC2)	+= stm32_fmc2_nand.o
>  
>  nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o
>  nand-objs += nand_amd.o
> diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c
> new file mode 100644
> index 0000000..dd5762a
> --- /dev/null
> +++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c
> @@ -0,0 +1,1729 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
> + * Author: Christophe Kerello <christophe.kerello@st.com> for STMicroelectronics

I'm not sure the "All rights reserved" has a meaning here.

But you definitely not have to add "for STMicroelectronics" because it
is already in the copyright.

> + */
> +
> +#include <linux/clk.h>
> +#include <linux/dmaengine.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/errno.h>
> +#include <linux/interrupt.h>
> +#include <linux/iopoll.h>
> +#include <linux/module.h>
> +#include <linux/mtd/rawnand.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +
> +/* Bad block marker length */
> +#define FMC2_BBM_LEN			2
> +
> +/* ECC step size */
> +#define FMC2_ECC_STEP_SIZE		512
> +
> +/* BCHDSRx registers length */
> +#define FMC2_BCHDSRS_LEN		20
> +
> +/* HECCR length */
> +#define FMC2_HECCR_LEN			4
> +
> +/* Max requests done for a 8k nand page size */
> +#define FMC2_MAX_SG_COUNT		16
> +
> +/* Max chip enable */
> +#define FMC2_MAX_CE			2
> +
> +/* Timings */
> +#define FMC2_THIZ			1
> +#define FMC2_TIO			8000
> +#define FMC2_TSYNC			3000
> +#define FMC2_PCR_TIMING_MASK		0xf
> +#define FMC2_PMEM_PATT_TIMING_MASK	0xff
> +
> +/* FMC2 Controller Registers */
> +#define FMC2_BCR1			0x0
> +#define FMC2_PCR			0x80
> +#define FMC2_SR				0x84
> +#define FMC2_PMEM			0x88
> +#define FMC2_PATT			0x8c
> +#define FMC2_HECCR			0x94
> +#define FMC2_CSQCR			0x200
> +#define FMC2_CSQCFGR1			0x204
> +#define FMC2_CSQCFGR2			0x208
> +#define FMC2_CSQCFGR3			0x20c
> +#define FMC2_CSQAR1			0x210
> +#define FMC2_CSQAR2			0x214
> +#define FMC2_CSQIER			0x220
> +#define FMC2_CSQISR			0x224
> +#define FMC2_CSQICR			0x228
> +#define FMC2_CSQEMSR			0x230
> +#define FMC2_BCHIER			0x250
> +#define FMC2_BCHISR			0x254
> +#define FMC2_BCHICR			0x258
> +#define FMC2_BCHPBR1			0x260
> +#define FMC2_BCHPBR2			0x264
> +#define FMC2_BCHPBR3			0x268
> +#define FMC2_BCHPBR4			0x26c
> +#define FMC2_BCHDSR0			0x27c
> +#define FMC2_BCHDSR1			0x280
> +#define FMC2_BCHDSR2			0x284
> +#define FMC2_BCHDSR3			0x288
> +#define FMC2_BCHDSR4			0x28c
> +
> +/* Register: FMC2_BCR1 */
> +#define FMC2_BCR1_FMC2EN		BIT(31)
> +
> +/* Register: FMC2_PCR */
> +#define FMC2_PCR_PWAITEN		BIT(1)
> +#define FMC2_PCR_PBKEN			BIT(2)
> +#define FMC2_PCR_PWID_MASK		GENMASK(5, 4)
> +#define FMC2_PCR_PWID(x)		(((x) & 0x3) << 4)
> +#define FMC2_PCR_PWID_BUSWIDTH_8	0
> +#define FMC2_PCR_PWID_BUSWIDTH_16	1
> +#define FMC2_PCR_ECCEN			BIT(6)
> +#define FMC2_PCR_ECCALG			BIT(8)
> +#define FMC2_PCR_TCLR_MASK		GENMASK(12, 9)
> +#define FMC2_PCR_TCLR(x)		(((x) & 0xf) << 9)
> +#define FMC2_PCR_TCLR_DEFAULT		0xf
> +#define FMC2_PCR_TAR_MASK		GENMASK(16, 13)
> +#define FMC2_PCR_TAR(x)			(((x) & 0xf) << 13)
> +#define FMC2_PCR_TAR_DEFAULT		0xf
> +#define FMC2_PCR_ECCSS_MASK		GENMASK(19, 17)
> +#define FMC2_PCR_ECCSS(x)		(((x) & 0x7) << 17)
> +#define FMC2_PCR_ECCSS_512		1
> +#define FMC2_PCR_ECCSS_2048		3
> +#define FMC2_PCR_BCHECC			BIT(24)
> +#define FMC2_PCR_WEN			BIT(25)
> +
> +/* Register: FMC2_SR */
> +#define FMC2_SR_NWRF			BIT(6)
> +
> +/* Register: FMC2_PMEM */
> +#define FMC2_PMEM_MEMSET(x)		(((x) & 0xff) << 0)
> +#define FMC2_PMEM_MEMWAIT(x)		(((x) & 0xff) << 8)
> +#define FMC2_PMEM_MEMHOLD(x)		(((x) & 0xff) << 16)
> +#define FMC2_PMEM_MEMHIZ(x)		(((x) & 0xff) << 24)
> +#define FMC2_PMEM_DEFAULT		0x0a0a0a0a
> +
> +/* Register: FMC2_PATT */
> +#define FMC2_PATT_ATTSET(x)		(((x) & 0xff) << 0)
> +#define FMC2_PATT_ATTWAIT(x)		(((x) & 0xff) << 8)
> +#define FMC2_PATT_ATTHOLD(x)		(((x) & 0xff) << 16)
> +#define FMC2_PATT_ATTHIZ(x)		(((x) & 0xff) << 24)
> +#define FMC2_PATT_DEFAULT		0x0a0a0a0a
> +
> +/* Register: FMC2_CSQCR */
> +#define FMC2_CSQCR_CSQSTART		BIT(0)
> +
> +/* Register: FMC2_CSQCFGR1 */
> +#define FMC2_CSQCFGR1_CMD2EN		BIT(1)
> +#define FMC2_CSQCFGR1_DMADEN		BIT(2)
> +#define FMC2_CSQCFGR1_ACYNBR(x)		(((x) & 0x7) << 4)
> +#define FMC2_CSQCFGR1_CMD1(x)		(((x) & 0xff) << 8)
> +#define FMC2_CSQCFGR1_CMD2(x)		(((x) & 0xff) << 16)
> +#define FMC2_CSQCFGR1_CMD1T		BIT(24)
> +#define FMC2_CSQCFGR1_CMD2T		BIT(25)
> +
> +/* Register: FMC2_CSQCFGR2 */
> +#define FMC2_CSQCFGR2_SQSDTEN		BIT(0)
> +#define FMC2_CSQCFGR2_RCMD2EN		BIT(1)
> +#define FMC2_CSQCFGR2_DMASEN		BIT(2)
> +#define FMC2_CSQCFGR2_RCMD1(x)		(((x) & 0xff) << 8)
> +#define FMC2_CSQCFGR2_RCMD2(x)		(((x) & 0xff) << 16)
> +#define FMC2_CSQCFGR2_RCMD1T		BIT(24)
> +#define FMC2_CSQCFGR2_RCMD2T		BIT(25)
> +
> +/* Register: FMC2_CSQCFGR3 */
> +#define FMC2_CSQCFGR3_SNBR(x)		(((x) & 0x1f) << 8)
> +#define FMC2_CSQCFGR3_AC1T		BIT(16)
> +#define FMC2_CSQCFGR3_AC2T		BIT(17)
> +#define FMC2_CSQCFGR3_AC3T		BIT(18)
> +#define FMC2_CSQCFGR3_AC4T		BIT(19)
> +#define FMC2_CSQCFGR3_AC5T		BIT(20)
> +#define FMC2_CSQCFGR3_SDT		BIT(21)
> +#define FMC2_CSQCFGR3_RAC1T		BIT(22)
> +#define FMC2_CSQCFGR3_RAC2T		BIT(23)
> +
> +/* Register: FMC2_CSQCAR1 */
> +#define FMC2_CSQCAR1_ADDC1(x)		(((x) & 0xff) << 0)
> +#define FMC2_CSQCAR1_ADDC2(x)		(((x) & 0xff) << 8)
> +#define FMC2_CSQCAR1_ADDC3(x)		(((x) & 0xff) << 16)
> +#define FMC2_CSQCAR1_ADDC4(x)		(((x) & 0xff) << 24)
> +
> +/* Register: FMC2_CSQCAR2 */
> +#define FMC2_CSQCAR2_ADDC5(x)		(((x) & 0xff) << 0)
> +#define FMC2_CSQCAR2_NANDCEN(x)		(((x) & 0x3) << 10)
> +#define FMC2_CSQCAR2_SAO(x)		(((x) & 0xffff) << 16)
> +
> +/* Register: FMC2_CSQIER */
> +#define FMC2_CSQIER_TCIE		BIT(0)
> +
> +/* Register: FMC2_CSQICR */
> +#define FMC2_CSQICR_CLEAR_IRQ		GENMASK(4, 0)
> +
> +/* Register: FMC2_CSQEMSR */
> +#define FMC2_CSQEMSR_SEM		GENMASK(15, 0)
> +
> +/* Register: FMC2_BCHIER */
> +#define FMC2_BCHIER_DERIE		BIT(1)
> +#define FMC2_BCHIER_EPBRIE		BIT(4)
> +
> +/* Register: FMC2_BCHICR */
> +#define FMC2_BCHICR_CLEAR_IRQ		GENMASK(4, 0)
> +
> +/* Register: FMC2_BCHDSR0 */
> +#define FMC2_BCHDSR0_DUE		BIT(0)
> +#define FMC2_BCHDSR0_DEF		BIT(1)
> +#define FMC2_BCHDSR0_DEN_MASK		GENMASK(7, 4)
> +#define FMC2_BCHDSR0_DEN_SHIFT		4
> +
> +/* Register: FMC2_BCHDSR1 */
> +#define FMC2_BCHDSR1_EBP1_MASK		GENMASK(12, 0)
> +#define FMC2_BCHDSR1_EBP2_MASK		GENMASK(28, 16)
> +#define FMC2_BCHDSR1_EBP2_SHIFT		16
> +
> +/* Register: FMC2_BCHDSR2 */
> +#define FMC2_BCHDSR2_EBP3_MASK		GENMASK(12, 0)
> +#define FMC2_BCHDSR2_EBP4_MASK		GENMASK(28, 16)
> +#define FMC2_BCHDSR2_EBP4_SHIFT		16
> +
> +/* Register: FMC2_BCHDSR3 */
> +#define FMC2_BCHDSR3_EBP5_MASK		GENMASK(12, 0)
> +#define FMC2_BCHDSR3_EBP6_MASK		GENMASK(28, 16)
> +#define FMC2_BCHDSR3_EBP6_SHIFT		16
> +
> +/* Register: FMC2_BCHDSR4 */
> +#define FMC2_BCHDSR4_EBP7_MASK		GENMASK(12, 0)
> +#define FMC2_BCHDSR4_EBP8_MASK		GENMASK(28, 16)
> +#define FMC2_BCHDSR4_EBP8_SHIFT		16
> +
> +enum stm32_fmc2_ecc {
> +	FMC2_ECC_HAM = 1,
> +	FMC2_ECC_BCH4 = 4,
> +	FMC2_ECC_BCH8 = 8
> +};
> +
> +struct stm32_fmc2_timings {
> +	u8 tclr;
> +	u8 tar;
> +	u8 thiz;
> +	u8 twait;
> +	u8 thold_mem;
> +	u8 tset_mem;
> +	u8 thold_att;
> +	u8 tset_att;
> +};
> +
> +struct stm32_fmc2 {
> +	struct nand_chip chip;
> +	struct device *dev;
> +	void __iomem *io_base;
> +	void __iomem *data_base[FMC2_MAX_CE];
> +	void __iomem *cmd_base[FMC2_MAX_CE];
> +	void __iomem *addr_base[FMC2_MAX_CE];
> +	phys_addr_t io_phys_addr;
> +	phys_addr_t data_phys_addr[FMC2_MAX_CE];
> +	struct clk *clk;
> +
> +	struct dma_chan *dma_tx_ch;
> +	struct dma_chan *dma_rx_ch;
> +	struct dma_chan *dma_ecc_ch;
> +	struct sg_table dma_data_sg;
> +	struct sg_table dma_ecc_sg;
> +	u8 *ecc_buf;
> +	int dma_ecc_len;
> +
> +	struct completion complete;
> +	struct completion dma_data_complete;
> +	struct completion dma_ecc_complete;
> +
> +	struct stm32_fmc2_timings timings;
> +	u8 cs_assigned;
> +	int cs_sel;
> +	int ncs;
> +	int cs_used[FMC2_MAX_CE];
> +};
> +
> +/* Enable irq sources in case of the sequencer is used */
> +static inline void stm32_fmc2_enable_seq_irq(struct stm32_fmc2 *fmc2)
> +{
> +	u32 csqier = readl_relaxed(fmc2->io_base + FMC2_CSQIER);
> +
> +	csqier |= FMC2_CSQIER_TCIE;
> +
> +	writel_relaxed(csqier, fmc2->io_base + FMC2_CSQIER);
> +}
> +
> +/* Disable irq sources in case of the sequencer is used */
> +static inline void stm32_fmc2_disable_seq_irq(struct stm32_fmc2 *fmc2)
> +{
> +	u32 csqier = readl_relaxed(fmc2->io_base + FMC2_CSQIER);
> +
> +	csqier &= ~FMC2_CSQIER_TCIE;
> +
> +	writel_relaxed(csqier, fmc2->io_base + FMC2_CSQIER);
> +}
> +
> +/* Clear irq sources in case of the sequencer is used */
> +static inline void stm32_fmc2_clear_seq_irq(struct stm32_fmc2 *fmc2)
> +{
> +	writel_relaxed(FMC2_CSQICR_CLEAR_IRQ, fmc2->io_base + FMC2_CSQICR);
> +}
> +
> +/* Select function */
> +static void stm32_fmc2_select_chip(struct nand_chip *chip, int chipnr)
> +{
> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
> +	struct dma_slave_config dma_cfg;
> +
> +	if (chipnr < 0 || chipnr >= fmc2->ncs)
> +		return;
> +
> +	if (fmc2->cs_used[chipnr] == fmc2->cs_sel)
> +		return;
> +
> +	fmc2->cs_sel = fmc2->cs_used[chipnr];
> +
> +	if (fmc2->dma_tx_ch && fmc2->dma_rx_ch) {
> +		memset(&dma_cfg, 0, sizeof(dma_cfg));
> +		dma_cfg.src_addr = fmc2->data_phys_addr[fmc2->cs_sel];
> +		dma_cfg.dst_addr = fmc2->data_phys_addr[fmc2->cs_sel];
> +		dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +		dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +		dma_cfg.src_maxburst = 32;
> +		dma_cfg.dst_maxburst = 32;
> +
> +		if (dmaengine_slave_config(fmc2->dma_tx_ch, &dma_cfg))
> +			dev_warn(fmc2->dev, "tx DMA engine slave config failed\n");
> +
> +		if (dmaengine_slave_config(fmc2->dma_rx_ch, &dma_cfg))
> +			dev_warn(fmc2->dev, "rx DMA engine slave config failed\n");
> +	}

What if there are two NAND chips using different timing modes? You
should probably reconfigure the timings registers, unless there are
already a set of timing registers per CS?
> +}
> +
> +/*
> + * ECC HAMMING calculation
> + * ECC is 3 bytes for 512 bytes of data (supports error correction up to
> + * max of 1-bit)
> + */
> +static inline void stm32_fmc2_ham_set_ecc(const u32 ecc_sta, u8 *ecc)
> +{
> +	ecc[0] = ecc_sta;
> +	ecc[1] = ecc_sta >> 8;
> +	ecc[2] = ecc_sta >> 16;
> +}
> +
> +static int stm32_fmc2_ham_correct(struct nand_chip *chip, uint8_t *dat,
> +				  uint8_t *read_ecc, uint8_t *calc_ecc)
> +{
> +	u8 bit_position = 0, b0, b1, b2;
> +	u32 byte_addr = 0, b;
> +	u32 i, shifting = 1;
> +
> +	/* Indicate which bit and byte is faulty (if any) */
> +	b0 = read_ecc[0] ^ calc_ecc[0];
> +	b1 = read_ecc[1] ^ calc_ecc[1];
> +	b2 = read_ecc[2] ^ calc_ecc[2];
> +	b = b0 | (b1 << 8) | (b2 << 16);
> +
> +	/* No errors */
> +	if (likely(!b))
> +		return 0;
> +
> +	/* Calculate bit position */
> +	for (i = 0; i < 3; i++) {
> +		switch (b % 4) {
> +		case 2:
> +			bit_position += shifting;
> +		case 1:
> +			break;
> +		default:
> +			return -EBADMSG;
> +		}
> +		shifting <<= 1;
> +		b >>= 2;
> +	}
> +
> +	/* Calculate byte position */
> +	shifting = 1;
> +	for (i = 0; i < 9; i++) {
> +		switch (b % 4) {
> +		case 2:
> +			byte_addr += shifting;
> +		case 1:
> +			break;
> +		default:
> +			return -EBADMSG;
> +		}
> +		shifting <<= 1;
> +		b >>= 2;
> +	}
> +
> +	/* Flip the bit */
> +	dat[byte_addr] ^= (1 << bit_position);
> +
> +	return 1;
> +}
> +
> +/* BCH algorithm correction */
> +static int stm32_fmc2_bch_decode(int eccsize, u8 *dat, u32 *ecc_sta)
> +{
> +	u32 bchdsr0 = ecc_sta[0];
> +	u32 bchdsr1 = ecc_sta[1];
> +	u32 bchdsr2 = ecc_sta[2];
> +	u32 bchdsr3 = ecc_sta[3];
> +	u32 bchdsr4 = ecc_sta[4];
> +	u16 pos[8];
> +	int i, den;
> +	unsigned int nb_errs = 0;
> +
> +	/* No errors found */
> +	if (likely(!(bchdsr0 & FMC2_BCHDSR0_DEF)))
> +		return 0;
> +
> +	/* Too many errors detected */
> +	if (unlikely(bchdsr0 & FMC2_BCHDSR0_DUE))
> +		return -EBADMSG;
> +
> +	pos[0] = bchdsr1 & FMC2_BCHDSR1_EBP1_MASK;
> +	pos[1] = (bchdsr1 & FMC2_BCHDSR1_EBP2_MASK) >> FMC2_BCHDSR1_EBP2_SHIFT;
> +	pos[2] = bchdsr2 & FMC2_BCHDSR2_EBP3_MASK;
> +	pos[3] = (bchdsr2 & FMC2_BCHDSR2_EBP4_MASK) >> FMC2_BCHDSR2_EBP4_SHIFT;
> +	pos[4] = bchdsr3 & FMC2_BCHDSR3_EBP5_MASK;
> +	pos[5] = (bchdsr3 & FMC2_BCHDSR3_EBP6_MASK) >> FMC2_BCHDSR3_EBP6_SHIFT;
> +	pos[6] = bchdsr4 & FMC2_BCHDSR4_EBP7_MASK;
> +	pos[7] = (bchdsr4 & FMC2_BCHDSR4_EBP8_MASK) >> FMC2_BCHDSR4_EBP8_SHIFT;
> +
> +	den = (bchdsr0 & FMC2_BCHDSR0_DEN_MASK) >> FMC2_BCHDSR0_DEN_SHIFT;
> +	for (i = 0; i < den; i++) {
> +		if (pos[i] < eccsize * 8) {
> +			change_bit(pos[i], (unsigned long *)dat);
> +			nb_errs++;
> +		}
> +	}
> +
> +	return nb_errs;
> +}
> +
> +/* Sequencer read/write configuration */
> +static void stm32_fmc2_rw_page_init(struct stm32_fmc2 *fmc2, int page,
> +				    int raw, bool write_data)
> +{
> +	struct nand_chip *chip = &fmc2->chip;
> +	struct mtd_info *mtd = nand_to_mtd(chip);
> +	u32 csqcfgr1, csqcfgr2, csqcfgr3;
> +	u32 csqar1, csqar2;
> +	u32 ecc_offset = mtd->writesize + FMC2_BBM_LEN;
> +	u32 pcr = readl_relaxed(fmc2->io_base + FMC2_PCR);
> +
> +	if (write_data)
> +		pcr |= FMC2_PCR_WEN;
> +	else
> +		pcr &= ~FMC2_PCR_WEN;
> +	writel_relaxed(pcr, fmc2->io_base + FMC2_PCR);
> +
> +	/*
> +	 * - Set Program Page/Page Read command
> +	 * - Enable DMA request data
> +	 * - Set timings
> +	 */
> +	csqcfgr1 = FMC2_CSQCFGR1_DMADEN | FMC2_CSQCFGR1_CMD1T;
> +	if (write_data)
> +		csqcfgr1 |= FMC2_CSQCFGR1_CMD1(NAND_CMD_SEQIN);
> +	else
> +		csqcfgr1 |= FMC2_CSQCFGR1_CMD1(NAND_CMD_READ0) |
> +			    FMC2_CSQCFGR1_CMD2EN |
> +			    FMC2_CSQCFGR1_CMD2(NAND_CMD_READSTART) |
> +			    FMC2_CSQCFGR1_CMD2T;
> +
> +	/*
> +	 * - Set Random Data Input/Random Data Read command
> +	 * - Enable the sequencer to access the Spare data area
> +	 * - Enable  DMA request status decoding for read
> +	 * - Set timings
> +	 */
> +	if (write_data)
> +		csqcfgr2 = FMC2_CSQCFGR2_RCMD1(NAND_CMD_RNDIN);
> +	else
> +		csqcfgr2 = FMC2_CSQCFGR2_RCMD1(NAND_CMD_RNDOUT) |
> +			   FMC2_CSQCFGR2_RCMD2EN |
> +			   FMC2_CSQCFGR2_RCMD2(NAND_CMD_RNDOUTSTART) |
> +			   FMC2_CSQCFGR2_RCMD1T |
> +			   FMC2_CSQCFGR2_RCMD2T;
> +	if (!raw) {
> +		csqcfgr2 |= write_data ? 0 : FMC2_CSQCFGR2_DMASEN;
> +		csqcfgr2 |= FMC2_CSQCFGR2_SQSDTEN;
> +	}
> +
> +	/*
> +	 * - Set the number of sectors to be written
> +	 * - Set timings
> +	 */
> +	csqcfgr3 = FMC2_CSQCFGR3_SNBR(chip->ecc.steps - 1);
> +	if (write_data) {
> +		csqcfgr3 |= FMC2_CSQCFGR3_RAC2T;
> +		if (chip->chipsize > SZ_128M)
> +			csqcfgr3 |= FMC2_CSQCFGR3_AC5T;
> +		else
> +			csqcfgr3 |= FMC2_CSQCFGR3_AC4T;
> +	}
> +
> +	/*
> +	 * Set the fourth first address cycles
> +	 * Byte 1 and byte 2 => column, we start at 0x0
> +	 * Byte 3 and byte 4 => page
> +	 */
> +	csqar1 = FMC2_CSQCAR1_ADDC3(page);
> +	csqar1 |= FMC2_CSQCAR1_ADDC4(page >> 8);
> +
> +	/*
> +	 * - Set chip enable number
> +	 * - Set ecc byte offset in the spare area
> +	 * - Calculate the number of address cycles to be issued
> +	 * - Set byte 5 of address cycle if needed
> +	 */
> +	csqar2 = FMC2_CSQCAR2_NANDCEN(fmc2->cs_sel);
> +	if (chip->options & NAND_BUSWIDTH_16)
> +		csqar2 |= FMC2_CSQCAR2_SAO(ecc_offset >> 1);
> +	else
> +		csqar2 |= FMC2_CSQCAR2_SAO(ecc_offset);
> +	if (chip->chipsize > SZ_128M) {
> +		csqcfgr1 |= FMC2_CSQCFGR1_ACYNBR(5);
> +		csqar2 |= FMC2_CSQCAR2_ADDC5(page >> 16);
> +	} else {
> +		csqcfgr1 |= FMC2_CSQCFGR1_ACYNBR(4);
> +	}
> +
> +	writel_relaxed(csqcfgr1, fmc2->io_base + FMC2_CSQCFGR1);
> +	writel_relaxed(csqcfgr2, fmc2->io_base + FMC2_CSQCFGR2);
> +	writel_relaxed(csqcfgr3, fmc2->io_base + FMC2_CSQCFGR3);
> +	writel_relaxed(csqar1, fmc2->io_base + FMC2_CSQAR1);
> +	writel_relaxed(csqar2, fmc2->io_base + FMC2_CSQAR2);
> +}
> +
> +static void stm32_fmc2_dma_callback(void *arg)
> +{
> +	complete((struct completion *)arg);
> +}
> +
> +/* Read/write data from/to a page */
> +static int stm32_fmc2_xfer(struct stm32_fmc2 *fmc2, const u8 *buf,
> +			   int raw, bool write_data)
> +{
> +	struct nand_chip *chip = &fmc2->chip;
> +	struct dma_async_tx_descriptor *desc_data, *desc_ecc;
> +	struct scatterlist *sg;
> +	struct dma_chan *dma_ch = fmc2->dma_rx_ch;
> +	enum dma_data_direction dma_data_dir = DMA_FROM_DEVICE;
> +	enum dma_transfer_direction dma_transfer_dir = DMA_DEV_TO_MEM;
> +	u32 csqcr = readl_relaxed(fmc2->io_base + FMC2_CSQCR);
> +	int eccsteps = chip->ecc.steps;
> +	int eccsize = chip->ecc.size;
> +	const u8 *p = buf;
> +	int s, ret;
> +
> +	/* Configure DMA data */
> +	if (write_data) {
> +		dma_data_dir = DMA_TO_DEVICE;
> +		dma_transfer_dir = DMA_MEM_TO_DEV;
> +		dma_ch = fmc2->dma_tx_ch;
> +	}
> +
> +	for_each_sg(fmc2->dma_data_sg.sgl, sg, eccsteps, s) {
> +		sg_set_buf(sg, p, eccsize);
> +		p += eccsize;
> +	}
> +
> +	ret = dma_map_sg(fmc2->dev, fmc2->dma_data_sg.sgl,
> +			 eccsteps, dma_data_dir);
> +	if (ret < 0)
> +		return ret;
> +
> +	desc_data = dmaengine_prep_slave_sg(dma_ch, fmc2->dma_data_sg.sgl,
> +					    eccsteps, dma_transfer_dir,
> +					    DMA_PREP_INTERRUPT);
> +	if (!desc_data) {
> +		ret = -ENOMEM;
> +		goto err_unmap_data;
> +	}
> +
> +	reinit_completion(&fmc2->dma_data_complete);
> +	reinit_completion(&fmc2->complete);
> +	desc_data->callback = stm32_fmc2_dma_callback;
> +	desc_data->callback_param = &fmc2->dma_data_complete;
> +	ret = dma_submit_error(dmaengine_submit(desc_data));
> +	if (ret)
> +		goto err_unmap_data;
> +
> +	dma_async_issue_pending(dma_ch);
> +
> +	if (!write_data && !raw) {
> +		/* Configure DMA ecc status */
> +		p = fmc2->ecc_buf;
> +		for_each_sg(fmc2->dma_ecc_sg.sgl, sg, eccsteps, s) {
> +			sg_set_buf(sg, p, fmc2->dma_ecc_len);
> +			p += fmc2->dma_ecc_len;
> +		}
> +
> +		ret = dma_map_sg(fmc2->dev, fmc2->dma_ecc_sg.sgl,
> +				 eccsteps, dma_data_dir);
> +		if (ret < 0)
> +			goto err_unmap_data;
> +
> +		desc_ecc = dmaengine_prep_slave_sg(fmc2->dma_ecc_ch,
> +						   fmc2->dma_ecc_sg.sgl,
> +						   eccsteps, dma_transfer_dir,
> +						   DMA_PREP_INTERRUPT);
> +		if (!desc_ecc) {
> +			ret = -ENOMEM;
> +			goto err_unmap_ecc;
> +		}
> +
> +		reinit_completion(&fmc2->dma_ecc_complete);
> +		desc_ecc->callback = stm32_fmc2_dma_callback;
> +		desc_ecc->callback_param = &fmc2->dma_ecc_complete;
> +		ret = dma_submit_error(dmaengine_submit(desc_ecc));
> +		if (ret)
> +			goto err_unmap_ecc;
> +
> +		dma_async_issue_pending(fmc2->dma_ecc_ch);
> +	}
> +
> +	stm32_fmc2_clear_seq_irq(fmc2);
> +	stm32_fmc2_enable_seq_irq(fmc2);
> +
> +	/* Start the transfer */
> +	csqcr |= FMC2_CSQCR_CSQSTART;
> +	writel_relaxed(csqcr, fmc2->io_base + FMC2_CSQCR);
> +
> +	/* Wait end of sequencer transfer */
> +	if (!wait_for_completion_timeout(&fmc2->complete,
> +					 msecs_to_jiffies(1000))) {
> +		dev_err(fmc2->dev, "seq timeout\n");
> +		stm32_fmc2_disable_seq_irq(fmc2);
> +		dmaengine_terminate_all(dma_ch);
> +		if (!write_data && !raw)
> +			dmaengine_terminate_all(fmc2->dma_ecc_ch);
> +		ret = -ETIMEDOUT;
> +		goto err_unmap_ecc;
> +	}
> +
> +	/* Wait DMA data transfer completion */
> +	if (!wait_for_completion_timeout(&fmc2->dma_data_complete,
> +					 msecs_to_jiffies(100))) {
> +		dev_err(fmc2->dev, "data DMA timeout\n");
> +		dmaengine_terminate_all(dma_ch);
> +		ret = -ETIMEDOUT;
> +	}
> +
> +	/* Wait DMA ecc transfer completion */
> +	if (!write_data && !raw) {
> +		if (!wait_for_completion_timeout(&fmc2->dma_ecc_complete,
> +						 msecs_to_jiffies(100))) {
> +			dev_err(fmc2->dev, "ecc DMA timeout\n");
> +			dmaengine_terminate_all(fmc2->dma_ecc_ch);
> +			ret = -ETIMEDOUT;
> +		}
> +	}
> +
> +err_unmap_ecc:
> +	if (!write_data && !raw)
> +		dma_unmap_sg(fmc2->dev, fmc2->dma_ecc_sg.sgl,
> +			     eccsteps, dma_data_dir);
> +
> +err_unmap_data:
> +	dma_unmap_sg(fmc2->dev, fmc2->dma_data_sg.sgl, eccsteps, dma_data_dir);
> +
> +	return ret;
> +}
> +
> +static int stm32_fmc2_sequencer_write(struct nand_chip *chip,
> +				      const u8 *buf, int oob_required,
> +				      int page, int raw)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(chip);
> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
> +	int ret;
> +
> +	/* Configure the sequencer */
> +	stm32_fmc2_rw_page_init(fmc2, page, raw, true);
> +
> +	/* Write the page */
> +	ret = stm32_fmc2_xfer(fmc2, buf, raw, true);
> +	if (ret)
> +		return ret;
> +
> +	/* Write oob */
> +	if (oob_required) {
> +		ret = nand_change_write_column_op(chip, mtd->writesize,
> +						  chip->oob_poi, mtd->oobsize,
> +						  false);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return nand_prog_page_end_op(chip);
> +}
> +
> +static int stm32_fmc2_sequencer_write_page(struct nand_chip *chip,
> +					   const uint8_t *buf,
> +					   int oob_required,
> +					   int page)
> +{
> +	return stm32_fmc2_sequencer_write(chip, buf, oob_required, page, false);
> +}
> +
> +static int stm32_fmc2_sequencer_write_page_raw(struct nand_chip *chip,
> +					       const uint8_t *buf,
> +					       int oob_required,
> +					       int page)
> +{
> +	return stm32_fmc2_sequencer_write(chip, buf, oob_required, page, true);
> +}
> +
> +/*
> + * Get a status indicating which sectors have errors
> + * Only available when the sequencer is used (BCH only)
> + */
> +static inline u16 stm32_fmc2_get_mapping_status(struct stm32_fmc2 *fmc2)
> +{
> +	u32 csqemsr = readl_relaxed(fmc2->io_base + FMC2_CSQEMSR);
> +
> +	return csqemsr & FMC2_CSQEMSR_SEM;
> +}
> +
> +static int stm32_fmc2_sequencer_read_page(struct nand_chip *chip,
> +					  uint8_t *buf,
> +					  int oob_required,
> +					  int page)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(chip);
> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
> +	int i, s, ret, eccsize = chip->ecc.size;
> +	int eccbytes = chip->ecc.bytes;
> +	int eccsteps = chip->ecc.steps;
> +	int eccstrength = chip->ecc.strength;
> +	u8 *p = buf;
> +	u8 *ecc_calc = chip->ecc.calc_buf;
> +	u8 *ecc_code = chip->ecc.code_buf;
> +	u32 *ecc_sta = (u32 *)fmc2->ecc_buf;
> +	u16 sta_map;
> +	unsigned int max_bitflips = 0;
> +
> +	/* Configure the sequencer */
> +	stm32_fmc2_rw_page_init(fmc2, page, 0, false);
> +
> +	/* Read the page */
> +	ret = stm32_fmc2_xfer(fmc2, buf, 0, false);
> +	if (ret)
> +		return ret;
> +
> +	sta_map = stm32_fmc2_get_mapping_status(fmc2);
> +
> +	/* Check if errors happen */
> +	if (likely(!sta_map)) {
> +		if (oob_required)
> +			return nand_change_read_column_op(chip, mtd->writesize,
> +							  chip->oob_poi,
> +							  mtd->oobsize, false);
> +
> +		return 0;
> +	}
> +
> +	/* Read oob */
> +	ret = nand_change_read_column_op(chip, mtd->writesize,
> +					 chip->oob_poi, mtd->oobsize, false);
> +	if (ret)
> +		return ret;
> +
> +	ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
> +					 chip->ecc.total);
> +	if (ret)
> +		return ret;
> +
> +	/* Correct data */
> +	for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) {
> +		int stat = 0;
> +
> +		if (eccstrength == FMC2_ECC_HAM) {
> +			/* Ecc_sta = FMC2_HECCR */
> +			if (sta_map & BIT(s)) {
> +				stm32_fmc2_ham_set_ecc(*ecc_sta, &ecc_calc[i]);
> +				stat = chip->ecc.correct(chip, p, &ecc_code[i],
> +							 &ecc_calc[i]);
> +			}
> +			ecc_sta++;
> +		} else {
> +			/*
> +			 * Ecc_sta[0] = FMC2_BCHDSR0
> +			 * Ecc_sta[1] = FMC2_BCHDSR1
> +			 * Ecc_sta[2] = FMC2_BCHDSR2
> +			 * Ecc_sta[3] = FMC2_BCHDSR3
> +			 * Ecc_sta[4] = FMC2_BCHDSR4
> +			 */
> +			if (sta_map & BIT(s))
> +				stat = stm32_fmc2_bch_decode(chip->ecc.size,
> +							     p, ecc_sta);
> +			ecc_sta += 5;
> +		}
> +
> +		if (stat == -EBADMSG)
> +			/* Check for empty pages with bitflips */
> +			stat = nand_check_erased_ecc_chunk(p, eccsize,
> +							   &ecc_code[i],
> +							   eccbytes,
> +							   NULL, 0,
> +							   eccstrength);
> +
> +		if (stat < 0) {
> +			mtd->ecc_stats.failed++;
> +		} else {
> +			mtd->ecc_stats.corrected += stat;
> +			max_bitflips = max_t(unsigned int, max_bitflips, stat);
> +		}
> +	}
> +
> +	return max_bitflips;
> +}
> +
> +static int stm32_fmc2_sequencer_read_page_raw(struct nand_chip *chip,
> +					      uint8_t *buf,
> +					      int oob_required,
> +					      int page)
> +{
> +	struct mtd_info *mtd = nand_to_mtd(chip);
> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
> +	int ret;
> +
> +	/* Configure the sequencer */
> +	stm32_fmc2_rw_page_init(fmc2, page, 1, false);
> +
> +	/* Read the page */
> +	ret = stm32_fmc2_xfer(fmc2, buf, 1, false);
> +	if (ret)
> +		return ret;
> +
> +	/* Read oob */
> +	if (oob_required)
> +		return nand_change_read_column_op(chip, mtd->writesize,
> +						  chip->oob_poi, mtd->oobsize,
> +						  false);
> +
> +	return 0;
> +}
> +
> +static irqreturn_t stm32_fmc2_irq(int irq, void *dev_id)
> +{
> +	struct stm32_fmc2 *fmc2 = (struct stm32_fmc2 *)dev_id;
> +
> +	stm32_fmc2_disable_seq_irq(fmc2);
> +
> +	complete(&fmc2->complete);
> +
> +	return IRQ_HANDLED;

You don't have any bit to check/ack/read?

> +}
> +
> +void stm32_fmc2_read_data(struct nand_chip *chip, void *buf,
> +			  unsigned int len, bool force_8bit)
> +{
> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
> +	void __iomem *io_addr_r = fmc2->data_base[fmc2->cs_sel];
> +	u8 *p = buf;
> +	unsigned int i;
> +
> +	if (force_8bit)
> +		goto read_8bit;
> +
> +	if (IS_ALIGNED((u32)buf, sizeof(u32)) && IS_ALIGNED(len, sizeof(u32))) {

If you selected BOUNCE_BUFFER in the options, buf is supposedly
aligned, or am I missing something?

> +		u32 *p = buf;
> +
> +		len >>= 2;

Please don't do such optimization by hand. Compilers are doing it for
it, so let's keep the code readable. Just do a regular "/=
sizeof(unsigned long)".

> +		for (i = 0; i < len; i++)
> +			p[i] = readl_relaxed(io_addr_r);
> +		return;
> +	}
> +
> +	if (chip->options & NAND_BUSWIDTH_16) {
> +		u16 *p = buf;
> +
> +		len >>= 1;

Ditto

> +		for (i = 0; i < len; i++)
> +			p[i] = readw_relaxed(io_addr_r);
> +		return;
> +	}
> +
> +read_8bit:
> +	for (i = 0; i < len; i++)
> +		p[i] = readb_relaxed(io_addr_r);
> +}
> +
> +void stm32_fmc2_write_data(struct nand_chip *chip, const void *buf,
> +			   unsigned int len, bool force_8bit)
> +{
> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
> +	void __iomem *io_addr_w = fmc2->data_base[fmc2->cs_sel];
> +	const u8 *p = buf;
> +	unsigned int i;
> +
> +	if (force_8bit)
> +		goto write_8bit;
> +
> +	if (IS_ALIGNED((u32)buf, sizeof(u32)) && IS_ALIGNED(len, sizeof(u32))) {
> +		const u32 *p = buf;
> +
> +		len >>= 2;
> +		for (i = 0; i < len; i++)
> +			writel_relaxed(p[i], io_addr_w);
> +		return;
> +	}
> +
> +	if (chip->options & NAND_BUSWIDTH_16) {
> +		const u16 *p = buf;
> +
> +		len >>= 1;
> +		for (i = 0; i < len; i++)
> +			writew_relaxed(p[i], io_addr_w);
> +		return;
> +	}
> +
> +write_8bit:
> +	for (i = 0; i < len; i++)
> +		writeb_relaxed(p[i], io_addr_w);
> +}
> +
> +static int stm32_fmc2_exec_op(struct nand_chip *chip,
> +			      const struct nand_operation *op,
> +			      bool check_only)

You should probably return 0 if check_only == true, otherwise you could
do twice each operation.

> +{
> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
> +	const struct nand_op_instr *instr = NULL;
> +	unsigned int op_id, i;
> +	int ret = 0;
> +
> +	for (op_id = 0; op_id < op->ninstrs; op_id++) {
> +		instr = &op->instrs[op_id];
> +
> +		switch (instr->type) {
> +		case NAND_OP_CMD_INSTR:
> +			writeb_relaxed(instr->ctx.cmd.opcode,
> +				       fmc2->cmd_base[fmc2->cs_sel]);
> +			break;
> +
> +		case NAND_OP_ADDR_INSTR:
> +			for (i = 0; i < instr->ctx.addr.naddrs; i++)
> +				writeb_relaxed(instr->ctx.addr.addrs[i],
> +					       fmc2->addr_base[fmc2->cs_sel]);
> +			break;
> +
> +		case NAND_OP_DATA_IN_INSTR:
> +			stm32_fmc2_read_data(chip, instr->ctx.data.buf.in,
> +					     instr->ctx.data.len,
> +					     instr->ctx.data.force_8bit);
> +			break;
> +
> +		case NAND_OP_DATA_OUT_INSTR:
> +			stm32_fmc2_write_data(chip, instr->ctx.data.buf.out,
> +					      instr->ctx.data.len,
> +					      instr->ctx.data.force_8bit);
> +			break;
> +
> +		case NAND_OP_WAITRDY_INSTR:
> +			ret = nand_soft_waitrdy(chip,
> +						instr->ctx.waitrdy.timeout_ms);
> +			break;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +/* Timings configuration */
> +static void stm32_fmc2_timings_init(struct stm32_fmc2 *fmc2)
> +{
> +	struct stm32_fmc2_timings *timings = &fmc2->timings;
> +	u32 pcr = readl_relaxed(fmc2->io_base + FMC2_PCR);
> +	u32 pmem, patt;
> +
> +	/* Set tclr/tar timings */
> +	pcr &= ~FMC2_PCR_TCLR_MASK;
> +	pcr |= FMC2_PCR_TCLR(timings->tclr);
> +	pcr &= ~FMC2_PCR_TAR_MASK;
> +	pcr |= FMC2_PCR_TAR(timings->tar);
> +
> +	/* Set tset/twait/thold/thiz timings in common bank */
> +	pmem = FMC2_PMEM_MEMSET(timings->tset_mem);
> +	pmem |= FMC2_PMEM_MEMWAIT(timings->twait);
> +	pmem |= FMC2_PMEM_MEMHOLD(timings->thold_mem);
> +	pmem |= FMC2_PMEM_MEMHIZ(timings->thiz);
> +
> +	/* Set tset/twait/thold/thiz timings in attribut bank */
> +	patt = FMC2_PATT_ATTSET(timings->tset_att);
> +	patt |= FMC2_PATT_ATTWAIT(timings->twait);
> +	patt |= FMC2_PATT_ATTHOLD(timings->thold_att);
> +	patt |= FMC2_PATT_ATTHIZ(timings->thiz);
> +
> +	writel_relaxed(pcr, fmc2->io_base + FMC2_PCR);
> +	writel_relaxed(pmem, fmc2->io_base + FMC2_PMEM);
> +	writel_relaxed(patt, fmc2->io_base + FMC2_PATT);
> +}
> +
> +/* Controller initialization */
> +static void stm32_fmc2_init(struct stm32_fmc2 *fmc2)
> +{
> +	u32 pcr = readl_relaxed(fmc2->io_base + FMC2_PCR);
> +	u32 bcr1 = readl_relaxed(fmc2->io_base + FMC2_BCR1);
> +
> +	/* Enable wait feature and nand flash memory bank */
> +	pcr |= FMC2_PCR_PWAITEN;
> +	pcr |= FMC2_PCR_PBKEN;
> +
> +	/* Set buswidth to 8 bits mode for identification */
> +	pcr &= ~FMC2_PCR_PWID_MASK;
> +
> +	/* Ecc logic is disabled */
> +	pcr &= ~FMC2_PCR_ECCEN;
> +
> +	/* Default mode */
> +	pcr &= ~FMC2_PCR_ECCALG;
> +	pcr &= ~FMC2_PCR_BCHECC;
> +	pcr &= ~FMC2_PCR_WEN;
> +
> +	/* Set default ecc sector size */
> +	pcr &= ~FMC2_PCR_ECCSS_MASK;
> +	pcr |= FMC2_PCR_ECCSS(FMC2_PCR_ECCSS_2048);
> +
> +	/* Set default tclr/tar timings */
> +	pcr &= ~FMC2_PCR_TCLR_MASK;
> +	pcr |= FMC2_PCR_TCLR(FMC2_PCR_TCLR_DEFAULT);
> +	pcr &= ~FMC2_PCR_TAR_MASK;
> +	pcr |= FMC2_PCR_TAR(FMC2_PCR_TAR_DEFAULT);
> +
> +	/* Enable FMC2 controller */
> +	bcr1 |= FMC2_BCR1_FMC2EN;
> +
> +	writel_relaxed(bcr1, fmc2->io_base + FMC2_BCR1);
> +	writel_relaxed(pcr, fmc2->io_base + FMC2_PCR);
> +	writel_relaxed(FMC2_PMEM_DEFAULT, fmc2->io_base + FMC2_PMEM);
> +	writel_relaxed(FMC2_PATT_DEFAULT, fmc2->io_base + FMC2_PATT);
> +}
> +
> +/* Controller configuration */
> +static void stm32_fmc2_setup(struct stm32_fmc2 *fmc2)
> +{
> +	struct nand_chip *chip = &fmc2->chip;
> +	u32 pcr = readl_relaxed(fmc2->io_base + FMC2_PCR);
> +
> +	/* Configure in HAMMING by default */

In other comments you use "Hamming", not "HAMMING".

And this comment is weird, you don't configure for Hamming below. Maybe
you meant that the controller is by default configured to work with
Hamming error detection?

> +	if (chip->ecc.strength == FMC2_ECC_BCH8) {
> +		pcr |= FMC2_PCR_ECCALG;
> +		pcr |= FMC2_PCR_BCHECC;
> +	} else if (chip->ecc.strength == FMC2_ECC_BCH4) {
> +		pcr |= FMC2_PCR_ECCALG;
> +	}
> +
> +	/* Set buswidth */
> +	if (chip->options & NAND_BUSWIDTH_16)
> +		pcr |= FMC2_PCR_PWID(FMC2_PCR_PWID_BUSWIDTH_16);
> +
> +	/* Set ecc sector size */
> +	pcr &= ~FMC2_PCR_ECCSS_MASK;
> +	pcr |= FMC2_PCR_ECCSS(FMC2_PCR_ECCSS_512);
> +
> +	writel_relaxed(pcr, fmc2->io_base + FMC2_PCR);
> +}
> +
> +/* Controller timings */
> +static void stm32_fmc2_calc_timings(struct stm32_fmc2 *fmc2,
> +				    const struct nand_sdr_timings *sdrt,
> +				    struct stm32_fmc2_timings *tims)
> +{
> +	unsigned long hclk = clk_get_rate(fmc2->clk);
> +	unsigned long hclkp = NSEC_PER_SEC / (hclk / 1000);
> +	int tar, tclr, thiz, twait, tset_mem, tset_att, thold_mem, thold_att;
> +
> +	tar = hclkp;
> +	if (tar < sdrt->tAR_min)
> +		tar = sdrt->tAR_min;
> +	tims->tar = DIV_ROUND_UP(tar, hclkp) - 1;
> +	if (tims->tar > FMC2_PCR_TIMING_MASK)
> +		tims->tar = FMC2_PCR_TIMING_MASK;
> +
> +	tclr = hclkp;
> +	if (tclr < sdrt->tCLR_min)
> +		tclr = sdrt->tCLR_min;
> +	tims->tclr = DIV_ROUND_UP(tclr, hclkp) - 1;
> +	if (tims->tclr > FMC2_PCR_TIMING_MASK)
> +		tims->tclr = FMC2_PCR_TIMING_MASK;
> +
> +	tims->thiz = FMC2_THIZ;
> +	thiz = (tims->thiz + 1) * hclkp;
> +
> +	/*
> +	 * tWAIT > tRP
> +	 * tWAIT > tWP
> +	 * tWAIT > tREA + tIO
> +	 */
> +	twait = hclkp;
> +	if (twait < sdrt->tRP_min)
> +		twait = sdrt->tRP_min;
> +	if (twait < sdrt->tWP_min)
> +		twait = sdrt->tWP_min;
> +	if (twait < sdrt->tREA_max + FMC2_TIO)
> +		twait = sdrt->tREA_max + FMC2_TIO;
> +	tims->twait = DIV_ROUND_UP(twait, hclkp);
> +	if (tims->twait == 0)
> +		tims->twait = 1;
> +	else if (tims->twait > FMC2_PMEM_PATT_TIMING_MASK)
> +		tims->twait = FMC2_PMEM_PATT_TIMING_MASK;
> +
> +	/*
> +	 * tSETUP_MEM > tCS - tWAIT
> +	 * tSETUP_MEM > tALS - tWAIT
> +	 * tSETUP_MEM > tDS - (tWAIT - tHIZ)
> +	 */
> +	tset_mem = hclkp;
> +	if (sdrt->tCS_min > twait && (tset_mem < sdrt->tCS_min - twait))
> +		tset_mem = sdrt->tCS_min - twait;
> +	if (sdrt->tALS_min > twait && (tset_mem < sdrt->tALS_min - twait))
> +		tset_mem = sdrt->tALS_min - twait;
> +	if (twait > thiz && (sdrt->tDS_min > twait - thiz) &&
> +	    (tset_mem < sdrt->tDS_min - (twait - thiz)))
> +		tset_mem = sdrt->tDS_min - (twait - thiz);
> +	tims->tset_mem = DIV_ROUND_UP(tset_mem, hclkp);
> +	if (tims->tset_mem == 0)
> +		tims->tset_mem = 1;
> +	else if (tims->tset_mem > FMC2_PMEM_PATT_TIMING_MASK)
> +		tims->tset_mem = FMC2_PMEM_PATT_TIMING_MASK;
> +
> +	/*
> +	 * tHOLD_MEM > tCH
> +	 * tHOLD_MEM > tREH - tSETUP_MEM
> +	 * tHOLD_MEM > max(tRC, tWC) - (tSETUP_MEM + tWAIT)
> +	 */
> +	thold_mem = hclkp;
> +	if (thold_mem < sdrt->tCH_min)
> +		thold_mem = sdrt->tCH_min;
> +	if (sdrt->tREH_min > tset_mem &&
> +	    (thold_mem < sdrt->tREH_min - tset_mem))
> +		thold_mem = sdrt->tREH_min - tset_mem;
> +	if ((sdrt->tRC_min > tset_mem + twait) &&
> +	    (thold_mem < sdrt->tRC_min - (tset_mem + twait)))
> +		thold_mem = sdrt->tRC_min - (tset_mem + twait);
> +	if ((sdrt->tWC_min > tset_mem + twait) &&
> +	    (thold_mem < sdrt->tWC_min - (tset_mem + twait)))
> +		thold_mem = sdrt->tWC_min - (tset_mem + twait);
> +	tims->thold_mem = DIV_ROUND_UP(thold_mem, hclkp);
> +	if (tims->thold_mem == 0)
> +		tims->thold_mem = 1;
> +	else if (tims->thold_mem > FMC2_PMEM_PATT_TIMING_MASK)
> +		tims->thold_mem = FMC2_PMEM_PATT_TIMING_MASK;
> +
> +	/*
> +	 * tSETUP_ATT > tCS - tWAIT
> +	 * tSETUP_ATT > tCLS - tWAIT
> +	 * tSETUP_ATT > tALS - tWAIT
> +	 * tSETUP_ATT > tRHW - tHOLD_MEM
> +	 * tSETUP_ATT > tDS - (tWAIT - tHIZ)
> +	 */
> +	tset_att = hclkp;
> +	if (sdrt->tCS_min > twait && (tset_att < sdrt->tCS_min - twait))
> +		tset_att = sdrt->tCS_min - twait;
> +	if (sdrt->tCLS_min > twait && (tset_att < sdrt->tCLS_min - twait))
> +		tset_att = sdrt->tCLS_min - twait;
> +	if (sdrt->tALS_min > twait && (tset_att < sdrt->tALS_min - twait))
> +		tset_att = sdrt->tALS_min - twait;
> +	if (sdrt->tRHW_min > thold_mem &&
> +	    (tset_att < sdrt->tRHW_min - thold_mem))
> +		tset_att = sdrt->tRHW_min - thold_mem;
> +	if (twait > thiz && (sdrt->tDS_min > twait - thiz) &&
> +	    (tset_att < sdrt->tDS_min - (twait - thiz)))
> +		tset_att = sdrt->tDS_min - (twait - thiz);
> +	tims->tset_att = DIV_ROUND_UP(tset_att, hclkp);
> +	if (tims->tset_att == 0)
> +		tims->tset_att = 1;
> +	else if (tims->tset_att > FMC2_PMEM_PATT_TIMING_MASK)
> +		tims->tset_att = FMC2_PMEM_PATT_TIMING_MASK;
> +
> +	/*
> +	 * tHOLD_ATT > tALH
> +	 * tHOLD_ATT > tCH
> +	 * tHOLD_ATT > tCLH
> +	 * tHOLD_ATT > tCOH
> +	 * tHOLD_ATT > tDH
> +	 * tHOLD_ATT > tWB + tIO + tSYNC - tSETUP_MEM
> +	 * tHOLD_ATT > tADL - tSETUP_MEM
> +	 * tHOLD_ATT > tWH - tSETUP_MEM
> +	 * tHOLD_ATT > tWHR - tSETUP_MEM
> +	 * tHOLD_ATT > tRC - (tSETUP_ATT + tWAIT)
> +	 * tHOLD_ATT > tWC - (tSETUP_ATT + tWAIT)
> +	 */
> +	thold_att = hclkp;
> +	if (thold_att < sdrt->tALH_min)
> +		thold_att = sdrt->tALH_min;
> +	if (thold_att < sdrt->tCH_min)
> +		thold_att = sdrt->tCH_min;
> +	if (thold_att < sdrt->tCLH_min)
> +		thold_att = sdrt->tCLH_min;
> +	if (thold_att < sdrt->tCOH_min)
> +		thold_att = sdrt->tCOH_min;
> +	if (thold_att < sdrt->tDH_min)
> +		thold_att = sdrt->tDH_min;
> +	if ((sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC > tset_mem) &&
> +	    (thold_att < sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem))
> +		thold_att = sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem;
> +	if (sdrt->tADL_min > tset_mem &&
> +	    (thold_att < sdrt->tADL_min - tset_mem))
> +		thold_att = sdrt->tADL_min - tset_mem;
> +	if (sdrt->tWH_min > tset_mem &&
> +	    (thold_att < sdrt->tWH_min - tset_mem))
> +		thold_att = sdrt->tWH_min - tset_mem;
> +	if (sdrt->tWHR_min > tset_mem &&
> +	    (thold_att < sdrt->tWHR_min - tset_mem))
> +		thold_att = sdrt->tWHR_min - tset_mem;
> +	if ((sdrt->tRC_min > tset_att + twait) &&
> +	    (thold_att < sdrt->tRC_min - (tset_att + twait)))
> +		thold_att = sdrt->tRC_min - (tset_att + twait);
> +	if ((sdrt->tWC_min > tset_att + twait) &&
> +	    (thold_att < sdrt->tWC_min - (tset_att + twait)))
> +		thold_att = sdrt->tWC_min - (tset_att + twait);
> +	tims->thold_att = DIV_ROUND_UP(thold_att, hclkp);
> +	if (tims->thold_att == 0)
> +		tims->thold_att = 1;
> +	else if (tims->thold_att > FMC2_PMEM_PATT_TIMING_MASK)
> +		tims->thold_att = FMC2_PMEM_PATT_TIMING_MASK;
> +}
> +
> +static int stm32_fmc2_setup_interface(struct nand_chip *chip, int chipnr,
> +				      const struct nand_data_interface *conf)
> +{
> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
> +	struct stm32_fmc2_timings tims;
> +	const struct nand_sdr_timings *sdrt;
> +
> +	sdrt = nand_get_sdr_timings(conf);
> +	if (IS_ERR(sdrt))
> +		return PTR_ERR(sdrt);
> +
> +	stm32_fmc2_calc_timings(fmc2, sdrt, &tims);

Why do you need 'tims' if the only use is to memcpy it in
fmc2->timings? I suppose you can get rid of it.

> +
> +	if (chipnr == NAND_DATA_IFACE_CHECK_ONLY)
> +		return 0;

If the controller may support all the timing modes, then you can
certainly return on CHECK_ONLY before *_calc_timings().

> +
> +	/* Save and apply timings */
> +	memcpy(&fmc2->timings, &tims, sizeof(tims));
> +	stm32_fmc2_timings_init(fmc2);
> +
> +	return 0;
> +}
> +
> +/* DMA configuration */
> +static int stm32_fmc2_dma_setup(struct stm32_fmc2 *fmc2, u8 nbsect)
> +{
> +	struct nand_chip *chip = &fmc2->chip;
> +	struct dma_slave_config dma_cfg;
> +	int ret;
> +
> +	memset(&dma_cfg, 0, sizeof(dma_cfg));
> +	dma_cfg.src_addr = fmc2->data_phys_addr[fmc2->cs_sel];
> +	dma_cfg.dst_addr = fmc2->data_phys_addr[fmc2->cs_sel];
> +	dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +	dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +	dma_cfg.src_maxburst = 32;
> +	dma_cfg.dst_maxburst = 32;
> +
> +	fmc2->dma_tx_ch = dma_request_slave_channel(fmc2->dev, "tx");
> +	if (fmc2->dma_tx_ch) {
> +		ret = dmaengine_slave_config(fmc2->dma_tx_ch, &dma_cfg);
> +		if (ret) {
> +			dev_err(fmc2->dev, "data tx DMA engine slave config failed\n");
> +			return ret;
> +		}
> +	}
> +
> +	fmc2->dma_rx_ch = dma_request_slave_channel(fmc2->dev, "rx");
> +	if (fmc2->dma_rx_ch) {
> +		ret = dmaengine_slave_config(fmc2->dma_rx_ch, &dma_cfg);
> +		if (ret) {
> +			dev_err(fmc2->dev, "data rx DMA engine slave config failed\n");
> +			return ret;
> +		}
> +
> +		fmc2->dma_ecc_ch = dma_request_slave_channel(fmc2->dev, "ecc");
> +		if (fmc2->dma_ecc_ch) {
> +			/*
> +			 * HAMMING: we read HECCR register
> +			 * BCH4/BCH8: we read BCHDSRSx registers
> +			 */
> +			memset(&dma_cfg, 0, sizeof(dma_cfg));
> +			dma_cfg.src_addr = fmc2->io_phys_addr;
> +			dma_cfg.src_addr += chip->ecc.strength == FMC2_ECC_HAM ?
> +					    FMC2_HECCR : FMC2_BCHDSR0;
> +			dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> +
> +			ret = dmaengine_slave_config(fmc2->dma_ecc_ch,
> +						     &dma_cfg);
> +			if (ret) {
> +				dev_err(fmc2->dev, "ecc DMA engine slave config failed\n");
> +				return ret;
> +			}
> +
> +			ret = sg_alloc_table(&fmc2->dma_ecc_sg,
> +					     nbsect, GFP_KERNEL);
> +			if (ret)
> +				return ret;
> +
> +			/* Calculate ecc length needed for one sector */
> +			fmc2->dma_ecc_len = chip->ecc.strength == FMC2_ECC_HAM ?
> +					    FMC2_HECCR_LEN : FMC2_BCHDSRS_LEN;
> +
> +			/* Allocate a buffer to store ecc status registers */
> +			fmc2->ecc_buf = devm_kzalloc(fmc2->dev,
> +						     fmc2->dma_ecc_len * nbsect,
> +						     GFP_KERNEL);
> +			if (!fmc2->ecc_buf)
> +				return -ENOMEM;
> +		} else {
> +			dev_err(fmc2->dev, "ecc DMA not defined in the device tree\n");
> +			return -ENOENT;
> +		}
> +	}
> +
> +	if (fmc2->dma_tx_ch && fmc2->dma_rx_ch) {
> +		ret = sg_alloc_table(&fmc2->dma_data_sg, nbsect, GFP_KERNEL);
> +		if (ret)
> +			return ret;
> +
> +		init_completion(&fmc2->dma_data_complete);
> +		init_completion(&fmc2->dma_ecc_complete);
> +	} else {
> +		dev_err(fmc2->dev, "rx/tx DMA not defined in the device tree\n");
> +		return -ENOENT;
> +	}
> +
> +	return 0;
> +}
> +
> +/* NAND callbacks setup */
> +static void stm32_fmc2_nand_callbacks_setup(struct stm32_fmc2 *fmc2)
> +{
> +	struct nand_chip *chip = &fmc2->chip;
> +
> +	/* Specific callbacks to read/write a page */
> +	chip->ecc.correct = stm32_fmc2_ham_correct;
> +	chip->ecc.write_page = stm32_fmc2_sequencer_write_page;
> +	chip->ecc.read_page = stm32_fmc2_sequencer_read_page;
> +	chip->ecc.write_page_raw = stm32_fmc2_sequencer_write_page_raw;
> +	chip->ecc.read_page_raw = stm32_fmc2_sequencer_read_page_raw;
> +	chip->options |= NAND_USE_BOUNCE_BUFFER;
> +
> +	/* Specific configurations depending on the algo used */
> +	if (chip->ecc.strength == FMC2_ECC_HAM)
> +		chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 4 : 3;
> +	else if (chip->ecc.strength == FMC2_ECC_BCH8)
> +		chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 14 : 13;
> +	else
> +		chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 8 : 7;
> +}
> +
> +/* FMC2 layout */
> +static int stm32_fmc2_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
> +					 struct mtd_oob_region *oobregion)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct nand_ecc_ctrl *ecc = &chip->ecc;
> +
> +	if (section)
> +		return -ERANGE;
> +
> +	oobregion->length = ecc->total;
> +	oobregion->offset = FMC2_BBM_LEN;
> +
> +	return 0;
> +}
> +
> +static int stm32_fmc2_nand_ooblayout_free(struct mtd_info *mtd, int section,
> +					  struct mtd_oob_region *oobregion)
> +{
> +	struct nand_chip *chip = mtd_to_nand(mtd);
> +	struct nand_ecc_ctrl *ecc = &chip->ecc;
> +
> +	if (section)
> +		return -ERANGE;
> +
> +	oobregion->length = mtd->oobsize - ecc->total - FMC2_BBM_LEN;
> +	oobregion->offset = ecc->total + FMC2_BBM_LEN;
> +
> +	return 0;
> +}
> +
> +const struct mtd_ooblayout_ops stm32_fmc2_nand_ooblayout_ops = {
> +	.ecc = stm32_fmc2_nand_ooblayout_ecc,
> +	.free = stm32_fmc2_nand_ooblayout_free,
> +};
> +
> +/* FMC2 caps */
> +static int stm32_fmc2_calc_ecc_bytes(int step_size, int strength)
> +{
> +	/* Hamming */
> +	if (strength == FMC2_ECC_HAM)
> +		return 4;
> +
> +	/* BCH8 */
> +	if (strength == FMC2_ECC_BCH8)
> +		return 14;
> +
> +	/* BCH4 */
> +	return 8;
> +}
> +
> +NAND_ECC_CAPS_SINGLE(stm32_fmc2_ecc_caps, stm32_fmc2_calc_ecc_bytes,
> +		     FMC2_ECC_STEP_SIZE,
> +		     FMC2_ECC_HAM, FMC2_ECC_BCH4, FMC2_ECC_BCH8);
> +
> +/* FMC2 controller ops */
> +static int stm32_fmc2_attach_chip(struct nand_chip *chip)
> +{
> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
> +	struct mtd_info *mtd = nand_to_mtd(chip);
> +	u8 nbsect;
> +	int ret;
> +
> +	/*
> +	 * Only NAND_ECC_HW mode is actually supported
> +	 * HAMMING => ecc.strength = 1
> +	 * BCH4 => ecc.strength = 4
> +	 * BCH8 => ecc.strength = 8
> +	 * ecc sector size = 512
> +	 */
> +	if (chip->ecc.mode != NAND_ECC_HW) {
> +		dev_err(fmc2->dev, "nand_ecc_mode is not well defined in the DT\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = nand_ecc_choose_conf(chip, &stm32_fmc2_ecc_caps,
> +				   mtd->oobsize - FMC2_BBM_LEN);
> +	if (ret) {
> +		dev_err(fmc2->dev, "no valid ECC settings set\n");
> +		return ret;
> +	}
> +
> +	nbsect = mtd->writesize / chip->ecc.size;
> +	if (nbsect > FMC2_MAX_SG_COUNT) {
> +		dev_err(fmc2->dev, "nand page size is not supported\n");
> +		return -EINVAL;
> +	}
> +
> +	if (chip->bbt_options & NAND_BBT_USE_FLASH)
> +		chip->bbt_options |= NAND_BBT_NO_OOB;
> +
> +	/* FMC2 setup routine */
> +	stm32_fmc2_setup(fmc2);
> +
> +	/* DMA setup */
> +	ret = stm32_fmc2_dma_setup(fmc2, nbsect);
> +	if (ret)
> +		return ret;
> +
> +	/* NAND callbacks setup */
> +	stm32_fmc2_nand_callbacks_setup(fmc2);
> +
> +	/* Define ECC layout */
> +	mtd_set_ooblayout(mtd, &stm32_fmc2_nand_ooblayout_ops);
> +
> +	return 0;
> +}
> +
> +static const struct nand_controller_ops stm32_fmc2_nand_controller_ops = {
> +	.attach_chip = stm32_fmc2_attach_chip,
> +};
> +
> +/* FMC2 probe */
> +static int stm32_fmc2_parse_child(struct device *dev,
> +				  struct stm32_fmc2 *fmc2,
> +				  struct device_node *dn)
> +{
> +	u32 cs;
> +	int ret, i;
> +
> +	if (!of_get_property(dn, "reg", &fmc2->ncs))
> +		return -EINVAL;
> +
> +	fmc2->ncs /= sizeof(u32);
> +	if (!fmc2->ncs) {
> +		dev_err(dev, "invalid reg property size\n");
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; i < fmc2->ncs; i++) {
> +		ret = of_property_read_u32_index(dn, "reg", i, &cs);
> +		if (ret) {
> +			dev_err(dev, "could not retrieve reg property: %d\n",
> +				ret);
> +			return ret;
> +		}
> +
> +		if (cs > FMC2_MAX_CE) {
> +			dev_err(dev, "invalid reg value: %d\n", cs);
> +			return -EINVAL;
> +		}
> +
> +		if (fmc2->cs_assigned & BIT(cs)) {
> +			dev_err(dev, "cs already assigned: %d\n", cs);
> +			return -EINVAL;
> +		}
> +
> +		fmc2->cs_assigned |= BIT(cs);
> +		fmc2->cs_used[i] = cs;
> +	}
> +
> +	/* Default cs used */

s/cs/CS/

> +	fmc2->cs_sel = fmc2->cs_used[0];
> +
> +	/* Timings */
> +	ret = of_property_read_u8_array(dn, "st,fmc2_timings",
> +					(u8 *)&fmc2->timings,
> +					sizeof(fmc2->timings));
> +	return ret ? 0 : 1;
> +}
> +
> +static int stm32_fmc2_parse_dt(struct device *dev,
> +			       struct stm32_fmc2 *fmc2)
> +{
> +	struct device_node *dn = dev->of_node;
> +	struct device_node *child;
> +	int nchips = of_get_child_count(dn);
> +	int ret = 0;
> +
> +	if (!nchips) {
> +		dev_err(dev, "NAND chip not defined\n");
> +		return -EINVAL;
> +	}
> +
> +	if (nchips > 1) {

If you have two CS, can't you have two NAND chips connected?

> +		dev_err(dev, "too many NAND chips defined\n");
> +		return -EINVAL;
> +	}
> +
> +	for_each_child_of_node(dn, child) {
> +		ret = stm32_fmc2_parse_child(dev, fmc2, child);
> +		if (ret < 0) {
> +			of_node_put(child);
> +			return ret;
> +		}
> +
> +		nand_set_flash_node(&fmc2->chip, child);
> +	}
> +
> +	return ret;
> +}
> +
> +static int stm32_fmc2_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct reset_control *rstc;
> +	struct stm32_fmc2 *fmc2;
> +	struct resource *res;
> +	struct mtd_info *mtd;
> +	struct nand_chip *chip;
> +	int i, j, ret, irq, timings_def;
> +
> +	fmc2 = devm_kzalloc(dev, sizeof(*fmc2), GFP_KERNEL);
> +	if (!fmc2)
> +		return -ENOMEM;
> +
> +	timings_def = stm32_fmc2_parse_dt(dev, fmc2);
> +	if (timings_def < 0)
> +		return timings_def;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	fmc2->io_base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(fmc2->io_base))
> +		return PTR_ERR(fmc2->io_base);
> +
> +	fmc2->io_phys_addr = res->start;
> +
> +	for (i = 0, j = 1; i < FMC2_MAX_CE; i++, j += 3) {

Maybe naming i 'chip_id' and j 'mem_region' or something else, more
meaningful than just i/j would be more clear?

> +		if (!(fmc2->cs_assigned & BIT(i)))
> +			continue;
> +
> +		res = platform_get_resource(pdev, IORESOURCE_MEM, j);
> +		fmc2->data_base[i] = devm_ioremap_resource(dev, res);
> +		if (IS_ERR(fmc2->data_base[i]))
> +			return PTR_ERR(fmc2->data_base[i]);
> +
> +		fmc2->data_phys_addr[i] = res->start;
> +
> +		res = platform_get_resource(pdev, IORESOURCE_MEM, j + 1);
> +		fmc2->cmd_base[i] = devm_ioremap_resource(dev, res);
> +		if (IS_ERR(fmc2->cmd_base[i]))
> +			return PTR_ERR(fmc2->cmd_base[i]);
> +
> +		res = platform_get_resource(pdev, IORESOURCE_MEM, j + 2);
> +		fmc2->addr_base[i] = devm_ioremap_resource(dev, res);
> +		if (IS_ERR(fmc2->addr_base[i]))
> +			return PTR_ERR(fmc2->addr_base[i]);
> +	}
> +
> +	irq = platform_get_irq(pdev, 0);
> +	ret = devm_request_irq(dev, irq, stm32_fmc2_irq, 0,
> +			       dev_name(dev), fmc2);
> +	if (ret) {
> +		dev_err(dev, "failed to request irq\n");
> +		return ret;
> +	}
> +
> +	init_completion(&fmc2->complete);
> +
> +	fmc2->clk = devm_clk_get(dev, NULL);
> +	if (IS_ERR(fmc2->clk))
> +		return PTR_ERR(fmc2->clk);
> +
> +	ret = clk_prepare_enable(fmc2->clk);
> +	if (ret) {
> +		dev_err(dev, "can not enable the clock\n");
> +		return ret;
> +	}
> +
> +	rstc = devm_reset_control_get(dev, NULL);
> +	if (!IS_ERR(rstc)) {
> +		reset_control_assert(rstc);
> +		reset_control_deassert(rstc);
> +	}
> +
> +	fmc2->dev = dev;
> +	mtd = nand_to_mtd(&fmc2->chip);
> +	chip = &fmc2->chip;
> +	nand_set_controller_data(chip, fmc2);
> +	mtd->dev.parent = dev;
> +
> +	chip->exec_op = stm32_fmc2_exec_op;
> +	chip->select_chip = stm32_fmc2_select_chip;
> +	chip->options |= NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE;
> +
> +	/* FMC2 init routine */
> +	stm32_fmc2_init(fmc2);
> +	if (timings_def)
> +		stm32_fmc2_timings_init(fmc2);

You already implement ->setup_data_interface, why do you need this
stm32_fmc2_timings_init()?

> +	else
> +		chip->setup_data_interface = stm32_fmc2_setup_interface;
> +
> +	/* Default settings */
> +	chip->ecc.mode = NAND_ECC_HW;
> +	chip->ecc.size = FMC2_ECC_STEP_SIZE;
> +	chip->ecc.strength = FMC2_ECC_BCH8;
> +
> +	/* Scan to find existence of the device */
> +	chip->dummy_controller.ops = &stm32_fmc2_nand_controller_ops;
> +	ret = nand_scan(chip, fmc2->ncs);
> +	if (ret)
> +		goto err_scan;
> +
> +	ret = mtd_device_register(mtd, NULL, 0);
> +	if (ret)
> +		goto err_device_register;
> +
> +	platform_set_drvdata(pdev, fmc2);
> +
> +	return 0;
> +
> +err_device_register:
> +	nand_cleanup(chip);
> +
> +err_scan:
> +	if (fmc2->dma_ecc_ch)
> +		dma_release_channel(fmc2->dma_ecc_ch);
> +	if (fmc2->dma_tx_ch)
> +		dma_release_channel(fmc2->dma_tx_ch);
> +	if (fmc2->dma_rx_ch)
> +		dma_release_channel(fmc2->dma_rx_ch);
> +
> +	sg_free_table(&fmc2->dma_data_sg);
> +	sg_free_table(&fmc2->dma_ecc_sg);
> +
> +	clk_disable_unprepare(fmc2->clk);
> +
> +	return ret;
> +}
> +
> +static int stm32_fmc2_remove(struct platform_device *pdev)
> +{
> +	struct stm32_fmc2 *fmc2 = platform_get_drvdata(pdev);
> +
> +	nand_release(&fmc2->chip);
> +
> +	if (fmc2->dma_ecc_ch)
> +		dma_release_channel(fmc2->dma_ecc_ch);
> +	if (fmc2->dma_tx_ch)
> +		dma_release_channel(fmc2->dma_tx_ch);
> +	if (fmc2->dma_rx_ch)
> +		dma_release_channel(fmc2->dma_rx_ch);
> +
> +	sg_free_table(&fmc2->dma_data_sg);
> +	sg_free_table(&fmc2->dma_ecc_sg);
> +
> +	clk_disable_unprepare(fmc2->clk);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int stm32_fmc2_suspend(struct device *dev)

Please remove the pre-processor conditionals and add
__maybe_unused in the suspend/resume functions definitions.

> +{
> +	struct stm32_fmc2 *fmc2 = dev_get_drvdata(dev);
> +
> +	clk_disable_unprepare(fmc2->clk);
> +
> +	pinctrl_pm_select_sleep_state(dev);
> +
> +	return 0;
> +}
> +
> +static int stm32_fmc2_resume(struct device *dev)
> +{
> +	struct stm32_fmc2 *fmc2 = dev_get_drvdata(dev);
> +	int i, ret;
> +
> +	pinctrl_pm_select_default_state(dev);
> +
> +	ret = clk_prepare_enable(fmc2->clk);
> +	if (ret) {
> +		dev_err(dev, "can not enable the clock\n");
> +		return ret;
> +	}
> +
> +	stm32_fmc2_init(fmc2);
> +	stm32_fmc2_timings_init(fmc2);
> +	stm32_fmc2_setup(fmc2);
> +
> +	for (i = 0; i < fmc2->ncs; i++)
> +		nand_reset(&fmc2->chip, i);

This means you have one different NAND chip wired on each CS.

We could have two CS wired to the same NAND chip. Calling nand_reset
twice would be harmless but a lost of time.

> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(stm32_fmc2_pm_ops, stm32_fmc2_suspend,
> +			 stm32_fmc2_resume);
> +
> +static const struct of_device_id stm32_fmc2_match[] = {
> +	{.compatible = "st,stm32mp15-fmc2"},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, stm32_fmc2_match);
> +
> +static struct platform_driver stm32_fmc2_driver = {
> +	.probe	= stm32_fmc2_probe,
> +	.remove	= stm32_fmc2_remove,
> +	.driver	= {
> +		.name = "stm32-fmc2",
> +		.of_match_table = stm32_fmc2_match,
> +		.pm = &stm32_fmc2_pm_ops,
> +	},
> +};
> +module_platform_driver(stm32_fmc2_driver);
> +
> +MODULE_ALIAS("platform:" DRIVER_NAME);
> +MODULE_AUTHOR("Christophe Kerello <christophe.kerello@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 FMC2 nand driver");
> +MODULE_LICENSE("GPL v2");


Thanks,
Miquèl

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/3] mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver
  2018-09-17 15:47 ` [PATCH 2/3] mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver christophe.kerello
                     ` (2 preceding siblings ...)
  2018-09-22 13:48   ` Miquel Raynal
@ 2018-09-23 11:34   ` Miquel Raynal
  2018-09-24 16:36     ` Christophe Kerello
  2018-09-24 17:23   ` Boris Brezillon
  4 siblings, 1 reply; 18+ messages in thread
From: Miquel Raynal @ 2018-09-23 11:34 UTC (permalink / raw)
  To: christophe.kerello
  Cc: boris.brezillon, richard, dwmw2, computersforpeace, marek.vasut,
	robh+dt, mark.rutland, linux-mtd, linux-kernel, devicetree,
	linux-stm32

Hi Christophe,

<christophe.kerello@st.com> wrote on Mon, 17 Sep 2018 17:47:39 +0200:

> From: Christophe Kerello <christophe.kerello@st.com>
> 
> The driver adds the support for the STMicroelectronics FMC2 NAND
> Controller found on STM32MP SOCs.
> 
> This patch is based on FMC2 command sequencer.
> The purpose of the command sequencer is to facilitate the programming
> and the reading of NAND flash pages with the ECC and to free the CPU
> of sequencing tasks.
> It requires one DMA channel for write and two DMA channels for read
> operations.
> 
> Only NAND_ECC_HW mode is actually supported.
> The driver supports a maximum 8k page size.
> The following ECC strength and step size are currently supported:
>  - nand-ecc-strength = <8>, nand-ecc-step-size = <512> (BCH8)
>  - nand-ecc-strength = <4>, nand-ecc-step-size = <512> (BCH4)
>  - nand-ecc-strength = <1>, nand-ecc-step-size = <512> (Extended ecc
>    based on HAMMING)
> 
> This patch has been tested on Micron MT29F8G08ABACAH4 and
> MT29F8G16ABACAH4
> 
> Signed-off-by: Christophe Kerello <christophe.kerello@st.com>
> ---


[...]

> +/* NAND callbacks setup */
> +static void stm32_fmc2_nand_callbacks_setup(struct stm32_fmc2 *fmc2)
> +{
> +	struct nand_chip *chip = &fmc2->chip;
> +
> +	/* Specific callbacks to read/write a page */
> +	chip->ecc.correct = stm32_fmc2_ham_correct;
> +	chip->ecc.write_page = stm32_fmc2_sequencer_write_page;
> +	chip->ecc.read_page = stm32_fmc2_sequencer_read_page;
> +	chip->ecc.write_page_raw = stm32_fmc2_sequencer_write_page_raw;
> +	chip->ecc.read_page_raw = stm32_fmc2_sequencer_read_page_raw;

Are you sure all the tests in mtd-utils are successful?


Thanks,
Miquèl

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 1/3] dt-bindings: mtd: stm32_fmc2: add STM32 FMC2 NAND controller documentation
  2018-09-22  8:34   ` Miquel Raynal
@ 2018-09-24 16:36     ` Christophe Kerello
  2018-09-24 17:17       ` Boris Brezillon
  0 siblings, 1 reply; 18+ messages in thread
From: Christophe Kerello @ 2018-09-24 16:36 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: boris.brezillon, richard, dwmw2, computersforpeace, marek.vasut,
	robh+dt, mark.rutland, linux-mtd, linux-kernel, devicetree,
	linux-stm32

Hi Miquèl,

On 09/22/2018 10:34 AM, Miquel Raynal wrote:
> Hi Christophe,
> 
> <christophe.kerello@st.com> wrote on Mon, 17 Sep 2018 17:47:38 +0200:
> 
>> From: Christophe Kerello <christophe.kerello@st.com>
>>
>> This patch adds the documentation of the device tree bindings for the STM32
>> FMC2 NAND controller.
>>
>> Signed-off-by: Christophe Kerello <christophe.kerello@st.com>
>> ---
>>   .../devicetree/bindings/mtd/stm32-fmc2-nand.txt    | 90 ++++++++++++++++++++++
>>   1 file changed, 90 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt
>>
>> diff --git a/Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt b/Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt
>> new file mode 100644
>> index 0000000..93eaf11
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/mtd/stm32-fmc2-nand.txt
>> @@ -0,0 +1,90 @@
>> +STMicroelectronics Flexible Memory Controller 2 (FMC2)
>> +NAND Interface
>> +
>> +Required properties:
>> +- compatible: "st,stm32mp15-fmc2"
> 
> I think this form is preferred:
> 
> "
> - compatible: Should be one of:
>                * st,stm32mp15-fmc2
> "

Ok, I will modify this comment.

> 
>> +- reg: the first contains the register location and length
> 
> the register location and length of...?
> 
>> +       the second contains the data common space used for cs0 and length
>> +       the third contains the cmd attribute space used for cs0 and length
>> +       the fourth contains the addr attribute space used for cs0 and length
>> +       the fifth contains the data common space used for cs1 and length
>> +       the sixth contains the cmd attribute space used for cs1 and length
>> +       the seventh contains the addr attribute space used for cs1 and length
> 
> Maybe you could simplify a bit with something like:
> 
> -reg: NAND flash controller memory areas.
>        First region ...
>        Regions 2 to 4 respectively contain the data, command, and
>        address space for CS0.
>        Regions 5 to 7 contain the same areas for CS1.

Ok, I will simplify the description of "reg" property.

> 
>> +- interrupts: The interrupt number
>> +- pinctrl-0: Standard Pinctrl phandle (see: pinctrl/pinctrl-bindings.txt)
>> +- clocks: Use common clock framework
>> +
>> +Optional properties:
>> +- resets: Reference to a reset controller asserting the FMC controller
>> +- dmas: DMA specifiers (see: dma/stm32-mdma.txt)
>> +- dma-names: Must be "tx", "rx" and "ecc"
>> +
>> +Optional children nodes:
>> +Children nodes represent the available nand chips.
> 
> Please s/nand/NAND/ in plain English.

Ok.

> 
>> +
>> +Optional properties:
>> +- nand-on-flash-bbt: see nand.txt
>> +- nand-ecc-strength: see nand.txt
>> +- nand-ecc-step-size: see nand.txt
>> +- st,fmc2_timings: array of 8 bytes for NAND timings. The meanings of
>> +  these bytes are:
>> +  byte 0 TCLR      : CLE to RE delay in number of AHB clock cycles, only 4 bits
>> +                     are valid. Zero means one clock cycle, 15 means 16 clock
>> +                     cycles.
>> +  byte 1 TAR       : ALE to RE delay, 4 bits are valid. Same format as TCLR.
>> +  byte 2 THIZ      : number of HCLK clock cycles during which the data bus is
>> +                     kept in Hi-Z (tristate) after the start of a write access.
>> +                     Only valid for write transactions. Zero means 1 cycle,
>> +                     255 means 256 cycles.
>> +  byte 3 TWAIT     : number of HCLK clock cycles to assert the command to the
>> +                     NAND flash in response to SMWAITn. Zero means 1 cycle,
>> +                     255 means 256 cycles.
>> +  byte 4 THOLD_MEM : common memory space timing
>> +                     number of HCLK clock cycles to hold the address (and data
>> +                     when writing) after the command deassertion. Zero means
>> +                     1 cycle, 255 means 256 cycles.
>> +  byte 5 TSET_MEM  : common memory space timing
>> +                     number of HCLK clock cycles to assert the address before
>> +                     the command is asserted. Zero means 1 cycle, 255 means 256
>> +                     cycles.
>> +  byte 6 THOLD_ATT : attribute memory space timing
>> +                     number of HCLK clock cycles to hold the address (and data
>> +                     when writing) after the command deassertion. Zero means
>> +                     1 cycle, 255 means 256 cycles.
>> +  byte 7 TSET_ATT  : attribute memory space timing
>> +                     number of HCLK clock cycles to assert the address before
>> +                     the command is asserted. Zero means 1 cycle, 255 means 256
>> +                     cycles.
> 
> Let me review the driver but this array of timings is really
> suspicious. I am pretty sure you don't need it in the DT.

"st,fmc2-timings" is an optional property that allow the end user to 
overwrite the timings calculated by setup_data_interface callback. By 
setting this property in the NAND flash memory device tree node, the end 
user could have a better throughput. For NON ONFI SLC NAND, timing mode 
0 is often used.

> 
>> +
>> +The following ECC strength and step size are currently supported:
>> + - nand-ecc-strength = <1>, nand-ecc-step-size = <512> (HAMMING)
>> + - nand-ecc-strength = <4>, nand-ecc-step-size = <512> (BCH4)
>> + - nand-ecc-strength = <8>, nand-ecc-step-size = <512> (BCH8) (default)
>> +
>> +Example:
>> +
>> +	fmc2_nand: fmc2_nand@58002000 {
>> +		compatible = "st,stm32mp15-fmc2";
>> +		reg = <0x58002000 0x1000>,
>> +		      <0x80000000 0x1000>,
>> +		      <0x88010000 0x1000>,
>> +		      <0x88020000 0x1000>,
>> +		      <0x81000000 0x1000>,
>> +		      <0x89010000 0x1000>,
>> +		      <0x89020000 0x1000>;
>> +		interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
>> +		clocks = <&rcc FMC_K>;
>> +		resets = <&rcc FMC_R>;
>> +		pinctrl-names = "default";
>> +		pinctrl-0 = <&fmc2_pins_a>;
>> +		#address-cells = <1>;
>> +		#size-cells = <0>;
>> +
>> +		nand@0 {
>> +			reg = <0>;
>> +			nand-on-flash-bbt;
>> +			st,fmc2_timings = /bits/ 8 <2 2 1 7 1 0 104 25>;
>> +			#address-cells = <1>;
>> +			#size-cells = <1>;
>> +		};
>> +	};
> 
> Thanks,
> Miquèl
> 

Thanks,
Christophe Kerello.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/3] mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver
  2018-09-22 13:48   ` Miquel Raynal
@ 2018-09-24 16:36     ` Christophe Kerello
  2018-09-24 17:26       ` Boris Brezillon
  2018-10-29  9:22       ` Miquel Raynal
  0 siblings, 2 replies; 18+ messages in thread
From: Christophe Kerello @ 2018-09-24 16:36 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: boris.brezillon, richard, dwmw2, computersforpeace, marek.vasut,
	robh+dt, mark.rutland, linux-mtd, linux-kernel, devicetree,
	linux-stm32

Hi Miquèl,

On 09/22/2018 03:48 PM, Miquel Raynal wrote:
> Hi Christophe,
> 
> I suppose you received the kbuildrobot issues already, please have a
> look at them.

Yes, kbuidroot issues will be solved in the v2 patchset.

> 
> The driver looks well, some comments below.
> 
> <christophe.kerello@st.com> wrote on Mon, 17 Sep 2018 17:47:39 +0200:
> 
>> From: Christophe Kerello <christophe.kerello@st.com>
>>
>> The driver adds the support for the STMicroelectronics FMC2 NAND
>> Controller found on STM32MP SOCs.
>>
>> This patch is based on FMC2 command sequencer.
>> The purpose of the command sequencer is to facilitate the programming
>> and the reading of NAND flash pages with the ECC and to free the CPU
>> of sequencing tasks.
>> It requires one DMA channel for write and two DMA channels for read
>> operations.
>>
>> Only NAND_ECC_HW mode is actually supported.
>> The driver supports a maximum 8k page size.
>> The following ECC strength and step size are currently supported:
>>   - nand-ecc-strength = <8>, nand-ecc-step-size = <512> (BCH8)
>>   - nand-ecc-strength = <4>, nand-ecc-step-size = <512> (BCH4)
>>   - nand-ecc-strength = <1>, nand-ecc-step-size = <512> (Extended ecc
>>     based on HAMMING)
>>
>> This patch has been tested on Micron MT29F8G08ABACAH4 and
>> MT29F8G16ABACAH4
>>
>> Signed-off-by: Christophe Kerello <christophe.kerello@st.com>
>> ---
>>   drivers/mtd/nand/raw/Kconfig           |    9 +
>>   drivers/mtd/nand/raw/Makefile          |    1 +
>>   drivers/mtd/nand/raw/stm32_fmc2_nand.c | 1729 ++++++++++++++++++++++++++++++++
>>   3 files changed, 1739 insertions(+)
>>   create mode 100644 drivers/mtd/nand/raw/stm32_fmc2_nand.c
>>
>> diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
>> index c7efc31..863662c 100644
>> --- a/drivers/mtd/nand/raw/Kconfig
>> +++ b/drivers/mtd/nand/raw/Kconfig
>> @@ -541,4 +541,13 @@ config MTD_NAND_TEGRA
>>   	  is supported. Extra OOB bytes when using HW ECC are currently
>>   	  not supported.
>>   
>> +config MTD_NAND_STM32_FMC2
>> +	tristate "Support for NAND controller on STM32MP SoCs"
>> +	depends on MACH_STM32MP157 || COMPILE_TEST
>> +	help
>> +	  Enables support for NAND Flash chips on SoCs containing the FMC2
>> +	  NAND controller. This controller is found on STM32MP SoCs.
>> +	  The driver supports a maximum 8k page size. HW ECC is enabled and
>> +	  supports a maximum 8-bit correction error per sector of 512 bytes.
> 
> HW ECC should not be enabled by default. 8-bit/512B of correctability
> is good but not that high and people might want to use software ECC in
> conjunction with raw accesses.

Yes, I agree. The driver only supports NAND_ECC_HW mode. NAND_ECC_SOFT 
mode was not requested by customers and was not implemented. The driver 
could be improved later to support mode like NAND_ECC_SOFT or 
NAND_ECC_ON_DIE. Should I remove "HW ECC is enabled" from Kconfig 
description?

> 
>> +
>>   endif # MTD_NAND
>> diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
>> index a6ef067..8c437e3 100644
>> --- a/drivers/mtd/nand/raw/Makefile
>> +++ b/drivers/mtd/nand/raw/Makefile
>> @@ -56,6 +56,7 @@ obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand/
>>   obj-$(CONFIG_MTD_NAND_QCOM)		+= qcom_nandc.o
>>   obj-$(CONFIG_MTD_NAND_MTK)		+= mtk_ecc.o mtk_nand.o
>>   obj-$(CONFIG_MTD_NAND_TEGRA)		+= tegra_nand.o
>> +obj-$(CONFIG_MTD_NAND_STM32_FMC2)	+= stm32_fmc2_nand.o
>>   
>>   nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o
>>   nand-objs += nand_amd.o
>> diff --git a/drivers/mtd/nand/raw/stm32_fmc2_nand.c b/drivers/mtd/nand/raw/stm32_fmc2_nand.c
>> new file mode 100644
>> index 0000000..dd5762a
>> --- /dev/null
>> +++ b/drivers/mtd/nand/raw/stm32_fmc2_nand.c
>> @@ -0,0 +1,1729 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
>> + * Author: Christophe Kerello <christophe.kerello@st.com> for STMicroelectronics
> 
> I'm not sure the "All rights reserved" has a meaning here.
> 
> But you definitely not have to add "for STMicroelectronics" because it
> is already in the copyright.

Ok.

> 
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/dmaengine.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/errno.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/iopoll.h>
>> +#include <linux/module.h>
>> +#include <linux/mtd/rawnand.h>
>> +#include <linux/pinctrl/consumer.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/reset.h>
>> +
>> +/* Bad block marker length */
>> +#define FMC2_BBM_LEN			2
>> +
>> +/* ECC step size */
>> +#define FMC2_ECC_STEP_SIZE		512
>> +
>> +/* BCHDSRx registers length */
>> +#define FMC2_BCHDSRS_LEN		20
>> +
>> +/* HECCR length */
>> +#define FMC2_HECCR_LEN			4
>> +
>> +/* Max requests done for a 8k nand page size */
>> +#define FMC2_MAX_SG_COUNT		16
>> +
>> +/* Max chip enable */
>> +#define FMC2_MAX_CE			2
>> +
>> +/* Timings */
>> +#define FMC2_THIZ			1
>> +#define FMC2_TIO			8000
>> +#define FMC2_TSYNC			3000
>> +#define FMC2_PCR_TIMING_MASK		0xf
>> +#define FMC2_PMEM_PATT_TIMING_MASK	0xff
>> +
>> +/* FMC2 Controller Registers */
>> +#define FMC2_BCR1			0x0
>> +#define FMC2_PCR			0x80
>> +#define FMC2_SR				0x84
>> +#define FMC2_PMEM			0x88
>> +#define FMC2_PATT			0x8c
>> +#define FMC2_HECCR			0x94
>> +#define FMC2_CSQCR			0x200
>> +#define FMC2_CSQCFGR1			0x204
>> +#define FMC2_CSQCFGR2			0x208
>> +#define FMC2_CSQCFGR3			0x20c
>> +#define FMC2_CSQAR1			0x210
>> +#define FMC2_CSQAR2			0x214
>> +#define FMC2_CSQIER			0x220
>> +#define FMC2_CSQISR			0x224
>> +#define FMC2_CSQICR			0x228
>> +#define FMC2_CSQEMSR			0x230
>> +#define FMC2_BCHIER			0x250
>> +#define FMC2_BCHISR			0x254
>> +#define FMC2_BCHICR			0x258
>> +#define FMC2_BCHPBR1			0x260
>> +#define FMC2_BCHPBR2			0x264
>> +#define FMC2_BCHPBR3			0x268
>> +#define FMC2_BCHPBR4			0x26c
>> +#define FMC2_BCHDSR0			0x27c
>> +#define FMC2_BCHDSR1			0x280
>> +#define FMC2_BCHDSR2			0x284
>> +#define FMC2_BCHDSR3			0x288
>> +#define FMC2_BCHDSR4			0x28c
>> +
>> +/* Register: FMC2_BCR1 */
>> +#define FMC2_BCR1_FMC2EN		BIT(31)
>> +
>> +/* Register: FMC2_PCR */
>> +#define FMC2_PCR_PWAITEN		BIT(1)
>> +#define FMC2_PCR_PBKEN			BIT(2)
>> +#define FMC2_PCR_PWID_MASK		GENMASK(5, 4)
>> +#define FMC2_PCR_PWID(x)		(((x) & 0x3) << 4)
>> +#define FMC2_PCR_PWID_BUSWIDTH_8	0
>> +#define FMC2_PCR_PWID_BUSWIDTH_16	1
>> +#define FMC2_PCR_ECCEN			BIT(6)
>> +#define FMC2_PCR_ECCALG			BIT(8)
>> +#define FMC2_PCR_TCLR_MASK		GENMASK(12, 9)
>> +#define FMC2_PCR_TCLR(x)		(((x) & 0xf) << 9)
>> +#define FMC2_PCR_TCLR_DEFAULT		0xf
>> +#define FMC2_PCR_TAR_MASK		GENMASK(16, 13)
>> +#define FMC2_PCR_TAR(x)			(((x) & 0xf) << 13)
>> +#define FMC2_PCR_TAR_DEFAULT		0xf
>> +#define FMC2_PCR_ECCSS_MASK		GENMASK(19, 17)
>> +#define FMC2_PCR_ECCSS(x)		(((x) & 0x7) << 17)
>> +#define FMC2_PCR_ECCSS_512		1
>> +#define FMC2_PCR_ECCSS_2048		3
>> +#define FMC2_PCR_BCHECC			BIT(24)
>> +#define FMC2_PCR_WEN			BIT(25)
>> +
>> +/* Register: FMC2_SR */
>> +#define FMC2_SR_NWRF			BIT(6)
>> +
>> +/* Register: FMC2_PMEM */
>> +#define FMC2_PMEM_MEMSET(x)		(((x) & 0xff) << 0)
>> +#define FMC2_PMEM_MEMWAIT(x)		(((x) & 0xff) << 8)
>> +#define FMC2_PMEM_MEMHOLD(x)		(((x) & 0xff) << 16)
>> +#define FMC2_PMEM_MEMHIZ(x)		(((x) & 0xff) << 24)
>> +#define FMC2_PMEM_DEFAULT		0x0a0a0a0a
>> +
>> +/* Register: FMC2_PATT */
>> +#define FMC2_PATT_ATTSET(x)		(((x) & 0xff) << 0)
>> +#define FMC2_PATT_ATTWAIT(x)		(((x) & 0xff) << 8)
>> +#define FMC2_PATT_ATTHOLD(x)		(((x) & 0xff) << 16)
>> +#define FMC2_PATT_ATTHIZ(x)		(((x) & 0xff) << 24)
>> +#define FMC2_PATT_DEFAULT		0x0a0a0a0a
>> +
>> +/* Register: FMC2_CSQCR */
>> +#define FMC2_CSQCR_CSQSTART		BIT(0)
>> +
>> +/* Register: FMC2_CSQCFGR1 */
>> +#define FMC2_CSQCFGR1_CMD2EN		BIT(1)
>> +#define FMC2_CSQCFGR1_DMADEN		BIT(2)
>> +#define FMC2_CSQCFGR1_ACYNBR(x)		(((x) & 0x7) << 4)
>> +#define FMC2_CSQCFGR1_CMD1(x)		(((x) & 0xff) << 8)
>> +#define FMC2_CSQCFGR1_CMD2(x)		(((x) & 0xff) << 16)
>> +#define FMC2_CSQCFGR1_CMD1T		BIT(24)
>> +#define FMC2_CSQCFGR1_CMD2T		BIT(25)
>> +
>> +/* Register: FMC2_CSQCFGR2 */
>> +#define FMC2_CSQCFGR2_SQSDTEN		BIT(0)
>> +#define FMC2_CSQCFGR2_RCMD2EN		BIT(1)
>> +#define FMC2_CSQCFGR2_DMASEN		BIT(2)
>> +#define FMC2_CSQCFGR2_RCMD1(x)		(((x) & 0xff) << 8)
>> +#define FMC2_CSQCFGR2_RCMD2(x)		(((x) & 0xff) << 16)
>> +#define FMC2_CSQCFGR2_RCMD1T		BIT(24)
>> +#define FMC2_CSQCFGR2_RCMD2T		BIT(25)
>> +
>> +/* Register: FMC2_CSQCFGR3 */
>> +#define FMC2_CSQCFGR3_SNBR(x)		(((x) & 0x1f) << 8)
>> +#define FMC2_CSQCFGR3_AC1T		BIT(16)
>> +#define FMC2_CSQCFGR3_AC2T		BIT(17)
>> +#define FMC2_CSQCFGR3_AC3T		BIT(18)
>> +#define FMC2_CSQCFGR3_AC4T		BIT(19)
>> +#define FMC2_CSQCFGR3_AC5T		BIT(20)
>> +#define FMC2_CSQCFGR3_SDT		BIT(21)
>> +#define FMC2_CSQCFGR3_RAC1T		BIT(22)
>> +#define FMC2_CSQCFGR3_RAC2T		BIT(23)
>> +
>> +/* Register: FMC2_CSQCAR1 */
>> +#define FMC2_CSQCAR1_ADDC1(x)		(((x) & 0xff) << 0)
>> +#define FMC2_CSQCAR1_ADDC2(x)		(((x) & 0xff) << 8)
>> +#define FMC2_CSQCAR1_ADDC3(x)		(((x) & 0xff) << 16)
>> +#define FMC2_CSQCAR1_ADDC4(x)		(((x) & 0xff) << 24)
>> +
>> +/* Register: FMC2_CSQCAR2 */
>> +#define FMC2_CSQCAR2_ADDC5(x)		(((x) & 0xff) << 0)
>> +#define FMC2_CSQCAR2_NANDCEN(x)		(((x) & 0x3) << 10)
>> +#define FMC2_CSQCAR2_SAO(x)		(((x) & 0xffff) << 16)
>> +
>> +/* Register: FMC2_CSQIER */
>> +#define FMC2_CSQIER_TCIE		BIT(0)
>> +
>> +/* Register: FMC2_CSQICR */
>> +#define FMC2_CSQICR_CLEAR_IRQ		GENMASK(4, 0)
>> +
>> +/* Register: FMC2_CSQEMSR */
>> +#define FMC2_CSQEMSR_SEM		GENMASK(15, 0)
>> +
>> +/* Register: FMC2_BCHIER */
>> +#define FMC2_BCHIER_DERIE		BIT(1)
>> +#define FMC2_BCHIER_EPBRIE		BIT(4)
>> +
>> +/* Register: FMC2_BCHICR */
>> +#define FMC2_BCHICR_CLEAR_IRQ		GENMASK(4, 0)
>> +
>> +/* Register: FMC2_BCHDSR0 */
>> +#define FMC2_BCHDSR0_DUE		BIT(0)
>> +#define FMC2_BCHDSR0_DEF		BIT(1)
>> +#define FMC2_BCHDSR0_DEN_MASK		GENMASK(7, 4)
>> +#define FMC2_BCHDSR0_DEN_SHIFT		4
>> +
>> +/* Register: FMC2_BCHDSR1 */
>> +#define FMC2_BCHDSR1_EBP1_MASK		GENMASK(12, 0)
>> +#define FMC2_BCHDSR1_EBP2_MASK		GENMASK(28, 16)
>> +#define FMC2_BCHDSR1_EBP2_SHIFT		16
>> +
>> +/* Register: FMC2_BCHDSR2 */
>> +#define FMC2_BCHDSR2_EBP3_MASK		GENMASK(12, 0)
>> +#define FMC2_BCHDSR2_EBP4_MASK		GENMASK(28, 16)
>> +#define FMC2_BCHDSR2_EBP4_SHIFT		16
>> +
>> +/* Register: FMC2_BCHDSR3 */
>> +#define FMC2_BCHDSR3_EBP5_MASK		GENMASK(12, 0)
>> +#define FMC2_BCHDSR3_EBP6_MASK		GENMASK(28, 16)
>> +#define FMC2_BCHDSR3_EBP6_SHIFT		16
>> +
>> +/* Register: FMC2_BCHDSR4 */
>> +#define FMC2_BCHDSR4_EBP7_MASK		GENMASK(12, 0)
>> +#define FMC2_BCHDSR4_EBP8_MASK		GENMASK(28, 16)
>> +#define FMC2_BCHDSR4_EBP8_SHIFT		16
>> +
>> +enum stm32_fmc2_ecc {
>> +	FMC2_ECC_HAM = 1,
>> +	FMC2_ECC_BCH4 = 4,
>> +	FMC2_ECC_BCH8 = 8
>> +};
>> +
>> +struct stm32_fmc2_timings {
>> +	u8 tclr;
>> +	u8 tar;
>> +	u8 thiz;
>> +	u8 twait;
>> +	u8 thold_mem;
>> +	u8 tset_mem;
>> +	u8 thold_att;
>> +	u8 tset_att;
>> +};
>> +
>> +struct stm32_fmc2 {
>> +	struct nand_chip chip;
>> +	struct device *dev;
>> +	void __iomem *io_base;
>> +	void __iomem *data_base[FMC2_MAX_CE];
>> +	void __iomem *cmd_base[FMC2_MAX_CE];
>> +	void __iomem *addr_base[FMC2_MAX_CE];
>> +	phys_addr_t io_phys_addr;
>> +	phys_addr_t data_phys_addr[FMC2_MAX_CE];
>> +	struct clk *clk;
>> +
>> +	struct dma_chan *dma_tx_ch;
>> +	struct dma_chan *dma_rx_ch;
>> +	struct dma_chan *dma_ecc_ch;
>> +	struct sg_table dma_data_sg;
>> +	struct sg_table dma_ecc_sg;
>> +	u8 *ecc_buf;
>> +	int dma_ecc_len;
>> +
>> +	struct completion complete;
>> +	struct completion dma_data_complete;
>> +	struct completion dma_ecc_complete;
>> +
>> +	struct stm32_fmc2_timings timings;
>> +	u8 cs_assigned;
>> +	int cs_sel;
>> +	int ncs;
>> +	int cs_used[FMC2_MAX_CE];
>> +};
>> +
>> +/* Enable irq sources in case of the sequencer is used */
>> +static inline void stm32_fmc2_enable_seq_irq(struct stm32_fmc2 *fmc2)
>> +{
>> +	u32 csqier = readl_relaxed(fmc2->io_base + FMC2_CSQIER);
>> +
>> +	csqier |= FMC2_CSQIER_TCIE;
>> +
>> +	writel_relaxed(csqier, fmc2->io_base + FMC2_CSQIER);
>> +}
>> +
>> +/* Disable irq sources in case of the sequencer is used */
>> +static inline void stm32_fmc2_disable_seq_irq(struct stm32_fmc2 *fmc2)
>> +{
>> +	u32 csqier = readl_relaxed(fmc2->io_base + FMC2_CSQIER);
>> +
>> +	csqier &= ~FMC2_CSQIER_TCIE;
>> +
>> +	writel_relaxed(csqier, fmc2->io_base + FMC2_CSQIER);
>> +}
>> +
>> +/* Clear irq sources in case of the sequencer is used */
>> +static inline void stm32_fmc2_clear_seq_irq(struct stm32_fmc2 *fmc2)
>> +{
>> +	writel_relaxed(FMC2_CSQICR_CLEAR_IRQ, fmc2->io_base + FMC2_CSQICR);
>> +}
>> +
>> +/* Select function */
>> +static void stm32_fmc2_select_chip(struct nand_chip *chip, int chipnr)
>> +{
>> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
>> +	struct dma_slave_config dma_cfg;
>> +
>> +	if (chipnr < 0 || chipnr >= fmc2->ncs)
>> +		return;
>> +
>> +	if (fmc2->cs_used[chipnr] == fmc2->cs_sel)
>> +		return;
>> +
>> +	fmc2->cs_sel = fmc2->cs_used[chipnr];
>> +
>> +	if (fmc2->dma_tx_ch && fmc2->dma_rx_ch) {
>> +		memset(&dma_cfg, 0, sizeof(dma_cfg));
>> +		dma_cfg.src_addr = fmc2->data_phys_addr[fmc2->cs_sel];
>> +		dma_cfg.dst_addr = fmc2->data_phys_addr[fmc2->cs_sel];
>> +		dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
>> +		dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
>> +		dma_cfg.src_maxburst = 32;
>> +		dma_cfg.dst_maxburst = 32;
>> +
>> +		if (dmaengine_slave_config(fmc2->dma_tx_ch, &dma_cfg))
>> +			dev_warn(fmc2->dev, "tx DMA engine slave config failed\n");
>> +
>> +		if (dmaengine_slave_config(fmc2->dma_rx_ch, &dma_cfg))
>> +			dev_warn(fmc2->dev, "rx DMA engine slave config failed\n");
>> +	}
> 
> What if there are two NAND chips using different timing modes? You
> should probably reconfigure the timings registers, unless there are
> already a set of timing registers per CS?

Yes, it's true. In case of 2 NAND chips, timings and pcr registers 
should have been reconfigured. But, the driver only supports one NAND 
chip connected to 1 or 2 CS. There was no requirement on our side to 
support 2 different NAND chips. I do not have a board to test such 
configuration, so i do not want to deliver this support without being 
able to test it on my side. The driver will be improved later to support 
2 different NAND chips, in case this configuration is requested by 
customers.

>> +}
>> +
>> +/*
>> + * ECC HAMMING calculation
>> + * ECC is 3 bytes for 512 bytes of data (supports error correction up to
>> + * max of 1-bit)
>> + */
>> +static inline void stm32_fmc2_ham_set_ecc(const u32 ecc_sta, u8 *ecc)
>> +{
>> +	ecc[0] = ecc_sta;
>> +	ecc[1] = ecc_sta >> 8;
>> +	ecc[2] = ecc_sta >> 16;
>> +}
>> +
>> +static int stm32_fmc2_ham_correct(struct nand_chip *chip, uint8_t *dat,
>> +				  uint8_t *read_ecc, uint8_t *calc_ecc)
>> +{
>> +	u8 bit_position = 0, b0, b1, b2;
>> +	u32 byte_addr = 0, b;
>> +	u32 i, shifting = 1;
>> +
>> +	/* Indicate which bit and byte is faulty (if any) */
>> +	b0 = read_ecc[0] ^ calc_ecc[0];
>> +	b1 = read_ecc[1] ^ calc_ecc[1];
>> +	b2 = read_ecc[2] ^ calc_ecc[2];
>> +	b = b0 | (b1 << 8) | (b2 << 16);
>> +
>> +	/* No errors */
>> +	if (likely(!b))
>> +		return 0;
>> +
>> +	/* Calculate bit position */
>> +	for (i = 0; i < 3; i++) {
>> +		switch (b % 4) {
>> +		case 2:
>> +			bit_position += shifting;
>> +		case 1:
>> +			break;
>> +		default:
>> +			return -EBADMSG;
>> +		}
>> +		shifting <<= 1;
>> +		b >>= 2;
>> +	}
>> +
>> +	/* Calculate byte position */
>> +	shifting = 1;
>> +	for (i = 0; i < 9; i++) {
>> +		switch (b % 4) {
>> +		case 2:
>> +			byte_addr += shifting;
>> +		case 1:
>> +			break;
>> +		default:
>> +			return -EBADMSG;
>> +		}
>> +		shifting <<= 1;
>> +		b >>= 2;
>> +	}
>> +
>> +	/* Flip the bit */
>> +	dat[byte_addr] ^= (1 << bit_position);
>> +
>> +	return 1;
>> +}
>> +
>> +/* BCH algorithm correction */
>> +static int stm32_fmc2_bch_decode(int eccsize, u8 *dat, u32 *ecc_sta)
>> +{
>> +	u32 bchdsr0 = ecc_sta[0];
>> +	u32 bchdsr1 = ecc_sta[1];
>> +	u32 bchdsr2 = ecc_sta[2];
>> +	u32 bchdsr3 = ecc_sta[3];
>> +	u32 bchdsr4 = ecc_sta[4];
>> +	u16 pos[8];
>> +	int i, den;
>> +	unsigned int nb_errs = 0;
>> +
>> +	/* No errors found */
>> +	if (likely(!(bchdsr0 & FMC2_BCHDSR0_DEF)))
>> +		return 0;
>> +
>> +	/* Too many errors detected */
>> +	if (unlikely(bchdsr0 & FMC2_BCHDSR0_DUE))
>> +		return -EBADMSG;
>> +
>> +	pos[0] = bchdsr1 & FMC2_BCHDSR1_EBP1_MASK;
>> +	pos[1] = (bchdsr1 & FMC2_BCHDSR1_EBP2_MASK) >> FMC2_BCHDSR1_EBP2_SHIFT;
>> +	pos[2] = bchdsr2 & FMC2_BCHDSR2_EBP3_MASK;
>> +	pos[3] = (bchdsr2 & FMC2_BCHDSR2_EBP4_MASK) >> FMC2_BCHDSR2_EBP4_SHIFT;
>> +	pos[4] = bchdsr3 & FMC2_BCHDSR3_EBP5_MASK;
>> +	pos[5] = (bchdsr3 & FMC2_BCHDSR3_EBP6_MASK) >> FMC2_BCHDSR3_EBP6_SHIFT;
>> +	pos[6] = bchdsr4 & FMC2_BCHDSR4_EBP7_MASK;
>> +	pos[7] = (bchdsr4 & FMC2_BCHDSR4_EBP8_MASK) >> FMC2_BCHDSR4_EBP8_SHIFT;
>> +
>> +	den = (bchdsr0 & FMC2_BCHDSR0_DEN_MASK) >> FMC2_BCHDSR0_DEN_SHIFT;
>> +	for (i = 0; i < den; i++) {
>> +		if (pos[i] < eccsize * 8) {
>> +			change_bit(pos[i], (unsigned long *)dat);
>> +			nb_errs++;
>> +		}
>> +	}
>> +
>> +	return nb_errs;
>> +}
>> +
>> +/* Sequencer read/write configuration */
>> +static void stm32_fmc2_rw_page_init(struct stm32_fmc2 *fmc2, int page,
>> +				    int raw, bool write_data)
>> +{
>> +	struct nand_chip *chip = &fmc2->chip;
>> +	struct mtd_info *mtd = nand_to_mtd(chip);
>> +	u32 csqcfgr1, csqcfgr2, csqcfgr3;
>> +	u32 csqar1, csqar2;
>> +	u32 ecc_offset = mtd->writesize + FMC2_BBM_LEN;
>> +	u32 pcr = readl_relaxed(fmc2->io_base + FMC2_PCR);
>> +
>> +	if (write_data)
>> +		pcr |= FMC2_PCR_WEN;
>> +	else
>> +		pcr &= ~FMC2_PCR_WEN;
>> +	writel_relaxed(pcr, fmc2->io_base + FMC2_PCR);
>> +
>> +	/*
>> +	 * - Set Program Page/Page Read command
>> +	 * - Enable DMA request data
>> +	 * - Set timings
>> +	 */
>> +	csqcfgr1 = FMC2_CSQCFGR1_DMADEN | FMC2_CSQCFGR1_CMD1T;
>> +	if (write_data)
>> +		csqcfgr1 |= FMC2_CSQCFGR1_CMD1(NAND_CMD_SEQIN);
>> +	else
>> +		csqcfgr1 |= FMC2_CSQCFGR1_CMD1(NAND_CMD_READ0) |
>> +			    FMC2_CSQCFGR1_CMD2EN |
>> +			    FMC2_CSQCFGR1_CMD2(NAND_CMD_READSTART) |
>> +			    FMC2_CSQCFGR1_CMD2T;
>> +
>> +	/*
>> +	 * - Set Random Data Input/Random Data Read command
>> +	 * - Enable the sequencer to access the Spare data area
>> +	 * - Enable  DMA request status decoding for read
>> +	 * - Set timings
>> +	 */
>> +	if (write_data)
>> +		csqcfgr2 = FMC2_CSQCFGR2_RCMD1(NAND_CMD_RNDIN);
>> +	else
>> +		csqcfgr2 = FMC2_CSQCFGR2_RCMD1(NAND_CMD_RNDOUT) |
>> +			   FMC2_CSQCFGR2_RCMD2EN |
>> +			   FMC2_CSQCFGR2_RCMD2(NAND_CMD_RNDOUTSTART) |
>> +			   FMC2_CSQCFGR2_RCMD1T |
>> +			   FMC2_CSQCFGR2_RCMD2T;
>> +	if (!raw) {
>> +		csqcfgr2 |= write_data ? 0 : FMC2_CSQCFGR2_DMASEN;
>> +		csqcfgr2 |= FMC2_CSQCFGR2_SQSDTEN;
>> +	}
>> +
>> +	/*
>> +	 * - Set the number of sectors to be written
>> +	 * - Set timings
>> +	 */
>> +	csqcfgr3 = FMC2_CSQCFGR3_SNBR(chip->ecc.steps - 1);
>> +	if (write_data) {
>> +		csqcfgr3 |= FMC2_CSQCFGR3_RAC2T;
>> +		if (chip->chipsize > SZ_128M)
>> +			csqcfgr3 |= FMC2_CSQCFGR3_AC5T;
>> +		else
>> +			csqcfgr3 |= FMC2_CSQCFGR3_AC4T;
>> +	}
>> +
>> +	/*
>> +	 * Set the fourth first address cycles
>> +	 * Byte 1 and byte 2 => column, we start at 0x0
>> +	 * Byte 3 and byte 4 => page
>> +	 */
>> +	csqar1 = FMC2_CSQCAR1_ADDC3(page);
>> +	csqar1 |= FMC2_CSQCAR1_ADDC4(page >> 8);
>> +
>> +	/*
>> +	 * - Set chip enable number
>> +	 * - Set ecc byte offset in the spare area
>> +	 * - Calculate the number of address cycles to be issued
>> +	 * - Set byte 5 of address cycle if needed
>> +	 */
>> +	csqar2 = FMC2_CSQCAR2_NANDCEN(fmc2->cs_sel);
>> +	if (chip->options & NAND_BUSWIDTH_16)
>> +		csqar2 |= FMC2_CSQCAR2_SAO(ecc_offset >> 1);
>> +	else
>> +		csqar2 |= FMC2_CSQCAR2_SAO(ecc_offset);
>> +	if (chip->chipsize > SZ_128M) {
>> +		csqcfgr1 |= FMC2_CSQCFGR1_ACYNBR(5);
>> +		csqar2 |= FMC2_CSQCAR2_ADDC5(page >> 16);
>> +	} else {
>> +		csqcfgr1 |= FMC2_CSQCFGR1_ACYNBR(4);
>> +	}
>> +
>> +	writel_relaxed(csqcfgr1, fmc2->io_base + FMC2_CSQCFGR1);
>> +	writel_relaxed(csqcfgr2, fmc2->io_base + FMC2_CSQCFGR2);
>> +	writel_relaxed(csqcfgr3, fmc2->io_base + FMC2_CSQCFGR3);
>> +	writel_relaxed(csqar1, fmc2->io_base + FMC2_CSQAR1);
>> +	writel_relaxed(csqar2, fmc2->io_base + FMC2_CSQAR2);
>> +}
>> +
>> +static void stm32_fmc2_dma_callback(void *arg)
>> +{
>> +	complete((struct completion *)arg);
>> +}
>> +
>> +/* Read/write data from/to a page */
>> +static int stm32_fmc2_xfer(struct stm32_fmc2 *fmc2, const u8 *buf,
>> +			   int raw, bool write_data)
>> +{
>> +	struct nand_chip *chip = &fmc2->chip;
>> +	struct dma_async_tx_descriptor *desc_data, *desc_ecc;
>> +	struct scatterlist *sg;
>> +	struct dma_chan *dma_ch = fmc2->dma_rx_ch;
>> +	enum dma_data_direction dma_data_dir = DMA_FROM_DEVICE;
>> +	enum dma_transfer_direction dma_transfer_dir = DMA_DEV_TO_MEM;
>> +	u32 csqcr = readl_relaxed(fmc2->io_base + FMC2_CSQCR);
>> +	int eccsteps = chip->ecc.steps;
>> +	int eccsize = chip->ecc.size;
>> +	const u8 *p = buf;
>> +	int s, ret;
>> +
>> +	/* Configure DMA data */
>> +	if (write_data) {
>> +		dma_data_dir = DMA_TO_DEVICE;
>> +		dma_transfer_dir = DMA_MEM_TO_DEV;
>> +		dma_ch = fmc2->dma_tx_ch;
>> +	}
>> +
>> +	for_each_sg(fmc2->dma_data_sg.sgl, sg, eccsteps, s) {
>> +		sg_set_buf(sg, p, eccsize);
>> +		p += eccsize;
>> +	}
>> +
>> +	ret = dma_map_sg(fmc2->dev, fmc2->dma_data_sg.sgl,
>> +			 eccsteps, dma_data_dir);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	desc_data = dmaengine_prep_slave_sg(dma_ch, fmc2->dma_data_sg.sgl,
>> +					    eccsteps, dma_transfer_dir,
>> +					    DMA_PREP_INTERRUPT);
>> +	if (!desc_data) {
>> +		ret = -ENOMEM;
>> +		goto err_unmap_data;
>> +	}
>> +
>> +	reinit_completion(&fmc2->dma_data_complete);
>> +	reinit_completion(&fmc2->complete);
>> +	desc_data->callback = stm32_fmc2_dma_callback;
>> +	desc_data->callback_param = &fmc2->dma_data_complete;
>> +	ret = dma_submit_error(dmaengine_submit(desc_data));
>> +	if (ret)
>> +		goto err_unmap_data;
>> +
>> +	dma_async_issue_pending(dma_ch);
>> +
>> +	if (!write_data && !raw) {
>> +		/* Configure DMA ecc status */
>> +		p = fmc2->ecc_buf;
>> +		for_each_sg(fmc2->dma_ecc_sg.sgl, sg, eccsteps, s) {
>> +			sg_set_buf(sg, p, fmc2->dma_ecc_len);
>> +			p += fmc2->dma_ecc_len;
>> +		}
>> +
>> +		ret = dma_map_sg(fmc2->dev, fmc2->dma_ecc_sg.sgl,
>> +				 eccsteps, dma_data_dir);
>> +		if (ret < 0)
>> +			goto err_unmap_data;
>> +
>> +		desc_ecc = dmaengine_prep_slave_sg(fmc2->dma_ecc_ch,
>> +						   fmc2->dma_ecc_sg.sgl,
>> +						   eccsteps, dma_transfer_dir,
>> +						   DMA_PREP_INTERRUPT);
>> +		if (!desc_ecc) {
>> +			ret = -ENOMEM;
>> +			goto err_unmap_ecc;
>> +		}
>> +
>> +		reinit_completion(&fmc2->dma_ecc_complete);
>> +		desc_ecc->callback = stm32_fmc2_dma_callback;
>> +		desc_ecc->callback_param = &fmc2->dma_ecc_complete;
>> +		ret = dma_submit_error(dmaengine_submit(desc_ecc));
>> +		if (ret)
>> +			goto err_unmap_ecc;
>> +
>> +		dma_async_issue_pending(fmc2->dma_ecc_ch);
>> +	}
>> +
>> +	stm32_fmc2_clear_seq_irq(fmc2);
>> +	stm32_fmc2_enable_seq_irq(fmc2);
>> +
>> +	/* Start the transfer */
>> +	csqcr |= FMC2_CSQCR_CSQSTART;
>> +	writel_relaxed(csqcr, fmc2->io_base + FMC2_CSQCR);
>> +
>> +	/* Wait end of sequencer transfer */
>> +	if (!wait_for_completion_timeout(&fmc2->complete,
>> +					 msecs_to_jiffies(1000))) {
>> +		dev_err(fmc2->dev, "seq timeout\n");
>> +		stm32_fmc2_disable_seq_irq(fmc2);
>> +		dmaengine_terminate_all(dma_ch);
>> +		if (!write_data && !raw)
>> +			dmaengine_terminate_all(fmc2->dma_ecc_ch);
>> +		ret = -ETIMEDOUT;
>> +		goto err_unmap_ecc;
>> +	}
>> +
>> +	/* Wait DMA data transfer completion */
>> +	if (!wait_for_completion_timeout(&fmc2->dma_data_complete,
>> +					 msecs_to_jiffies(100))) {
>> +		dev_err(fmc2->dev, "data DMA timeout\n");
>> +		dmaengine_terminate_all(dma_ch);
>> +		ret = -ETIMEDOUT;
>> +	}
>> +
>> +	/* Wait DMA ecc transfer completion */
>> +	if (!write_data && !raw) {
>> +		if (!wait_for_completion_timeout(&fmc2->dma_ecc_complete,
>> +						 msecs_to_jiffies(100))) {
>> +			dev_err(fmc2->dev, "ecc DMA timeout\n");
>> +			dmaengine_terminate_all(fmc2->dma_ecc_ch);
>> +			ret = -ETIMEDOUT;
>> +		}
>> +	}
>> +
>> +err_unmap_ecc:
>> +	if (!write_data && !raw)
>> +		dma_unmap_sg(fmc2->dev, fmc2->dma_ecc_sg.sgl,
>> +			     eccsteps, dma_data_dir);
>> +
>> +err_unmap_data:
>> +	dma_unmap_sg(fmc2->dev, fmc2->dma_data_sg.sgl, eccsteps, dma_data_dir);
>> +
>> +	return ret;
>> +}
>> +
>> +static int stm32_fmc2_sequencer_write(struct nand_chip *chip,
>> +				      const u8 *buf, int oob_required,
>> +				      int page, int raw)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(chip);
>> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
>> +	int ret;
>> +
>> +	/* Configure the sequencer */
>> +	stm32_fmc2_rw_page_init(fmc2, page, raw, true);
>> +
>> +	/* Write the page */
>> +	ret = stm32_fmc2_xfer(fmc2, buf, raw, true);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Write oob */
>> +	if (oob_required) {
>> +		ret = nand_change_write_column_op(chip, mtd->writesize,
>> +						  chip->oob_poi, mtd->oobsize,
>> +						  false);
>> +		if (ret)
>> +			return ret;
>> +	}
>> +
>> +	return nand_prog_page_end_op(chip);
>> +}
>> +
>> +static int stm32_fmc2_sequencer_write_page(struct nand_chip *chip,
>> +					   const uint8_t *buf,
>> +					   int oob_required,
>> +					   int page)
>> +{
>> +	return stm32_fmc2_sequencer_write(chip, buf, oob_required, page, false);
>> +}
>> +
>> +static int stm32_fmc2_sequencer_write_page_raw(struct nand_chip *chip,
>> +					       const uint8_t *buf,
>> +					       int oob_required,
>> +					       int page)
>> +{
>> +	return stm32_fmc2_sequencer_write(chip, buf, oob_required, page, true);
>> +}
>> +
>> +/*
>> + * Get a status indicating which sectors have errors
>> + * Only available when the sequencer is used (BCH only)
>> + */
>> +static inline u16 stm32_fmc2_get_mapping_status(struct stm32_fmc2 *fmc2)
>> +{
>> +	u32 csqemsr = readl_relaxed(fmc2->io_base + FMC2_CSQEMSR);
>> +
>> +	return csqemsr & FMC2_CSQEMSR_SEM;
>> +}
>> +
>> +static int stm32_fmc2_sequencer_read_page(struct nand_chip *chip,
>> +					  uint8_t *buf,
>> +					  int oob_required,
>> +					  int page)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(chip);
>> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
>> +	int i, s, ret, eccsize = chip->ecc.size;
>> +	int eccbytes = chip->ecc.bytes;
>> +	int eccsteps = chip->ecc.steps;
>> +	int eccstrength = chip->ecc.strength;
>> +	u8 *p = buf;
>> +	u8 *ecc_calc = chip->ecc.calc_buf;
>> +	u8 *ecc_code = chip->ecc.code_buf;
>> +	u32 *ecc_sta = (u32 *)fmc2->ecc_buf;
>> +	u16 sta_map;
>> +	unsigned int max_bitflips = 0;
>> +
>> +	/* Configure the sequencer */
>> +	stm32_fmc2_rw_page_init(fmc2, page, 0, false);
>> +
>> +	/* Read the page */
>> +	ret = stm32_fmc2_xfer(fmc2, buf, 0, false);
>> +	if (ret)
>> +		return ret;
>> +
>> +	sta_map = stm32_fmc2_get_mapping_status(fmc2);
>> +
>> +	/* Check if errors happen */
>> +	if (likely(!sta_map)) {
>> +		if (oob_required)
>> +			return nand_change_read_column_op(chip, mtd->writesize,
>> +							  chip->oob_poi,
>> +							  mtd->oobsize, false);
>> +
>> +		return 0;
>> +	}
>> +
>> +	/* Read oob */
>> +	ret = nand_change_read_column_op(chip, mtd->writesize,
>> +					 chip->oob_poi, mtd->oobsize, false);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
>> +					 chip->ecc.total);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Correct data */
>> +	for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) {
>> +		int stat = 0;
>> +
>> +		if (eccstrength == FMC2_ECC_HAM) {
>> +			/* Ecc_sta = FMC2_HECCR */
>> +			if (sta_map & BIT(s)) {
>> +				stm32_fmc2_ham_set_ecc(*ecc_sta, &ecc_calc[i]);
>> +				stat = chip->ecc.correct(chip, p, &ecc_code[i],
>> +							 &ecc_calc[i]);
>> +			}
>> +			ecc_sta++;
>> +		} else {
>> +			/*
>> +			 * Ecc_sta[0] = FMC2_BCHDSR0
>> +			 * Ecc_sta[1] = FMC2_BCHDSR1
>> +			 * Ecc_sta[2] = FMC2_BCHDSR2
>> +			 * Ecc_sta[3] = FMC2_BCHDSR3
>> +			 * Ecc_sta[4] = FMC2_BCHDSR4
>> +			 */
>> +			if (sta_map & BIT(s))
>> +				stat = stm32_fmc2_bch_decode(chip->ecc.size,
>> +							     p, ecc_sta);
>> +			ecc_sta += 5;
>> +		}
>> +
>> +		if (stat == -EBADMSG)
>> +			/* Check for empty pages with bitflips */
>> +			stat = nand_check_erased_ecc_chunk(p, eccsize,
>> +							   &ecc_code[i],
>> +							   eccbytes,
>> +							   NULL, 0,
>> +							   eccstrength);
>> +
>> +		if (stat < 0) {
>> +			mtd->ecc_stats.failed++;
>> +		} else {
>> +			mtd->ecc_stats.corrected += stat;
>> +			max_bitflips = max_t(unsigned int, max_bitflips, stat);
>> +		}
>> +	}
>> +
>> +	return max_bitflips;
>> +}
>> +
>> +static int stm32_fmc2_sequencer_read_page_raw(struct nand_chip *chip,
>> +					      uint8_t *buf,
>> +					      int oob_required,
>> +					      int page)
>> +{
>> +	struct mtd_info *mtd = nand_to_mtd(chip);
>> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
>> +	int ret;
>> +
>> +	/* Configure the sequencer */
>> +	stm32_fmc2_rw_page_init(fmc2, page, 1, false);
>> +
>> +	/* Read the page */
>> +	ret = stm32_fmc2_xfer(fmc2, buf, 1, false);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* Read oob */
>> +	if (oob_required)
>> +		return nand_change_read_column_op(chip, mtd->writesize,
>> +						  chip->oob_poi, mtd->oobsize,
>> +						  false);
>> +
>> +	return 0;
>> +}
>> +
>> +static irqreturn_t stm32_fmc2_irq(int irq, void *dev_id)
>> +{
>> +	struct stm32_fmc2 *fmc2 = (struct stm32_fmc2 *)dev_id;
>> +
>> +	stm32_fmc2_disable_seq_irq(fmc2);
>> +
>> +	complete(&fmc2->complete);
>> +
>> +	return IRQ_HANDLED;
> 
> You don't have any bit to check/ack/read?

No, nothing to check.

> 
>> +}
>> +
>> +void stm32_fmc2_read_data(struct nand_chip *chip, void *buf,
>> +			  unsigned int len, bool force_8bit)
>> +{
>> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
>> +	void __iomem *io_addr_r = fmc2->data_base[fmc2->cs_sel];
>> +	u8 *p = buf;
>> +	unsigned int i;
>> +
>> +	if (force_8bit)
>> +		goto read_8bit;
>> +
>> +	if (IS_ALIGNED((u32)buf, sizeof(u32)) && IS_ALIGNED(len, sizeof(u32))) {
> 
> If you selected BOUNCE_BUFFER in the options, buf is supposedly
> aligned, or am I missing something?

2 FMC2 internal modes can be used:
  - Sequencer mode (Patch 2/3): dmas are used and NAND_USE_BOUNCE_BUFFER 
option is selected.
  - Manual mode (Patch 3/3): no dma channel is used and 
NAND_USE_BOUNCE_BUFFER is not selected.
Should i select NAND_USE_BOUNCE_BUFFER for sequencer and manual mode, 
and remove IS_ALIGNED test on buf?

> 
>> +		u32 *p = buf;
>> +
>> +		len >>= 2;
> 
> Please don't do such optimization by hand. Compilers are doing it for
> it, so let's keep the code readable. Just do a regular "/=
> sizeof(unsigned long)".

Ok, i will replace this line with: len /= sizeof(u32)

> 
>> +		for (i = 0; i < len; i++)
>> +			p[i] = readl_relaxed(io_addr_r);
>> +		return;
>> +	}
>> +
>> +	if (chip->options & NAND_BUSWIDTH_16) {
>> +		u16 *p = buf;
>> +
>> +		len >>= 1;
> 
> Ditto

Ok, i will replace this line with: len /= sizeof(u16)
Same modifications will be done in stm32_fmc2_write_data function.

> 
>> +		for (i = 0; i < len; i++)
>> +			p[i] = readw_relaxed(io_addr_r);
>> +		return;
>> +	}
>> +
>> +read_8bit:
>> +	for (i = 0; i < len; i++)
>> +		p[i] = readb_relaxed(io_addr_r);
>> +}
>> +
>> +void stm32_fmc2_write_data(struct nand_chip *chip, const void *buf,
>> +			   unsigned int len, bool force_8bit)
>> +{
>> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
>> +	void __iomem *io_addr_w = fmc2->data_base[fmc2->cs_sel];
>> +	const u8 *p = buf;
>> +	unsigned int i;
>> +
>> +	if (force_8bit)
>> +		goto write_8bit;
>> +
>> +	if (IS_ALIGNED((u32)buf, sizeof(u32)) && IS_ALIGNED(len, sizeof(u32))) {
>> +		const u32 *p = buf;
>> +
>> +		len >>= 2;
>> +		for (i = 0; i < len; i++)
>> +			writel_relaxed(p[i], io_addr_w);
>> +		return;
>> +	}
>> +
>> +	if (chip->options & NAND_BUSWIDTH_16) {
>> +		const u16 *p = buf;
>> +
>> +		len >>= 1;
>> +		for (i = 0; i < len; i++)
>> +			writew_relaxed(p[i], io_addr_w);
>> +		return;
>> +	}
>> +
>> +write_8bit:
>> +	for (i = 0; i < len; i++)
>> +		writeb_relaxed(p[i], io_addr_w);
>> +}
>> +
>> +static int stm32_fmc2_exec_op(struct nand_chip *chip,
>> +			      const struct nand_operation *op,
>> +			      bool check_only)
> 
> You should probably return 0 if check_only == true, otherwise you could
> do twice each operation.

Ok.

> 
>> +{
>> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
>> +	const struct nand_op_instr *instr = NULL;
>> +	unsigned int op_id, i;
>> +	int ret = 0;
>> +
>> +	for (op_id = 0; op_id < op->ninstrs; op_id++) {
>> +		instr = &op->instrs[op_id];
>> +
>> +		switch (instr->type) {
>> +		case NAND_OP_CMD_INSTR:
>> +			writeb_relaxed(instr->ctx.cmd.opcode,
>> +				       fmc2->cmd_base[fmc2->cs_sel]);
>> +			break;
>> +
>> +		case NAND_OP_ADDR_INSTR:
>> +			for (i = 0; i < instr->ctx.addr.naddrs; i++)
>> +				writeb_relaxed(instr->ctx.addr.addrs[i],
>> +					       fmc2->addr_base[fmc2->cs_sel]);
>> +			break;
>> +
>> +		case NAND_OP_DATA_IN_INSTR:
>> +			stm32_fmc2_read_data(chip, instr->ctx.data.buf.in,
>> +					     instr->ctx.data.len,
>> +					     instr->ctx.data.force_8bit);
>> +			break;
>> +
>> +		case NAND_OP_DATA_OUT_INSTR:
>> +			stm32_fmc2_write_data(chip, instr->ctx.data.buf.out,
>> +					      instr->ctx.data.len,
>> +					      instr->ctx.data.force_8bit);
>> +			break;
>> +
>> +		case NAND_OP_WAITRDY_INSTR:
>> +			ret = nand_soft_waitrdy(chip,
>> +						instr->ctx.waitrdy.timeout_ms);
>> +			break;
>> +		}
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +/* Timings configuration */
>> +static void stm32_fmc2_timings_init(struct stm32_fmc2 *fmc2)
>> +{
>> +	struct stm32_fmc2_timings *timings = &fmc2->timings;
>> +	u32 pcr = readl_relaxed(fmc2->io_base + FMC2_PCR);
>> +	u32 pmem, patt;
>> +
>> +	/* Set tclr/tar timings */
>> +	pcr &= ~FMC2_PCR_TCLR_MASK;
>> +	pcr |= FMC2_PCR_TCLR(timings->tclr);
>> +	pcr &= ~FMC2_PCR_TAR_MASK;
>> +	pcr |= FMC2_PCR_TAR(timings->tar);
>> +
>> +	/* Set tset/twait/thold/thiz timings in common bank */
>> +	pmem = FMC2_PMEM_MEMSET(timings->tset_mem);
>> +	pmem |= FMC2_PMEM_MEMWAIT(timings->twait);
>> +	pmem |= FMC2_PMEM_MEMHOLD(timings->thold_mem);
>> +	pmem |= FMC2_PMEM_MEMHIZ(timings->thiz);
>> +
>> +	/* Set tset/twait/thold/thiz timings in attribut bank */
>> +	patt = FMC2_PATT_ATTSET(timings->tset_att);
>> +	patt |= FMC2_PATT_ATTWAIT(timings->twait);
>> +	patt |= FMC2_PATT_ATTHOLD(timings->thold_att);
>> +	patt |= FMC2_PATT_ATTHIZ(timings->thiz);
>> +
>> +	writel_relaxed(pcr, fmc2->io_base + FMC2_PCR);
>> +	writel_relaxed(pmem, fmc2->io_base + FMC2_PMEM);
>> +	writel_relaxed(patt, fmc2->io_base + FMC2_PATT);
>> +}
>> +
>> +/* Controller initialization */
>> +static void stm32_fmc2_init(struct stm32_fmc2 *fmc2)
>> +{
>> +	u32 pcr = readl_relaxed(fmc2->io_base + FMC2_PCR);
>> +	u32 bcr1 = readl_relaxed(fmc2->io_base + FMC2_BCR1);
>> +
>> +	/* Enable wait feature and nand flash memory bank */
>> +	pcr |= FMC2_PCR_PWAITEN;
>> +	pcr |= FMC2_PCR_PBKEN;
>> +
>> +	/* Set buswidth to 8 bits mode for identification */
>> +	pcr &= ~FMC2_PCR_PWID_MASK;
>> +
>> +	/* Ecc logic is disabled */
>> +	pcr &= ~FMC2_PCR_ECCEN;
>> +
>> +	/* Default mode */
>> +	pcr &= ~FMC2_PCR_ECCALG;
>> +	pcr &= ~FMC2_PCR_BCHECC;
>> +	pcr &= ~FMC2_PCR_WEN;
>> +
>> +	/* Set default ecc sector size */
>> +	pcr &= ~FMC2_PCR_ECCSS_MASK;
>> +	pcr |= FMC2_PCR_ECCSS(FMC2_PCR_ECCSS_2048);
>> +
>> +	/* Set default tclr/tar timings */
>> +	pcr &= ~FMC2_PCR_TCLR_MASK;
>> +	pcr |= FMC2_PCR_TCLR(FMC2_PCR_TCLR_DEFAULT);
>> +	pcr &= ~FMC2_PCR_TAR_MASK;
>> +	pcr |= FMC2_PCR_TAR(FMC2_PCR_TAR_DEFAULT);
>> +
>> +	/* Enable FMC2 controller */
>> +	bcr1 |= FMC2_BCR1_FMC2EN;
>> +
>> +	writel_relaxed(bcr1, fmc2->io_base + FMC2_BCR1);
>> +	writel_relaxed(pcr, fmc2->io_base + FMC2_PCR);
>> +	writel_relaxed(FMC2_PMEM_DEFAULT, fmc2->io_base + FMC2_PMEM);
>> +	writel_relaxed(FMC2_PATT_DEFAULT, fmc2->io_base + FMC2_PATT);
>> +}
>> +
>> +/* Controller configuration */
>> +static void stm32_fmc2_setup(struct stm32_fmc2 *fmc2)
>> +{
>> +	struct nand_chip *chip = &fmc2->chip;
>> +	u32 pcr = readl_relaxed(fmc2->io_base + FMC2_PCR);
>> +
>> +	/* Configure in HAMMING by default */
> 
> In other comments you use "Hamming", not "HAMMING".
> 
> And this comment is weird, you don't configure for Hamming below. Maybe
> you meant that the controller is by default configured to work with
> Hamming error detection?

Yes, it's true. Hamming error detection is set by default. Below 
implementation configures the controller in BCH4 or BCH8. I will modify 
the comment.

> 
>> +	if (chip->ecc.strength == FMC2_ECC_BCH8) {
>> +		pcr |= FMC2_PCR_ECCALG;
>> +		pcr |= FMC2_PCR_BCHECC;
>> +	} else if (chip->ecc.strength == FMC2_ECC_BCH4) {
>> +		pcr |= FMC2_PCR_ECCALG;
>> +	}
>> +
>> +	/* Set buswidth */
>> +	if (chip->options & NAND_BUSWIDTH_16)
>> +		pcr |= FMC2_PCR_PWID(FMC2_PCR_PWID_BUSWIDTH_16);
>> +
>> +	/* Set ecc sector size */
>> +	pcr &= ~FMC2_PCR_ECCSS_MASK;
>> +	pcr |= FMC2_PCR_ECCSS(FMC2_PCR_ECCSS_512);
>> +
>> +	writel_relaxed(pcr, fmc2->io_base + FMC2_PCR);
>> +}
>> +
>> +/* Controller timings */
>> +static void stm32_fmc2_calc_timings(struct stm32_fmc2 *fmc2,
>> +				    const struct nand_sdr_timings *sdrt,
>> +				    struct stm32_fmc2_timings *tims)
>> +{
>> +	unsigned long hclk = clk_get_rate(fmc2->clk);
>> +	unsigned long hclkp = NSEC_PER_SEC / (hclk / 1000);
>> +	int tar, tclr, thiz, twait, tset_mem, tset_att, thold_mem, thold_att;
>> +
>> +	tar = hclkp;
>> +	if (tar < sdrt->tAR_min)
>> +		tar = sdrt->tAR_min;
>> +	tims->tar = DIV_ROUND_UP(tar, hclkp) - 1;
>> +	if (tims->tar > FMC2_PCR_TIMING_MASK)
>> +		tims->tar = FMC2_PCR_TIMING_MASK;
>> +
>> +	tclr = hclkp;
>> +	if (tclr < sdrt->tCLR_min)
>> +		tclr = sdrt->tCLR_min;
>> +	tims->tclr = DIV_ROUND_UP(tclr, hclkp) - 1;
>> +	if (tims->tclr > FMC2_PCR_TIMING_MASK)
>> +		tims->tclr = FMC2_PCR_TIMING_MASK;
>> +
>> +	tims->thiz = FMC2_THIZ;
>> +	thiz = (tims->thiz + 1) * hclkp;
>> +
>> +	/*
>> +	 * tWAIT > tRP
>> +	 * tWAIT > tWP
>> +	 * tWAIT > tREA + tIO
>> +	 */
>> +	twait = hclkp;
>> +	if (twait < sdrt->tRP_min)
>> +		twait = sdrt->tRP_min;
>> +	if (twait < sdrt->tWP_min)
>> +		twait = sdrt->tWP_min;
>> +	if (twait < sdrt->tREA_max + FMC2_TIO)
>> +		twait = sdrt->tREA_max + FMC2_TIO;
>> +	tims->twait = DIV_ROUND_UP(twait, hclkp);
>> +	if (tims->twait == 0)
>> +		tims->twait = 1;
>> +	else if (tims->twait > FMC2_PMEM_PATT_TIMING_MASK)
>> +		tims->twait = FMC2_PMEM_PATT_TIMING_MASK;
>> +
>> +	/*
>> +	 * tSETUP_MEM > tCS - tWAIT
>> +	 * tSETUP_MEM > tALS - tWAIT
>> +	 * tSETUP_MEM > tDS - (tWAIT - tHIZ)
>> +	 */
>> +	tset_mem = hclkp;
>> +	if (sdrt->tCS_min > twait && (tset_mem < sdrt->tCS_min - twait))
>> +		tset_mem = sdrt->tCS_min - twait;
>> +	if (sdrt->tALS_min > twait && (tset_mem < sdrt->tALS_min - twait))
>> +		tset_mem = sdrt->tALS_min - twait;
>> +	if (twait > thiz && (sdrt->tDS_min > twait - thiz) &&
>> +	    (tset_mem < sdrt->tDS_min - (twait - thiz)))
>> +		tset_mem = sdrt->tDS_min - (twait - thiz);
>> +	tims->tset_mem = DIV_ROUND_UP(tset_mem, hclkp);
>> +	if (tims->tset_mem == 0)
>> +		tims->tset_mem = 1;
>> +	else if (tims->tset_mem > FMC2_PMEM_PATT_TIMING_MASK)
>> +		tims->tset_mem = FMC2_PMEM_PATT_TIMING_MASK;
>> +
>> +	/*
>> +	 * tHOLD_MEM > tCH
>> +	 * tHOLD_MEM > tREH - tSETUP_MEM
>> +	 * tHOLD_MEM > max(tRC, tWC) - (tSETUP_MEM + tWAIT)
>> +	 */
>> +	thold_mem = hclkp;
>> +	if (thold_mem < sdrt->tCH_min)
>> +		thold_mem = sdrt->tCH_min;
>> +	if (sdrt->tREH_min > tset_mem &&
>> +	    (thold_mem < sdrt->tREH_min - tset_mem))
>> +		thold_mem = sdrt->tREH_min - tset_mem;
>> +	if ((sdrt->tRC_min > tset_mem + twait) &&
>> +	    (thold_mem < sdrt->tRC_min - (tset_mem + twait)))
>> +		thold_mem = sdrt->tRC_min - (tset_mem + twait);
>> +	if ((sdrt->tWC_min > tset_mem + twait) &&
>> +	    (thold_mem < sdrt->tWC_min - (tset_mem + twait)))
>> +		thold_mem = sdrt->tWC_min - (tset_mem + twait);
>> +	tims->thold_mem = DIV_ROUND_UP(thold_mem, hclkp);
>> +	if (tims->thold_mem == 0)
>> +		tims->thold_mem = 1;
>> +	else if (tims->thold_mem > FMC2_PMEM_PATT_TIMING_MASK)
>> +		tims->thold_mem = FMC2_PMEM_PATT_TIMING_MASK;
>> +
>> +	/*
>> +	 * tSETUP_ATT > tCS - tWAIT
>> +	 * tSETUP_ATT > tCLS - tWAIT
>> +	 * tSETUP_ATT > tALS - tWAIT
>> +	 * tSETUP_ATT > tRHW - tHOLD_MEM
>> +	 * tSETUP_ATT > tDS - (tWAIT - tHIZ)
>> +	 */
>> +	tset_att = hclkp;
>> +	if (sdrt->tCS_min > twait && (tset_att < sdrt->tCS_min - twait))
>> +		tset_att = sdrt->tCS_min - twait;
>> +	if (sdrt->tCLS_min > twait && (tset_att < sdrt->tCLS_min - twait))
>> +		tset_att = sdrt->tCLS_min - twait;
>> +	if (sdrt->tALS_min > twait && (tset_att < sdrt->tALS_min - twait))
>> +		tset_att = sdrt->tALS_min - twait;
>> +	if (sdrt->tRHW_min > thold_mem &&
>> +	    (tset_att < sdrt->tRHW_min - thold_mem))
>> +		tset_att = sdrt->tRHW_min - thold_mem;
>> +	if (twait > thiz && (sdrt->tDS_min > twait - thiz) &&
>> +	    (tset_att < sdrt->tDS_min - (twait - thiz)))
>> +		tset_att = sdrt->tDS_min - (twait - thiz);
>> +	tims->tset_att = DIV_ROUND_UP(tset_att, hclkp);
>> +	if (tims->tset_att == 0)
>> +		tims->tset_att = 1;
>> +	else if (tims->tset_att > FMC2_PMEM_PATT_TIMING_MASK)
>> +		tims->tset_att = FMC2_PMEM_PATT_TIMING_MASK;
>> +
>> +	/*
>> +	 * tHOLD_ATT > tALH
>> +	 * tHOLD_ATT > tCH
>> +	 * tHOLD_ATT > tCLH
>> +	 * tHOLD_ATT > tCOH
>> +	 * tHOLD_ATT > tDH
>> +	 * tHOLD_ATT > tWB + tIO + tSYNC - tSETUP_MEM
>> +	 * tHOLD_ATT > tADL - tSETUP_MEM
>> +	 * tHOLD_ATT > tWH - tSETUP_MEM
>> +	 * tHOLD_ATT > tWHR - tSETUP_MEM
>> +	 * tHOLD_ATT > tRC - (tSETUP_ATT + tWAIT)
>> +	 * tHOLD_ATT > tWC - (tSETUP_ATT + tWAIT)
>> +	 */
>> +	thold_att = hclkp;
>> +	if (thold_att < sdrt->tALH_min)
>> +		thold_att = sdrt->tALH_min;
>> +	if (thold_att < sdrt->tCH_min)
>> +		thold_att = sdrt->tCH_min;
>> +	if (thold_att < sdrt->tCLH_min)
>> +		thold_att = sdrt->tCLH_min;
>> +	if (thold_att < sdrt->tCOH_min)
>> +		thold_att = sdrt->tCOH_min;
>> +	if (thold_att < sdrt->tDH_min)
>> +		thold_att = sdrt->tDH_min;
>> +	if ((sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC > tset_mem) &&
>> +	    (thold_att < sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem))
>> +		thold_att = sdrt->tWB_max + FMC2_TIO + FMC2_TSYNC - tset_mem;
>> +	if (sdrt->tADL_min > tset_mem &&
>> +	    (thold_att < sdrt->tADL_min - tset_mem))
>> +		thold_att = sdrt->tADL_min - tset_mem;
>> +	if (sdrt->tWH_min > tset_mem &&
>> +	    (thold_att < sdrt->tWH_min - tset_mem))
>> +		thold_att = sdrt->tWH_min - tset_mem;
>> +	if (sdrt->tWHR_min > tset_mem &&
>> +	    (thold_att < sdrt->tWHR_min - tset_mem))
>> +		thold_att = sdrt->tWHR_min - tset_mem;
>> +	if ((sdrt->tRC_min > tset_att + twait) &&
>> +	    (thold_att < sdrt->tRC_min - (tset_att + twait)))
>> +		thold_att = sdrt->tRC_min - (tset_att + twait);
>> +	if ((sdrt->tWC_min > tset_att + twait) &&
>> +	    (thold_att < sdrt->tWC_min - (tset_att + twait)))
>> +		thold_att = sdrt->tWC_min - (tset_att + twait);
>> +	tims->thold_att = DIV_ROUND_UP(thold_att, hclkp);
>> +	if (tims->thold_att == 0)
>> +		tims->thold_att = 1;
>> +	else if (tims->thold_att > FMC2_PMEM_PATT_TIMING_MASK)
>> +		tims->thold_att = FMC2_PMEM_PATT_TIMING_MASK;
>> +}
>> +
>> +static int stm32_fmc2_setup_interface(struct nand_chip *chip, int chipnr,
>> +				      const struct nand_data_interface *conf)
>> +{
>> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
>> +	struct stm32_fmc2_timings tims;
>> +	const struct nand_sdr_timings *sdrt;
>> +
>> +	sdrt = nand_get_sdr_timings(conf);
>> +	if (IS_ERR(sdrt))
>> +		return PTR_ERR(sdrt);
>> +
>> +	stm32_fmc2_calc_timings(fmc2, sdrt, &tims);
> 
> Why do you need 'tims' if the only use is to memcpy it in
> fmc2->timings? I suppose you can get rid of it.

Yes, it is not needed to use a local variable.

> 
>> +
>> +	if (chipnr == NAND_DATA_IFACE_CHECK_ONLY)
>> +		return 0;
> 
> If the controller may support all the timing modes, then you can
> certainly return on CHECK_ONLY before *_calc_timings().

Yes, i will move this check before stm32_fmc2_calc_timings().

> 
>> +
>> +	/* Save and apply timings */
>> +	memcpy(&fmc2->timings, &tims, sizeof(tims));
>> +	stm32_fmc2_timings_init(fmc2);
>> +
>> +	return 0;
>> +}
>> +
>> +/* DMA configuration */
>> +static int stm32_fmc2_dma_setup(struct stm32_fmc2 *fmc2, u8 nbsect)
>> +{
>> +	struct nand_chip *chip = &fmc2->chip;
>> +	struct dma_slave_config dma_cfg;
>> +	int ret;
>> +
>> +	memset(&dma_cfg, 0, sizeof(dma_cfg));
>> +	dma_cfg.src_addr = fmc2->data_phys_addr[fmc2->cs_sel];
>> +	dma_cfg.dst_addr = fmc2->data_phys_addr[fmc2->cs_sel];
>> +	dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
>> +	dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
>> +	dma_cfg.src_maxburst = 32;
>> +	dma_cfg.dst_maxburst = 32;
>> +
>> +	fmc2->dma_tx_ch = dma_request_slave_channel(fmc2->dev, "tx");
>> +	if (fmc2->dma_tx_ch) {
>> +		ret = dmaengine_slave_config(fmc2->dma_tx_ch, &dma_cfg);
>> +		if (ret) {
>> +			dev_err(fmc2->dev, "data tx DMA engine slave config failed\n");
>> +			return ret;
>> +		}
>> +	}
>> +
>> +	fmc2->dma_rx_ch = dma_request_slave_channel(fmc2->dev, "rx");
>> +	if (fmc2->dma_rx_ch) {
>> +		ret = dmaengine_slave_config(fmc2->dma_rx_ch, &dma_cfg);
>> +		if (ret) {
>> +			dev_err(fmc2->dev, "data rx DMA engine slave config failed\n");
>> +			return ret;
>> +		}
>> +
>> +		fmc2->dma_ecc_ch = dma_request_slave_channel(fmc2->dev, "ecc");
>> +		if (fmc2->dma_ecc_ch) {
>> +			/*
>> +			 * HAMMING: we read HECCR register
>> +			 * BCH4/BCH8: we read BCHDSRSx registers
>> +			 */
>> +			memset(&dma_cfg, 0, sizeof(dma_cfg));
>> +			dma_cfg.src_addr = fmc2->io_phys_addr;
>> +			dma_cfg.src_addr += chip->ecc.strength == FMC2_ECC_HAM ?
>> +					    FMC2_HECCR : FMC2_BCHDSR0;
>> +			dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
>> +
>> +			ret = dmaengine_slave_config(fmc2->dma_ecc_ch,
>> +						     &dma_cfg);
>> +			if (ret) {
>> +				dev_err(fmc2->dev, "ecc DMA engine slave config failed\n");
>> +				return ret;
>> +			}
>> +
>> +			ret = sg_alloc_table(&fmc2->dma_ecc_sg,
>> +					     nbsect, GFP_KERNEL);
>> +			if (ret)
>> +				return ret;
>> +
>> +			/* Calculate ecc length needed for one sector */
>> +			fmc2->dma_ecc_len = chip->ecc.strength == FMC2_ECC_HAM ?
>> +					    FMC2_HECCR_LEN : FMC2_BCHDSRS_LEN;
>> +
>> +			/* Allocate a buffer to store ecc status registers */
>> +			fmc2->ecc_buf = devm_kzalloc(fmc2->dev,
>> +						     fmc2->dma_ecc_len * nbsect,
>> +						     GFP_KERNEL);
>> +			if (!fmc2->ecc_buf)
>> +				return -ENOMEM;
>> +		} else {
>> +			dev_err(fmc2->dev, "ecc DMA not defined in the device tree\n");
>> +			return -ENOENT;
>> +		}
>> +	}
>> +
>> +	if (fmc2->dma_tx_ch && fmc2->dma_rx_ch) {
>> +		ret = sg_alloc_table(&fmc2->dma_data_sg, nbsect, GFP_KERNEL);
>> +		if (ret)
>> +			return ret;
>> +
>> +		init_completion(&fmc2->dma_data_complete);
>> +		init_completion(&fmc2->dma_ecc_complete);
>> +	} else {
>> +		dev_err(fmc2->dev, "rx/tx DMA not defined in the device tree\n");
>> +		return -ENOENT;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/* NAND callbacks setup */
>> +static void stm32_fmc2_nand_callbacks_setup(struct stm32_fmc2 *fmc2)
>> +{
>> +	struct nand_chip *chip = &fmc2->chip;
>> +
>> +	/* Specific callbacks to read/write a page */
>> +	chip->ecc.correct = stm32_fmc2_ham_correct;
>> +	chip->ecc.write_page = stm32_fmc2_sequencer_write_page;
>> +	chip->ecc.read_page = stm32_fmc2_sequencer_read_page;
>> +	chip->ecc.write_page_raw = stm32_fmc2_sequencer_write_page_raw;
>> +	chip->ecc.read_page_raw = stm32_fmc2_sequencer_read_page_raw;
>> +	chip->options |= NAND_USE_BOUNCE_BUFFER;
>> +
>> +	/* Specific configurations depending on the algo used */
>> +	if (chip->ecc.strength == FMC2_ECC_HAM)
>> +		chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 4 : 3;
>> +	else if (chip->ecc.strength == FMC2_ECC_BCH8)
>> +		chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 14 : 13;
>> +	else
>> +		chip->ecc.bytes = chip->options & NAND_BUSWIDTH_16 ? 8 : 7;
>> +}
>> +
>> +/* FMC2 layout */
>> +static int stm32_fmc2_nand_ooblayout_ecc(struct mtd_info *mtd, int section,
>> +					 struct mtd_oob_region *oobregion)
>> +{
>> +	struct nand_chip *chip = mtd_to_nand(mtd);
>> +	struct nand_ecc_ctrl *ecc = &chip->ecc;
>> +
>> +	if (section)
>> +		return -ERANGE;
>> +
>> +	oobregion->length = ecc->total;
>> +	oobregion->offset = FMC2_BBM_LEN;
>> +
>> +	return 0;
>> +}
>> +
>> +static int stm32_fmc2_nand_ooblayout_free(struct mtd_info *mtd, int section,
>> +					  struct mtd_oob_region *oobregion)
>> +{
>> +	struct nand_chip *chip = mtd_to_nand(mtd);
>> +	struct nand_ecc_ctrl *ecc = &chip->ecc;
>> +
>> +	if (section)
>> +		return -ERANGE;
>> +
>> +	oobregion->length = mtd->oobsize - ecc->total - FMC2_BBM_LEN;
>> +	oobregion->offset = ecc->total + FMC2_BBM_LEN;
>> +
>> +	return 0;
>> +}
>> +
>> +const struct mtd_ooblayout_ops stm32_fmc2_nand_ooblayout_ops = {
>> +	.ecc = stm32_fmc2_nand_ooblayout_ecc,
>> +	.free = stm32_fmc2_nand_ooblayout_free,
>> +};
>> +
>> +/* FMC2 caps */
>> +static int stm32_fmc2_calc_ecc_bytes(int step_size, int strength)
>> +{
>> +	/* Hamming */
>> +	if (strength == FMC2_ECC_HAM)
>> +		return 4;
>> +
>> +	/* BCH8 */
>> +	if (strength == FMC2_ECC_BCH8)
>> +		return 14;
>> +
>> +	/* BCH4 */
>> +	return 8;
>> +}
>> +
>> +NAND_ECC_CAPS_SINGLE(stm32_fmc2_ecc_caps, stm32_fmc2_calc_ecc_bytes,
>> +		     FMC2_ECC_STEP_SIZE,
>> +		     FMC2_ECC_HAM, FMC2_ECC_BCH4, FMC2_ECC_BCH8);
>> +
>> +/* FMC2 controller ops */
>> +static int stm32_fmc2_attach_chip(struct nand_chip *chip)
>> +{
>> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
>> +	struct mtd_info *mtd = nand_to_mtd(chip);
>> +	u8 nbsect;
>> +	int ret;
>> +
>> +	/*
>> +	 * Only NAND_ECC_HW mode is actually supported
>> +	 * HAMMING => ecc.strength = 1
>> +	 * BCH4 => ecc.strength = 4
>> +	 * BCH8 => ecc.strength = 8
>> +	 * ecc sector size = 512
>> +	 */
>> +	if (chip->ecc.mode != NAND_ECC_HW) {
>> +		dev_err(fmc2->dev, "nand_ecc_mode is not well defined in the DT\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	ret = nand_ecc_choose_conf(chip, &stm32_fmc2_ecc_caps,
>> +				   mtd->oobsize - FMC2_BBM_LEN);
>> +	if (ret) {
>> +		dev_err(fmc2->dev, "no valid ECC settings set\n");
>> +		return ret;
>> +	}
>> +
>> +	nbsect = mtd->writesize / chip->ecc.size;
>> +	if (nbsect > FMC2_MAX_SG_COUNT) {
>> +		dev_err(fmc2->dev, "nand page size is not supported\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (chip->bbt_options & NAND_BBT_USE_FLASH)
>> +		chip->bbt_options |= NAND_BBT_NO_OOB;
>> +
>> +	/* FMC2 setup routine */
>> +	stm32_fmc2_setup(fmc2);
>> +
>> +	/* DMA setup */
>> +	ret = stm32_fmc2_dma_setup(fmc2, nbsect);
>> +	if (ret)
>> +		return ret;
>> +
>> +	/* NAND callbacks setup */
>> +	stm32_fmc2_nand_callbacks_setup(fmc2);
>> +
>> +	/* Define ECC layout */
>> +	mtd_set_ooblayout(mtd, &stm32_fmc2_nand_ooblayout_ops);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct nand_controller_ops stm32_fmc2_nand_controller_ops = {
>> +	.attach_chip = stm32_fmc2_attach_chip,
>> +};
>> +
>> +/* FMC2 probe */
>> +static int stm32_fmc2_parse_child(struct device *dev,
>> +				  struct stm32_fmc2 *fmc2,
>> +				  struct device_node *dn)
>> +{
>> +	u32 cs;
>> +	int ret, i;
>> +
>> +	if (!of_get_property(dn, "reg", &fmc2->ncs))
>> +		return -EINVAL;
>> +
>> +	fmc2->ncs /= sizeof(u32);
>> +	if (!fmc2->ncs) {
>> +		dev_err(dev, "invalid reg property size\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	for (i = 0; i < fmc2->ncs; i++) {
>> +		ret = of_property_read_u32_index(dn, "reg", i, &cs);
>> +		if (ret) {
>> +			dev_err(dev, "could not retrieve reg property: %d\n",
>> +				ret);
>> +			return ret;
>> +		}
>> +
>> +		if (cs > FMC2_MAX_CE) {
>> +			dev_err(dev, "invalid reg value: %d\n", cs);
>> +			return -EINVAL;
>> +		}
>> +
>> +		if (fmc2->cs_assigned & BIT(cs)) {
>> +			dev_err(dev, "cs already assigned: %d\n", cs);
>> +			return -EINVAL;
>> +		}
>> +
>> +		fmc2->cs_assigned |= BIT(cs);
>> +		fmc2->cs_used[i] = cs;
>> +	}
>> +
>> +	/* Default cs used */
> 
> s/cs/CS/

OK

> 
>> +	fmc2->cs_sel = fmc2->cs_used[0];
>> +
>> +	/* Timings */
>> +	ret = of_property_read_u8_array(dn, "st,fmc2_timings",
>> +					(u8 *)&fmc2->timings,
>> +					sizeof(fmc2->timings));
>> +	return ret ? 0 : 1;
>> +}
>> +
>> +static int stm32_fmc2_parse_dt(struct device *dev,
>> +			       struct stm32_fmc2 *fmc2)
>> +{
>> +	struct device_node *dn = dev->of_node;
>> +	struct device_node *child;
>> +	int nchips = of_get_child_count(dn);
>> +	int ret = 0;
>> +
>> +	if (!nchips) {
>> +		dev_err(dev, "NAND chip not defined\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (nchips > 1) {
> 
> If you have two CS, can't you have two NAND chips connected?
> 

No HW board has been designed with 2 NAND chips connected. I am not able 
to test this configuration. The driver will be improved when i will be 
able to test such configuration.

>> +		dev_err(dev, "too many NAND chips defined\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	for_each_child_of_node(dn, child) {
>> +		ret = stm32_fmc2_parse_child(dev, fmc2, child);
>> +		if (ret < 0) {
>> +			of_node_put(child);
>> +			return ret;
>> +		}
>> +
>> +		nand_set_flash_node(&fmc2->chip, child);
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int stm32_fmc2_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct reset_control *rstc;
>> +	struct stm32_fmc2 *fmc2;
>> +	struct resource *res;
>> +	struct mtd_info *mtd;
>> +	struct nand_chip *chip;
>> +	int i, j, ret, irq, timings_def;
>> +
>> +	fmc2 = devm_kzalloc(dev, sizeof(*fmc2), GFP_KERNEL);
>> +	if (!fmc2)
>> +		return -ENOMEM;
>> +
>> +	timings_def = stm32_fmc2_parse_dt(dev, fmc2);
>> +	if (timings_def < 0)
>> +		return timings_def;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	fmc2->io_base = devm_ioremap_resource(dev, res);
>> +	if (IS_ERR(fmc2->io_base))
>> +		return PTR_ERR(fmc2->io_base);
>> +
>> +	fmc2->io_phys_addr = res->start;
>> +
>> +	for (i = 0, j = 1; i < FMC2_MAX_CE; i++, j += 3) {
> 
> Maybe naming i 'chip_id' and j 'mem_region' or something else, more
> meaningful than just i/j would be more clear?

Ok.

> 
>> +		if (!(fmc2->cs_assigned & BIT(i)))
>> +			continue;
>> +
>> +		res = platform_get_resource(pdev, IORESOURCE_MEM, j);
>> +		fmc2->data_base[i] = devm_ioremap_resource(dev, res);
>> +		if (IS_ERR(fmc2->data_base[i]))
>> +			return PTR_ERR(fmc2->data_base[i]);
>> +
>> +		fmc2->data_phys_addr[i] = res->start;
>> +
>> +		res = platform_get_resource(pdev, IORESOURCE_MEM, j + 1);
>> +		fmc2->cmd_base[i] = devm_ioremap_resource(dev, res);
>> +		if (IS_ERR(fmc2->cmd_base[i]))
>> +			return PTR_ERR(fmc2->cmd_base[i]);
>> +
>> +		res = platform_get_resource(pdev, IORESOURCE_MEM, j + 2);
>> +		fmc2->addr_base[i] = devm_ioremap_resource(dev, res);
>> +		if (IS_ERR(fmc2->addr_base[i]))
>> +			return PTR_ERR(fmc2->addr_base[i]);
>> +	}
>> +
>> +	irq = platform_get_irq(pdev, 0);
>> +	ret = devm_request_irq(dev, irq, stm32_fmc2_irq, 0,
>> +			       dev_name(dev), fmc2);
>> +	if (ret) {
>> +		dev_err(dev, "failed to request irq\n");
>> +		return ret;
>> +	}
>> +
>> +	init_completion(&fmc2->complete);
>> +
>> +	fmc2->clk = devm_clk_get(dev, NULL);
>> +	if (IS_ERR(fmc2->clk))
>> +		return PTR_ERR(fmc2->clk);
>> +
>> +	ret = clk_prepare_enable(fmc2->clk);
>> +	if (ret) {
>> +		dev_err(dev, "can not enable the clock\n");
>> +		return ret;
>> +	}
>> +
>> +	rstc = devm_reset_control_get(dev, NULL);
>> +	if (!IS_ERR(rstc)) {
>> +		reset_control_assert(rstc);
>> +		reset_control_deassert(rstc);
>> +	}
>> +
>> +	fmc2->dev = dev;
>> +	mtd = nand_to_mtd(&fmc2->chip);
>> +	chip = &fmc2->chip;
>> +	nand_set_controller_data(chip, fmc2);
>> +	mtd->dev.parent = dev;
>> +
>> +	chip->exec_op = stm32_fmc2_exec_op;
>> +	chip->select_chip = stm32_fmc2_select_chip;
>> +	chip->options |= NAND_BUSWIDTH_AUTO | NAND_NO_SUBPAGE_WRITE;
>> +
>> +	/* FMC2 init routine */
>> +	stm32_fmc2_init(fmc2);
>> +	if (timings_def)
>> +		stm32_fmc2_timings_init(fmc2);
> 
> You already implement ->setup_data_interface, why do you need this
> stm32_fmc2_timings_init()?

"st,fmc2-timings" is an optional property that allow the end user to 
overwrite the timings calculated by setup_data_interface callback. In 
case this property is defined in the device tree, timings are directly 
set and stm32_fmc2_setup_interface() will be never called.

> 
>> +	else
>> +		chip->setup_data_interface = stm32_fmc2_setup_interface;
>> +
>> +	/* Default settings */
>> +	chip->ecc.mode = NAND_ECC_HW;
>> +	chip->ecc.size = FMC2_ECC_STEP_SIZE;
>> +	chip->ecc.strength = FMC2_ECC_BCH8;
>> +
>> +	/* Scan to find existence of the device */
>> +	chip->dummy_controller.ops = &stm32_fmc2_nand_controller_ops;
>> +	ret = nand_scan(chip, fmc2->ncs);
>> +	if (ret)
>> +		goto err_scan;
>> +
>> +	ret = mtd_device_register(mtd, NULL, 0);
>> +	if (ret)
>> +		goto err_device_register;
>> +
>> +	platform_set_drvdata(pdev, fmc2);
>> +
>> +	return 0;
>> +
>> +err_device_register:
>> +	nand_cleanup(chip);
>> +
>> +err_scan:
>> +	if (fmc2->dma_ecc_ch)
>> +		dma_release_channel(fmc2->dma_ecc_ch);
>> +	if (fmc2->dma_tx_ch)
>> +		dma_release_channel(fmc2->dma_tx_ch);
>> +	if (fmc2->dma_rx_ch)
>> +		dma_release_channel(fmc2->dma_rx_ch);
>> +
>> +	sg_free_table(&fmc2->dma_data_sg);
>> +	sg_free_table(&fmc2->dma_ecc_sg);
>> +
>> +	clk_disable_unprepare(fmc2->clk);
>> +
>> +	return ret;
>> +}
>> +
>> +static int stm32_fmc2_remove(struct platform_device *pdev)
>> +{
>> +	struct stm32_fmc2 *fmc2 = platform_get_drvdata(pdev);
>> +
>> +	nand_release(&fmc2->chip);
>> +
>> +	if (fmc2->dma_ecc_ch)
>> +		dma_release_channel(fmc2->dma_ecc_ch);
>> +	if (fmc2->dma_tx_ch)
>> +		dma_release_channel(fmc2->dma_tx_ch);
>> +	if (fmc2->dma_rx_ch)
>> +		dma_release_channel(fmc2->dma_rx_ch);
>> +
>> +	sg_free_table(&fmc2->dma_data_sg);
>> +	sg_free_table(&fmc2->dma_ecc_sg);
>> +
>> +	clk_disable_unprepare(fmc2->clk);
>> +
>> +	return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int stm32_fmc2_suspend(struct device *dev)
> 
> Please remove the pre-processor conditionals and add
> __maybe_unused in the suspend/resume functions definitions.

Ok.

> 
>> +{
>> +	struct stm32_fmc2 *fmc2 = dev_get_drvdata(dev);
>> +
>> +	clk_disable_unprepare(fmc2->clk);
>> +
>> +	pinctrl_pm_select_sleep_state(dev);
>> +
>> +	return 0;
>> +}
>> +
>> +static int stm32_fmc2_resume(struct device *dev)
>> +{
>> +	struct stm32_fmc2 *fmc2 = dev_get_drvdata(dev);
>> +	int i, ret;
>> +
>> +	pinctrl_pm_select_default_state(dev);
>> +
>> +	ret = clk_prepare_enable(fmc2->clk);
>> +	if (ret) {
>> +		dev_err(dev, "can not enable the clock\n");
>> +		return ret;
>> +	}
>> +
>> +	stm32_fmc2_init(fmc2);
>> +	stm32_fmc2_timings_init(fmc2);
>> +	stm32_fmc2_setup(fmc2);
>> +
>> +	for (i = 0; i < fmc2->ncs; i++)
>> +		nand_reset(&fmc2->chip, i);
> 
> This means you have one different NAND chip wired on each CS.
> 
> We could have two CS wired to the same NAND chip. Calling nand_reset
> twice would be harmless but a lost of time.

Ok, so i will call nand_reset() only once as this driver only supports
one or two CS wired connected to the same NAND chip.

> 
>> +
>> +	return 0;
>> +}
>> +#endif
>> +
>> +static SIMPLE_DEV_PM_OPS(stm32_fmc2_pm_ops, stm32_fmc2_suspend,
>> +			 stm32_fmc2_resume);
>> +
>> +static const struct of_device_id stm32_fmc2_match[] = {
>> +	{.compatible = "st,stm32mp15-fmc2"},
>> +	{}
>> +};
>> +MODULE_DEVICE_TABLE(of, stm32_fmc2_match);
>> +
>> +static struct platform_driver stm32_fmc2_driver = {
>> +	.probe	= stm32_fmc2_probe,
>> +	.remove	= stm32_fmc2_remove,
>> +	.driver	= {
>> +		.name = "stm32-fmc2",
>> +		.of_match_table = stm32_fmc2_match,
>> +		.pm = &stm32_fmc2_pm_ops,
>> +	},
>> +};
>> +module_platform_driver(stm32_fmc2_driver);
>> +
>> +MODULE_ALIAS("platform:" DRIVER_NAME);
>> +MODULE_AUTHOR("Christophe Kerello <christophe.kerello@st.com>");
>> +MODULE_DESCRIPTION("STMicroelectronics STM32 FMC2 nand driver");
>> +MODULE_LICENSE("GPL v2");
> 
> 
> Thanks,
> Miquèl
> 

Thanks,
Christophe Kerello.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/3] mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver
  2018-09-23 11:34   ` Miquel Raynal
@ 2018-09-24 16:36     ` Christophe Kerello
  0 siblings, 0 replies; 18+ messages in thread
From: Christophe Kerello @ 2018-09-24 16:36 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: boris.brezillon, richard, dwmw2, computersforpeace, marek.vasut,
	robh+dt, mark.rutland, linux-mtd, linux-kernel, devicetree,
	linux-stm32

Hi Miquèl,

On 09/23/2018 01:34 PM, Miquel Raynal wrote:
> Hi Christophe,
> 
> <christophe.kerello@st.com> wrote on Mon, 17 Sep 2018 17:47:39 +0200:
> 
>> From: Christophe Kerello <christophe.kerello@st.com>
>>
>> The driver adds the support for the STMicroelectronics FMC2 NAND
>> Controller found on STM32MP SOCs.
>>
>> This patch is based on FMC2 command sequencer.
>> The purpose of the command sequencer is to facilitate the programming
>> and the reading of NAND flash pages with the ECC and to free the CPU
>> of sequencing tasks.
>> It requires one DMA channel for write and two DMA channels for read
>> operations.
>>
>> Only NAND_ECC_HW mode is actually supported.
>> The driver supports a maximum 8k page size.
>> The following ECC strength and step size are currently supported:
>>   - nand-ecc-strength = <8>, nand-ecc-step-size = <512> (BCH8)
>>   - nand-ecc-strength = <4>, nand-ecc-step-size = <512> (BCH4)
>>   - nand-ecc-strength = <1>, nand-ecc-step-size = <512> (Extended ecc
>>     based on HAMMING)
>>
>> This patch has been tested on Micron MT29F8G08ABACAH4 and
>> MT29F8G16ABACAH4
>>
>> Signed-off-by: Christophe Kerello <christophe.kerello@st.com>
>> ---
> 
> 
> [...]
> 
>> +/* NAND callbacks setup */
>> +static void stm32_fmc2_nand_callbacks_setup(struct stm32_fmc2 *fmc2)
>> +{
>> +	struct nand_chip *chip = &fmc2->chip;
>> +
>> +	/* Specific callbacks to read/write a page */
>> +	chip->ecc.correct = stm32_fmc2_ham_correct;
>> +	chip->ecc.write_page = stm32_fmc2_sequencer_write_page;
>> +	chip->ecc.read_page = stm32_fmc2_sequencer_read_page;
>> +	chip->ecc.write_page_raw = stm32_fmc2_sequencer_write_page_raw;
>> +	chip->ecc.read_page_raw = stm32_fmc2_sequencer_read_page_raw;
> 
> Are you sure all the tests in mtd-utils are successful?

I am not sure to understand the question, so i have ran some of them: 
mtd_nandbiterrs.ko, mtd_speedtest.ko and mtd_oobtest.ko on a 2 MBytes 
partition.

# insmod mtd_nandbiterrs.ko dev=1
[  235.062876]
[  235.062904] ==================================================
[  235.068747] mtd_nandbiterrs: MTD device: 1
[  235.072904] mtd_nandbiterrs: MTD device size 2097152, 
eraseblock=262144, page=4096, oob=224
[  235.081177] mtd_nandbiterrs: Device uses 1 subpages of 4096 bytes
[  235.087284] mtd_nandbiterrs: Using page=0, offset=0, eraseblock=0
[  235.094109] mtd_nandbiterrs: incremental biterrors test
[  235.098778] mtd_nandbiterrs: write_page
[  235.103126] mtd_nandbiterrs: rewrite page
[  235.106793] mtd_nandbiterrs: read_page
[  235.110501] mtd_nandbiterrs: verify_page
[  235.114406] mtd_nandbiterrs: Successfully corrected 0 bit errors per 
subpage
[  235.121124] mtd_nandbiterrs: Inserted biterror @ 0/5
[  235.126117] mtd_nandbiterrs: rewrite page
[  235.130490] mtd_nandbiterrs: read_page
[  235.134247] mtd_nandbiterrs: Read reported 1 corrected bit errors
[  235.139923] mtd_nandbiterrs: verify_page
[  235.144124] mtd_nandbiterrs: Successfully corrected 1 bit errors per 
subpage
[  235.150891] mtd_nandbiterrs: Inserted biterror @ 0/2
[  235.155888] mtd_nandbiterrs: rewrite page
[  235.160244] mtd_nandbiterrs: read_page
[  235.164001] mtd_nandbiterrs: Read reported 2 corrected bit errors
[  235.169687] mtd_nandbiterrs: verify_page
[  235.173881] mtd_nandbiterrs: Successfully corrected 2 bit errors per 
subpage
[  235.180653] mtd_nandbiterrs: Inserted biterror @ 0/0
[  235.185631] mtd_nandbiterrs: rewrite page
[  235.190052] mtd_nandbiterrs: read_page
[  235.193743] mtd_nandbiterrs: Read reported 3 corrected bit errors
[  235.199449] mtd_nandbiterrs: verify_page
[  235.203631] mtd_nandbiterrs: Successfully corrected 3 bit errors per 
subpage
[  235.210415] mtd_nandbiterrs: Inserted biterror @ 1/7
[  235.215391] mtd_nandbiterrs: rewrite page
[  235.219760] mtd_nandbiterrs: read_page
[  235.223507] mtd_nandbiterrs: Read reported 4 corrected bit errors
[  235.229210] mtd_nandbiterrs: verify_page
[  235.233392] mtd_nandbiterrs: Successfully corrected 4 bit errors per 
subpage
[  235.240175] mtd_nandbiterrs: Inserted biterror @ 1/5
[  235.245149] mtd_nandbiterrs: rewrite page
[  235.249516] mtd_nandbiterrs: read_page
[  235.253272] mtd_nandbiterrs: Read reported 5 corrected bit errors
[  235.258968] mtd_nandbiterrs: verify_page
[  235.263151] mtd_nandbiterrs: Successfully corrected 5 bit errors per 
subpage
[  235.269933] mtd_nandbiterrs: Inserted biterror @ 1/2
[  235.274923] mtd_nandbiterrs: rewrite page
[  235.279272] mtd_nandbiterrs: read_page
[  235.283021] mtd_nandbiterrs: Read reported 6 corrected bit errors
[  235.288727] mtd_nandbiterrs: verify_page
[  235.292910] mtd_nandbiterrs: Successfully corrected 6 bit errors per 
subpage
[  235.299694] mtd_nandbiterrs: Inserted biterror @ 1/0
[  235.304671] mtd_nandbiterrs: rewrite page
[  235.309028] mtd_nandbiterrs: read_page
[  235.312780] mtd_nandbiterrs: Read reported 7 corrected bit errors
[  235.318489] mtd_nandbiterrs: verify_page
[  235.322673] mtd_nandbiterrs: Successfully corrected 7 bit errors per 
subpage
[  235.329457] mtd_nandbiterrs: Inserted biterror @ 2/6
[  235.334432] mtd_nandbiterrs: rewrite page
[  235.338796] mtd_nandbiterrs: read_page
[  235.342526] mtd_nandbiterrs: Read reported 8 corrected bit errors
[  235.348254] mtd_nandbiterrs: verify_page
[  235.352441] mtd_nandbiterrs: Successfully corrected 8 bit errors per 
subpage
[  235.359223] mtd_nandbiterrs: Inserted biterror @ 2/5
[  235.364197] mtd_nandbiterrs: rewrite page
[  235.368565] mtd_nandbiterrs: read_page
[  235.372283] mtd_nandbiterrs: error: read failed at 0x0
[  235.377087] mtd_nandbiterrs: After 9 biterrors per subpage, read 
reported error -74
[  235.386307] mtd_nandbiterrs: finished successfully.
[  235.389711] ==================================================

# insmod mtd_speedtest.ko dev=1
[  256.862822]
[  256.862850] =================================================
[  256.868608] mtd_speedtest: MTD device: 1
[  256.872562] mtd_speedtest: MTD device size 2097152, eraseblock size 
262144, page size 4096, count of eraseblocks 8, pages per eraseblock 64, 
OOB size 224
[  256.889840] mtd_test: scanning for bad eraseblocks
[  256.893233] mtd_test: scanned 8 eraseblocks, 0 are bad
[  256.904002] mtd_speedtest: testing eraseblock write speed
[  257.133918] mtd_speedtest: eraseblock write speed is 9102 KiB/s
[  257.138376] mtd_speedtest: testing eraseblock read speed
[  257.308241] mtd_speedtest: eraseblock read speed is 12487 KiB/s
[  257.332093] mtd_speedtest: testing page write speed
[  257.563379] mtd_speedtest: page write speed is 9022 KiB/s
[  257.567314] mtd_speedtest: testing page read speed
[  257.737934] mtd_speedtest: page read speed is 12412 KiB/s
[  257.761272] mtd_speedtest: testing 2 page write speed
[  257.991708] mtd_speedtest: 2 page write speed is 9061 KiB/s
[  257.995830] mtd_speedtest: testing 2 page read speed
[  258.165996] mtd_speedtest: 2 page read speed is 12412 KiB/s
[  258.170102] mtd_speedtest: Testing erase speed
[  258.193952] mtd_speedtest: erase speed is 107789 KiB/s
[  258.197620] mtd_speedtest: Testing 2x multi-block erase speed
[  258.209026] mtd_speedtest: 2x multi-block erase speed is 409600 KiB/s
[  258.214010] mtd_speedtest: Testing 4x multi-block erase speed
[  258.225402] mtd_speedtest: 4x multi-block erase speed is 409600 KiB/s
[  258.230374] mtd_speedtest: Testing 8x multi-block erase speed
[  258.241771] mtd_speedtest: 8x multi-block erase speed is 409600 KiB/s
[  258.246757] mtd_speedtest: Testing 16x multi-block erase speed
[  258.258227] mtd_speedtest: 16x multi-block erase speed is 409600 KiB/s
[  258.263297] mtd_speedtest: Testing 32x multi-block erase speed
[  258.274767] mtd_speedtest: 32x multi-block erase speed is 409600 KiB/s
[  258.279826] mtd_speedtest: Testing 64x multi-block erase speed
[  258.291307] mtd_speedtest: 64x multi-block erase speed is 409600 KiB/s
[  258.296384] mtd_speedtest: finished
[  258.299857] =================================================

# insmod mtd_oobtest.ko dev=1 bitflip_limit=2
[  993.770824]
[  993.770853] =================================================
[  993.776689] mtd_oobtest: MTD device: 1
[  993.780348] mtd_oobtest: MTD device size 2097152, eraseblock size 
262144, page size 4096, count of eraseblocks 8, pages per eraseblock 64, 
OOB size 224
[  993.793977] mtd_test: scanning for bad eraseblocks
[  993.798716] mtd_test: scanned 8 eraseblocks, 0 are bad
[  993.803855] mtd_oobtest: test 1 of 5
[  993.813081] mtd_oobtest: writing OOBs of whole device
[  993.830940] mtd_oobtest: written up to eraseblock 0
[  993.934094] mtd_oobtest: written 8 eraseblocks
[  993.937080] mtd_oobtest: verifying all eraseblocks
[  993.944908] mtd_oobtest: verified up to eraseblock 0
[  993.969427] mtd_oobtest: verified 8 eraseblocks
[  993.972499] mtd_oobtest: test 2 of 5
[  993.985613] mtd_oobtest: writing OOBs of whole device
[  994.003509] mtd_oobtest: written up to eraseblock 0
[  994.106738] mtd_oobtest: written 8 eraseblocks
[  994.109712] mtd_oobtest: verifying all eraseblocks
[  994.117442] mtd_oobtest: verified up to eraseblock 0
[  994.141342] mtd_oobtest: verified 8 eraseblocks
[  994.144421] mtd_oobtest: test 3 of 5
[  994.160526] mtd_oobtest: writing OOBs of whole device
[  994.178486] mtd_oobtest: written up to eraseblock 0
[  994.279164] mtd_oobtest: written 8 eraseblocks
[  994.282136] mtd_oobtest: verifying all eraseblocks
[  994.292790] mtd_oobtest: verified up to eraseblock 0
[  994.337322] mtd_oobtest: verified 8 eraseblocks
[  994.340379] mtd_oobtest: test 4 of 5
[  994.355692] mtd_oobtest: attempting to start write past end of OOB
[  994.360406] mtd_oobtest: an error is expected...
[  994.365036] mtd_oobtest: error occurred as expected
[  994.369895] mtd_oobtest: attempting to start read past end of OOB
[  994.376005] mtd_oobtest: an error is expected...
[  994.380604] mtd_oobtest: error occurred as expected
[  994.385494] mtd_oobtest: attempting to write past end of device
[  994.391398] mtd_oobtest: an error is expected...
[  994.396027] mtd_oobtest: error occurred as expected
[  994.400888] mtd_oobtest: attempting to read past end of device
[  994.406752] mtd_oobtest: an error is expected...
[  994.411335] mtd_oobtest: error occurred as expected
[  994.416936] mtd_oobtest: attempting to write past end of device
[  994.422127] mtd_oobtest: an error is expected...
[  994.426757] mtd_oobtest: error occurred as expected
[  994.431616] mtd_oobtest: attempting to read past end of device
[  994.437461] mtd_oobtest: an error is expected...
[  994.442061] mtd_oobtest: error occurred as expected
[  994.446948] mtd_oobtest: test 5 of 5
[  994.456172] mtd_oobtest: writing OOBs of whole device
[  994.459991] mtd_oobtest: written up to eraseblock 0
[  994.464842] mtd_oobtest: written up to eraseblock 0
[  994.472018] mtd_oobtest: written 7 eraseblocks
[  994.475007] mtd_oobtest: verifying all eraseblocks
[  994.479878] mtd_oobtest: verified up to eraseblock 0
[  994.485314] mtd_oobtest: verified 7 eraseblocks
[  994.489267] mtd_oobtest: finished with 0 errors
[  994.493828] =================================================

> 
> 
> Thanks,
> Miquèl
> 

Thanks,
Christophe Kerello.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 1/3] dt-bindings: mtd: stm32_fmc2: add STM32 FMC2 NAND controller documentation
  2018-09-24 16:36     ` Christophe Kerello
@ 2018-09-24 17:17       ` Boris Brezillon
  2018-09-25  9:14         ` Christophe Kerello
  0 siblings, 1 reply; 18+ messages in thread
From: Boris Brezillon @ 2018-09-24 17:17 UTC (permalink / raw)
  To: Christophe Kerello
  Cc: Miquel Raynal, richard, dwmw2, computersforpeace, marek.vasut,
	robh+dt, mark.rutland, linux-mtd, linux-kernel, devicetree,
	linux-stm32

Hi Christophe,

On Mon, 24 Sep 2018 18:36:27 +0200
Christophe Kerello <christophe.kerello@st.com> wrote:

> >> +- st,fmc2_timings: array of 8 bytes for NAND timings. The meanings of
> >> +  these bytes are:
> >> +  byte 0 TCLR      : CLE to RE delay in number of AHB clock cycles, only 4 bits
> >> +                     are valid. Zero means one clock cycle, 15 means 16 clock
> >> +                     cycles.
> >> +  byte 1 TAR       : ALE to RE delay, 4 bits are valid. Same format as TCLR.
> >> +  byte 2 THIZ      : number of HCLK clock cycles during which the data bus is
> >> +                     kept in Hi-Z (tristate) after the start of a write access.
> >> +                     Only valid for write transactions. Zero means 1 cycle,
> >> +                     255 means 256 cycles.
> >> +  byte 3 TWAIT     : number of HCLK clock cycles to assert the command to the
> >> +                     NAND flash in response to SMWAITn. Zero means 1 cycle,
> >> +                     255 means 256 cycles.
> >> +  byte 4 THOLD_MEM : common memory space timing
> >> +                     number of HCLK clock cycles to hold the address (and data
> >> +                     when writing) after the command deassertion. Zero means
> >> +                     1 cycle, 255 means 256 cycles.
> >> +  byte 5 TSET_MEM  : common memory space timing
> >> +                     number of HCLK clock cycles to assert the address before
> >> +                     the command is asserted. Zero means 1 cycle, 255 means 256
> >> +                     cycles.
> >> +  byte 6 THOLD_ATT : attribute memory space timing
> >> +                     number of HCLK clock cycles to hold the address (and data
> >> +                     when writing) after the command deassertion. Zero means
> >> +                     1 cycle, 255 means 256 cycles.
> >> +  byte 7 TSET_ATT  : attribute memory space timing
> >> +                     number of HCLK clock cycles to assert the address before
> >> +                     the command is asserted. Zero means 1 cycle, 255 means 256
> >> +                     cycles.  
> > 
> > Let me review the driver but this array of timings is really
> > suspicious. I am pretty sure you don't need it in the DT.  
> 
> "st,fmc2-timings" is an optional property that allow the end user to 
> overwrite the timings calculated by setup_data_interface callback. By 
> setting this property in the NAND flash memory device tree node, the end 
> user could have a better throughput. For NON ONFI SLC NAND, timing mode 
> 0 is often used.

Exactly the kind of tweaking I'd like to avoid. If the NAND is not ONFI,
the vendor driver (nand_<manufacturer>.c) can overwrite
chip->default_onfi_timing_mode, and if the ONFI timings modes are not
exactly matching the NAND spec and you need the exact timings, then we
should consider adding a manufacturer hook to let the manufacturer
driver tweak the timings. In any case, I'm not willing to accept
timings description in the DT.

Regards,

Boris

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/3] mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver
  2018-09-17 15:47 ` [PATCH 2/3] mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver christophe.kerello
                     ` (3 preceding siblings ...)
  2018-09-23 11:34   ` Miquel Raynal
@ 2018-09-24 17:23   ` Boris Brezillon
  2018-09-25  9:14     ` Christophe Kerello
  4 siblings, 1 reply; 18+ messages in thread
From: Boris Brezillon @ 2018-09-24 17:23 UTC (permalink / raw)
  To: christophe.kerello
  Cc: miquel.raynal, richard, dwmw2, computersforpeace, marek.vasut,
	robh+dt, mark.rutland, linux-mtd, linux-kernel, devicetree,
	linux-stm32

Hi Christophe,

On Mon, 17 Sep 2018 17:47:39 +0200
<christophe.kerello@st.com> wrote:

> +struct stm32_fmc2 {
> +	struct nand_chip chip;
> +	struct device *dev;
> +	void __iomem *io_base;
> +	void __iomem *data_base[FMC2_MAX_CE];
> +	void __iomem *cmd_base[FMC2_MAX_CE];
> +	void __iomem *addr_base[FMC2_MAX_CE];
> +	phys_addr_t io_phys_addr;
> +	phys_addr_t data_phys_addr[FMC2_MAX_CE];
> +	struct clk *clk;
> +
> +	struct dma_chan *dma_tx_ch;
> +	struct dma_chan *dma_rx_ch;
> +	struct dma_chan *dma_ecc_ch;
> +	struct sg_table dma_data_sg;
> +	struct sg_table dma_ecc_sg;
> +	u8 *ecc_buf;
> +	int dma_ecc_len;
> +
> +	struct completion complete;
> +	struct completion dma_data_complete;
> +	struct completion dma_ecc_complete;
> +
> +	struct stm32_fmc2_timings timings;
> +	u8 cs_assigned;
> +	int cs_sel;
> +	int ncs;
> +	int cs_used[FMC2_MAX_CE];
> +};

Can we have a clear separation between the NAND controller and NAND
chip structures. I know you only support a single chip per-controller
right now, but I prefer to have things clearly separated from the
beginning. 

Regards,

Boris

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/3] mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver
  2018-09-24 16:36     ` Christophe Kerello
@ 2018-09-24 17:26       ` Boris Brezillon
  2018-10-29  9:22       ` Miquel Raynal
  1 sibling, 0 replies; 18+ messages in thread
From: Boris Brezillon @ 2018-09-24 17:26 UTC (permalink / raw)
  To: Christophe Kerello
  Cc: Miquel Raynal, richard, dwmw2, computersforpeace, marek.vasut,
	robh+dt, mark.rutland, linux-mtd, linux-kernel, devicetree,
	linux-stm32

On Mon, 24 Sep 2018 18:36:36 +0200
Christophe Kerello <christophe.kerello@st.com> wrote:

> >> +static int stm32_fmc2_resume(struct device *dev)
> >> +{
> >> +	struct stm32_fmc2 *fmc2 = dev_get_drvdata(dev);
> >> +	int i, ret;
> >> +
> >> +	pinctrl_pm_select_default_state(dev);
> >> +
> >> +	ret = clk_prepare_enable(fmc2->clk);
> >> +	if (ret) {
> >> +		dev_err(dev, "can not enable the clock\n");
> >> +		return ret;
> >> +	}
> >> +
> >> +	stm32_fmc2_init(fmc2);
> >> +	stm32_fmc2_timings_init(fmc2);
> >> +	stm32_fmc2_setup(fmc2);
> >> +
> >> +	for (i = 0; i < fmc2->ncs; i++)
> >> +		nand_reset(&fmc2->chip, i);  
> > 
> > This means you have one different NAND chip wired on each CS.
> > 
> > We could have two CS wired to the same NAND chip. Calling nand_reset
> > twice would be harmless but a lost of time.

Actually, you have to call nand_reset() for each CS, otherwise not all
dies are reset.

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 1/3] dt-bindings: mtd: stm32_fmc2: add STM32 FMC2 NAND controller documentation
  2018-09-24 17:17       ` Boris Brezillon
@ 2018-09-25  9:14         ` Christophe Kerello
  0 siblings, 0 replies; 18+ messages in thread
From: Christophe Kerello @ 2018-09-25  9:14 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: mark.rutland, devicetree, richard, linux-kernel, marek.vasut,
	robh+dt, linux-mtd, Miquel Raynal, computersforpeace, dwmw2,
	linux-stm32

Hi Boris,

On 09/24/2018 07:17 PM, Boris Brezillon wrote:
> Hi Christophe,
> 
> On Mon, 24 Sep 2018 18:36:27 +0200
> Christophe Kerello <christophe.kerello@st.com> wrote:
> 
>>>> +- st,fmc2_timings: array of 8 bytes for NAND timings. The meanings of
>>>> +  these bytes are:
>>>> +  byte 0 TCLR      : CLE to RE delay in number of AHB clock cycles, only 4 bits
>>>> +                     are valid. Zero means one clock cycle, 15 means 16 clock
>>>> +                     cycles.
>>>> +  byte 1 TAR       : ALE to RE delay, 4 bits are valid. Same format as TCLR.
>>>> +  byte 2 THIZ      : number of HCLK clock cycles during which the data bus is
>>>> +                     kept in Hi-Z (tristate) after the start of a write access.
>>>> +                     Only valid for write transactions. Zero means 1 cycle,
>>>> +                     255 means 256 cycles.
>>>> +  byte 3 TWAIT     : number of HCLK clock cycles to assert the command to the
>>>> +                     NAND flash in response to SMWAITn. Zero means 1 cycle,
>>>> +                     255 means 256 cycles.
>>>> +  byte 4 THOLD_MEM : common memory space timing
>>>> +                     number of HCLK clock cycles to hold the address (and data
>>>> +                     when writing) after the command deassertion. Zero means
>>>> +                     1 cycle, 255 means 256 cycles.
>>>> +  byte 5 TSET_MEM  : common memory space timing
>>>> +                     number of HCLK clock cycles to assert the address before
>>>> +                     the command is asserted. Zero means 1 cycle, 255 means 256
>>>> +                     cycles.
>>>> +  byte 6 THOLD_ATT : attribute memory space timing
>>>> +                     number of HCLK clock cycles to hold the address (and data
>>>> +                     when writing) after the command deassertion. Zero means
>>>> +                     1 cycle, 255 means 256 cycles.
>>>> +  byte 7 TSET_ATT  : attribute memory space timing
>>>> +                     number of HCLK clock cycles to assert the address before
>>>> +                     the command is asserted. Zero means 1 cycle, 255 means 256
>>>> +                     cycles.
>>>
>>> Let me review the driver but this array of timings is really
>>> suspicious. I am pretty sure you don't need it in the DT.
>>
>> "st,fmc2-timings" is an optional property that allow the end user to
>> overwrite the timings calculated by setup_data_interface callback. By
>> setting this property in the NAND flash memory device tree node, the end
>> user could have a better throughput. For NON ONFI SLC NAND, timing mode
>> 0 is often used.
> 
> Exactly the kind of tweaking I'd like to avoid. If the NAND is not ONFI,
> the vendor driver (nand_<manufacturer>.c) can overwrite
> chip->default_onfi_timing_mode, and if the ONFI timings modes are not
> exactly matching the NAND spec and you need the exact timings, then we
> should consider adding a manufacturer hook to let the manufacturer
> driver tweak the timings. In any case, I'm not willing to accept
> timings description in the DT.
> 

Ok, I understand the way it should work. This property will be removed 
from the device tree bindings and the timings will be only calculated by 
calling setup_data_interface callback in the driver.

Regards,
Christophe Kerello.

> Regards,
> 
> Boris
> 
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
> 

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/3] mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver
  2018-09-24 17:23   ` Boris Brezillon
@ 2018-09-25  9:14     ` Christophe Kerello
  0 siblings, 0 replies; 18+ messages in thread
From: Christophe Kerello @ 2018-09-25  9:14 UTC (permalink / raw)
  To: Boris Brezillon
  Cc: miquel.raynal, richard, dwmw2, computersforpeace, marek.vasut,
	robh+dt, mark.rutland, linux-mtd, linux-kernel, devicetree,
	linux-stm32

Hi Boris,

On 09/24/2018 07:23 PM, Boris Brezillon wrote:
> Hi Christophe,
> 
> On Mon, 17 Sep 2018 17:47:39 +0200
> <christophe.kerello@st.com> wrote:
> 
>> +struct stm32_fmc2 {
>> +	struct nand_chip chip;
>> +	struct device *dev;
>> +	void __iomem *io_base;
>> +	void __iomem *data_base[FMC2_MAX_CE];
>> +	void __iomem *cmd_base[FMC2_MAX_CE];
>> +	void __iomem *addr_base[FMC2_MAX_CE];
>> +	phys_addr_t io_phys_addr;
>> +	phys_addr_t data_phys_addr[FMC2_MAX_CE];
>> +	struct clk *clk;
>> +
>> +	struct dma_chan *dma_tx_ch;
>> +	struct dma_chan *dma_rx_ch;
>> +	struct dma_chan *dma_ecc_ch;
>> +	struct sg_table dma_data_sg;
>> +	struct sg_table dma_ecc_sg;
>> +	u8 *ecc_buf;
>> +	int dma_ecc_len;
>> +
>> +	struct completion complete;
>> +	struct completion dma_data_complete;
>> +	struct completion dma_ecc_complete;
>> +
>> +	struct stm32_fmc2_timings timings;
>> +	u8 cs_assigned;
>> +	int cs_sel;
>> +	int ncs;
>> +	int cs_used[FMC2_MAX_CE];
>> +};
> 
> Can we have a clear separation between the NAND controller and NAND
> chip structures. I know you only support a single chip per-controller
> right now, but I prefer to have things clearly separated from the
> beginning.

Yes, I can create 2 structures: one for the controller (stm32_fmc2) and 
one for the NAND chip (stm32_fmc2_nand_chip).

Regards,
Christophe Kerello.

> 
> Regards,
> 
> Boris
> 

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/3] mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver
  2018-09-24 16:36     ` Christophe Kerello
  2018-09-24 17:26       ` Boris Brezillon
@ 2018-10-29  9:22       ` Miquel Raynal
  1 sibling, 0 replies; 18+ messages in thread
From: Miquel Raynal @ 2018-10-29  9:22 UTC (permalink / raw)
  To: Christophe Kerello
  Cc: boris.brezillon, richard, dwmw2, computersforpeace, marek.vasut,
	robh+dt, mark.rutland, linux-mtd, linux-kernel, devicetree,
	linux-stm32

Hi Christophe,

Sorry for the delay, here are some answers from my previous comments.
Maybe you already addressed them, in this case please ignore them.

Also, please run and correct 'checkpatch.pl --strict' issues (mostly
uses of uint8_t instead of u8 but also a warning about the compatible).

Overall the driver is in a pretty good shape and should enter the next
release. I'll apply the patches after -rc1 once I'll have your v3+ with
everything corrected.

[...]

> >> index c7efc31..863662c 100644
> >> --- a/drivers/mtd/nand/raw/Kconfig
> >> +++ b/drivers/mtd/nand/raw/Kconfig
> >> @@ -541,4 +541,13 @@ config MTD_NAND_TEGRA
> >>   	  is supported. Extra OOB bytes when using HW ECC are currently
> >>   	  not supported.  
> >>   >> +config MTD_NAND_STM32_FMC2  
> >> +	tristate "Support for NAND controller on STM32MP SoCs"
> >> +	depends on MACH_STM32MP157 || COMPILE_TEST
> >> +	help
> >> +	  Enables support for NAND Flash chips on SoCs containing the FMC2
> >> +	  NAND controller. This controller is found on STM32MP SoCs.
> >> +	  The driver supports a maximum 8k page size. HW ECC is enabled and
> >> +	  supports a maximum 8-bit correction error per sector of 512 bytes.
> > > HW ECC should not be enabled by default. 8-bit/512B of correctability  
> > is good but not that high and people might want to use software ECC in
> > conjunction with raw accesses.  
> 
> Yes, I agree. The driver only supports NAND_ECC_HW mode. NAND_ECC_SOFT mode was not requested by customers and was not implemented. The driver could be improved later to support mode like NAND_ECC_SOFT or NAND_ECC_ON_DIE. Should I remove "HW ECC is enabled" from Kconfig description?

Yes, please.

[...]

> >> +/* Select function */
> >> +static void stm32_fmc2_select_chip(struct nand_chip *chip, int chipnr)
> >> +{
> >> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
> >> +	struct dma_slave_config dma_cfg;
> >> +
> >> +	if (chipnr < 0 || chipnr >= fmc2->ncs)
> >> +		return;
> >> +
> >> +	if (fmc2->cs_used[chipnr] == fmc2->cs_sel)
> >> +		return;
> >> +
> >> +	fmc2->cs_sel = fmc2->cs_used[chipnr];
> >> +
> >> +	if (fmc2->dma_tx_ch && fmc2->dma_rx_ch) {
> >> +		memset(&dma_cfg, 0, sizeof(dma_cfg));
> >> +		dma_cfg.src_addr = fmc2->data_phys_addr[fmc2->cs_sel];
> >> +		dma_cfg.dst_addr = fmc2->data_phys_addr[fmc2->cs_sel];
> >> +		dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> >> +		dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
> >> +		dma_cfg.src_maxburst = 32;
> >> +		dma_cfg.dst_maxburst = 32;
> >> +
> >> +		if (dmaengine_slave_config(fmc2->dma_tx_ch, &dma_cfg))
> >> +			dev_warn(fmc2->dev, "tx DMA engine slave config failed\n");
> >> +
> >> +		if (dmaengine_slave_config(fmc2->dma_rx_ch, &dma_cfg))
> >> +			dev_warn(fmc2->dev, "rx DMA engine slave config failed\n");
> >> +	}
> > > What if there are two NAND chips using different timing modes? You  
> > should probably reconfigure the timings registers, unless there are
> > already a set of timing registers per CS?  
> 
> Yes, it's true. In case of 2 NAND chips, timings and pcr registers should have been reconfigured. But, the driver only supports one NAND chip connected to 1 or 2 CS. There was no requirement on our side to support 2 different NAND chips. I do not have a board to test such configuration, so i do not want to deliver this support without being able to test it on my side. The driver will be improved later to support 2 different NAND chips, in case this configuration is requested by customers.

Sure, I'm not requesting you to support 2 NAND chips, I'm just
requesting to write this driver in a manner so that adding support for a
2nd NAND chip would be easy thanks to a better software design. That's
actually something that is done in the marvell_nand.c driver if you
need inspiration.


[...]

> >> +
> >> +void stm32_fmc2_read_data(struct nand_chip *chip, void *buf,
> >> +			  unsigned int len, bool force_8bit)
> >> +{
> >> +	struct stm32_fmc2 *fmc2 = nand_get_controller_data(chip);
> >> +	void __iomem *io_addr_r = fmc2->data_base[fmc2->cs_sel];
> >> +	u8 *p = buf;
> >> +	unsigned int i;
> >> +
> >> +	if (force_8bit)
> >> +		goto read_8bit;
> >> +
> >> +	if (IS_ALIGNED((u32)buf, sizeof(u32)) && IS_ALIGNED(len, sizeof(u32))) {
> > > If you selected BOUNCE_BUFFER in the options, buf is supposedly  
> > aligned, or am I missing something?  
> 
> 2 FMC2 internal modes can be used:
>   - Sequencer mode (Patch 2/3): dmas are used and NAND_USE_BOUNCE_BUFFER option is selected.
>   - Manual mode (Patch 3/3): no dma channel is used and NAND_USE_BOUNCE_BUFFER is not selected.
> Should i select NAND_USE_BOUNCE_BUFFER for sequencer and manual mode, and remove IS_ALIGNED test on buf?

If it's only useful in manual mode after patch 3/3, then the logic for
it should be in patch 3 also.

Anyway, unless numbers show a significant drop off in the throughput
(but I suppose the sequencer mode is faster anyway?) I think this is a
good idea to always use the bounce buffer and keep the code simple.

[...]

> >> +static int stm32_fmc2_parse_dt(struct device *dev,
> >> +			       struct stm32_fmc2 *fmc2)
> >> +{
> >> +	struct device_node *dn = dev->of_node;
> >> +	struct device_node *child;
> >> +	int nchips = of_get_child_count(dn);
> >> +	int ret = 0;
> >> +
> >> +	if (!nchips) {
> >> +		dev_err(dev, "NAND chip not defined\n");
> >> +		return -EINVAL;
> >> +	}
> >> +
> >> +	if (nchips > 1) {
> > > If you have two CS, can't you have two NAND chips connected?  
> >   
> No HW board has been designed with 2 NAND chips connected. I am not able to test this configuration. The driver will be improved when i will be able to test such configuration.
> 

Ok.


Thanks,
Miquèl

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2018-10-29  9:22 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-17 15:47 [PATCH 0/3] mtd: rawnand: add STM32 FMC2 NAND flash controller driver christophe.kerello
2018-09-17 15:47 ` [PATCH 1/3] dt-bindings: mtd: stm32_fmc2: add STM32 FMC2 NAND controller documentation christophe.kerello
2018-09-22  8:34   ` Miquel Raynal
2018-09-24 16:36     ` Christophe Kerello
2018-09-24 17:17       ` Boris Brezillon
2018-09-25  9:14         ` Christophe Kerello
2018-09-17 15:47 ` [PATCH 2/3] mtd: rawnand: stm32_fmc2: add STM32 FMC2 NAND flash controller driver christophe.kerello
2018-09-17 17:05   ` kbuild test robot
2018-09-17 17:32   ` kbuild test robot
2018-09-22 13:48   ` Miquel Raynal
2018-09-24 16:36     ` Christophe Kerello
2018-09-24 17:26       ` Boris Brezillon
2018-10-29  9:22       ` Miquel Raynal
2018-09-23 11:34   ` Miquel Raynal
2018-09-24 16:36     ` Christophe Kerello
2018-09-24 17:23   ` Boris Brezillon
2018-09-25  9:14     ` Christophe Kerello
2018-09-17 15:47 ` [PATCH 3/3] mtd: rawnand: stm32_fmc2: add manual mode christophe.kerello

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).