All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 0/4] Renesas NAND controller support
@ 2021-12-17 14:20 ` Miquel Raynal
  0 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-17 14:20 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Rob Herring, devicetree, Milan Stevanovic, Jimmy Lalande,
	Thomas Petazzoni, linux-renesas-soc, Magnus Damm,
	Gareth Williams, Phil Edworthy, Geert Uytterhoeven, Wolfram Sang,
	Chris Brandt, Ralph Siemsen, Miquel Raynal

Hello,

Here is a short series bringing support for Renesas NAND controller
found on R-Car Gen3 and RZ/N1 SoCs.

The driver has been tested with a fully-upstream device tree on top of a
v5.16-rc4. The DT used is very close to the r9a06g032-db.

Cheers,
Miquèl

Changes in v6:
* Added Geert's R-by on the DT patch.
* Changed all the naming due to Wolfram's feedback. This controller is
  also found on the R-Car Gen3 family. Hence I used a much more generic
  name for the driver, the bindings, the variables, etc. The bindings
  have been updated to match the two different SoC families as well.
* Updated the MODULE_LICENSE macro as suggested by Wolfram.
 
Changes in v5:
* Add Rob's ack on the bindings.
* Dropped the #address/size-cells properties (handled by nand-controller.yaml).
* Fixed a typo reported by the kernel test robot (when building as a module).

Changes in v4:
* Set unevaluatedProperties set to false in the bindings.
* Change the clock names by removing the nand_ prefix which is
  redundant, even though the clocks are named like this in the spec. The
  name remains clear enough anyway.

Changes in v3:
* Rebased on top of a fully-upstream recent kernel.
* Renamed the clocks in the bindings and the driver to match the
  documentation (lower-cased): nand_hclk & nand_eclk.
* Added a new commit describing the NAND controller in the r9a06g032
  DTSI.
* Added the Reviewed-by and Tested-by tags received.

Changes in v2:
* Added the family-specific rzn1 compatible as suggested by Geert.
  Updated the bindings, the binding file name, the compatible used in
  the driver, the MAINTAINERS entry, etc.
* Added an ARCH_RENESAS Kconfig dependency.
* Changed the type (to unsigned) of a couple of variables.
* Returned earlier when possible to reduce indentation.
* Used platform_get_irq_optional() instead of platform_get_irq() to avoid
  a useless warning.
* Handled probe deferral correctly.
* Applied a massive s/nfc/nandc/ as suggested by Geert to avoid
  confusions with the near-field-communication device.
* Mentioned Evatronix as original authors of the IP in the commit log and
  in the header.
* Added an additional check on the validity of the child nodes reg property.
* A couple of style fixes.

Miquel Raynal (4):
  dt-bindings: mtd: renesas: Describe Renesas R-Car Gen3 & RZ/N1 NAND
    controller
  mtd: rawnand: renesas: Add new NAND controller driver
  MAINTAINERS: Add an entry for Renesas NAND controller
  ARM: dts: r9a06g032: Describe the NAND controller

 .../bindings/mtd/renesas-nandc.yaml           |   66 +
 MAINTAINERS                                   |    7 +
 arch/arm/boot/dts/r9a06g032.dtsi              |   11 +
 drivers/mtd/nand/raw/Kconfig                  |    7 +
 drivers/mtd/nand/raw/Makefile                 |    1 +
 .../mtd/nand/raw/renesas-nand-controller.c    | 1424 +++++++++++++++++
 6 files changed, 1516 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
 create mode 100644 drivers/mtd/nand/raw/renesas-nand-controller.c

-- 
2.27.0


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

* [PATCH v6 0/4] Renesas NAND controller support
@ 2021-12-17 14:20 ` Miquel Raynal
  0 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-17 14:20 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Rob Herring, devicetree, Milan Stevanovic, Jimmy Lalande,
	Thomas Petazzoni, linux-renesas-soc, Magnus Damm,
	Gareth Williams, Phil Edworthy, Geert Uytterhoeven, Wolfram Sang,
	Chris Brandt, Ralph Siemsen, Miquel Raynal

Hello,

Here is a short series bringing support for Renesas NAND controller
found on R-Car Gen3 and RZ/N1 SoCs.

The driver has been tested with a fully-upstream device tree on top of a
v5.16-rc4. The DT used is very close to the r9a06g032-db.

Cheers,
Miquèl

Changes in v6:
* Added Geert's R-by on the DT patch.
* Changed all the naming due to Wolfram's feedback. This controller is
  also found on the R-Car Gen3 family. Hence I used a much more generic
  name for the driver, the bindings, the variables, etc. The bindings
  have been updated to match the two different SoC families as well.
* Updated the MODULE_LICENSE macro as suggested by Wolfram.
 
Changes in v5:
* Add Rob's ack on the bindings.
* Dropped the #address/size-cells properties (handled by nand-controller.yaml).
* Fixed a typo reported by the kernel test robot (when building as a module).

Changes in v4:
* Set unevaluatedProperties set to false in the bindings.
* Change the clock names by removing the nand_ prefix which is
  redundant, even though the clocks are named like this in the spec. The
  name remains clear enough anyway.

Changes in v3:
* Rebased on top of a fully-upstream recent kernel.
* Renamed the clocks in the bindings and the driver to match the
  documentation (lower-cased): nand_hclk & nand_eclk.
* Added a new commit describing the NAND controller in the r9a06g032
  DTSI.
* Added the Reviewed-by and Tested-by tags received.

Changes in v2:
* Added the family-specific rzn1 compatible as suggested by Geert.
  Updated the bindings, the binding file name, the compatible used in
  the driver, the MAINTAINERS entry, etc.
* Added an ARCH_RENESAS Kconfig dependency.
* Changed the type (to unsigned) of a couple of variables.
* Returned earlier when possible to reduce indentation.
* Used platform_get_irq_optional() instead of platform_get_irq() to avoid
  a useless warning.
* Handled probe deferral correctly.
* Applied a massive s/nfc/nandc/ as suggested by Geert to avoid
  confusions with the near-field-communication device.
* Mentioned Evatronix as original authors of the IP in the commit log and
  in the header.
* Added an additional check on the validity of the child nodes reg property.
* A couple of style fixes.

Miquel Raynal (4):
  dt-bindings: mtd: renesas: Describe Renesas R-Car Gen3 & RZ/N1 NAND
    controller
  mtd: rawnand: renesas: Add new NAND controller driver
  MAINTAINERS: Add an entry for Renesas NAND controller
  ARM: dts: r9a06g032: Describe the NAND controller

 .../bindings/mtd/renesas-nandc.yaml           |   66 +
 MAINTAINERS                                   |    7 +
 arch/arm/boot/dts/r9a06g032.dtsi              |   11 +
 drivers/mtd/nand/raw/Kconfig                  |    7 +
 drivers/mtd/nand/raw/Makefile                 |    1 +
 .../mtd/nand/raw/renesas-nand-controller.c    | 1424 +++++++++++++++++
 6 files changed, 1516 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
 create mode 100644 drivers/mtd/nand/raw/renesas-nand-controller.c

-- 
2.27.0


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

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

* [PATCH v6 1/4] dt-bindings: mtd: renesas: Describe Renesas R-Car Gen3 & RZ/N1 NAND controller
  2021-12-17 14:20 ` Miquel Raynal
@ 2021-12-17 14:20   ` Miquel Raynal
  -1 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-17 14:20 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Rob Herring, devicetree, Milan Stevanovic, Jimmy Lalande,
	Thomas Petazzoni, linux-renesas-soc, Magnus Damm,
	Gareth Williams, Phil Edworthy, Geert Uytterhoeven, Wolfram Sang,
	Chris Brandt, Ralph Siemsen, Miquel Raynal, Geert Uytterhoeven,
	Rob Herring

Add a Yaml description for this Renesas NAND controller.

As this controller is embedded on different SoC families, provide:
* a family-specific "r-car-gen3" compatible and a more specific
  "r8a77951" one
* a family-specific "rzn1" compatible and a more specific "r9a06g032"
  one

More compatibles can be added later if new SoCs with this controller
must be supported.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/mtd/renesas-nandc.yaml           | 66 +++++++++++++++++++
 1 file changed, 66 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/renesas-nandc.yaml

diff --git a/Documentation/devicetree/bindings/mtd/renesas-nandc.yaml b/Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
new file mode 100644
index 000000000000..71886dbda35c
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mtd/renesas-nandc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas R-Car Gen3 & RZ/N1x NAND flash controller device tree bindings
+
+maintainers:
+  - Miquel Raynal <miquel.raynal@bootlin.com>
+
+allOf:
+  - $ref: "nand-controller.yaml"
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          - enum:
+              - renesas,r8a77951-nandc
+          - const: renesas,rcar-gen3-nandc
+
+      - items:
+          - enum:
+              - renesas,r9a06g032-nandc
+          - const: renesas,rzn1-nandc
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: APB host controller clock
+      - description: External NAND bus clock
+
+  clock-names:
+    items:
+      - const: hclk
+      - const: eclk
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - interrupts
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/r9a06g032-sysctrl.h>
+
+    nand-controller@40102000 {
+        compatible = "renesas,r9a06g032-nandc", "renesas,rzn1-nandc";
+        reg = <0x40102000 0x2000>;
+        interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&sysctrl R9A06G032_HCLK_NAND>, <&sysctrl R9A06G032_CLK_NAND>;
+        clock-names = "hclk", "eclk";
+        #address-cells = <1>;
+        #size-cells = <0>;
+    };
-- 
2.27.0


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

* [PATCH v6 1/4] dt-bindings: mtd: renesas: Describe Renesas R-Car Gen3 & RZ/N1 NAND controller
@ 2021-12-17 14:20   ` Miquel Raynal
  0 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-17 14:20 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Rob Herring, devicetree, Milan Stevanovic, Jimmy Lalande,
	Thomas Petazzoni, linux-renesas-soc, Magnus Damm,
	Gareth Williams, Phil Edworthy, Geert Uytterhoeven, Wolfram Sang,
	Chris Brandt, Ralph Siemsen, Miquel Raynal, Geert Uytterhoeven,
	Rob Herring

Add a Yaml description for this Renesas NAND controller.

As this controller is embedded on different SoC families, provide:
* a family-specific "r-car-gen3" compatible and a more specific
  "r8a77951" one
* a family-specific "rzn1" compatible and a more specific "r9a06g032"
  one

More compatibles can be added later if new SoCs with this controller
must be supported.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/mtd/renesas-nandc.yaml           | 66 +++++++++++++++++++
 1 file changed, 66 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/renesas-nandc.yaml

diff --git a/Documentation/devicetree/bindings/mtd/renesas-nandc.yaml b/Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
new file mode 100644
index 000000000000..71886dbda35c
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mtd/renesas-nandc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas R-Car Gen3 & RZ/N1x NAND flash controller device tree bindings
+
+maintainers:
+  - Miquel Raynal <miquel.raynal@bootlin.com>
+
+allOf:
+  - $ref: "nand-controller.yaml"
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          - enum:
+              - renesas,r8a77951-nandc
+          - const: renesas,rcar-gen3-nandc
+
+      - items:
+          - enum:
+              - renesas,r9a06g032-nandc
+          - const: renesas,rzn1-nandc
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    items:
+      - description: APB host controller clock
+      - description: External NAND bus clock
+
+  clock-names:
+    items:
+      - const: hclk
+      - const: eclk
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - interrupts
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/r9a06g032-sysctrl.h>
+
+    nand-controller@40102000 {
+        compatible = "renesas,r9a06g032-nandc", "renesas,rzn1-nandc";
+        reg = <0x40102000 0x2000>;
+        interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&sysctrl R9A06G032_HCLK_NAND>, <&sysctrl R9A06G032_CLK_NAND>;
+        clock-names = "hclk", "eclk";
+        #address-cells = <1>;
+        #size-cells = <0>;
+    };
-- 
2.27.0


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

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

* [PATCH v6 2/4] mtd: rawnand: renesas: Add new NAND controller driver
  2021-12-17 14:20 ` Miquel Raynal
@ 2021-12-17 14:20   ` Miquel Raynal
  -1 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-17 14:20 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Rob Herring, devicetree, Milan Stevanovic, Jimmy Lalande,
	Thomas Petazzoni, linux-renesas-soc, Magnus Damm,
	Gareth Williams, Phil Edworthy, Geert Uytterhoeven, Wolfram Sang,
	Chris Brandt, Ralph Siemsen, Miquel Raynal

Introduce Renesas NAND controller driver which currently supports the
following features on R-Car Gen3 and RZ/N1 SoCs:
- All ONFI timing modes
- Different configurations of its internal ECC controller
- On-die (not tested) and software ECC support
- Several chips (not tested)
- Subpage accesses
- DMA and PIO

This controller was originally provided by Evatronix before being bought
by Cadence.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Tested-by: Ralph Siemsen <ralph.siemsen@linaro.org>
---
 drivers/mtd/nand/raw/Kconfig                  |    7 +
 drivers/mtd/nand/raw/Makefile                 |    1 +
 .../mtd/nand/raw/renesas-nand-controller.c    | 1424 +++++++++++++++++
 3 files changed, 1432 insertions(+)
 create mode 100644 drivers/mtd/nand/raw/renesas-nand-controller.c

diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index 67b7cb67c030..fead9e2c505b 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -461,6 +461,13 @@ config MTD_NAND_PL35X
 	  Enables support for PrimeCell SMC PL351 and PL353 NAND
 	  controller found on Zynq7000.
 
+config MTD_NAND_RENESAS
+	tristate "Renesas R-Car Gen3 & RZ/N1 NAND controller"
+	depends on ARCH_RENESAS || COMPILE_TEST
+	help
+	  Enables support for the NAND controller found on Renesas R-Car
+	  Gen3 and RZ/N1 SoC families.
+
 comment "Misc"
 
 config MTD_SM_COMMON
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index 2f97958c3a33..88a566513c56 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_MTD_NAND_ARASAN)		+= arasan-nand-controller.o
 obj-$(CONFIG_MTD_NAND_INTEL_LGM)	+= intel-nand-controller.o
 obj-$(CONFIG_MTD_NAND_ROCKCHIP)		+= rockchip-nand-controller.o
 obj-$(CONFIG_MTD_NAND_PL35X)		+= pl35x-nand-controller.o
+obj-$(CONFIG_MTD_NAND_RENESAS)		+= renesas-nand-controller.o
 
 nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o nand_ids.o
 nand-objs += nand_onfi.o
diff --git a/drivers/mtd/nand/raw/renesas-nand-controller.c b/drivers/mtd/nand/raw/renesas-nand-controller.c
new file mode 100644
index 000000000000..428e08362956
--- /dev/null
+++ b/drivers/mtd/nand/raw/renesas-nand-controller.c
@@ -0,0 +1,1424 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Evatronix/Renesas R-Car Gen3, RZ/N1D, RZ/N1S, RZ/N1L NAND controller driver
+ *
+ * Copyright (C) 2021 Schneider Electric
+ * Author: Miquel RAYNAL <miquel.raynal@bootlin.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define COMMAND_REG 0x00
+#define   COMMAND_SEQ(x) FIELD_PREP(GENMASK(5, 0), (x))
+#define     COMMAND_SEQ_10 COMMAND_SEQ(0x2A)
+#define     COMMAND_SEQ_12 COMMAND_SEQ(0x0C)
+#define     COMMAND_SEQ_18 COMMAND_SEQ(0x32)
+#define     COMMAND_SEQ_19 COMMAND_SEQ(0x13)
+#define     COMMAND_SEQ_GEN_IN COMMAND_SEQ_18
+#define     COMMAND_SEQ_GEN_OUT COMMAND_SEQ_19
+#define     COMMAND_SEQ_READ_PAGE COMMAND_SEQ_10
+#define     COMMAND_SEQ_WRITE_PAGE COMMAND_SEQ_12
+#define   COMMAND_INPUT_SEL_AHBS 0
+#define   COMMAND_INPUT_SEL_DMA BIT(6)
+#define   COMMAND_FIFO_SEL 0
+#define   COMMAND_DATA_SEL BIT(7)
+#define   COMMAND_0(x) FIELD_PREP(GENMASK(15, 8), (x))
+#define   COMMAND_1(x) FIELD_PREP(GENMASK(23, 16), (x))
+#define   COMMAND_2(x) FIELD_PREP(GENMASK(31, 24), (x))
+
+#define CONTROL_REG 0x04
+#define   CONTROL_CHECK_RB_LINE 0
+#define   CONTROL_ECC_BLOCK_SIZE(x) FIELD_PREP(GENMASK(2, 1), (x))
+#define     CONTROL_ECC_BLOCK_SIZE_256 CONTROL_ECC_BLOCK_SIZE(0)
+#define     CONTROL_ECC_BLOCK_SIZE_512 CONTROL_ECC_BLOCK_SIZE(1)
+#define     CONTROL_ECC_BLOCK_SIZE_1024 CONTROL_ECC_BLOCK_SIZE(2)
+#define   CONTROL_INT_EN BIT(4)
+#define   CONTROL_ECC_EN BIT(5)
+#define   CONTROL_BLOCK_SIZE(x) FIELD_PREP(GENMASK(7, 6), (x))
+#define     CONTROL_BLOCK_SIZE_32P CONTROL_BLOCK_SIZE(0)
+#define     CONTROL_BLOCK_SIZE_64P CONTROL_BLOCK_SIZE(1)
+#define     CONTROL_BLOCK_SIZE_128P CONTROL_BLOCK_SIZE(2)
+#define     CONTROL_BLOCK_SIZE_256P CONTROL_BLOCK_SIZE(3)
+
+#define STATUS_REG 0x8
+#define   MEM_RDY(cs, reg) (FIELD_GET(GENMASK(3, 0), (reg)) & BIT(cs))
+#define   CTRL_RDY(reg) (FIELD_GET(BIT(8), (reg)) == 0)
+
+#define ECC_CTRL_REG 0x18
+#define   ECC_CTRL_CAP(x) FIELD_PREP(GENMASK(2, 0), (x))
+#define     ECC_CTRL_CAP_2B ECC_CTRL_CAP(0)
+#define     ECC_CTRL_CAP_4B ECC_CTRL_CAP(1)
+#define     ECC_CTRL_CAP_8B ECC_CTRL_CAP(2)
+#define     ECC_CTRL_CAP_16B ECC_CTRL_CAP(3)
+#define     ECC_CTRL_CAP_24B ECC_CTRL_CAP(4)
+#define     ECC_CTRL_CAP_32B ECC_CTRL_CAP(5)
+#define   ECC_CTRL_ERR_THRESHOLD(x) FIELD_PREP(GENMASK(13, 8), (x))
+
+#define INT_MASK_REG 0x10
+#define INT_STATUS_REG 0x14
+#define   INT_CMD_END BIT(1)
+#define   INT_DMA_END BIT(3)
+#define   INT_MEM_RDY(cs) FIELD_PREP(GENMASK(11, 8), BIT(cs))
+#define   INT_DMA_ENDED BIT(3)
+#define   MEM_IS_RDY(cs, reg) (FIELD_GET(GENMASK(11, 8), (reg)) & BIT(cs))
+#define   DMA_HAS_ENDED(reg) FIELD_GET(BIT(3), (reg))
+
+#define ECC_OFFSET_REG 0x1C
+#define   ECC_OFFSET(x) FIELD_PREP(GENMASK(15, 0), (x))
+
+#define ECC_STAT_REG 0x20
+#define   ECC_STAT_CORRECTABLE(cs, reg) (FIELD_GET(GENMASK(3, 0), (reg)) & BIT(cs))
+#define   ECC_STAT_UNCORRECTABLE(cs, reg) (FIELD_GET(GENMASK(11, 8), (reg)) & BIT(cs))
+
+#define ADDR0_COL_REG 0x24
+#define   ADDR0_COL(x) FIELD_PREP(GENMASK(15, 0), (x))
+
+#define ADDR0_ROW_REG 0x28
+#define   ADDR0_ROW(x) FIELD_PREP(GENMASK(23, 0), (x))
+
+#define ADDR1_COL_REG 0x2C
+#define   ADDR1_COL(x) FIELD_PREP(GENMASK(15, 0), (x))
+
+#define ADDR1_ROW_REG 0x30
+#define   ADDR1_ROW(x) FIELD_PREP(GENMASK(23, 0), (x))
+
+#define FIFO_DATA_REG 0x38
+
+#define DATA_REG 0x3C
+
+#define DATA_REG_SIZE_REG 0x40
+
+#define DMA_ADDR_LOW_REG 0x64
+
+#define DMA_ADDR_HIGH_REG 0x68
+
+#define DMA_CNT_REG 0x6C
+
+#define DMA_CTRL_REG 0x70
+#define   DMA_CTRL_INCREMENT_BURST_4 0
+#define   DMA_CTRL_REGISTER_MANAGED_MODE 0
+#define   DMA_CTRL_START BIT(7)
+
+#define MEM_CTRL_REG 0x80
+#define   MEM_CTRL_CS(cs) FIELD_PREP(GENMASK(1, 0), (cs))
+#define   MEM_CTRL_DIS_WP(cs) FIELD_PREP(GENMASK(11, 8), BIT((cs)))
+
+#define DATA_SIZE_REG 0x84
+#define   DATA_SIZE(x) FIELD_PREP(GENMASK(14, 0), (x))
+
+#define TIMINGS_ASYN_REG 0x88
+#define   TIMINGS_ASYN_TRWP(x) FIELD_PREP(GENMASK(3, 0), max((x), 1U) - 1)
+#define   TIMINGS_ASYN_TRWH(x) FIELD_PREP(GENMASK(7, 4), max((x), 1U) - 1)
+
+#define TIM_SEQ0_REG 0x90
+#define   TIM_SEQ0_TCCS(x) FIELD_PREP(GENMASK(5, 0), max((x), 1U) - 1)
+#define   TIM_SEQ0_TADL(x) FIELD_PREP(GENMASK(13, 8), max((x), 1U) - 1)
+#define   TIM_SEQ0_TRHW(x) FIELD_PREP(GENMASK(21, 16), max((x), 1U) - 1)
+#define   TIM_SEQ0_TWHR(x) FIELD_PREP(GENMASK(29, 24), max((x), 1U) - 1)
+
+#define TIM_SEQ1_REG 0x94
+#define   TIM_SEQ1_TWB(x) FIELD_PREP(GENMASK(5, 0), max((x), 1U) - 1)
+#define   TIM_SEQ1_TRR(x) FIELD_PREP(GENMASK(13, 8), max((x), 1U) - 1)
+#define   TIM_SEQ1_TWW(x) FIELD_PREP(GENMASK(21, 16), max((x), 1U) - 1)
+
+#define TIM_GEN_SEQ0_REG 0x98
+#define   TIM_GEN_SEQ0_D0(x) FIELD_PREP(GENMASK(5, 0), max((x), 1U) - 1)
+#define   TIM_GEN_SEQ0_D1(x) FIELD_PREP(GENMASK(13, 8), max((x), 1U) - 1)
+#define   TIM_GEN_SEQ0_D2(x) FIELD_PREP(GENMASK(21, 16), max((x), 1U) - 1)
+#define   TIM_GEN_SEQ0_D3(x) FIELD_PREP(GENMASK(29, 24), max((x), 1U) - 1)
+
+#define TIM_GEN_SEQ1_REG 0x9c
+#define   TIM_GEN_SEQ1_D4(x) FIELD_PREP(GENMASK(5, 0), max((x), 1U) - 1)
+#define   TIM_GEN_SEQ1_D5(x) FIELD_PREP(GENMASK(13, 8), max((x), 1U) - 1)
+#define   TIM_GEN_SEQ1_D6(x) FIELD_PREP(GENMASK(21, 16), max((x), 1U) - 1)
+#define   TIM_GEN_SEQ1_D7(x) FIELD_PREP(GENMASK(29, 24), max((x), 1U) - 1)
+
+#define TIM_GEN_SEQ2_REG 0xA0
+#define   TIM_GEN_SEQ2_D8(x) FIELD_PREP(GENMASK(5, 0), max((x), 1U) - 1)
+#define   TIM_GEN_SEQ2_D9(x) FIELD_PREP(GENMASK(13, 8), max((x), 1U) - 1)
+#define   TIM_GEN_SEQ2_D10(x) FIELD_PREP(GENMASK(21, 16), max((x), 1U) - 1)
+#define   TIM_GEN_SEQ2_D11(x) FIELD_PREP(GENMASK(29, 24), max((x), 1U) - 1)
+
+#define FIFO_INIT_REG 0xB4
+#define   FIFO_INIT BIT(0)
+
+#define FIFO_STATE_REG 0xB4
+#define   FIFO_STATE_R_EMPTY(reg) FIELD_GET(BIT(0), (reg))
+#define   FIFO_STATE_W_FULL(reg) FIELD_GET(BIT(1), (reg))
+#define   FIFO_STATE_C_EMPTY(reg) FIELD_GET(BIT(2), (reg))
+#define   FIFO_STATE_R_FULL(reg) FIELD_GET(BIT(6), (reg))
+#define   FIFO_STATE_W_EMPTY(reg) FIELD_GET(BIT(7), (reg))
+
+#define GEN_SEQ_CTRL_REG 0xB8
+#define   GEN_SEQ_CMD0_EN BIT(0)
+#define   GEN_SEQ_CMD1_EN BIT(1)
+#define   GEN_SEQ_CMD2_EN BIT(2)
+#define   GEN_SEQ_CMD3_EN BIT(3)
+#define   GEN_SEQ_COL_A0(x) FIELD_PREP(GENMASK(5, 4), min((x), 2U))
+#define   GEN_SEQ_COL_A1(x) FIELD_PREP(GENMASK(7, 6), min((x), 2U))
+#define   GEN_SEQ_ROW_A0(x) FIELD_PREP(GENMASK(9, 8), min((x), 3U))
+#define   GEN_SEQ_ROW_A1(x) FIELD_PREP(GENMASK(11, 10), min((x), 3U))
+#define   GEN_SEQ_DATA_EN BIT(12)
+#define   GEN_SEQ_DELAY_EN(x) FIELD_PREP(GENMASK(14, 13), (x))
+#define     GEN_SEQ_DELAY0_EN GEN_SEQ_DELAY_EN(1)
+#define     GEN_SEQ_DELAY1_EN GEN_SEQ_DELAY_EN(2)
+#define   GEN_SEQ_IMD_SEQ BIT(15)
+#define   GEN_SEQ_COMMAND_3(x) FIELD_PREP(GENMASK(26, 16), (x))
+
+#define DMA_TLVL_REG 0x114
+#define   DMA_TLVL(x) FIELD_PREP(GENMASK(7, 0), (x))
+#define   DMA_TLVL_MAX DMA_TLVL(0xFF)
+
+#define TIM_GEN_SEQ3_REG 0x134
+#define   TIM_GEN_SEQ3_D12(x) FIELD_PREP(GENMASK(5, 0), max((x), 1U) - 1)
+
+#define ECC_CNT_REG 0x14C
+#define   ECC_CNT(cs, reg) FIELD_GET(GENMASK(5, 0), (reg) >> ((cs) * 8))
+
+#define RNANDC_CS_NUM 4
+
+#define TO_CYCLES64(ps, period_ns) ((unsigned int)DIV_ROUND_UP_ULL(div_u64(ps, 1000), \
+								   period_ns))
+
+struct rnand_chip_sel {
+	unsigned int cs;
+};
+
+struct rnand_chip {
+	struct nand_chip chip;
+	struct list_head node;
+	int selected_die;
+	u32 ctrl;
+	unsigned int nsels;
+	u32 control;
+	u32 ecc_ctrl;
+	u32 timings_asyn;
+	u32 tim_seq0;
+	u32 tim_seq1;
+	u32 tim_gen_seq0;
+	u32 tim_gen_seq1;
+	u32 tim_gen_seq2;
+	u32 tim_gen_seq3;
+	struct rnand_chip_sel sels[];
+};
+
+struct rnandc {
+	struct nand_controller controller;
+	struct device *dev;
+	void __iomem *regs;
+	struct clk *hclk;
+	struct clk *eclk;
+	unsigned long assigned_cs;
+	struct list_head chips;
+	struct nand_chip *selected_chip;
+	struct completion complete;
+	bool use_polling;
+	u8 *buf;
+	unsigned int buf_sz;
+};
+
+struct rnandc_op {
+	u32 command;
+	u32 addr0_col;
+	u32 addr0_row;
+	u32 addr1_col;
+	u32 addr1_row;
+	u32 data_size;
+	u32 ecc_offset;
+	u32 gen_seq_ctrl;
+	u8 *buf;
+	bool read;
+	unsigned int len;
+};
+
+static inline struct rnandc *to_rnandc(struct nand_controller *ctrl)
+{
+	return container_of(ctrl, struct rnandc, controller);
+}
+
+static inline struct rnand_chip *to_rnand(struct nand_chip *chip)
+{
+	return container_of(chip, struct rnand_chip, chip);
+}
+
+static inline unsigned int to_rnandc_cs(struct rnand_chip *nand)
+{
+	return nand->sels[nand->selected_die].cs;
+}
+
+static void rnandc_dis_correction(struct rnandc *rnandc)
+{
+	u32 control;
+
+	control = readl_relaxed(rnandc->regs + CONTROL_REG);
+	control &= ~CONTROL_ECC_EN;
+	writel_relaxed(control, rnandc->regs + CONTROL_REG);
+}
+
+static void rnandc_en_correction(struct rnandc *rnandc)
+{
+	u32 control;
+
+	control = readl_relaxed(rnandc->regs + CONTROL_REG);
+	control |= CONTROL_ECC_EN;
+	writel_relaxed(control, rnandc->regs + CONTROL_REG);
+}
+
+static void rnandc_clear_status(struct rnandc *rnandc)
+{
+	writel_relaxed(0, rnandc->regs + INT_STATUS_REG);
+	writel_relaxed(0, rnandc->regs + ECC_STAT_REG);
+	writel_relaxed(0, rnandc->regs + ECC_CNT_REG);
+}
+
+static void rnandc_dis_interrupts(struct rnandc *rnandc)
+{
+	writel_relaxed(0, rnandc->regs + INT_MASK_REG);
+}
+
+static void rnandc_en_interrupts(struct rnandc *rnandc, u32 val)
+{
+	if (!rnandc->use_polling)
+		writel_relaxed(val, rnandc->regs + INT_MASK_REG);
+}
+
+static void rnandc_clear_fifo(struct rnandc *rnandc)
+{
+	writel_relaxed(FIFO_INIT, rnandc->regs + FIFO_INIT_REG);
+}
+
+static void rnandc_select_target(struct nand_chip *chip, int die_nr)
+{
+	struct rnand_chip *rnand = to_rnand(chip);
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+	unsigned int cs = rnand->sels[die_nr].cs;
+
+	if (chip == rnandc->selected_chip && die_nr == rnand->selected_die)
+		return;
+
+	rnandc_clear_status(rnandc);
+	writel_relaxed(MEM_CTRL_CS(cs) | MEM_CTRL_DIS_WP(cs), rnandc->regs + MEM_CTRL_REG);
+	writel_relaxed(rnand->control, rnandc->regs + CONTROL_REG);
+	writel_relaxed(rnand->ecc_ctrl, rnandc->regs + ECC_CTRL_REG);
+	writel_relaxed(rnand->timings_asyn, rnandc->regs + TIMINGS_ASYN_REG);
+	writel_relaxed(rnand->tim_seq0, rnandc->regs + TIM_SEQ0_REG);
+	writel_relaxed(rnand->tim_seq1, rnandc->regs + TIM_SEQ1_REG);
+	writel_relaxed(rnand->tim_gen_seq0, rnandc->regs + TIM_GEN_SEQ0_REG);
+	writel_relaxed(rnand->tim_gen_seq1, rnandc->regs + TIM_GEN_SEQ1_REG);
+	writel_relaxed(rnand->tim_gen_seq2, rnandc->regs + TIM_GEN_SEQ2_REG);
+	writel_relaxed(rnand->tim_gen_seq3, rnandc->regs + TIM_GEN_SEQ3_REG);
+
+	rnandc->selected_chip = chip;
+	rnand->selected_die = die_nr;
+}
+
+static void rnandc_trigger_op(struct rnandc *rnandc, struct rnandc_op *rop)
+{
+	writel_relaxed(rop->addr0_col, rnandc->regs + ADDR0_COL_REG);
+	writel_relaxed(rop->addr0_row, rnandc->regs + ADDR0_ROW_REG);
+	writel_relaxed(rop->addr1_col, rnandc->regs + ADDR1_COL_REG);
+	writel_relaxed(rop->addr1_row, rnandc->regs + ADDR1_ROW_REG);
+	writel_relaxed(rop->ecc_offset, rnandc->regs + ECC_OFFSET_REG);
+	writel_relaxed(rop->gen_seq_ctrl, rnandc->regs + GEN_SEQ_CTRL_REG);
+	writel_relaxed(DATA_SIZE(rop->len), rnandc->regs + DATA_SIZE_REG);
+	writel_relaxed(rop->command, rnandc->regs + COMMAND_REG);
+}
+
+static void rnandc_trigger_dma(struct rnandc *rnandc)
+{
+	writel_relaxed(DMA_CTRL_INCREMENT_BURST_4 |
+		       DMA_CTRL_REGISTER_MANAGED_MODE |
+		       DMA_CTRL_START, rnandc->regs + DMA_CTRL_REG);
+}
+
+static irqreturn_t rnandc_irq_handler(int irq, void *private)
+{
+	struct rnandc *rnandc = private;
+
+	rnandc_dis_interrupts(rnandc);
+	complete(&rnandc->complete);
+
+	return IRQ_HANDLED;
+}
+
+static int rnandc_wait_end_of_op(struct rnandc *rnandc,
+				 struct nand_chip *chip)
+{
+	struct rnand_chip *rnand = to_rnand(chip);
+	unsigned int cs = to_rnandc_cs(rnand);
+	u32 status;
+	int ret;
+
+	ret = readl_poll_timeout(rnandc->regs + STATUS_REG, status,
+				 MEM_RDY(cs, status) && CTRL_RDY(status),
+				 1, 100000);
+	if (ret)
+		dev_err(rnandc->dev, "Operation timed out, status: 0x%08x\n",
+			status);
+
+	return ret;
+}
+
+static int rnandc_wait_end_of_io(struct rnandc *rnandc,
+				 struct nand_chip *chip)
+{
+	int timeout_ms = 1000;
+	int ret;
+
+	if (rnandc->use_polling) {
+		struct rnand_chip *rnand = to_rnand(chip);
+		unsigned int cs = to_rnandc_cs(rnand);
+		u32 status;
+
+		ret = readl_poll_timeout(rnandc->regs + INT_STATUS_REG, status,
+					 MEM_IS_RDY(cs, status) &
+					 DMA_HAS_ENDED(status),
+					 0, timeout_ms * 1000);
+	} else {
+		ret = wait_for_completion_timeout(&rnandc->complete,
+						  msecs_to_jiffies(timeout_ms));
+		if (!ret)
+			ret = -ETIMEDOUT;
+		else
+			ret = 0;
+	}
+
+	return ret;
+}
+
+static int rnandc_read_page_hw_ecc(struct nand_chip *chip, u8 *buf,
+				   int oob_required, int page)
+{
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct rnand_chip *rnand = to_rnand(chip);
+	unsigned int cs = to_rnandc_cs(rnand);
+	struct rnandc_op rop = {
+		.command = COMMAND_INPUT_SEL_DMA | COMMAND_0(NAND_CMD_READ0) |
+			   COMMAND_2(NAND_CMD_READSTART) | COMMAND_FIFO_SEL |
+			   COMMAND_SEQ_READ_PAGE,
+		.addr0_row = page,
+		.len = mtd->writesize,
+		.ecc_offset = ECC_OFFSET(mtd->writesize + 2),
+	};
+	unsigned int max_bitflips = 0;
+	dma_addr_t dma_addr;
+	u32 ecc_stat;
+	int bf, ret, i;
+
+	/* Prepare controller */
+	rnandc_select_target(chip, chip->cur_cs);
+	rnandc_clear_status(rnandc);
+	reinit_completion(&rnandc->complete);
+	rnandc_en_interrupts(rnandc, INT_DMA_ENDED);
+	rnandc_en_correction(rnandc);
+
+	/* Configure DMA */
+	dma_addr = dma_map_single(rnandc->dev, rnandc->buf, mtd->writesize,
+				  DMA_FROM_DEVICE);
+	writel(dma_addr, rnandc->regs + DMA_ADDR_LOW_REG);
+	writel(mtd->writesize, rnandc->regs + DMA_CNT_REG);
+	writel(DMA_TLVL_MAX, rnandc->regs + DMA_TLVL_REG);
+
+	rnandc_trigger_op(rnandc, &rop);
+	rnandc_trigger_dma(rnandc);
+
+	ret = rnandc_wait_end_of_io(rnandc, chip);
+	dma_unmap_single(rnandc->dev, dma_addr, mtd->writesize, DMA_FROM_DEVICE);
+	rnandc_dis_correction(rnandc);
+	if (ret) {
+		dev_err(rnandc->dev, "Read page operation never ending\n");
+		return ret;
+	}
+
+	ecc_stat = readl_relaxed(rnandc->regs + ECC_STAT_REG);
+
+	if (oob_required || ECC_STAT_UNCORRECTABLE(cs, ecc_stat)) {
+		ret = nand_change_read_column_op(chip, mtd->writesize,
+						 chip->oob_poi, mtd->oobsize,
+						 false);
+		if (ret)
+			return ret;
+	}
+
+	if (ECC_STAT_UNCORRECTABLE(cs, ecc_stat)) {
+		for (i = 0; i < chip->ecc.steps; i++) {
+			unsigned int off = i * chip->ecc.size;
+			unsigned int eccoff = i * chip->ecc.bytes;
+
+			bf = nand_check_erased_ecc_chunk(rnandc->buf + off,
+							 chip->ecc.size,
+							 chip->oob_poi + 2 + eccoff,
+							 chip->ecc.bytes,
+							 NULL, 0,
+							 chip->ecc.strength);
+			if (bf < 0) {
+				mtd->ecc_stats.failed++;
+			} else {
+				mtd->ecc_stats.corrected += bf;
+				max_bitflips = max_t(unsigned int, max_bitflips, bf);
+			}
+		}
+	} else if (ECC_STAT_CORRECTABLE(cs, ecc_stat)) {
+		bf = ECC_CNT(cs, readl_relaxed(rnandc->regs + ECC_CNT_REG));
+		/*
+		 * The number of bitflips is an approximation given the fact
+		 * that this controller does not provide per-chunk details but
+		 * only gives statistics on the entire page.
+		 */
+		mtd->ecc_stats.corrected += bf;
+	}
+
+	memcpy(buf, rnandc->buf, mtd->writesize);
+
+	return 0;
+}
+
+static int rnandc_read_subpage_hw_ecc(struct nand_chip *chip, u32 req_offset,
+				      u32 req_len, u8 *bufpoi, int page)
+{
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct rnand_chip *rnand = to_rnand(chip);
+	unsigned int cs = to_rnandc_cs(rnand);
+	unsigned int page_off = round_down(req_offset, chip->ecc.size);
+	unsigned int real_len = round_up(req_offset + req_len - page_off,
+					 chip->ecc.size);
+	unsigned int start_chunk = page_off / chip->ecc.size;
+	unsigned int nchunks = real_len / chip->ecc.size;
+	unsigned int ecc_off = 2 + (start_chunk * chip->ecc.bytes);
+	struct rnandc_op rop = {
+		.command = COMMAND_INPUT_SEL_AHBS | COMMAND_0(NAND_CMD_READ0) |
+			   COMMAND_2(NAND_CMD_READSTART) | COMMAND_FIFO_SEL |
+			   COMMAND_SEQ_READ_PAGE,
+		.addr0_row = page,
+		.addr0_col = page_off,
+		.len = real_len,
+		.ecc_offset = ECC_OFFSET(mtd->writesize + ecc_off),
+	};
+	unsigned int max_bitflips = 0, i;
+	u32 ecc_stat;
+	int bf, ret;
+
+	/* Prepare controller */
+	rnandc_select_target(chip, chip->cur_cs);
+	rnandc_clear_status(rnandc);
+	rnandc_en_correction(rnandc);
+	rnandc_trigger_op(rnandc, &rop);
+
+	while (!FIFO_STATE_C_EMPTY(readl(rnandc->regs + FIFO_STATE_REG)))
+		cpu_relax();
+
+	while (FIFO_STATE_R_EMPTY(readl(rnandc->regs + FIFO_STATE_REG)))
+		cpu_relax();
+
+	ioread32_rep(rnandc->regs + FIFO_DATA_REG, bufpoi + page_off,
+		     real_len / 4);
+
+	if (!FIFO_STATE_R_EMPTY(readl(rnandc->regs + FIFO_STATE_REG))) {
+		dev_err(rnandc->dev, "Clearing residual data in the read FIFO\n");
+		rnandc_clear_fifo(rnandc);
+	}
+
+	ret = rnandc_wait_end_of_op(rnandc, chip);
+	rnandc_dis_correction(rnandc);
+	if (ret) {
+		dev_err(rnandc->dev, "Read subpage operation never ending\n");
+		return ret;
+	}
+
+	ecc_stat = readl_relaxed(rnandc->regs + ECC_STAT_REG);
+
+	if (ECC_STAT_UNCORRECTABLE(cs, ecc_stat)) {
+		ret = nand_change_read_column_op(chip, mtd->writesize,
+						 chip->oob_poi, mtd->oobsize,
+						 false);
+		if (ret)
+			return ret;
+
+		for (i = start_chunk; i < nchunks; i++) {
+			unsigned int dataoff = i * chip->ecc.size;
+			unsigned int eccoff = 2 + (i * chip->ecc.bytes);
+
+			bf = nand_check_erased_ecc_chunk(bufpoi + dataoff,
+							 chip->ecc.size,
+							 chip->oob_poi + eccoff,
+							 chip->ecc.bytes,
+							 NULL, 0,
+							 chip->ecc.strength);
+			if (bf < 0) {
+				mtd->ecc_stats.failed++;
+			} else {
+				mtd->ecc_stats.corrected += bf;
+				max_bitflips = max_t(unsigned int, max_bitflips, bf);
+			}
+		}
+	} else if (ECC_STAT_CORRECTABLE(cs, ecc_stat)) {
+		bf = ECC_CNT(cs, readl_relaxed(rnandc->regs + ECC_CNT_REG));
+		/*
+		 * The number of bitflips is an approximation given the fact
+		 * that this controller does not provide per-chunk details but
+		 * only gives statistics on the entire page.
+		 */
+		mtd->ecc_stats.corrected += bf;
+	}
+
+	return 0;
+}
+
+static int rnandc_write_page_hw_ecc(struct nand_chip *chip, const u8 *buf,
+				    int oob_required, int page)
+{
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct rnand_chip *rnand = to_rnand(chip);
+	unsigned int cs = to_rnandc_cs(rnand);
+	struct rnandc_op rop = {
+		.command = COMMAND_INPUT_SEL_DMA | COMMAND_0(NAND_CMD_SEQIN) |
+			   COMMAND_1(NAND_CMD_PAGEPROG) | COMMAND_FIFO_SEL |
+			   COMMAND_SEQ_WRITE_PAGE,
+		.addr0_row = page,
+		.len = mtd->writesize,
+		.ecc_offset = ECC_OFFSET(mtd->writesize + 2),
+	};
+	dma_addr_t dma_addr;
+	int ret;
+
+	memcpy(rnandc->buf, buf, mtd->writesize);
+
+	/* Prepare controller */
+	rnandc_select_target(chip, chip->cur_cs);
+	rnandc_clear_status(rnandc);
+	reinit_completion(&rnandc->complete);
+	rnandc_en_interrupts(rnandc, INT_MEM_RDY(cs));
+	rnandc_en_correction(rnandc);
+
+	/* Configure DMA */
+	dma_addr = dma_map_single(rnandc->dev, (void *)rnandc->buf, mtd->writesize,
+				  DMA_TO_DEVICE);
+	writel(dma_addr, rnandc->regs + DMA_ADDR_LOW_REG);
+	writel(mtd->writesize, rnandc->regs + DMA_CNT_REG);
+	writel(DMA_TLVL_MAX, rnandc->regs + DMA_TLVL_REG);
+
+	rnandc_trigger_op(rnandc, &rop);
+	rnandc_trigger_dma(rnandc);
+
+	ret = rnandc_wait_end_of_io(rnandc, chip);
+	dma_unmap_single(rnandc->dev, dma_addr, mtd->writesize, DMA_TO_DEVICE);
+	rnandc_dis_correction(rnandc);
+	if (ret) {
+		dev_err(rnandc->dev, "Write page operation never ending\n");
+		return ret;
+	}
+
+	if (!oob_required)
+		return 0;
+
+	return nand_change_write_column_op(chip, mtd->writesize, chip->oob_poi,
+					   mtd->oobsize, false);
+}
+
+static int rnandc_write_subpage_hw_ecc(struct nand_chip *chip, u32 req_offset,
+				       u32 req_len, const u8 *bufpoi,
+				       int oob_required, int page)
+{
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	unsigned int page_off = round_down(req_offset, chip->ecc.size);
+	unsigned int real_len = round_up(req_offset + req_len - page_off,
+					 chip->ecc.size);
+	unsigned int start_chunk = page_off / chip->ecc.size;
+	unsigned int ecc_off = 2 + (start_chunk * chip->ecc.bytes);
+	struct rnandc_op rop = {
+		.command = COMMAND_INPUT_SEL_AHBS | COMMAND_0(NAND_CMD_SEQIN) |
+			   COMMAND_1(NAND_CMD_PAGEPROG) | COMMAND_FIFO_SEL |
+			   COMMAND_SEQ_WRITE_PAGE,
+		.addr0_row = page,
+		.addr0_col = page_off,
+		.len = real_len,
+		.ecc_offset = ECC_OFFSET(mtd->writesize + ecc_off),
+	};
+	int ret;
+
+	/* Prepare controller */
+	rnandc_select_target(chip, chip->cur_cs);
+	rnandc_clear_status(rnandc);
+	rnandc_en_correction(rnandc);
+	rnandc_trigger_op(rnandc, &rop);
+
+	while (FIFO_STATE_W_FULL(readl(rnandc->regs + FIFO_STATE_REG)))
+		cpu_relax();
+
+	iowrite32_rep(rnandc->regs + FIFO_DATA_REG, bufpoi + page_off,
+		      real_len / 4);
+
+	while (!FIFO_STATE_W_EMPTY(readl(rnandc->regs + FIFO_STATE_REG)))
+		cpu_relax();
+
+	ret = rnandc_wait_end_of_op(rnandc, chip);
+	rnandc_dis_correction(rnandc);
+	if (ret) {
+		dev_err(rnandc->dev, "Write subpage operation never ending\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * This controller is simple enough and thus does not need to use the parser
+ * provided by the core, instead, handle every situation here.
+ */
+static int rnandc_exec_op(struct nand_chip *chip,
+			  const struct nand_operation *op, bool check_only)
+{
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+	const struct nand_op_instr *instr = NULL;
+	struct rnandc_op rop = {
+		.command = COMMAND_INPUT_SEL_AHBS,
+		.gen_seq_ctrl = GEN_SEQ_IMD_SEQ,
+	};
+	unsigned int cmd_phase = 0, addr_phase = 0, data_phase = 0,
+		delay_phase = 0, delays = 0;
+	unsigned int op_id, col_addrs, row_addrs, naddrs, remainder, words, i;
+	const u8 *addrs;
+	u32 last_bytes;
+	int ret;
+
+	if (!check_only)
+		rnandc_select_target(chip, op->cs);
+
+	for (op_id = 0; op_id < op->ninstrs; op_id++) {
+		instr = &op->instrs[op_id];
+
+		nand_op_trace("  ", instr);
+
+		switch (instr->type) {
+		case NAND_OP_CMD_INSTR:
+			switch (cmd_phase++) {
+			case 0:
+				rop.command |= COMMAND_0(instr->ctx.cmd.opcode);
+				rop.gen_seq_ctrl |= GEN_SEQ_CMD0_EN;
+				break;
+			case 1:
+				rop.gen_seq_ctrl |= GEN_SEQ_COMMAND_3(instr->ctx.cmd.opcode);
+				rop.gen_seq_ctrl |= GEN_SEQ_CMD3_EN;
+				if (addr_phase == 0)
+					addr_phase = 1;
+				break;
+			case 2:
+				rop.command |= COMMAND_2(instr->ctx.cmd.opcode);
+				rop.gen_seq_ctrl |= GEN_SEQ_CMD2_EN;
+				if (addr_phase <= 1)
+					addr_phase = 2;
+				break;
+			case 3:
+				rop.command |= COMMAND_1(instr->ctx.cmd.opcode);
+				rop.gen_seq_ctrl |= GEN_SEQ_CMD1_EN;
+				if (addr_phase <= 1)
+					addr_phase = 2;
+				if (delay_phase == 0)
+					delay_phase = 1;
+				if (data_phase == 0)
+					data_phase = 1;
+				break;
+			default:
+				return -EOPNOTSUPP;
+			}
+			break;
+
+		case NAND_OP_ADDR_INSTR:
+			addrs = instr->ctx.addr.addrs;
+			naddrs = instr->ctx.addr.naddrs;
+			if (naddrs > 5)
+				return -EOPNOTSUPP;
+
+			col_addrs = min(2U, naddrs);
+			row_addrs = naddrs > 2 ? naddrs - col_addrs : 0;
+
+			switch (addr_phase++) {
+			case 0:
+				for (i = 0; i < col_addrs; i++)
+					rop.addr0_col |= addrs[i] << (i * 8);
+				rop.gen_seq_ctrl |= GEN_SEQ_COL_A0(col_addrs);
+
+				for (i = 0; i < row_addrs; i++)
+					rop.addr0_row |= addrs[2 + i] << (i * 8);
+				rop.gen_seq_ctrl |= GEN_SEQ_ROW_A0(row_addrs);
+
+				if (cmd_phase == 0)
+					cmd_phase = 1;
+				break;
+			case 1:
+				for (i = 0; i < col_addrs; i++)
+					rop.addr1_col |= addrs[i] << (i * 8);
+				rop.gen_seq_ctrl |= GEN_SEQ_COL_A1(col_addrs);
+
+				for (i = 0; i < row_addrs; i++)
+					rop.addr1_row |= addrs[2 + i] << (i * 8);
+				rop.gen_seq_ctrl |= GEN_SEQ_ROW_A1(row_addrs);
+
+				if (cmd_phase <= 1)
+					cmd_phase = 2;
+				break;
+			default:
+				return -EOPNOTSUPP;
+			}
+			break;
+
+		case NAND_OP_DATA_IN_INSTR:
+			rop.read = true;
+			fallthrough;
+		case NAND_OP_DATA_OUT_INSTR:
+			rop.gen_seq_ctrl |= GEN_SEQ_DATA_EN;
+			rop.buf = instr->ctx.data.buf.in;
+			rop.len = instr->ctx.data.len;
+			rop.command |= COMMAND_FIFO_SEL;
+
+			switch (data_phase++) {
+			case 0:
+				if (cmd_phase <= 2)
+					cmd_phase = 3;
+				if (addr_phase <= 1)
+					addr_phase = 2;
+				if (delay_phase == 0)
+					delay_phase = 1;
+				break;
+			default:
+				return -EOPNOTSUPP;
+			}
+			break;
+
+		case NAND_OP_WAITRDY_INSTR:
+			switch (delay_phase++) {
+			case 0:
+				rop.gen_seq_ctrl |= GEN_SEQ_DELAY0_EN;
+
+				if (cmd_phase <= 2)
+					cmd_phase = 3;
+				break;
+			case 1:
+				rop.gen_seq_ctrl |= GEN_SEQ_DELAY1_EN;
+
+				if (cmd_phase <= 3)
+					cmd_phase = 4;
+				if (data_phase == 0)
+					data_phase = 1;
+				break;
+			default:
+				return -EOPNOTSUPP;
+			}
+			break;
+		}
+	}
+
+	/*
+	 * Sequence 19 is generic and dedicated to write operations.
+	 * Sequence 18 is also generic and works for all other operations.
+	 */
+	if (rop.buf && !rop.read)
+		rop.command |= COMMAND_SEQ_GEN_OUT;
+	else
+		rop.command |= COMMAND_SEQ_GEN_IN;
+
+	if (delays > 1) {
+		dev_err(rnandc->dev, "Cannot handle more than one wait delay\n");
+		return -EOPNOTSUPP;
+	}
+
+	if (check_only)
+		return 0;
+
+	rnandc_trigger_op(rnandc, &rop);
+
+	words = rop.len / sizeof(u32);
+	remainder = rop.len % sizeof(u32);
+	if (rop.buf && rop.read) {
+		while (!FIFO_STATE_C_EMPTY(readl(rnandc->regs + FIFO_STATE_REG)))
+			cpu_relax();
+
+		while (FIFO_STATE_R_EMPTY(readl(rnandc->regs + FIFO_STATE_REG)))
+			cpu_relax();
+
+		ioread32_rep(rnandc->regs + FIFO_DATA_REG, rop.buf, words);
+		if (remainder) {
+			last_bytes = readl_relaxed(rnandc->regs + FIFO_DATA_REG);
+			memcpy(rop.buf + (words * sizeof(u32)), &last_bytes,
+			       remainder);
+		}
+
+		if (!FIFO_STATE_R_EMPTY(readl(rnandc->regs + FIFO_STATE_REG))) {
+			dev_warn(rnandc->dev,
+				 "Clearing residual data in the read FIFO\n");
+			rnandc_clear_fifo(rnandc);
+		}
+	} else if (rop.len && !rop.read) {
+		while (FIFO_STATE_W_FULL(readl(rnandc->regs + FIFO_STATE_REG)))
+			cpu_relax();
+
+		iowrite32_rep(rnandc->regs + FIFO_DATA_REG, rop.buf,
+			      DIV_ROUND_UP(rop.len, 4));
+
+		if (remainder) {
+			last_bytes = 0;
+			memcpy(&last_bytes, rop.buf + (words * sizeof(u32)), remainder);
+			writel_relaxed(last_bytes, rnandc->regs + FIFO_DATA_REG);
+		}
+
+		while (!FIFO_STATE_W_EMPTY(readl(rnandc->regs + FIFO_STATE_REG)))
+			cpu_relax();
+	}
+
+	ret = rnandc_wait_end_of_op(rnandc, chip);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int rnandc_setup_interface(struct nand_chip *chip, int chipnr,
+				  const struct nand_interface_config *conf)
+{
+	struct rnand_chip *rnand = to_rnand(chip);
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+	unsigned int period_ns = 1000000000 / clk_get_rate(rnandc->eclk);
+	const struct nand_sdr_timings *sdr;
+	unsigned int cyc, cle, ale, bef_dly, ca_to_data;
+
+	sdr = nand_get_sdr_timings(conf);
+	if (IS_ERR(sdr))
+		return PTR_ERR(sdr);
+
+	if (sdr->tRP_min != sdr->tWP_min || sdr->tREH_min != sdr->tWH_min) {
+		dev_err(rnandc->dev, "Read and write hold times must be identical\n");
+		return -EINVAL;
+	}
+
+	if (chipnr < 0)
+		return 0;
+
+	rnand->timings_asyn =
+		TIMINGS_ASYN_TRWP(TO_CYCLES64(sdr->tRP_min, period_ns)) |
+		TIMINGS_ASYN_TRWH(TO_CYCLES64(sdr->tREH_min, period_ns));
+	rnand->tim_seq0 =
+		TIM_SEQ0_TCCS(TO_CYCLES64(sdr->tCCS_min, period_ns)) |
+		TIM_SEQ0_TADL(TO_CYCLES64(sdr->tADL_min, period_ns)) |
+		TIM_SEQ0_TRHW(TO_CYCLES64(sdr->tRHW_min, period_ns)) |
+		TIM_SEQ0_TWHR(TO_CYCLES64(sdr->tWHR_min, period_ns));
+	rnand->tim_seq1 =
+		TIM_SEQ1_TWB(TO_CYCLES64(sdr->tWB_max, period_ns)) |
+		TIM_SEQ1_TRR(TO_CYCLES64(sdr->tRR_min, period_ns)) |
+		TIM_SEQ1_TWW(TO_CYCLES64(sdr->tWW_min, period_ns));
+
+	cyc = sdr->tDS_min + sdr->tDH_min;
+	cle = sdr->tCLH_min + sdr->tCLS_min;
+	ale = sdr->tALH_min + sdr->tALS_min;
+	bef_dly = sdr->tWB_max - sdr->tDH_min;
+	ca_to_data = sdr->tWHR_min + sdr->tREA_max - sdr->tDH_min;
+
+	/*
+	 * D0 = CMD -> ADDR = tCLH + tCLS - 1 cycle
+	 * D1 = CMD -> CMD = tCLH + tCLS - 1 cycle
+	 * D2 = CMD -> DLY = tWB - tDH
+	 * D3 = CMD -> DATA = tWHR + tREA - tDH
+	 */
+	rnand->tim_gen_seq0 =
+		TIM_GEN_SEQ0_D0(TO_CYCLES64(cle - cyc, period_ns)) |
+		TIM_GEN_SEQ0_D1(TO_CYCLES64(cle - cyc, period_ns)) |
+		TIM_GEN_SEQ0_D2(TO_CYCLES64(bef_dly, period_ns)) |
+		TIM_GEN_SEQ0_D3(TO_CYCLES64(ca_to_data, period_ns));
+
+	/*
+	 * D4 = ADDR -> CMD = tALH + tALS - 1 cyle
+	 * D5 = ADDR -> ADDR = tALH + tALS - 1 cyle
+	 * D6 = ADDR -> DLY = tWB - tDH
+	 * D7 = ADDR -> DATA = tWHR + tREA - tDH
+	 */
+	rnand->tim_gen_seq1 =
+		TIM_GEN_SEQ1_D4(TO_CYCLES64(ale - cyc, period_ns)) |
+		TIM_GEN_SEQ1_D5(TO_CYCLES64(ale - cyc, period_ns)) |
+		TIM_GEN_SEQ1_D6(TO_CYCLES64(bef_dly, period_ns)) |
+		TIM_GEN_SEQ1_D7(TO_CYCLES64(ca_to_data, period_ns));
+
+	/*
+	 * D8 = DLY -> DATA = tRR + tREA
+	 * D9 = DLY -> CMD = tRR
+	 * D10 = DATA -> CMD = tCLH + tCLS - 1 cycle
+	 * D11 = DATA -> DLY = tWB - tDH
+	 */
+	rnand->tim_gen_seq2 =
+		TIM_GEN_SEQ2_D8(TO_CYCLES64(sdr->tRR_min + sdr->tREA_max, period_ns)) |
+		TIM_GEN_SEQ2_D9(TO_CYCLES64(sdr->tRR_min, period_ns)) |
+		TIM_GEN_SEQ2_D10(TO_CYCLES64(cle - cyc, period_ns)) |
+		TIM_GEN_SEQ2_D11(TO_CYCLES64(bef_dly, period_ns));
+
+	/* D12 = DATA -> END = tCLH - tDH */
+	rnand->tim_gen_seq3 =
+		TIM_GEN_SEQ3_D12(TO_CYCLES64(sdr->tCLH_min - sdr->tDH_min, period_ns));
+
+	return 0;
+}
+
+static int rnandc_ooblayout_ecc(struct mtd_info *mtd, int section,
+				struct mtd_oob_region *oobregion)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	unsigned int eccbytes = round_up(chip->ecc.bytes, 4) * chip->ecc.steps;
+
+	if (section)
+		return -ERANGE;
+
+	oobregion->offset = 2;
+	oobregion->length = eccbytes;
+
+	return 0;
+}
+
+static int rnandc_ooblayout_free(struct mtd_info *mtd, int section,
+				 struct mtd_oob_region *oobregion)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	unsigned int eccbytes = round_up(chip->ecc.bytes, 4) * chip->ecc.steps;
+
+	if (section)
+		return -ERANGE;
+
+	oobregion->offset = 2 + eccbytes;
+	oobregion->length = mtd->oobsize - oobregion->offset;
+
+	return 0;
+}
+
+static const struct mtd_ooblayout_ops rnandc_ooblayout_ops = {
+	.ecc = rnandc_ooblayout_ecc,
+	.free = rnandc_ooblayout_free,
+};
+
+static int rnandc_hw_ecc_controller_init(struct nand_chip *chip)
+{
+	struct rnand_chip *rnand = to_rnand(chip);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+
+	if (mtd->writesize > SZ_16K) {
+		dev_err(rnandc->dev, "Unsupported page size\n");
+		return -EINVAL;
+	}
+
+	switch (chip->ecc.size) {
+	case SZ_256:
+		rnand->control |= CONTROL_ECC_BLOCK_SIZE_256;
+		break;
+	case SZ_512:
+		rnand->control |= CONTROL_ECC_BLOCK_SIZE_512;
+		break;
+	case SZ_1K:
+		rnand->control |= CONTROL_ECC_BLOCK_SIZE_1024;
+		break;
+	default:
+		dev_err(rnandc->dev, "Unsupported ECC chunk size\n");
+		return -EINVAL;
+	}
+
+	switch (chip->ecc.strength) {
+	case 2:
+		chip->ecc.bytes = 4;
+		rnand->ecc_ctrl |= ECC_CTRL_CAP_2B;
+		break;
+	case 4:
+		chip->ecc.bytes = 7;
+		rnand->ecc_ctrl |= ECC_CTRL_CAP_4B;
+		break;
+	case 8:
+		chip->ecc.bytes = 14;
+		rnand->ecc_ctrl |= ECC_CTRL_CAP_8B;
+		break;
+	case 16:
+		chip->ecc.bytes = 28;
+		rnand->ecc_ctrl |= ECC_CTRL_CAP_16B;
+		break;
+	case 24:
+		chip->ecc.bytes = 42;
+		rnand->ecc_ctrl |= ECC_CTRL_CAP_24B;
+		break;
+	case 32:
+		chip->ecc.bytes = 56;
+		rnand->ecc_ctrl |= ECC_CTRL_CAP_32B;
+		break;
+	default:
+		dev_err(rnandc->dev, "Unsupported ECC strength\n");
+		return -EINVAL;
+	}
+
+	rnand->ecc_ctrl |= ECC_CTRL_ERR_THRESHOLD(chip->ecc.strength);
+
+	mtd_set_ooblayout(mtd, &rnandc_ooblayout_ops);
+	chip->ecc.steps = mtd->writesize / chip->ecc.size;
+	chip->ecc.read_page = rnandc_read_page_hw_ecc;
+	chip->ecc.read_subpage = rnandc_read_subpage_hw_ecc;
+	chip->ecc.write_page = rnandc_write_page_hw_ecc;
+	chip->ecc.write_subpage = rnandc_write_subpage_hw_ecc;
+
+	return 0;
+}
+
+static int rnandc_ecc_init(struct nand_chip *chip)
+{
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+	const struct nand_ecc_props *requirements =
+		nanddev_get_ecc_requirements(&chip->base);
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+	int ret;
+
+	if (ecc->engine_type != NAND_ECC_ENGINE_TYPE_NONE &&
+	    (!ecc->size || !ecc->strength)) {
+		if (requirements->step_size && requirements->strength) {
+			ecc->size = requirements->step_size;
+			ecc->strength = requirements->strength;
+		} else {
+			dev_err(rnandc->dev, "No minimum ECC strength\n");
+			return -EINVAL;
+		}
+	}
+
+	switch (ecc->engine_type) {
+	case NAND_ECC_ENGINE_TYPE_ON_HOST:
+		ret = rnandc_hw_ecc_controller_init(chip);
+		if (ret)
+			return ret;
+		break;
+	case NAND_ECC_ENGINE_TYPE_NONE:
+	case NAND_ECC_ENGINE_TYPE_SOFT:
+	case NAND_ECC_ENGINE_TYPE_ON_DIE:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int rnandc_attach_chip(struct nand_chip *chip)
+{
+	struct rnand_chip *rnand = to_rnand(chip);
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct nand_memory_organization *memorg = nanddev_get_memorg(&chip->base);
+	int ret;
+
+	/* Do not store BBT bits in the OOB section as it is not protected */
+	if (chip->bbt_options & NAND_BBT_USE_FLASH)
+		chip->bbt_options |= NAND_BBT_NO_OOB;
+
+	if (mtd->writesize <= 512) {
+		dev_err(rnandc->dev, "Small page devices not supported\n");
+		return -EINVAL;
+	}
+
+	rnand->control |= CONTROL_CHECK_RB_LINE | CONTROL_INT_EN;
+
+	switch (memorg->pages_per_eraseblock) {
+	case 32:
+		rnand->control |= CONTROL_BLOCK_SIZE_32P;
+		break;
+	case 64:
+		rnand->control |= CONTROL_BLOCK_SIZE_64P;
+		break;
+	case 128:
+		rnand->control |= CONTROL_BLOCK_SIZE_128P;
+		break;
+	case 256:
+		rnand->control |= CONTROL_BLOCK_SIZE_256P;
+		break;
+	default:
+		dev_err(rnandc->dev, "Unsupported memory organization\n");
+		return -EINVAL;
+	}
+
+	chip->options |= NAND_SUBPAGE_READ;
+
+	ret = rnandc_ecc_init(chip);
+	if (ret) {
+		dev_err(rnandc->dev, "ECC initialization failed (%d)\n", ret);
+		return ret;
+	}
+
+	/* Force an update of the configuration registers */
+	rnand->selected_die = -1;
+
+	return 0;
+}
+
+static const struct nand_controller_ops rnandc_ops = {
+	.attach_chip = rnandc_attach_chip,
+	.exec_op = rnandc_exec_op,
+	.setup_interface = rnandc_setup_interface,
+};
+
+static int rnandc_alloc_dma_buf(struct rnandc *rnandc,
+				struct mtd_info *new_mtd)
+{
+	unsigned int max_len = new_mtd->writesize + new_mtd->oobsize;
+	struct rnand_chip *entry, *temp;
+	struct nand_chip *chip;
+	struct mtd_info *mtd;
+
+	list_for_each_entry_safe(entry, temp, &rnandc->chips, node) {
+		chip = &entry->chip;
+		mtd = nand_to_mtd(chip);
+		max_len = max(max_len, mtd->writesize + mtd->oobsize);
+	}
+
+	if (rnandc->buf && rnandc->buf_sz < max_len) {
+		devm_kfree(rnandc->dev, rnandc->buf);
+		rnandc->buf = NULL;
+	}
+
+	if (!rnandc->buf) {
+		rnandc->buf_sz = max_len;
+		rnandc->buf = devm_kmalloc(rnandc->dev, max_len,
+					   GFP_KERNEL | GFP_DMA);
+		if (!rnandc->buf)
+			return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int rnandc_chip_init(struct rnandc *rnandc, struct device_node *np)
+{
+	struct rnand_chip *rnand;
+	struct mtd_info *mtd;
+	struct nand_chip *chip;
+	int nsels, ret, i;
+	u32 cs;
+
+	nsels = of_property_count_elems_of_size(np, "reg", sizeof(u32));
+	if (nsels <= 0) {
+		ret = (nsels < 0) ? nsels : -EINVAL;
+		dev_err(rnandc->dev, "Invalid reg property (%d)\n", ret);
+		return ret;
+	}
+
+	/* Alloc the driver's NAND chip structure */
+	rnand = devm_kzalloc(rnandc->dev, struct_size(rnand, sels, nsels),
+			     GFP_KERNEL);
+	if (!rnand)
+		return -ENOMEM;
+
+	rnand->nsels = nsels;
+	rnand->selected_die = -1;
+
+	for (i = 0; i < nsels; i++) {
+		ret = of_property_read_u32_index(np, "reg", i, &cs);
+		if (ret) {
+			dev_err(rnandc->dev, "Incomplete reg property (%d)\n", ret);
+			return ret;
+		}
+
+		if (cs >= RNANDC_CS_NUM) {
+			dev_err(rnandc->dev, "Invalid reg property (%d)\n", cs);
+			return -EINVAL;
+		}
+
+		if (test_and_set_bit(cs, &rnandc->assigned_cs)) {
+			dev_err(rnandc->dev, "CS %d already assigned\n", cs);
+			return -EINVAL;
+		}
+
+		/*
+		 * No need to check for RB or WP properties, there is a 1:1
+		 * mandatory mapping with the CS.
+		 */
+		rnand->sels[i].cs = cs;
+	}
+
+	chip = &rnand->chip;
+	chip->controller = &rnandc->controller;
+	nand_set_flash_node(chip, np);
+
+	mtd = nand_to_mtd(chip);
+	mtd->dev.parent = rnandc->dev;
+	if (!mtd->name) {
+		dev_err(rnandc->dev, "Missing MTD label\n");
+		return -EINVAL;
+	}
+
+	ret = nand_scan(chip, rnand->nsels);
+	if (ret) {
+		dev_err(rnandc->dev, "Failed to scan the NAND chip (%d)\n", ret);
+		return ret;
+	}
+
+	ret = rnandc_alloc_dma_buf(rnandc, mtd);
+	if (ret)
+		goto cleanup_nand;
+
+	ret = mtd_device_register(mtd, NULL, 0);
+	if (ret) {
+		dev_err(rnandc->dev, "Failed to register MTD device (%d)\n", ret);
+		goto cleanup_nand;
+	}
+
+	list_add_tail(&rnand->node, &rnandc->chips);
+
+	return 0;
+
+cleanup_nand:
+	nand_cleanup(chip);
+
+	return ret;
+}
+
+static void rnandc_chips_cleanup(struct rnandc *rnandc)
+{
+	struct rnand_chip *entry, *temp;
+	struct nand_chip *chip;
+	int ret;
+
+	list_for_each_entry_safe(entry, temp, &rnandc->chips, node) {
+		chip = &entry->chip;
+		ret = mtd_device_unregister(nand_to_mtd(chip));
+		WARN_ON(ret);
+		nand_cleanup(chip);
+		list_del(&entry->node);
+	}
+}
+
+static int rnandc_chips_init(struct rnandc *rnandc)
+{
+	struct device_node *np;
+	int ret;
+
+	for_each_child_of_node(rnandc->dev->of_node, np) {
+		ret = rnandc_chip_init(rnandc, np);
+		if (ret) {
+			of_node_put(np);
+			goto cleanup_chips;
+		}
+	}
+
+	return 0;
+
+cleanup_chips:
+	rnandc_chips_cleanup(rnandc);
+
+	return ret;
+}
+
+static int rnandc_probe(struct platform_device *pdev)
+{
+	struct rnandc *rnandc;
+	int irq, ret;
+
+	rnandc = devm_kzalloc(&pdev->dev, sizeof(*rnandc), GFP_KERNEL);
+	if (!rnandc)
+		return -ENOMEM;
+
+	rnandc->dev = &pdev->dev;
+	nand_controller_init(&rnandc->controller);
+	rnandc->controller.ops = &rnandc_ops;
+	INIT_LIST_HEAD(&rnandc->chips);
+	init_completion(&rnandc->complete);
+
+	rnandc->regs = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(rnandc->regs))
+		return PTR_ERR(rnandc->regs);
+
+	/* APB clock */
+	rnandc->hclk = devm_clk_get(&pdev->dev, "hclk");
+	if (IS_ERR(rnandc->hclk))
+		return PTR_ERR(rnandc->hclk);
+
+	/* External NAND bus clock */
+	rnandc->eclk = devm_clk_get(&pdev->dev, "eclk");
+	if (IS_ERR(rnandc->eclk))
+		return PTR_ERR(rnandc->eclk);
+
+	ret = clk_prepare_enable(rnandc->hclk);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(rnandc->eclk);
+	if (ret)
+		goto disable_hclk;
+
+	rnandc_dis_interrupts(rnandc);
+	irq = platform_get_irq_optional(pdev, 0);
+	if (irq == -EPROBE_DEFER) {
+		ret = irq;
+		goto disable_eclk;
+	} else if (irq < 0) {
+		dev_info(&pdev->dev, "No IRQ found, fallback to polling\n");
+		rnandc->use_polling = true;
+	} else {
+		ret = devm_request_irq(&pdev->dev, irq, rnandc_irq_handler, 0,
+				       "renesas-nand-controller", rnandc);
+		if (ret < 0)
+			goto disable_eclk;
+	}
+
+	ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
+	if (ret)
+		goto disable_eclk;
+
+	rnandc_clear_fifo(rnandc);
+
+	platform_set_drvdata(pdev, rnandc);
+
+	ret = rnandc_chips_init(rnandc);
+	if (ret)
+		goto disable_eclk;
+
+	return 0;
+
+disable_eclk:
+	clk_disable_unprepare(rnandc->eclk);
+disable_hclk:
+	clk_disable_unprepare(rnandc->hclk);
+
+	return ret;
+}
+
+static int rnandc_remove(struct platform_device *pdev)
+{
+	struct rnandc *rnandc = platform_get_drvdata(pdev);
+
+	rnandc_chips_cleanup(rnandc);
+
+	clk_disable_unprepare(rnandc->eclk);
+	clk_disable_unprepare(rnandc->hclk);
+
+	return 0;
+}
+
+static const struct of_device_id rnandc_id_table[] = {
+	{ .compatible = "renesas,rcar-gen3-nandc" },
+	{ .compatible = "renesas,rzn1-nandc" },
+	{} /* sentinel */
+};
+MODULE_DEVICE_TABLE(of, rnandc_id_table);
+
+static struct platform_driver rnandc_driver = {
+	.driver = {
+		.name = "renesas-nandc",
+		.of_match_table = of_match_ptr(rnandc_id_table),
+	},
+	.probe = rnandc_probe,
+	.remove = rnandc_remove,
+};
+module_platform_driver(rnandc_driver);
+
+MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
+MODULE_DESCRIPTION("Renesas R-Car Gen3 & RZ/N1 NAND controller driver");
+MODULE_LICENSE("GPL v2");
-- 
2.27.0


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

* [PATCH v6 2/4] mtd: rawnand: renesas: Add new NAND controller driver
@ 2021-12-17 14:20   ` Miquel Raynal
  0 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-17 14:20 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Rob Herring, devicetree, Milan Stevanovic, Jimmy Lalande,
	Thomas Petazzoni, linux-renesas-soc, Magnus Damm,
	Gareth Williams, Phil Edworthy, Geert Uytterhoeven, Wolfram Sang,
	Chris Brandt, Ralph Siemsen, Miquel Raynal

Introduce Renesas NAND controller driver which currently supports the
following features on R-Car Gen3 and RZ/N1 SoCs:
- All ONFI timing modes
- Different configurations of its internal ECC controller
- On-die (not tested) and software ECC support
- Several chips (not tested)
- Subpage accesses
- DMA and PIO

This controller was originally provided by Evatronix before being bought
by Cadence.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Tested-by: Ralph Siemsen <ralph.siemsen@linaro.org>
---
 drivers/mtd/nand/raw/Kconfig                  |    7 +
 drivers/mtd/nand/raw/Makefile                 |    1 +
 .../mtd/nand/raw/renesas-nand-controller.c    | 1424 +++++++++++++++++
 3 files changed, 1432 insertions(+)
 create mode 100644 drivers/mtd/nand/raw/renesas-nand-controller.c

diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig
index 67b7cb67c030..fead9e2c505b 100644
--- a/drivers/mtd/nand/raw/Kconfig
+++ b/drivers/mtd/nand/raw/Kconfig
@@ -461,6 +461,13 @@ config MTD_NAND_PL35X
 	  Enables support for PrimeCell SMC PL351 and PL353 NAND
 	  controller found on Zynq7000.
 
+config MTD_NAND_RENESAS
+	tristate "Renesas R-Car Gen3 & RZ/N1 NAND controller"
+	depends on ARCH_RENESAS || COMPILE_TEST
+	help
+	  Enables support for the NAND controller found on Renesas R-Car
+	  Gen3 and RZ/N1 SoC families.
+
 comment "Misc"
 
 config MTD_SM_COMMON
diff --git a/drivers/mtd/nand/raw/Makefile b/drivers/mtd/nand/raw/Makefile
index 2f97958c3a33..88a566513c56 100644
--- a/drivers/mtd/nand/raw/Makefile
+++ b/drivers/mtd/nand/raw/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_MTD_NAND_ARASAN)		+= arasan-nand-controller.o
 obj-$(CONFIG_MTD_NAND_INTEL_LGM)	+= intel-nand-controller.o
 obj-$(CONFIG_MTD_NAND_ROCKCHIP)		+= rockchip-nand-controller.o
 obj-$(CONFIG_MTD_NAND_PL35X)		+= pl35x-nand-controller.o
+obj-$(CONFIG_MTD_NAND_RENESAS)		+= renesas-nand-controller.o
 
 nand-objs := nand_base.o nand_legacy.o nand_bbt.o nand_timings.o nand_ids.o
 nand-objs += nand_onfi.o
diff --git a/drivers/mtd/nand/raw/renesas-nand-controller.c b/drivers/mtd/nand/raw/renesas-nand-controller.c
new file mode 100644
index 000000000000..428e08362956
--- /dev/null
+++ b/drivers/mtd/nand/raw/renesas-nand-controller.c
@@ -0,0 +1,1424 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Evatronix/Renesas R-Car Gen3, RZ/N1D, RZ/N1S, RZ/N1L NAND controller driver
+ *
+ * Copyright (C) 2021 Schneider Electric
+ * Author: Miquel RAYNAL <miquel.raynal@bootlin.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define COMMAND_REG 0x00
+#define   COMMAND_SEQ(x) FIELD_PREP(GENMASK(5, 0), (x))
+#define     COMMAND_SEQ_10 COMMAND_SEQ(0x2A)
+#define     COMMAND_SEQ_12 COMMAND_SEQ(0x0C)
+#define     COMMAND_SEQ_18 COMMAND_SEQ(0x32)
+#define     COMMAND_SEQ_19 COMMAND_SEQ(0x13)
+#define     COMMAND_SEQ_GEN_IN COMMAND_SEQ_18
+#define     COMMAND_SEQ_GEN_OUT COMMAND_SEQ_19
+#define     COMMAND_SEQ_READ_PAGE COMMAND_SEQ_10
+#define     COMMAND_SEQ_WRITE_PAGE COMMAND_SEQ_12
+#define   COMMAND_INPUT_SEL_AHBS 0
+#define   COMMAND_INPUT_SEL_DMA BIT(6)
+#define   COMMAND_FIFO_SEL 0
+#define   COMMAND_DATA_SEL BIT(7)
+#define   COMMAND_0(x) FIELD_PREP(GENMASK(15, 8), (x))
+#define   COMMAND_1(x) FIELD_PREP(GENMASK(23, 16), (x))
+#define   COMMAND_2(x) FIELD_PREP(GENMASK(31, 24), (x))
+
+#define CONTROL_REG 0x04
+#define   CONTROL_CHECK_RB_LINE 0
+#define   CONTROL_ECC_BLOCK_SIZE(x) FIELD_PREP(GENMASK(2, 1), (x))
+#define     CONTROL_ECC_BLOCK_SIZE_256 CONTROL_ECC_BLOCK_SIZE(0)
+#define     CONTROL_ECC_BLOCK_SIZE_512 CONTROL_ECC_BLOCK_SIZE(1)
+#define     CONTROL_ECC_BLOCK_SIZE_1024 CONTROL_ECC_BLOCK_SIZE(2)
+#define   CONTROL_INT_EN BIT(4)
+#define   CONTROL_ECC_EN BIT(5)
+#define   CONTROL_BLOCK_SIZE(x) FIELD_PREP(GENMASK(7, 6), (x))
+#define     CONTROL_BLOCK_SIZE_32P CONTROL_BLOCK_SIZE(0)
+#define     CONTROL_BLOCK_SIZE_64P CONTROL_BLOCK_SIZE(1)
+#define     CONTROL_BLOCK_SIZE_128P CONTROL_BLOCK_SIZE(2)
+#define     CONTROL_BLOCK_SIZE_256P CONTROL_BLOCK_SIZE(3)
+
+#define STATUS_REG 0x8
+#define   MEM_RDY(cs, reg) (FIELD_GET(GENMASK(3, 0), (reg)) & BIT(cs))
+#define   CTRL_RDY(reg) (FIELD_GET(BIT(8), (reg)) == 0)
+
+#define ECC_CTRL_REG 0x18
+#define   ECC_CTRL_CAP(x) FIELD_PREP(GENMASK(2, 0), (x))
+#define     ECC_CTRL_CAP_2B ECC_CTRL_CAP(0)
+#define     ECC_CTRL_CAP_4B ECC_CTRL_CAP(1)
+#define     ECC_CTRL_CAP_8B ECC_CTRL_CAP(2)
+#define     ECC_CTRL_CAP_16B ECC_CTRL_CAP(3)
+#define     ECC_CTRL_CAP_24B ECC_CTRL_CAP(4)
+#define     ECC_CTRL_CAP_32B ECC_CTRL_CAP(5)
+#define   ECC_CTRL_ERR_THRESHOLD(x) FIELD_PREP(GENMASK(13, 8), (x))
+
+#define INT_MASK_REG 0x10
+#define INT_STATUS_REG 0x14
+#define   INT_CMD_END BIT(1)
+#define   INT_DMA_END BIT(3)
+#define   INT_MEM_RDY(cs) FIELD_PREP(GENMASK(11, 8), BIT(cs))
+#define   INT_DMA_ENDED BIT(3)
+#define   MEM_IS_RDY(cs, reg) (FIELD_GET(GENMASK(11, 8), (reg)) & BIT(cs))
+#define   DMA_HAS_ENDED(reg) FIELD_GET(BIT(3), (reg))
+
+#define ECC_OFFSET_REG 0x1C
+#define   ECC_OFFSET(x) FIELD_PREP(GENMASK(15, 0), (x))
+
+#define ECC_STAT_REG 0x20
+#define   ECC_STAT_CORRECTABLE(cs, reg) (FIELD_GET(GENMASK(3, 0), (reg)) & BIT(cs))
+#define   ECC_STAT_UNCORRECTABLE(cs, reg) (FIELD_GET(GENMASK(11, 8), (reg)) & BIT(cs))
+
+#define ADDR0_COL_REG 0x24
+#define   ADDR0_COL(x) FIELD_PREP(GENMASK(15, 0), (x))
+
+#define ADDR0_ROW_REG 0x28
+#define   ADDR0_ROW(x) FIELD_PREP(GENMASK(23, 0), (x))
+
+#define ADDR1_COL_REG 0x2C
+#define   ADDR1_COL(x) FIELD_PREP(GENMASK(15, 0), (x))
+
+#define ADDR1_ROW_REG 0x30
+#define   ADDR1_ROW(x) FIELD_PREP(GENMASK(23, 0), (x))
+
+#define FIFO_DATA_REG 0x38
+
+#define DATA_REG 0x3C
+
+#define DATA_REG_SIZE_REG 0x40
+
+#define DMA_ADDR_LOW_REG 0x64
+
+#define DMA_ADDR_HIGH_REG 0x68
+
+#define DMA_CNT_REG 0x6C
+
+#define DMA_CTRL_REG 0x70
+#define   DMA_CTRL_INCREMENT_BURST_4 0
+#define   DMA_CTRL_REGISTER_MANAGED_MODE 0
+#define   DMA_CTRL_START BIT(7)
+
+#define MEM_CTRL_REG 0x80
+#define   MEM_CTRL_CS(cs) FIELD_PREP(GENMASK(1, 0), (cs))
+#define   MEM_CTRL_DIS_WP(cs) FIELD_PREP(GENMASK(11, 8), BIT((cs)))
+
+#define DATA_SIZE_REG 0x84
+#define   DATA_SIZE(x) FIELD_PREP(GENMASK(14, 0), (x))
+
+#define TIMINGS_ASYN_REG 0x88
+#define   TIMINGS_ASYN_TRWP(x) FIELD_PREP(GENMASK(3, 0), max((x), 1U) - 1)
+#define   TIMINGS_ASYN_TRWH(x) FIELD_PREP(GENMASK(7, 4), max((x), 1U) - 1)
+
+#define TIM_SEQ0_REG 0x90
+#define   TIM_SEQ0_TCCS(x) FIELD_PREP(GENMASK(5, 0), max((x), 1U) - 1)
+#define   TIM_SEQ0_TADL(x) FIELD_PREP(GENMASK(13, 8), max((x), 1U) - 1)
+#define   TIM_SEQ0_TRHW(x) FIELD_PREP(GENMASK(21, 16), max((x), 1U) - 1)
+#define   TIM_SEQ0_TWHR(x) FIELD_PREP(GENMASK(29, 24), max((x), 1U) - 1)
+
+#define TIM_SEQ1_REG 0x94
+#define   TIM_SEQ1_TWB(x) FIELD_PREP(GENMASK(5, 0), max((x), 1U) - 1)
+#define   TIM_SEQ1_TRR(x) FIELD_PREP(GENMASK(13, 8), max((x), 1U) - 1)
+#define   TIM_SEQ1_TWW(x) FIELD_PREP(GENMASK(21, 16), max((x), 1U) - 1)
+
+#define TIM_GEN_SEQ0_REG 0x98
+#define   TIM_GEN_SEQ0_D0(x) FIELD_PREP(GENMASK(5, 0), max((x), 1U) - 1)
+#define   TIM_GEN_SEQ0_D1(x) FIELD_PREP(GENMASK(13, 8), max((x), 1U) - 1)
+#define   TIM_GEN_SEQ0_D2(x) FIELD_PREP(GENMASK(21, 16), max((x), 1U) - 1)
+#define   TIM_GEN_SEQ0_D3(x) FIELD_PREP(GENMASK(29, 24), max((x), 1U) - 1)
+
+#define TIM_GEN_SEQ1_REG 0x9c
+#define   TIM_GEN_SEQ1_D4(x) FIELD_PREP(GENMASK(5, 0), max((x), 1U) - 1)
+#define   TIM_GEN_SEQ1_D5(x) FIELD_PREP(GENMASK(13, 8), max((x), 1U) - 1)
+#define   TIM_GEN_SEQ1_D6(x) FIELD_PREP(GENMASK(21, 16), max((x), 1U) - 1)
+#define   TIM_GEN_SEQ1_D7(x) FIELD_PREP(GENMASK(29, 24), max((x), 1U) - 1)
+
+#define TIM_GEN_SEQ2_REG 0xA0
+#define   TIM_GEN_SEQ2_D8(x) FIELD_PREP(GENMASK(5, 0), max((x), 1U) - 1)
+#define   TIM_GEN_SEQ2_D9(x) FIELD_PREP(GENMASK(13, 8), max((x), 1U) - 1)
+#define   TIM_GEN_SEQ2_D10(x) FIELD_PREP(GENMASK(21, 16), max((x), 1U) - 1)
+#define   TIM_GEN_SEQ2_D11(x) FIELD_PREP(GENMASK(29, 24), max((x), 1U) - 1)
+
+#define FIFO_INIT_REG 0xB4
+#define   FIFO_INIT BIT(0)
+
+#define FIFO_STATE_REG 0xB4
+#define   FIFO_STATE_R_EMPTY(reg) FIELD_GET(BIT(0), (reg))
+#define   FIFO_STATE_W_FULL(reg) FIELD_GET(BIT(1), (reg))
+#define   FIFO_STATE_C_EMPTY(reg) FIELD_GET(BIT(2), (reg))
+#define   FIFO_STATE_R_FULL(reg) FIELD_GET(BIT(6), (reg))
+#define   FIFO_STATE_W_EMPTY(reg) FIELD_GET(BIT(7), (reg))
+
+#define GEN_SEQ_CTRL_REG 0xB8
+#define   GEN_SEQ_CMD0_EN BIT(0)
+#define   GEN_SEQ_CMD1_EN BIT(1)
+#define   GEN_SEQ_CMD2_EN BIT(2)
+#define   GEN_SEQ_CMD3_EN BIT(3)
+#define   GEN_SEQ_COL_A0(x) FIELD_PREP(GENMASK(5, 4), min((x), 2U))
+#define   GEN_SEQ_COL_A1(x) FIELD_PREP(GENMASK(7, 6), min((x), 2U))
+#define   GEN_SEQ_ROW_A0(x) FIELD_PREP(GENMASK(9, 8), min((x), 3U))
+#define   GEN_SEQ_ROW_A1(x) FIELD_PREP(GENMASK(11, 10), min((x), 3U))
+#define   GEN_SEQ_DATA_EN BIT(12)
+#define   GEN_SEQ_DELAY_EN(x) FIELD_PREP(GENMASK(14, 13), (x))
+#define     GEN_SEQ_DELAY0_EN GEN_SEQ_DELAY_EN(1)
+#define     GEN_SEQ_DELAY1_EN GEN_SEQ_DELAY_EN(2)
+#define   GEN_SEQ_IMD_SEQ BIT(15)
+#define   GEN_SEQ_COMMAND_3(x) FIELD_PREP(GENMASK(26, 16), (x))
+
+#define DMA_TLVL_REG 0x114
+#define   DMA_TLVL(x) FIELD_PREP(GENMASK(7, 0), (x))
+#define   DMA_TLVL_MAX DMA_TLVL(0xFF)
+
+#define TIM_GEN_SEQ3_REG 0x134
+#define   TIM_GEN_SEQ3_D12(x) FIELD_PREP(GENMASK(5, 0), max((x), 1U) - 1)
+
+#define ECC_CNT_REG 0x14C
+#define   ECC_CNT(cs, reg) FIELD_GET(GENMASK(5, 0), (reg) >> ((cs) * 8))
+
+#define RNANDC_CS_NUM 4
+
+#define TO_CYCLES64(ps, period_ns) ((unsigned int)DIV_ROUND_UP_ULL(div_u64(ps, 1000), \
+								   period_ns))
+
+struct rnand_chip_sel {
+	unsigned int cs;
+};
+
+struct rnand_chip {
+	struct nand_chip chip;
+	struct list_head node;
+	int selected_die;
+	u32 ctrl;
+	unsigned int nsels;
+	u32 control;
+	u32 ecc_ctrl;
+	u32 timings_asyn;
+	u32 tim_seq0;
+	u32 tim_seq1;
+	u32 tim_gen_seq0;
+	u32 tim_gen_seq1;
+	u32 tim_gen_seq2;
+	u32 tim_gen_seq3;
+	struct rnand_chip_sel sels[];
+};
+
+struct rnandc {
+	struct nand_controller controller;
+	struct device *dev;
+	void __iomem *regs;
+	struct clk *hclk;
+	struct clk *eclk;
+	unsigned long assigned_cs;
+	struct list_head chips;
+	struct nand_chip *selected_chip;
+	struct completion complete;
+	bool use_polling;
+	u8 *buf;
+	unsigned int buf_sz;
+};
+
+struct rnandc_op {
+	u32 command;
+	u32 addr0_col;
+	u32 addr0_row;
+	u32 addr1_col;
+	u32 addr1_row;
+	u32 data_size;
+	u32 ecc_offset;
+	u32 gen_seq_ctrl;
+	u8 *buf;
+	bool read;
+	unsigned int len;
+};
+
+static inline struct rnandc *to_rnandc(struct nand_controller *ctrl)
+{
+	return container_of(ctrl, struct rnandc, controller);
+}
+
+static inline struct rnand_chip *to_rnand(struct nand_chip *chip)
+{
+	return container_of(chip, struct rnand_chip, chip);
+}
+
+static inline unsigned int to_rnandc_cs(struct rnand_chip *nand)
+{
+	return nand->sels[nand->selected_die].cs;
+}
+
+static void rnandc_dis_correction(struct rnandc *rnandc)
+{
+	u32 control;
+
+	control = readl_relaxed(rnandc->regs + CONTROL_REG);
+	control &= ~CONTROL_ECC_EN;
+	writel_relaxed(control, rnandc->regs + CONTROL_REG);
+}
+
+static void rnandc_en_correction(struct rnandc *rnandc)
+{
+	u32 control;
+
+	control = readl_relaxed(rnandc->regs + CONTROL_REG);
+	control |= CONTROL_ECC_EN;
+	writel_relaxed(control, rnandc->regs + CONTROL_REG);
+}
+
+static void rnandc_clear_status(struct rnandc *rnandc)
+{
+	writel_relaxed(0, rnandc->regs + INT_STATUS_REG);
+	writel_relaxed(0, rnandc->regs + ECC_STAT_REG);
+	writel_relaxed(0, rnandc->regs + ECC_CNT_REG);
+}
+
+static void rnandc_dis_interrupts(struct rnandc *rnandc)
+{
+	writel_relaxed(0, rnandc->regs + INT_MASK_REG);
+}
+
+static void rnandc_en_interrupts(struct rnandc *rnandc, u32 val)
+{
+	if (!rnandc->use_polling)
+		writel_relaxed(val, rnandc->regs + INT_MASK_REG);
+}
+
+static void rnandc_clear_fifo(struct rnandc *rnandc)
+{
+	writel_relaxed(FIFO_INIT, rnandc->regs + FIFO_INIT_REG);
+}
+
+static void rnandc_select_target(struct nand_chip *chip, int die_nr)
+{
+	struct rnand_chip *rnand = to_rnand(chip);
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+	unsigned int cs = rnand->sels[die_nr].cs;
+
+	if (chip == rnandc->selected_chip && die_nr == rnand->selected_die)
+		return;
+
+	rnandc_clear_status(rnandc);
+	writel_relaxed(MEM_CTRL_CS(cs) | MEM_CTRL_DIS_WP(cs), rnandc->regs + MEM_CTRL_REG);
+	writel_relaxed(rnand->control, rnandc->regs + CONTROL_REG);
+	writel_relaxed(rnand->ecc_ctrl, rnandc->regs + ECC_CTRL_REG);
+	writel_relaxed(rnand->timings_asyn, rnandc->regs + TIMINGS_ASYN_REG);
+	writel_relaxed(rnand->tim_seq0, rnandc->regs + TIM_SEQ0_REG);
+	writel_relaxed(rnand->tim_seq1, rnandc->regs + TIM_SEQ1_REG);
+	writel_relaxed(rnand->tim_gen_seq0, rnandc->regs + TIM_GEN_SEQ0_REG);
+	writel_relaxed(rnand->tim_gen_seq1, rnandc->regs + TIM_GEN_SEQ1_REG);
+	writel_relaxed(rnand->tim_gen_seq2, rnandc->regs + TIM_GEN_SEQ2_REG);
+	writel_relaxed(rnand->tim_gen_seq3, rnandc->regs + TIM_GEN_SEQ3_REG);
+
+	rnandc->selected_chip = chip;
+	rnand->selected_die = die_nr;
+}
+
+static void rnandc_trigger_op(struct rnandc *rnandc, struct rnandc_op *rop)
+{
+	writel_relaxed(rop->addr0_col, rnandc->regs + ADDR0_COL_REG);
+	writel_relaxed(rop->addr0_row, rnandc->regs + ADDR0_ROW_REG);
+	writel_relaxed(rop->addr1_col, rnandc->regs + ADDR1_COL_REG);
+	writel_relaxed(rop->addr1_row, rnandc->regs + ADDR1_ROW_REG);
+	writel_relaxed(rop->ecc_offset, rnandc->regs + ECC_OFFSET_REG);
+	writel_relaxed(rop->gen_seq_ctrl, rnandc->regs + GEN_SEQ_CTRL_REG);
+	writel_relaxed(DATA_SIZE(rop->len), rnandc->regs + DATA_SIZE_REG);
+	writel_relaxed(rop->command, rnandc->regs + COMMAND_REG);
+}
+
+static void rnandc_trigger_dma(struct rnandc *rnandc)
+{
+	writel_relaxed(DMA_CTRL_INCREMENT_BURST_4 |
+		       DMA_CTRL_REGISTER_MANAGED_MODE |
+		       DMA_CTRL_START, rnandc->regs + DMA_CTRL_REG);
+}
+
+static irqreturn_t rnandc_irq_handler(int irq, void *private)
+{
+	struct rnandc *rnandc = private;
+
+	rnandc_dis_interrupts(rnandc);
+	complete(&rnandc->complete);
+
+	return IRQ_HANDLED;
+}
+
+static int rnandc_wait_end_of_op(struct rnandc *rnandc,
+				 struct nand_chip *chip)
+{
+	struct rnand_chip *rnand = to_rnand(chip);
+	unsigned int cs = to_rnandc_cs(rnand);
+	u32 status;
+	int ret;
+
+	ret = readl_poll_timeout(rnandc->regs + STATUS_REG, status,
+				 MEM_RDY(cs, status) && CTRL_RDY(status),
+				 1, 100000);
+	if (ret)
+		dev_err(rnandc->dev, "Operation timed out, status: 0x%08x\n",
+			status);
+
+	return ret;
+}
+
+static int rnandc_wait_end_of_io(struct rnandc *rnandc,
+				 struct nand_chip *chip)
+{
+	int timeout_ms = 1000;
+	int ret;
+
+	if (rnandc->use_polling) {
+		struct rnand_chip *rnand = to_rnand(chip);
+		unsigned int cs = to_rnandc_cs(rnand);
+		u32 status;
+
+		ret = readl_poll_timeout(rnandc->regs + INT_STATUS_REG, status,
+					 MEM_IS_RDY(cs, status) &
+					 DMA_HAS_ENDED(status),
+					 0, timeout_ms * 1000);
+	} else {
+		ret = wait_for_completion_timeout(&rnandc->complete,
+						  msecs_to_jiffies(timeout_ms));
+		if (!ret)
+			ret = -ETIMEDOUT;
+		else
+			ret = 0;
+	}
+
+	return ret;
+}
+
+static int rnandc_read_page_hw_ecc(struct nand_chip *chip, u8 *buf,
+				   int oob_required, int page)
+{
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct rnand_chip *rnand = to_rnand(chip);
+	unsigned int cs = to_rnandc_cs(rnand);
+	struct rnandc_op rop = {
+		.command = COMMAND_INPUT_SEL_DMA | COMMAND_0(NAND_CMD_READ0) |
+			   COMMAND_2(NAND_CMD_READSTART) | COMMAND_FIFO_SEL |
+			   COMMAND_SEQ_READ_PAGE,
+		.addr0_row = page,
+		.len = mtd->writesize,
+		.ecc_offset = ECC_OFFSET(mtd->writesize + 2),
+	};
+	unsigned int max_bitflips = 0;
+	dma_addr_t dma_addr;
+	u32 ecc_stat;
+	int bf, ret, i;
+
+	/* Prepare controller */
+	rnandc_select_target(chip, chip->cur_cs);
+	rnandc_clear_status(rnandc);
+	reinit_completion(&rnandc->complete);
+	rnandc_en_interrupts(rnandc, INT_DMA_ENDED);
+	rnandc_en_correction(rnandc);
+
+	/* Configure DMA */
+	dma_addr = dma_map_single(rnandc->dev, rnandc->buf, mtd->writesize,
+				  DMA_FROM_DEVICE);
+	writel(dma_addr, rnandc->regs + DMA_ADDR_LOW_REG);
+	writel(mtd->writesize, rnandc->regs + DMA_CNT_REG);
+	writel(DMA_TLVL_MAX, rnandc->regs + DMA_TLVL_REG);
+
+	rnandc_trigger_op(rnandc, &rop);
+	rnandc_trigger_dma(rnandc);
+
+	ret = rnandc_wait_end_of_io(rnandc, chip);
+	dma_unmap_single(rnandc->dev, dma_addr, mtd->writesize, DMA_FROM_DEVICE);
+	rnandc_dis_correction(rnandc);
+	if (ret) {
+		dev_err(rnandc->dev, "Read page operation never ending\n");
+		return ret;
+	}
+
+	ecc_stat = readl_relaxed(rnandc->regs + ECC_STAT_REG);
+
+	if (oob_required || ECC_STAT_UNCORRECTABLE(cs, ecc_stat)) {
+		ret = nand_change_read_column_op(chip, mtd->writesize,
+						 chip->oob_poi, mtd->oobsize,
+						 false);
+		if (ret)
+			return ret;
+	}
+
+	if (ECC_STAT_UNCORRECTABLE(cs, ecc_stat)) {
+		for (i = 0; i < chip->ecc.steps; i++) {
+			unsigned int off = i * chip->ecc.size;
+			unsigned int eccoff = i * chip->ecc.bytes;
+
+			bf = nand_check_erased_ecc_chunk(rnandc->buf + off,
+							 chip->ecc.size,
+							 chip->oob_poi + 2 + eccoff,
+							 chip->ecc.bytes,
+							 NULL, 0,
+							 chip->ecc.strength);
+			if (bf < 0) {
+				mtd->ecc_stats.failed++;
+			} else {
+				mtd->ecc_stats.corrected += bf;
+				max_bitflips = max_t(unsigned int, max_bitflips, bf);
+			}
+		}
+	} else if (ECC_STAT_CORRECTABLE(cs, ecc_stat)) {
+		bf = ECC_CNT(cs, readl_relaxed(rnandc->regs + ECC_CNT_REG));
+		/*
+		 * The number of bitflips is an approximation given the fact
+		 * that this controller does not provide per-chunk details but
+		 * only gives statistics on the entire page.
+		 */
+		mtd->ecc_stats.corrected += bf;
+	}
+
+	memcpy(buf, rnandc->buf, mtd->writesize);
+
+	return 0;
+}
+
+static int rnandc_read_subpage_hw_ecc(struct nand_chip *chip, u32 req_offset,
+				      u32 req_len, u8 *bufpoi, int page)
+{
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct rnand_chip *rnand = to_rnand(chip);
+	unsigned int cs = to_rnandc_cs(rnand);
+	unsigned int page_off = round_down(req_offset, chip->ecc.size);
+	unsigned int real_len = round_up(req_offset + req_len - page_off,
+					 chip->ecc.size);
+	unsigned int start_chunk = page_off / chip->ecc.size;
+	unsigned int nchunks = real_len / chip->ecc.size;
+	unsigned int ecc_off = 2 + (start_chunk * chip->ecc.bytes);
+	struct rnandc_op rop = {
+		.command = COMMAND_INPUT_SEL_AHBS | COMMAND_0(NAND_CMD_READ0) |
+			   COMMAND_2(NAND_CMD_READSTART) | COMMAND_FIFO_SEL |
+			   COMMAND_SEQ_READ_PAGE,
+		.addr0_row = page,
+		.addr0_col = page_off,
+		.len = real_len,
+		.ecc_offset = ECC_OFFSET(mtd->writesize + ecc_off),
+	};
+	unsigned int max_bitflips = 0, i;
+	u32 ecc_stat;
+	int bf, ret;
+
+	/* Prepare controller */
+	rnandc_select_target(chip, chip->cur_cs);
+	rnandc_clear_status(rnandc);
+	rnandc_en_correction(rnandc);
+	rnandc_trigger_op(rnandc, &rop);
+
+	while (!FIFO_STATE_C_EMPTY(readl(rnandc->regs + FIFO_STATE_REG)))
+		cpu_relax();
+
+	while (FIFO_STATE_R_EMPTY(readl(rnandc->regs + FIFO_STATE_REG)))
+		cpu_relax();
+
+	ioread32_rep(rnandc->regs + FIFO_DATA_REG, bufpoi + page_off,
+		     real_len / 4);
+
+	if (!FIFO_STATE_R_EMPTY(readl(rnandc->regs + FIFO_STATE_REG))) {
+		dev_err(rnandc->dev, "Clearing residual data in the read FIFO\n");
+		rnandc_clear_fifo(rnandc);
+	}
+
+	ret = rnandc_wait_end_of_op(rnandc, chip);
+	rnandc_dis_correction(rnandc);
+	if (ret) {
+		dev_err(rnandc->dev, "Read subpage operation never ending\n");
+		return ret;
+	}
+
+	ecc_stat = readl_relaxed(rnandc->regs + ECC_STAT_REG);
+
+	if (ECC_STAT_UNCORRECTABLE(cs, ecc_stat)) {
+		ret = nand_change_read_column_op(chip, mtd->writesize,
+						 chip->oob_poi, mtd->oobsize,
+						 false);
+		if (ret)
+			return ret;
+
+		for (i = start_chunk; i < nchunks; i++) {
+			unsigned int dataoff = i * chip->ecc.size;
+			unsigned int eccoff = 2 + (i * chip->ecc.bytes);
+
+			bf = nand_check_erased_ecc_chunk(bufpoi + dataoff,
+							 chip->ecc.size,
+							 chip->oob_poi + eccoff,
+							 chip->ecc.bytes,
+							 NULL, 0,
+							 chip->ecc.strength);
+			if (bf < 0) {
+				mtd->ecc_stats.failed++;
+			} else {
+				mtd->ecc_stats.corrected += bf;
+				max_bitflips = max_t(unsigned int, max_bitflips, bf);
+			}
+		}
+	} else if (ECC_STAT_CORRECTABLE(cs, ecc_stat)) {
+		bf = ECC_CNT(cs, readl_relaxed(rnandc->regs + ECC_CNT_REG));
+		/*
+		 * The number of bitflips is an approximation given the fact
+		 * that this controller does not provide per-chunk details but
+		 * only gives statistics on the entire page.
+		 */
+		mtd->ecc_stats.corrected += bf;
+	}
+
+	return 0;
+}
+
+static int rnandc_write_page_hw_ecc(struct nand_chip *chip, const u8 *buf,
+				    int oob_required, int page)
+{
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct rnand_chip *rnand = to_rnand(chip);
+	unsigned int cs = to_rnandc_cs(rnand);
+	struct rnandc_op rop = {
+		.command = COMMAND_INPUT_SEL_DMA | COMMAND_0(NAND_CMD_SEQIN) |
+			   COMMAND_1(NAND_CMD_PAGEPROG) | COMMAND_FIFO_SEL |
+			   COMMAND_SEQ_WRITE_PAGE,
+		.addr0_row = page,
+		.len = mtd->writesize,
+		.ecc_offset = ECC_OFFSET(mtd->writesize + 2),
+	};
+	dma_addr_t dma_addr;
+	int ret;
+
+	memcpy(rnandc->buf, buf, mtd->writesize);
+
+	/* Prepare controller */
+	rnandc_select_target(chip, chip->cur_cs);
+	rnandc_clear_status(rnandc);
+	reinit_completion(&rnandc->complete);
+	rnandc_en_interrupts(rnandc, INT_MEM_RDY(cs));
+	rnandc_en_correction(rnandc);
+
+	/* Configure DMA */
+	dma_addr = dma_map_single(rnandc->dev, (void *)rnandc->buf, mtd->writesize,
+				  DMA_TO_DEVICE);
+	writel(dma_addr, rnandc->regs + DMA_ADDR_LOW_REG);
+	writel(mtd->writesize, rnandc->regs + DMA_CNT_REG);
+	writel(DMA_TLVL_MAX, rnandc->regs + DMA_TLVL_REG);
+
+	rnandc_trigger_op(rnandc, &rop);
+	rnandc_trigger_dma(rnandc);
+
+	ret = rnandc_wait_end_of_io(rnandc, chip);
+	dma_unmap_single(rnandc->dev, dma_addr, mtd->writesize, DMA_TO_DEVICE);
+	rnandc_dis_correction(rnandc);
+	if (ret) {
+		dev_err(rnandc->dev, "Write page operation never ending\n");
+		return ret;
+	}
+
+	if (!oob_required)
+		return 0;
+
+	return nand_change_write_column_op(chip, mtd->writesize, chip->oob_poi,
+					   mtd->oobsize, false);
+}
+
+static int rnandc_write_subpage_hw_ecc(struct nand_chip *chip, u32 req_offset,
+				       u32 req_len, const u8 *bufpoi,
+				       int oob_required, int page)
+{
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	unsigned int page_off = round_down(req_offset, chip->ecc.size);
+	unsigned int real_len = round_up(req_offset + req_len - page_off,
+					 chip->ecc.size);
+	unsigned int start_chunk = page_off / chip->ecc.size;
+	unsigned int ecc_off = 2 + (start_chunk * chip->ecc.bytes);
+	struct rnandc_op rop = {
+		.command = COMMAND_INPUT_SEL_AHBS | COMMAND_0(NAND_CMD_SEQIN) |
+			   COMMAND_1(NAND_CMD_PAGEPROG) | COMMAND_FIFO_SEL |
+			   COMMAND_SEQ_WRITE_PAGE,
+		.addr0_row = page,
+		.addr0_col = page_off,
+		.len = real_len,
+		.ecc_offset = ECC_OFFSET(mtd->writesize + ecc_off),
+	};
+	int ret;
+
+	/* Prepare controller */
+	rnandc_select_target(chip, chip->cur_cs);
+	rnandc_clear_status(rnandc);
+	rnandc_en_correction(rnandc);
+	rnandc_trigger_op(rnandc, &rop);
+
+	while (FIFO_STATE_W_FULL(readl(rnandc->regs + FIFO_STATE_REG)))
+		cpu_relax();
+
+	iowrite32_rep(rnandc->regs + FIFO_DATA_REG, bufpoi + page_off,
+		      real_len / 4);
+
+	while (!FIFO_STATE_W_EMPTY(readl(rnandc->regs + FIFO_STATE_REG)))
+		cpu_relax();
+
+	ret = rnandc_wait_end_of_op(rnandc, chip);
+	rnandc_dis_correction(rnandc);
+	if (ret) {
+		dev_err(rnandc->dev, "Write subpage operation never ending\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * This controller is simple enough and thus does not need to use the parser
+ * provided by the core, instead, handle every situation here.
+ */
+static int rnandc_exec_op(struct nand_chip *chip,
+			  const struct nand_operation *op, bool check_only)
+{
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+	const struct nand_op_instr *instr = NULL;
+	struct rnandc_op rop = {
+		.command = COMMAND_INPUT_SEL_AHBS,
+		.gen_seq_ctrl = GEN_SEQ_IMD_SEQ,
+	};
+	unsigned int cmd_phase = 0, addr_phase = 0, data_phase = 0,
+		delay_phase = 0, delays = 0;
+	unsigned int op_id, col_addrs, row_addrs, naddrs, remainder, words, i;
+	const u8 *addrs;
+	u32 last_bytes;
+	int ret;
+
+	if (!check_only)
+		rnandc_select_target(chip, op->cs);
+
+	for (op_id = 0; op_id < op->ninstrs; op_id++) {
+		instr = &op->instrs[op_id];
+
+		nand_op_trace("  ", instr);
+
+		switch (instr->type) {
+		case NAND_OP_CMD_INSTR:
+			switch (cmd_phase++) {
+			case 0:
+				rop.command |= COMMAND_0(instr->ctx.cmd.opcode);
+				rop.gen_seq_ctrl |= GEN_SEQ_CMD0_EN;
+				break;
+			case 1:
+				rop.gen_seq_ctrl |= GEN_SEQ_COMMAND_3(instr->ctx.cmd.opcode);
+				rop.gen_seq_ctrl |= GEN_SEQ_CMD3_EN;
+				if (addr_phase == 0)
+					addr_phase = 1;
+				break;
+			case 2:
+				rop.command |= COMMAND_2(instr->ctx.cmd.opcode);
+				rop.gen_seq_ctrl |= GEN_SEQ_CMD2_EN;
+				if (addr_phase <= 1)
+					addr_phase = 2;
+				break;
+			case 3:
+				rop.command |= COMMAND_1(instr->ctx.cmd.opcode);
+				rop.gen_seq_ctrl |= GEN_SEQ_CMD1_EN;
+				if (addr_phase <= 1)
+					addr_phase = 2;
+				if (delay_phase == 0)
+					delay_phase = 1;
+				if (data_phase == 0)
+					data_phase = 1;
+				break;
+			default:
+				return -EOPNOTSUPP;
+			}
+			break;
+
+		case NAND_OP_ADDR_INSTR:
+			addrs = instr->ctx.addr.addrs;
+			naddrs = instr->ctx.addr.naddrs;
+			if (naddrs > 5)
+				return -EOPNOTSUPP;
+
+			col_addrs = min(2U, naddrs);
+			row_addrs = naddrs > 2 ? naddrs - col_addrs : 0;
+
+			switch (addr_phase++) {
+			case 0:
+				for (i = 0; i < col_addrs; i++)
+					rop.addr0_col |= addrs[i] << (i * 8);
+				rop.gen_seq_ctrl |= GEN_SEQ_COL_A0(col_addrs);
+
+				for (i = 0; i < row_addrs; i++)
+					rop.addr0_row |= addrs[2 + i] << (i * 8);
+				rop.gen_seq_ctrl |= GEN_SEQ_ROW_A0(row_addrs);
+
+				if (cmd_phase == 0)
+					cmd_phase = 1;
+				break;
+			case 1:
+				for (i = 0; i < col_addrs; i++)
+					rop.addr1_col |= addrs[i] << (i * 8);
+				rop.gen_seq_ctrl |= GEN_SEQ_COL_A1(col_addrs);
+
+				for (i = 0; i < row_addrs; i++)
+					rop.addr1_row |= addrs[2 + i] << (i * 8);
+				rop.gen_seq_ctrl |= GEN_SEQ_ROW_A1(row_addrs);
+
+				if (cmd_phase <= 1)
+					cmd_phase = 2;
+				break;
+			default:
+				return -EOPNOTSUPP;
+			}
+			break;
+
+		case NAND_OP_DATA_IN_INSTR:
+			rop.read = true;
+			fallthrough;
+		case NAND_OP_DATA_OUT_INSTR:
+			rop.gen_seq_ctrl |= GEN_SEQ_DATA_EN;
+			rop.buf = instr->ctx.data.buf.in;
+			rop.len = instr->ctx.data.len;
+			rop.command |= COMMAND_FIFO_SEL;
+
+			switch (data_phase++) {
+			case 0:
+				if (cmd_phase <= 2)
+					cmd_phase = 3;
+				if (addr_phase <= 1)
+					addr_phase = 2;
+				if (delay_phase == 0)
+					delay_phase = 1;
+				break;
+			default:
+				return -EOPNOTSUPP;
+			}
+			break;
+
+		case NAND_OP_WAITRDY_INSTR:
+			switch (delay_phase++) {
+			case 0:
+				rop.gen_seq_ctrl |= GEN_SEQ_DELAY0_EN;
+
+				if (cmd_phase <= 2)
+					cmd_phase = 3;
+				break;
+			case 1:
+				rop.gen_seq_ctrl |= GEN_SEQ_DELAY1_EN;
+
+				if (cmd_phase <= 3)
+					cmd_phase = 4;
+				if (data_phase == 0)
+					data_phase = 1;
+				break;
+			default:
+				return -EOPNOTSUPP;
+			}
+			break;
+		}
+	}
+
+	/*
+	 * Sequence 19 is generic and dedicated to write operations.
+	 * Sequence 18 is also generic and works for all other operations.
+	 */
+	if (rop.buf && !rop.read)
+		rop.command |= COMMAND_SEQ_GEN_OUT;
+	else
+		rop.command |= COMMAND_SEQ_GEN_IN;
+
+	if (delays > 1) {
+		dev_err(rnandc->dev, "Cannot handle more than one wait delay\n");
+		return -EOPNOTSUPP;
+	}
+
+	if (check_only)
+		return 0;
+
+	rnandc_trigger_op(rnandc, &rop);
+
+	words = rop.len / sizeof(u32);
+	remainder = rop.len % sizeof(u32);
+	if (rop.buf && rop.read) {
+		while (!FIFO_STATE_C_EMPTY(readl(rnandc->regs + FIFO_STATE_REG)))
+			cpu_relax();
+
+		while (FIFO_STATE_R_EMPTY(readl(rnandc->regs + FIFO_STATE_REG)))
+			cpu_relax();
+
+		ioread32_rep(rnandc->regs + FIFO_DATA_REG, rop.buf, words);
+		if (remainder) {
+			last_bytes = readl_relaxed(rnandc->regs + FIFO_DATA_REG);
+			memcpy(rop.buf + (words * sizeof(u32)), &last_bytes,
+			       remainder);
+		}
+
+		if (!FIFO_STATE_R_EMPTY(readl(rnandc->regs + FIFO_STATE_REG))) {
+			dev_warn(rnandc->dev,
+				 "Clearing residual data in the read FIFO\n");
+			rnandc_clear_fifo(rnandc);
+		}
+	} else if (rop.len && !rop.read) {
+		while (FIFO_STATE_W_FULL(readl(rnandc->regs + FIFO_STATE_REG)))
+			cpu_relax();
+
+		iowrite32_rep(rnandc->regs + FIFO_DATA_REG, rop.buf,
+			      DIV_ROUND_UP(rop.len, 4));
+
+		if (remainder) {
+			last_bytes = 0;
+			memcpy(&last_bytes, rop.buf + (words * sizeof(u32)), remainder);
+			writel_relaxed(last_bytes, rnandc->regs + FIFO_DATA_REG);
+		}
+
+		while (!FIFO_STATE_W_EMPTY(readl(rnandc->regs + FIFO_STATE_REG)))
+			cpu_relax();
+	}
+
+	ret = rnandc_wait_end_of_op(rnandc, chip);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int rnandc_setup_interface(struct nand_chip *chip, int chipnr,
+				  const struct nand_interface_config *conf)
+{
+	struct rnand_chip *rnand = to_rnand(chip);
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+	unsigned int period_ns = 1000000000 / clk_get_rate(rnandc->eclk);
+	const struct nand_sdr_timings *sdr;
+	unsigned int cyc, cle, ale, bef_dly, ca_to_data;
+
+	sdr = nand_get_sdr_timings(conf);
+	if (IS_ERR(sdr))
+		return PTR_ERR(sdr);
+
+	if (sdr->tRP_min != sdr->tWP_min || sdr->tREH_min != sdr->tWH_min) {
+		dev_err(rnandc->dev, "Read and write hold times must be identical\n");
+		return -EINVAL;
+	}
+
+	if (chipnr < 0)
+		return 0;
+
+	rnand->timings_asyn =
+		TIMINGS_ASYN_TRWP(TO_CYCLES64(sdr->tRP_min, period_ns)) |
+		TIMINGS_ASYN_TRWH(TO_CYCLES64(sdr->tREH_min, period_ns));
+	rnand->tim_seq0 =
+		TIM_SEQ0_TCCS(TO_CYCLES64(sdr->tCCS_min, period_ns)) |
+		TIM_SEQ0_TADL(TO_CYCLES64(sdr->tADL_min, period_ns)) |
+		TIM_SEQ0_TRHW(TO_CYCLES64(sdr->tRHW_min, period_ns)) |
+		TIM_SEQ0_TWHR(TO_CYCLES64(sdr->tWHR_min, period_ns));
+	rnand->tim_seq1 =
+		TIM_SEQ1_TWB(TO_CYCLES64(sdr->tWB_max, period_ns)) |
+		TIM_SEQ1_TRR(TO_CYCLES64(sdr->tRR_min, period_ns)) |
+		TIM_SEQ1_TWW(TO_CYCLES64(sdr->tWW_min, period_ns));
+
+	cyc = sdr->tDS_min + sdr->tDH_min;
+	cle = sdr->tCLH_min + sdr->tCLS_min;
+	ale = sdr->tALH_min + sdr->tALS_min;
+	bef_dly = sdr->tWB_max - sdr->tDH_min;
+	ca_to_data = sdr->tWHR_min + sdr->tREA_max - sdr->tDH_min;
+
+	/*
+	 * D0 = CMD -> ADDR = tCLH + tCLS - 1 cycle
+	 * D1 = CMD -> CMD = tCLH + tCLS - 1 cycle
+	 * D2 = CMD -> DLY = tWB - tDH
+	 * D3 = CMD -> DATA = tWHR + tREA - tDH
+	 */
+	rnand->tim_gen_seq0 =
+		TIM_GEN_SEQ0_D0(TO_CYCLES64(cle - cyc, period_ns)) |
+		TIM_GEN_SEQ0_D1(TO_CYCLES64(cle - cyc, period_ns)) |
+		TIM_GEN_SEQ0_D2(TO_CYCLES64(bef_dly, period_ns)) |
+		TIM_GEN_SEQ0_D3(TO_CYCLES64(ca_to_data, period_ns));
+
+	/*
+	 * D4 = ADDR -> CMD = tALH + tALS - 1 cyle
+	 * D5 = ADDR -> ADDR = tALH + tALS - 1 cyle
+	 * D6 = ADDR -> DLY = tWB - tDH
+	 * D7 = ADDR -> DATA = tWHR + tREA - tDH
+	 */
+	rnand->tim_gen_seq1 =
+		TIM_GEN_SEQ1_D4(TO_CYCLES64(ale - cyc, period_ns)) |
+		TIM_GEN_SEQ1_D5(TO_CYCLES64(ale - cyc, period_ns)) |
+		TIM_GEN_SEQ1_D6(TO_CYCLES64(bef_dly, period_ns)) |
+		TIM_GEN_SEQ1_D7(TO_CYCLES64(ca_to_data, period_ns));
+
+	/*
+	 * D8 = DLY -> DATA = tRR + tREA
+	 * D9 = DLY -> CMD = tRR
+	 * D10 = DATA -> CMD = tCLH + tCLS - 1 cycle
+	 * D11 = DATA -> DLY = tWB - tDH
+	 */
+	rnand->tim_gen_seq2 =
+		TIM_GEN_SEQ2_D8(TO_CYCLES64(sdr->tRR_min + sdr->tREA_max, period_ns)) |
+		TIM_GEN_SEQ2_D9(TO_CYCLES64(sdr->tRR_min, period_ns)) |
+		TIM_GEN_SEQ2_D10(TO_CYCLES64(cle - cyc, period_ns)) |
+		TIM_GEN_SEQ2_D11(TO_CYCLES64(bef_dly, period_ns));
+
+	/* D12 = DATA -> END = tCLH - tDH */
+	rnand->tim_gen_seq3 =
+		TIM_GEN_SEQ3_D12(TO_CYCLES64(sdr->tCLH_min - sdr->tDH_min, period_ns));
+
+	return 0;
+}
+
+static int rnandc_ooblayout_ecc(struct mtd_info *mtd, int section,
+				struct mtd_oob_region *oobregion)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	unsigned int eccbytes = round_up(chip->ecc.bytes, 4) * chip->ecc.steps;
+
+	if (section)
+		return -ERANGE;
+
+	oobregion->offset = 2;
+	oobregion->length = eccbytes;
+
+	return 0;
+}
+
+static int rnandc_ooblayout_free(struct mtd_info *mtd, int section,
+				 struct mtd_oob_region *oobregion)
+{
+	struct nand_chip *chip = mtd_to_nand(mtd);
+	unsigned int eccbytes = round_up(chip->ecc.bytes, 4) * chip->ecc.steps;
+
+	if (section)
+		return -ERANGE;
+
+	oobregion->offset = 2 + eccbytes;
+	oobregion->length = mtd->oobsize - oobregion->offset;
+
+	return 0;
+}
+
+static const struct mtd_ooblayout_ops rnandc_ooblayout_ops = {
+	.ecc = rnandc_ooblayout_ecc,
+	.free = rnandc_ooblayout_free,
+};
+
+static int rnandc_hw_ecc_controller_init(struct nand_chip *chip)
+{
+	struct rnand_chip *rnand = to_rnand(chip);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+
+	if (mtd->writesize > SZ_16K) {
+		dev_err(rnandc->dev, "Unsupported page size\n");
+		return -EINVAL;
+	}
+
+	switch (chip->ecc.size) {
+	case SZ_256:
+		rnand->control |= CONTROL_ECC_BLOCK_SIZE_256;
+		break;
+	case SZ_512:
+		rnand->control |= CONTROL_ECC_BLOCK_SIZE_512;
+		break;
+	case SZ_1K:
+		rnand->control |= CONTROL_ECC_BLOCK_SIZE_1024;
+		break;
+	default:
+		dev_err(rnandc->dev, "Unsupported ECC chunk size\n");
+		return -EINVAL;
+	}
+
+	switch (chip->ecc.strength) {
+	case 2:
+		chip->ecc.bytes = 4;
+		rnand->ecc_ctrl |= ECC_CTRL_CAP_2B;
+		break;
+	case 4:
+		chip->ecc.bytes = 7;
+		rnand->ecc_ctrl |= ECC_CTRL_CAP_4B;
+		break;
+	case 8:
+		chip->ecc.bytes = 14;
+		rnand->ecc_ctrl |= ECC_CTRL_CAP_8B;
+		break;
+	case 16:
+		chip->ecc.bytes = 28;
+		rnand->ecc_ctrl |= ECC_CTRL_CAP_16B;
+		break;
+	case 24:
+		chip->ecc.bytes = 42;
+		rnand->ecc_ctrl |= ECC_CTRL_CAP_24B;
+		break;
+	case 32:
+		chip->ecc.bytes = 56;
+		rnand->ecc_ctrl |= ECC_CTRL_CAP_32B;
+		break;
+	default:
+		dev_err(rnandc->dev, "Unsupported ECC strength\n");
+		return -EINVAL;
+	}
+
+	rnand->ecc_ctrl |= ECC_CTRL_ERR_THRESHOLD(chip->ecc.strength);
+
+	mtd_set_ooblayout(mtd, &rnandc_ooblayout_ops);
+	chip->ecc.steps = mtd->writesize / chip->ecc.size;
+	chip->ecc.read_page = rnandc_read_page_hw_ecc;
+	chip->ecc.read_subpage = rnandc_read_subpage_hw_ecc;
+	chip->ecc.write_page = rnandc_write_page_hw_ecc;
+	chip->ecc.write_subpage = rnandc_write_subpage_hw_ecc;
+
+	return 0;
+}
+
+static int rnandc_ecc_init(struct nand_chip *chip)
+{
+	struct nand_ecc_ctrl *ecc = &chip->ecc;
+	const struct nand_ecc_props *requirements =
+		nanddev_get_ecc_requirements(&chip->base);
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+	int ret;
+
+	if (ecc->engine_type != NAND_ECC_ENGINE_TYPE_NONE &&
+	    (!ecc->size || !ecc->strength)) {
+		if (requirements->step_size && requirements->strength) {
+			ecc->size = requirements->step_size;
+			ecc->strength = requirements->strength;
+		} else {
+			dev_err(rnandc->dev, "No minimum ECC strength\n");
+			return -EINVAL;
+		}
+	}
+
+	switch (ecc->engine_type) {
+	case NAND_ECC_ENGINE_TYPE_ON_HOST:
+		ret = rnandc_hw_ecc_controller_init(chip);
+		if (ret)
+			return ret;
+		break;
+	case NAND_ECC_ENGINE_TYPE_NONE:
+	case NAND_ECC_ENGINE_TYPE_SOFT:
+	case NAND_ECC_ENGINE_TYPE_ON_DIE:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int rnandc_attach_chip(struct nand_chip *chip)
+{
+	struct rnand_chip *rnand = to_rnand(chip);
+	struct rnandc *rnandc = to_rnandc(chip->controller);
+	struct mtd_info *mtd = nand_to_mtd(chip);
+	struct nand_memory_organization *memorg = nanddev_get_memorg(&chip->base);
+	int ret;
+
+	/* Do not store BBT bits in the OOB section as it is not protected */
+	if (chip->bbt_options & NAND_BBT_USE_FLASH)
+		chip->bbt_options |= NAND_BBT_NO_OOB;
+
+	if (mtd->writesize <= 512) {
+		dev_err(rnandc->dev, "Small page devices not supported\n");
+		return -EINVAL;
+	}
+
+	rnand->control |= CONTROL_CHECK_RB_LINE | CONTROL_INT_EN;
+
+	switch (memorg->pages_per_eraseblock) {
+	case 32:
+		rnand->control |= CONTROL_BLOCK_SIZE_32P;
+		break;
+	case 64:
+		rnand->control |= CONTROL_BLOCK_SIZE_64P;
+		break;
+	case 128:
+		rnand->control |= CONTROL_BLOCK_SIZE_128P;
+		break;
+	case 256:
+		rnand->control |= CONTROL_BLOCK_SIZE_256P;
+		break;
+	default:
+		dev_err(rnandc->dev, "Unsupported memory organization\n");
+		return -EINVAL;
+	}
+
+	chip->options |= NAND_SUBPAGE_READ;
+
+	ret = rnandc_ecc_init(chip);
+	if (ret) {
+		dev_err(rnandc->dev, "ECC initialization failed (%d)\n", ret);
+		return ret;
+	}
+
+	/* Force an update of the configuration registers */
+	rnand->selected_die = -1;
+
+	return 0;
+}
+
+static const struct nand_controller_ops rnandc_ops = {
+	.attach_chip = rnandc_attach_chip,
+	.exec_op = rnandc_exec_op,
+	.setup_interface = rnandc_setup_interface,
+};
+
+static int rnandc_alloc_dma_buf(struct rnandc *rnandc,
+				struct mtd_info *new_mtd)
+{
+	unsigned int max_len = new_mtd->writesize + new_mtd->oobsize;
+	struct rnand_chip *entry, *temp;
+	struct nand_chip *chip;
+	struct mtd_info *mtd;
+
+	list_for_each_entry_safe(entry, temp, &rnandc->chips, node) {
+		chip = &entry->chip;
+		mtd = nand_to_mtd(chip);
+		max_len = max(max_len, mtd->writesize + mtd->oobsize);
+	}
+
+	if (rnandc->buf && rnandc->buf_sz < max_len) {
+		devm_kfree(rnandc->dev, rnandc->buf);
+		rnandc->buf = NULL;
+	}
+
+	if (!rnandc->buf) {
+		rnandc->buf_sz = max_len;
+		rnandc->buf = devm_kmalloc(rnandc->dev, max_len,
+					   GFP_KERNEL | GFP_DMA);
+		if (!rnandc->buf)
+			return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int rnandc_chip_init(struct rnandc *rnandc, struct device_node *np)
+{
+	struct rnand_chip *rnand;
+	struct mtd_info *mtd;
+	struct nand_chip *chip;
+	int nsels, ret, i;
+	u32 cs;
+
+	nsels = of_property_count_elems_of_size(np, "reg", sizeof(u32));
+	if (nsels <= 0) {
+		ret = (nsels < 0) ? nsels : -EINVAL;
+		dev_err(rnandc->dev, "Invalid reg property (%d)\n", ret);
+		return ret;
+	}
+
+	/* Alloc the driver's NAND chip structure */
+	rnand = devm_kzalloc(rnandc->dev, struct_size(rnand, sels, nsels),
+			     GFP_KERNEL);
+	if (!rnand)
+		return -ENOMEM;
+
+	rnand->nsels = nsels;
+	rnand->selected_die = -1;
+
+	for (i = 0; i < nsels; i++) {
+		ret = of_property_read_u32_index(np, "reg", i, &cs);
+		if (ret) {
+			dev_err(rnandc->dev, "Incomplete reg property (%d)\n", ret);
+			return ret;
+		}
+
+		if (cs >= RNANDC_CS_NUM) {
+			dev_err(rnandc->dev, "Invalid reg property (%d)\n", cs);
+			return -EINVAL;
+		}
+
+		if (test_and_set_bit(cs, &rnandc->assigned_cs)) {
+			dev_err(rnandc->dev, "CS %d already assigned\n", cs);
+			return -EINVAL;
+		}
+
+		/*
+		 * No need to check for RB or WP properties, there is a 1:1
+		 * mandatory mapping with the CS.
+		 */
+		rnand->sels[i].cs = cs;
+	}
+
+	chip = &rnand->chip;
+	chip->controller = &rnandc->controller;
+	nand_set_flash_node(chip, np);
+
+	mtd = nand_to_mtd(chip);
+	mtd->dev.parent = rnandc->dev;
+	if (!mtd->name) {
+		dev_err(rnandc->dev, "Missing MTD label\n");
+		return -EINVAL;
+	}
+
+	ret = nand_scan(chip, rnand->nsels);
+	if (ret) {
+		dev_err(rnandc->dev, "Failed to scan the NAND chip (%d)\n", ret);
+		return ret;
+	}
+
+	ret = rnandc_alloc_dma_buf(rnandc, mtd);
+	if (ret)
+		goto cleanup_nand;
+
+	ret = mtd_device_register(mtd, NULL, 0);
+	if (ret) {
+		dev_err(rnandc->dev, "Failed to register MTD device (%d)\n", ret);
+		goto cleanup_nand;
+	}
+
+	list_add_tail(&rnand->node, &rnandc->chips);
+
+	return 0;
+
+cleanup_nand:
+	nand_cleanup(chip);
+
+	return ret;
+}
+
+static void rnandc_chips_cleanup(struct rnandc *rnandc)
+{
+	struct rnand_chip *entry, *temp;
+	struct nand_chip *chip;
+	int ret;
+
+	list_for_each_entry_safe(entry, temp, &rnandc->chips, node) {
+		chip = &entry->chip;
+		ret = mtd_device_unregister(nand_to_mtd(chip));
+		WARN_ON(ret);
+		nand_cleanup(chip);
+		list_del(&entry->node);
+	}
+}
+
+static int rnandc_chips_init(struct rnandc *rnandc)
+{
+	struct device_node *np;
+	int ret;
+
+	for_each_child_of_node(rnandc->dev->of_node, np) {
+		ret = rnandc_chip_init(rnandc, np);
+		if (ret) {
+			of_node_put(np);
+			goto cleanup_chips;
+		}
+	}
+
+	return 0;
+
+cleanup_chips:
+	rnandc_chips_cleanup(rnandc);
+
+	return ret;
+}
+
+static int rnandc_probe(struct platform_device *pdev)
+{
+	struct rnandc *rnandc;
+	int irq, ret;
+
+	rnandc = devm_kzalloc(&pdev->dev, sizeof(*rnandc), GFP_KERNEL);
+	if (!rnandc)
+		return -ENOMEM;
+
+	rnandc->dev = &pdev->dev;
+	nand_controller_init(&rnandc->controller);
+	rnandc->controller.ops = &rnandc_ops;
+	INIT_LIST_HEAD(&rnandc->chips);
+	init_completion(&rnandc->complete);
+
+	rnandc->regs = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(rnandc->regs))
+		return PTR_ERR(rnandc->regs);
+
+	/* APB clock */
+	rnandc->hclk = devm_clk_get(&pdev->dev, "hclk");
+	if (IS_ERR(rnandc->hclk))
+		return PTR_ERR(rnandc->hclk);
+
+	/* External NAND bus clock */
+	rnandc->eclk = devm_clk_get(&pdev->dev, "eclk");
+	if (IS_ERR(rnandc->eclk))
+		return PTR_ERR(rnandc->eclk);
+
+	ret = clk_prepare_enable(rnandc->hclk);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(rnandc->eclk);
+	if (ret)
+		goto disable_hclk;
+
+	rnandc_dis_interrupts(rnandc);
+	irq = platform_get_irq_optional(pdev, 0);
+	if (irq == -EPROBE_DEFER) {
+		ret = irq;
+		goto disable_eclk;
+	} else if (irq < 0) {
+		dev_info(&pdev->dev, "No IRQ found, fallback to polling\n");
+		rnandc->use_polling = true;
+	} else {
+		ret = devm_request_irq(&pdev->dev, irq, rnandc_irq_handler, 0,
+				       "renesas-nand-controller", rnandc);
+		if (ret < 0)
+			goto disable_eclk;
+	}
+
+	ret = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
+	if (ret)
+		goto disable_eclk;
+
+	rnandc_clear_fifo(rnandc);
+
+	platform_set_drvdata(pdev, rnandc);
+
+	ret = rnandc_chips_init(rnandc);
+	if (ret)
+		goto disable_eclk;
+
+	return 0;
+
+disable_eclk:
+	clk_disable_unprepare(rnandc->eclk);
+disable_hclk:
+	clk_disable_unprepare(rnandc->hclk);
+
+	return ret;
+}
+
+static int rnandc_remove(struct platform_device *pdev)
+{
+	struct rnandc *rnandc = platform_get_drvdata(pdev);
+
+	rnandc_chips_cleanup(rnandc);
+
+	clk_disable_unprepare(rnandc->eclk);
+	clk_disable_unprepare(rnandc->hclk);
+
+	return 0;
+}
+
+static const struct of_device_id rnandc_id_table[] = {
+	{ .compatible = "renesas,rcar-gen3-nandc" },
+	{ .compatible = "renesas,rzn1-nandc" },
+	{} /* sentinel */
+};
+MODULE_DEVICE_TABLE(of, rnandc_id_table);
+
+static struct platform_driver rnandc_driver = {
+	.driver = {
+		.name = "renesas-nandc",
+		.of_match_table = of_match_ptr(rnandc_id_table),
+	},
+	.probe = rnandc_probe,
+	.remove = rnandc_remove,
+};
+module_platform_driver(rnandc_driver);
+
+MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com>");
+MODULE_DESCRIPTION("Renesas R-Car Gen3 & RZ/N1 NAND controller driver");
+MODULE_LICENSE("GPL v2");
-- 
2.27.0


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

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

* [PATCH v6 3/4] MAINTAINERS: Add an entry for Renesas NAND controller
  2021-12-17 14:20 ` Miquel Raynal
@ 2021-12-17 14:20   ` Miquel Raynal
  -1 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-17 14:20 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Rob Herring, devicetree, Milan Stevanovic, Jimmy Lalande,
	Thomas Petazzoni, linux-renesas-soc, Magnus Damm,
	Gareth Williams, Phil Edworthy, Geert Uytterhoeven, Wolfram Sang,
	Chris Brandt, Ralph Siemsen, Miquel Raynal

Point to the driver and the bindings.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 43007f2d29e0..4b357bef6ea0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16287,6 +16287,13 @@ S:	Supported
 F:	Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
 F:	drivers/iio/adc/rzg2l_adc.c
 
+RENESAS R-CAR GEN3 & RZ/N1 NAND CONTROLLER DRIVER
+M:	Miquel Raynal <miquel.raynal@bootlin.com>
+L:	linux-mtd@lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
+F:	drivers/mtd/nand/raw/renesas-nand-controller.c
+
 RESET CONTROLLER FRAMEWORK
 M:	Philipp Zabel <p.zabel@pengutronix.de>
 S:	Maintained
-- 
2.27.0


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

* [PATCH v6 3/4] MAINTAINERS: Add an entry for Renesas NAND controller
@ 2021-12-17 14:20   ` Miquel Raynal
  0 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-17 14:20 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Rob Herring, devicetree, Milan Stevanovic, Jimmy Lalande,
	Thomas Petazzoni, linux-renesas-soc, Magnus Damm,
	Gareth Williams, Phil Edworthy, Geert Uytterhoeven, Wolfram Sang,
	Chris Brandt, Ralph Siemsen, Miquel Raynal

Point to the driver and the bindings.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
---
 MAINTAINERS | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 43007f2d29e0..4b357bef6ea0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16287,6 +16287,13 @@ S:	Supported
 F:	Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
 F:	drivers/iio/adc/rzg2l_adc.c
 
+RENESAS R-CAR GEN3 & RZ/N1 NAND CONTROLLER DRIVER
+M:	Miquel Raynal <miquel.raynal@bootlin.com>
+L:	linux-mtd@lists.infradead.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
+F:	drivers/mtd/nand/raw/renesas-nand-controller.c
+
 RESET CONTROLLER FRAMEWORK
 M:	Philipp Zabel <p.zabel@pengutronix.de>
 S:	Maintained
-- 
2.27.0


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

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

* [PATCH v6 4/4] ARM: dts: r9a06g032: Describe the NAND controller
  2021-12-17 14:20 ` Miquel Raynal
@ 2021-12-17 14:20   ` Miquel Raynal
  -1 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-17 14:20 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Rob Herring, devicetree, Milan Stevanovic, Jimmy Lalande,
	Thomas Petazzoni, linux-renesas-soc, Magnus Damm,
	Gareth Williams, Phil Edworthy, Geert Uytterhoeven, Wolfram Sang,
	Chris Brandt, Ralph Siemsen, Miquel Raynal, Geert Uytterhoeven

Describe the NAND controller embedded in r9a06g032 SoCs.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
 arch/arm/boot/dts/r9a06g032.dtsi | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/arch/arm/boot/dts/r9a06g032.dtsi b/arch/arm/boot/dts/r9a06g032.dtsi
index c47896e4ab58..db657224688a 100644
--- a/arch/arm/boot/dts/r9a06g032.dtsi
+++ b/arch/arm/boot/dts/r9a06g032.dtsi
@@ -173,6 +173,17 @@ pinctrl: pinctrl@40067000 {
 			status = "okay";
 		};
 
+		nand_controller: nand-controller@40102000 {
+			compatible = "renesas,r9a06g032-nandc", "renesas,rzn1-nandc";
+			reg = <0x40102000 0x2000>;
+			interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&sysctrl R9A06G032_HCLK_NAND>, <&sysctrl R9A06G032_CLK_NAND>;
+			clock-names = "hclk", "eclk";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "disabled";
+		};
+
 		gic: interrupt-controller@44101000 {
 			compatible = "arm,gic-400", "arm,cortex-a7-gic";
 			interrupt-controller;
-- 
2.27.0


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

* [PATCH v6 4/4] ARM: dts: r9a06g032: Describe the NAND controller
@ 2021-12-17 14:20   ` Miquel Raynal
  0 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-17 14:20 UTC (permalink / raw)
  To: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Rob Herring, devicetree, Milan Stevanovic, Jimmy Lalande,
	Thomas Petazzoni, linux-renesas-soc, Magnus Damm,
	Gareth Williams, Phil Edworthy, Geert Uytterhoeven, Wolfram Sang,
	Chris Brandt, Ralph Siemsen, Miquel Raynal, Geert Uytterhoeven

Describe the NAND controller embedded in r9a06g032 SoCs.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
 arch/arm/boot/dts/r9a06g032.dtsi | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/arch/arm/boot/dts/r9a06g032.dtsi b/arch/arm/boot/dts/r9a06g032.dtsi
index c47896e4ab58..db657224688a 100644
--- a/arch/arm/boot/dts/r9a06g032.dtsi
+++ b/arch/arm/boot/dts/r9a06g032.dtsi
@@ -173,6 +173,17 @@ pinctrl: pinctrl@40067000 {
 			status = "okay";
 		};
 
+		nand_controller: nand-controller@40102000 {
+			compatible = "renesas,r9a06g032-nandc", "renesas,rzn1-nandc";
+			reg = <0x40102000 0x2000>;
+			interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&sysctrl R9A06G032_HCLK_NAND>, <&sysctrl R9A06G032_CLK_NAND>;
+			clock-names = "hclk", "eclk";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "disabled";
+		};
+
 		gic: interrupt-controller@44101000 {
 			compatible = "arm,gic-400", "arm,cortex-a7-gic";
 			interrupt-controller;
-- 
2.27.0


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

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

* Re: [PATCH v6 1/4] dt-bindings: mtd: renesas: Describe Renesas R-Car Gen3 & RZ/N1 NAND controller
  2021-12-17 14:20   ` Miquel Raynal
@ 2021-12-17 15:44     ` Geert Uytterhoeven
  -1 siblings, 0 replies; 32+ messages in thread
From: Geert Uytterhoeven @ 2021-12-17 15:44 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, MTD Maling List, Rob Herring,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Milan Stevanovic, Jimmy Lalande, Thomas Petazzoni, Linux-Renesas,
	Magnus Damm, Gareth Williams, Phil Edworthy, Wolfram Sang,
	Chris Brandt, Ralph Siemsen, Geert Uytterhoeven, Rob Herring

Hi Miquel,

On Fri, Dec 17, 2021 at 3:20 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> Add a Yaml description for this Renesas NAND controller.
>
> As this controller is embedded on different SoC families, provide:
> * a family-specific "r-car-gen3" compatible and a more specific
>   "r8a77951" one
> * a family-specific "rzn1" compatible and a more specific "r9a06g032"
>   one
>
> More compatibles can be added later if new SoCs with this controller
> must be supported.
>
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> Reviewed-by: Rob Herring <robh@kernel.org>

Thanks for the update!

> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
> @@ -0,0 +1,66 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mtd/renesas-nandc.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Renesas R-Car Gen3 & RZ/N1x NAND flash controller device tree bindings
> +
> +maintainers:
> +  - Miquel Raynal <miquel.raynal@bootlin.com>
> +
> +allOf:
> +  - $ref: "nand-controller.yaml"
> +
> +properties:
> +  compatible:
> +    oneOf:
> +      - items:
> +          - enum:
> +              - renesas,r8a77951-nandc
> +          - const: renesas,rcar-gen3-nandc

Might be a bit premature to add these before they have been tested,
and because there are small differences in integration, cfr. below.

> +
> +      - items:
> +          - enum:
> +              - renesas,r9a06g032-nandc
> +          - const: renesas,rzn1-nandc
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  clocks:
> +    items:
> +      - description: APB host controller clock
> +      - description: External NAND bus clock
> +
> +  clock-names:
> +    items:
> +      - const: hclk
> +      - const: eclk

On R-Car Gen3, there's a single module clock.
Plus a power-domain to manage that.

Actually the RZ/N1 clock driver also registers a PM Domain, so letting
Runtime PM manage the clocks may work on RZ/N1, too...

On R-Car Gen3, there's also a module reset.

> +
> +required:
> +  - compatible
> +  - reg
> +  - clocks
> +  - clock-names
> +  - interrupts
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +    #include <dt-bindings/clock/r9a06g032-sysctrl.h>
> +
> +    nand-controller@40102000 {
> +        compatible = "renesas,r9a06g032-nandc", "renesas,rzn1-nandc";
> +        reg = <0x40102000 0x2000>;
> +        interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
> +        clocks = <&sysctrl R9A06G032_HCLK_NAND>, <&sysctrl R9A06G032_CLK_NAND>;
> +        clock-names = "hclk", "eclk";
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +    };

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

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

* Re: [PATCH v6 1/4] dt-bindings: mtd: renesas: Describe Renesas R-Car Gen3 & RZ/N1 NAND controller
@ 2021-12-17 15:44     ` Geert Uytterhoeven
  0 siblings, 0 replies; 32+ messages in thread
From: Geert Uytterhoeven @ 2021-12-17 15:44 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, MTD Maling List, Rob Herring,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Milan Stevanovic, Jimmy Lalande, Thomas Petazzoni, Linux-Renesas,
	Magnus Damm, Gareth Williams, Phil Edworthy, Wolfram Sang,
	Chris Brandt, Ralph Siemsen, Geert Uytterhoeven, Rob Herring

Hi Miquel,

On Fri, Dec 17, 2021 at 3:20 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> Add a Yaml description for this Renesas NAND controller.
>
> As this controller is embedded on different SoC families, provide:
> * a family-specific "r-car-gen3" compatible and a more specific
>   "r8a77951" one
> * a family-specific "rzn1" compatible and a more specific "r9a06g032"
>   one
>
> More compatibles can be added later if new SoCs with this controller
> must be supported.
>
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> Reviewed-by: Rob Herring <robh@kernel.org>

Thanks for the update!

> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
> @@ -0,0 +1,66 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/mtd/renesas-nandc.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Renesas R-Car Gen3 & RZ/N1x NAND flash controller device tree bindings
> +
> +maintainers:
> +  - Miquel Raynal <miquel.raynal@bootlin.com>
> +
> +allOf:
> +  - $ref: "nand-controller.yaml"
> +
> +properties:
> +  compatible:
> +    oneOf:
> +      - items:
> +          - enum:
> +              - renesas,r8a77951-nandc
> +          - const: renesas,rcar-gen3-nandc

Might be a bit premature to add these before they have been tested,
and because there are small differences in integration, cfr. below.

> +
> +      - items:
> +          - enum:
> +              - renesas,r9a06g032-nandc
> +          - const: renesas,rzn1-nandc
> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  clocks:
> +    items:
> +      - description: APB host controller clock
> +      - description: External NAND bus clock
> +
> +  clock-names:
> +    items:
> +      - const: hclk
> +      - const: eclk

On R-Car Gen3, there's a single module clock.
Plus a power-domain to manage that.

Actually the RZ/N1 clock driver also registers a PM Domain, so letting
Runtime PM manage the clocks may work on RZ/N1, too...

On R-Car Gen3, there's also a module reset.

> +
> +required:
> +  - compatible
> +  - reg
> +  - clocks
> +  - clock-names
> +  - interrupts
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +    #include <dt-bindings/clock/r9a06g032-sysctrl.h>
> +
> +    nand-controller@40102000 {
> +        compatible = "renesas,r9a06g032-nandc", "renesas,rzn1-nandc";
> +        reg = <0x40102000 0x2000>;
> +        interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
> +        clocks = <&sysctrl R9A06G032_HCLK_NAND>, <&sysctrl R9A06G032_CLK_NAND>;
> +        clock-names = "hclk", "eclk";
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +    };

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

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

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

* Re: [PATCH v6 1/4] dt-bindings: mtd: renesas: Describe Renesas R-Car Gen3 & RZ/N1 NAND controller
  2021-12-17 15:44     ` Geert Uytterhoeven
@ 2021-12-17 15:51       ` Miquel Raynal
  -1 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-17 15:51 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, MTD Maling List, Rob Herring,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Milan Stevanovic, Jimmy Lalande, Thomas Petazzoni, Linux-Renesas,
	Magnus Damm, Gareth Williams, Phil Edworthy, Wolfram Sang,
	Chris Brandt, Ralph Siemsen, Geert Uytterhoeven, Rob Herring

Hi Geert,

geert@linux-m68k.org wrote on Fri, 17 Dec 2021 16:44:59 +0100:

> Hi Miquel,
> 
> On Fri, Dec 17, 2021 at 3:20 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> > Add a Yaml description for this Renesas NAND controller.
> >
> > As this controller is embedded on different SoC families, provide:
> > * a family-specific "r-car-gen3" compatible and a more specific
> >   "r8a77951" one
> > * a family-specific "rzn1" compatible and a more specific "r9a06g032"
> >   one
> >
> > More compatibles can be added later if new SoCs with this controller
> > must be supported.
> >
> > Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> > Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> > Reviewed-by: Rob Herring <robh@kernel.org>  
> 
> Thanks for the update!
> 
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
> > @@ -0,0 +1,66 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/mtd/renesas-nandc.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Renesas R-Car Gen3 & RZ/N1x NAND flash controller device tree bindings
> > +
> > +maintainers:
> > +  - Miquel Raynal <miquel.raynal@bootlin.com>
> > +
> > +allOf:
> > +  - $ref: "nand-controller.yaml"
> > +
> > +properties:
> > +  compatible:
> > +    oneOf:
> > +      - items:
> > +          - enum:
> > +              - renesas,r8a77951-nandc
> > +          - const: renesas,rcar-gen3-nandc  
> 
> Might be a bit premature to add these before they have been tested,
> and because there are small differences in integration, cfr. below.
> 
> > +
> > +      - items:
> > +          - enum:
> > +              - renesas,r9a06g032-nandc
> > +          - const: renesas,rzn1-nandc
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  interrupts:
> > +    maxItems: 1
> > +
> > +  clocks:
> > +    items:
> > +      - description: APB host controller clock
> > +      - description: External NAND bus clock
> > +
> > +  clock-names:
> > +    items:
> > +      - const: hclk
> > +      - const: eclk  
> 
> On R-Car Gen3, there's a single module clock.
> Plus a power-domain to manage that.
> 
> Actually the RZ/N1 clock driver also registers a PM Domain, so letting
> Runtime PM manage the clocks may work on RZ/N1, too...
> 
> On R-Car Gen3, there's also a module reset.

Ok, I didn't know. I propose to drop the r-car-gen3 compatible entirely
from the driver and the binding when I'll apply the series. Is it fine
for you?

> 
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - clocks
> > +  - clock-names
> > +  - interrupts
> > +
> > +unevaluatedProperties: false
> > +
> > +examples:
> > +  - |
> > +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> > +    #include <dt-bindings/clock/r9a06g032-sysctrl.h>
> > +
> > +    nand-controller@40102000 {
> > +        compatible = "renesas,r9a06g032-nandc", "renesas,rzn1-nandc";
> > +        reg = <0x40102000 0x2000>;
> > +        interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
> > +        clocks = <&sysctrl R9A06G032_HCLK_NAND>, <&sysctrl R9A06G032_CLK_NAND>;
> > +        clock-names = "hclk", "eclk";
> > +        #address-cells = <1>;
> > +        #size-cells = <0>;
> > +    };  
> 
> Gr{oetje,eeting}s,
> 
>                         Geert
> 
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
> 
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
>                                 -- Linus Torvalds


Thanks,
Miquèl

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

* Re: [PATCH v6 1/4] dt-bindings: mtd: renesas: Describe Renesas R-Car Gen3 & RZ/N1 NAND controller
@ 2021-12-17 15:51       ` Miquel Raynal
  0 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-17 15:51 UTC (permalink / raw)
  To: Geert Uytterhoeven
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, MTD Maling List, Rob Herring,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Milan Stevanovic, Jimmy Lalande, Thomas Petazzoni, Linux-Renesas,
	Magnus Damm, Gareth Williams, Phil Edworthy, Wolfram Sang,
	Chris Brandt, Ralph Siemsen, Geert Uytterhoeven, Rob Herring

Hi Geert,

geert@linux-m68k.org wrote on Fri, 17 Dec 2021 16:44:59 +0100:

> Hi Miquel,
> 
> On Fri, Dec 17, 2021 at 3:20 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> > Add a Yaml description for this Renesas NAND controller.
> >
> > As this controller is embedded on different SoC families, provide:
> > * a family-specific "r-car-gen3" compatible and a more specific
> >   "r8a77951" one
> > * a family-specific "rzn1" compatible and a more specific "r9a06g032"
> >   one
> >
> > More compatibles can be added later if new SoCs with this controller
> > must be supported.
> >
> > Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> > Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> > Reviewed-by: Rob Herring <robh@kernel.org>  
> 
> Thanks for the update!
> 
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
> > @@ -0,0 +1,66 @@
> > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/mtd/renesas-nandc.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: Renesas R-Car Gen3 & RZ/N1x NAND flash controller device tree bindings
> > +
> > +maintainers:
> > +  - Miquel Raynal <miquel.raynal@bootlin.com>
> > +
> > +allOf:
> > +  - $ref: "nand-controller.yaml"
> > +
> > +properties:
> > +  compatible:
> > +    oneOf:
> > +      - items:
> > +          - enum:
> > +              - renesas,r8a77951-nandc
> > +          - const: renesas,rcar-gen3-nandc  
> 
> Might be a bit premature to add these before they have been tested,
> and because there are small differences in integration, cfr. below.
> 
> > +
> > +      - items:
> > +          - enum:
> > +              - renesas,r9a06g032-nandc
> > +          - const: renesas,rzn1-nandc
> > +
> > +  reg:
> > +    maxItems: 1
> > +
> > +  interrupts:
> > +    maxItems: 1
> > +
> > +  clocks:
> > +    items:
> > +      - description: APB host controller clock
> > +      - description: External NAND bus clock
> > +
> > +  clock-names:
> > +    items:
> > +      - const: hclk
> > +      - const: eclk  
> 
> On R-Car Gen3, there's a single module clock.
> Plus a power-domain to manage that.
> 
> Actually the RZ/N1 clock driver also registers a PM Domain, so letting
> Runtime PM manage the clocks may work on RZ/N1, too...
> 
> On R-Car Gen3, there's also a module reset.

Ok, I didn't know. I propose to drop the r-car-gen3 compatible entirely
from the driver and the binding when I'll apply the series. Is it fine
for you?

> 
> > +
> > +required:
> > +  - compatible
> > +  - reg
> > +  - clocks
> > +  - clock-names
> > +  - interrupts
> > +
> > +unevaluatedProperties: false
> > +
> > +examples:
> > +  - |
> > +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> > +    #include <dt-bindings/clock/r9a06g032-sysctrl.h>
> > +
> > +    nand-controller@40102000 {
> > +        compatible = "renesas,r9a06g032-nandc", "renesas,rzn1-nandc";
> > +        reg = <0x40102000 0x2000>;
> > +        interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>;
> > +        clocks = <&sysctrl R9A06G032_HCLK_NAND>, <&sysctrl R9A06G032_CLK_NAND>;
> > +        clock-names = "hclk", "eclk";
> > +        #address-cells = <1>;
> > +        #size-cells = <0>;
> > +    };  
> 
> Gr{oetje,eeting}s,
> 
>                         Geert
> 
> --
> Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
> 
> In personal conversations with technical people, I call myself a hacker. But
> when I'm talking to journalists I just say "programmer" or something like that.
>                                 -- Linus Torvalds


Thanks,
Miquèl

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

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

* Re: [PATCH v6 1/4] dt-bindings: mtd: renesas: Describe Renesas R-Car Gen3 & RZ/N1 NAND controller
  2021-12-17 15:51       ` Miquel Raynal
@ 2021-12-17 15:52         ` Geert Uytterhoeven
  -1 siblings, 0 replies; 32+ messages in thread
From: Geert Uytterhoeven @ 2021-12-17 15:52 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, MTD Maling List, Rob Herring,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Milan Stevanovic, Jimmy Lalande, Thomas Petazzoni, Linux-Renesas,
	Magnus Damm, Gareth Williams, Phil Edworthy, Wolfram Sang,
	Chris Brandt, Ralph Siemsen, Geert Uytterhoeven, Rob Herring

Hi Miquel,

On Fri, Dec 17, 2021 at 4:51 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> geert@linux-m68k.org wrote on Fri, 17 Dec 2021 16:44:59 +0100:
> > On Fri, Dec 17, 2021 at 3:20 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> > > Add a Yaml description for this Renesas NAND controller.
> > >
> > > As this controller is embedded on different SoC families, provide:
> > > * a family-specific "r-car-gen3" compatible and a more specific
> > >   "r8a77951" one
> > > * a family-specific "rzn1" compatible and a more specific "r9a06g032"
> > >   one
> > >
> > > More compatibles can be added later if new SoCs with this controller
> > > must be supported.
> > >
> > > Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> > > Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> > > Reviewed-by: Rob Herring <robh@kernel.org>
> >
> > Thanks for the update!
> >
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
> > > @@ -0,0 +1,66 @@
> > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/mtd/renesas-nandc.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: Renesas R-Car Gen3 & RZ/N1x NAND flash controller device tree bindings
> > > +
> > > +maintainers:
> > > +  - Miquel Raynal <miquel.raynal@bootlin.com>
> > > +
> > > +allOf:
> > > +  - $ref: "nand-controller.yaml"
> > > +
> > > +properties:
> > > +  compatible:
> > > +    oneOf:
> > > +      - items:
> > > +          - enum:
> > > +              - renesas,r8a77951-nandc
> > > +          - const: renesas,rcar-gen3-nandc
> >
> > Might be a bit premature to add these before they have been tested,
> > and because there are small differences in integration, cfr. below.
> >
> > > +
> > > +      - items:
> > > +          - enum:
> > > +              - renesas,r9a06g032-nandc
> > > +          - const: renesas,rzn1-nandc
> > > +
> > > +  reg:
> > > +    maxItems: 1
> > > +
> > > +  interrupts:
> > > +    maxItems: 1
> > > +
> > > +  clocks:
> > > +    items:
> > > +      - description: APB host controller clock
> > > +      - description: External NAND bus clock
> > > +
> > > +  clock-names:
> > > +    items:
> > > +      - const: hclk
> > > +      - const: eclk
> >
> > On R-Car Gen3, there's a single module clock.
> > Plus a power-domain to manage that.
> >
> > Actually the RZ/N1 clock driver also registers a PM Domain, so letting
> > Runtime PM manage the clocks may work on RZ/N1, too...
> >
> > On R-Car Gen3, there's also a module reset.
>
> Ok, I didn't know. I propose to drop the r-car-gen3 compatible entirely
> from the driver and the binding when I'll apply the series. Is it fine
> for you?

Yes, that's fine for me. Thanks!

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

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

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

* Re: [PATCH v6 1/4] dt-bindings: mtd: renesas: Describe Renesas R-Car Gen3 & RZ/N1 NAND controller
@ 2021-12-17 15:52         ` Geert Uytterhoeven
  0 siblings, 0 replies; 32+ messages in thread
From: Geert Uytterhoeven @ 2021-12-17 15:52 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, MTD Maling List, Rob Herring,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	Milan Stevanovic, Jimmy Lalande, Thomas Petazzoni, Linux-Renesas,
	Magnus Damm, Gareth Williams, Phil Edworthy, Wolfram Sang,
	Chris Brandt, Ralph Siemsen, Geert Uytterhoeven, Rob Herring

Hi Miquel,

On Fri, Dec 17, 2021 at 4:51 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> geert@linux-m68k.org wrote on Fri, 17 Dec 2021 16:44:59 +0100:
> > On Fri, Dec 17, 2021 at 3:20 PM Miquel Raynal <miquel.raynal@bootlin.com> wrote:
> > > Add a Yaml description for this Renesas NAND controller.
> > >
> > > As this controller is embedded on different SoC families, provide:
> > > * a family-specific "r-car-gen3" compatible and a more specific
> > >   "r8a77951" one
> > > * a family-specific "rzn1" compatible and a more specific "r9a06g032"
> > >   one
> > >
> > > More compatibles can be added later if new SoCs with this controller
> > > must be supported.
> > >
> > > Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> > > Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> > > Reviewed-by: Rob Herring <robh@kernel.org>
> >
> > Thanks for the update!
> >
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
> > > @@ -0,0 +1,66 @@
> > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/mtd/renesas-nandc.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: Renesas R-Car Gen3 & RZ/N1x NAND flash controller device tree bindings
> > > +
> > > +maintainers:
> > > +  - Miquel Raynal <miquel.raynal@bootlin.com>
> > > +
> > > +allOf:
> > > +  - $ref: "nand-controller.yaml"
> > > +
> > > +properties:
> > > +  compatible:
> > > +    oneOf:
> > > +      - items:
> > > +          - enum:
> > > +              - renesas,r8a77951-nandc
> > > +          - const: renesas,rcar-gen3-nandc
> >
> > Might be a bit premature to add these before they have been tested,
> > and because there are small differences in integration, cfr. below.
> >
> > > +
> > > +      - items:
> > > +          - enum:
> > > +              - renesas,r9a06g032-nandc
> > > +          - const: renesas,rzn1-nandc
> > > +
> > > +  reg:
> > > +    maxItems: 1
> > > +
> > > +  interrupts:
> > > +    maxItems: 1
> > > +
> > > +  clocks:
> > > +    items:
> > > +      - description: APB host controller clock
> > > +      - description: External NAND bus clock
> > > +
> > > +  clock-names:
> > > +    items:
> > > +      - const: hclk
> > > +      - const: eclk
> >
> > On R-Car Gen3, there's a single module clock.
> > Plus a power-domain to manage that.
> >
> > Actually the RZ/N1 clock driver also registers a PM Domain, so letting
> > Runtime PM manage the clocks may work on RZ/N1, too...
> >
> > On R-Car Gen3, there's also a module reset.
>
> Ok, I didn't know. I propose to drop the r-car-gen3 compatible entirely
> from the driver and the binding when I'll apply the series. Is it fine
> for you?

Yes, that's fine for me. Thanks!

Gr{oetje,eeting}s,

                        Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

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

* Re: [PATCH v6 3/4] MAINTAINERS: Add an entry for Renesas NAND controller
  2021-12-17 14:20   ` Miquel Raynal
@ 2021-12-17 21:34     ` Wolfram Sang
  -1 siblings, 0 replies; 32+ messages in thread
From: Wolfram Sang @ 2021-12-17 21:34 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd, Rob Herring,
	devicetree, Milan Stevanovic, Jimmy Lalande, Thomas Petazzoni,
	linux-renesas-soc, Magnus Damm, Gareth Williams, Phil Edworthy,
	Geert Uytterhoeven, Chris Brandt, Ralph Siemsen

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


> +RENESAS R-CAR GEN3 & RZ/N1 NAND CONTROLLER DRIVER
> +M:	Miquel Raynal <miquel.raynal@bootlin.com>
> +L:	linux-mtd@lists.infradead.org

Could you add

L:      linux-renesas-soc@vger.kernel.org

as well please?

> +S:	Maintained
> +F:	Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
> +F:	drivers/mtd/nand/raw/renesas-nand-controller.c

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [PATCH v6 3/4] MAINTAINERS: Add an entry for Renesas NAND controller
@ 2021-12-17 21:34     ` Wolfram Sang
  0 siblings, 0 replies; 32+ messages in thread
From: Wolfram Sang @ 2021-12-17 21:34 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd, Rob Herring,
	devicetree, Milan Stevanovic, Jimmy Lalande, Thomas Petazzoni,
	linux-renesas-soc, Magnus Damm, Gareth Williams, Phil Edworthy,
	Geert Uytterhoeven, Chris Brandt, Ralph Siemsen


[-- Attachment #1.1: Type: text/plain, Size: 347 bytes --]


> +RENESAS R-CAR GEN3 & RZ/N1 NAND CONTROLLER DRIVER
> +M:	Miquel Raynal <miquel.raynal@bootlin.com>
> +L:	linux-mtd@lists.infradead.org

Could you add

L:      linux-renesas-soc@vger.kernel.org

as well please?

> +S:	Maintained
> +F:	Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
> +F:	drivers/mtd/nand/raw/renesas-nand-controller.c

[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 144 bytes --]

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

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

* Re: [PATCH v6 2/4] mtd: rawnand: renesas: Add new NAND controller driver
  2021-12-17 14:20   ` Miquel Raynal
@ 2021-12-17 21:37     ` Wolfram Sang
  -1 siblings, 0 replies; 32+ messages in thread
From: Wolfram Sang @ 2021-12-17 21:37 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd, Rob Herring,
	devicetree, Milan Stevanovic, Jimmy Lalande, Thomas Petazzoni,
	linux-renesas-soc, Magnus Damm, Gareth Williams, Phil Edworthy,
	Geert Uytterhoeven, Chris Brandt, Ralph Siemsen

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

On Fri, Dec 17, 2021 at 03:20:31PM +0100, Miquel Raynal wrote:
> Introduce Renesas NAND controller driver which currently supports the
> following features on R-Car Gen3 and RZ/N1 SoCs:
> - All ONFI timing modes
> - Different configurations of its internal ECC controller
> - On-die (not tested) and software ECC support
> - Several chips (not tested)
> - Subpage accesses
> - DMA and PIO
> 
> This controller was originally provided by Evatronix before being bought
> by Cadence.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Tested-by: Ralph Siemsen <ralph.siemsen@linaro.org>

As I mentioned before, I can't test the driver and have no bandwidth for
a proper review. However, it looks really good from a glimpse and I
think we are in good shape for Gen3 additions if we need them somewhen.
So:

Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [PATCH v6 2/4] mtd: rawnand: renesas: Add new NAND controller driver
@ 2021-12-17 21:37     ` Wolfram Sang
  0 siblings, 0 replies; 32+ messages in thread
From: Wolfram Sang @ 2021-12-17 21:37 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd, Rob Herring,
	devicetree, Milan Stevanovic, Jimmy Lalande, Thomas Petazzoni,
	linux-renesas-soc, Magnus Damm, Gareth Williams, Phil Edworthy,
	Geert Uytterhoeven, Chris Brandt, Ralph Siemsen


[-- Attachment #1.1: Type: text/plain, Size: 898 bytes --]

On Fri, Dec 17, 2021 at 03:20:31PM +0100, Miquel Raynal wrote:
> Introduce Renesas NAND controller driver which currently supports the
> following features on R-Car Gen3 and RZ/N1 SoCs:
> - All ONFI timing modes
> - Different configurations of its internal ECC controller
> - On-die (not tested) and software ECC support
> - Several chips (not tested)
> - Subpage accesses
> - DMA and PIO
> 
> This controller was originally provided by Evatronix before being bought
> by Cadence.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Tested-by: Ralph Siemsen <ralph.siemsen@linaro.org>

As I mentioned before, I can't test the driver and have no bandwidth for
a proper review. However, it looks really good from a glimpse and I
think we are in good shape for Gen3 additions if we need them somewhen.
So:

Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>


[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 144 bytes --]

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

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

* Re: [PATCH v6 1/4] dt-bindings: mtd: renesas: Describe Renesas R-Car Gen3 & RZ/N1 NAND controller
  2021-12-17 14:20   ` Miquel Raynal
@ 2021-12-17 21:38     ` Wolfram Sang
  -1 siblings, 0 replies; 32+ messages in thread
From: Wolfram Sang @ 2021-12-17 21:38 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd, Rob Herring,
	devicetree, Milan Stevanovic, Jimmy Lalande, Thomas Petazzoni,
	linux-renesas-soc, Magnus Damm, Gareth Williams, Phil Edworthy,
	Geert Uytterhoeven, Chris Brandt, Ralph Siemsen,
	Geert Uytterhoeven, Rob Herring

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

On Fri, Dec 17, 2021 at 03:20:30PM +0100, Miquel Raynal wrote:
> Add a Yaml description for this Renesas NAND controller.
> 
> As this controller is embedded on different SoC families, provide:
> * a family-specific "r-car-gen3" compatible and a more specific
>   "r8a77951" one
> * a family-specific "rzn1" compatible and a more specific "r9a06g032"
>   one
> 
> More compatibles can be added later if new SoCs with this controller
> must be supported.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> Reviewed-by: Rob Herring <robh@kernel.org>

Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [PATCH v6 1/4] dt-bindings: mtd: renesas: Describe Renesas R-Car Gen3 & RZ/N1 NAND controller
@ 2021-12-17 21:38     ` Wolfram Sang
  0 siblings, 0 replies; 32+ messages in thread
From: Wolfram Sang @ 2021-12-17 21:38 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd, Rob Herring,
	devicetree, Milan Stevanovic, Jimmy Lalande, Thomas Petazzoni,
	linux-renesas-soc, Magnus Damm, Gareth Williams, Phil Edworthy,
	Geert Uytterhoeven, Chris Brandt, Ralph Siemsen,
	Geert Uytterhoeven, Rob Herring


[-- Attachment #1.1: Type: text/plain, Size: 699 bytes --]

On Fri, Dec 17, 2021 at 03:20:30PM +0100, Miquel Raynal wrote:
> Add a Yaml description for this Renesas NAND controller.
> 
> As this controller is embedded on different SoC families, provide:
> * a family-specific "r-car-gen3" compatible and a more specific
>   "r8a77951" one
> * a family-specific "rzn1" compatible and a more specific "r9a06g032"
>   one
> 
> More compatibles can be added later if new SoCs with this controller
> must be supported.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> Reviewed-by: Rob Herring <robh@kernel.org>

Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>


[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 144 bytes --]

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

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

* Re: [PATCH v6 4/4] ARM: dts: r9a06g032: Describe the NAND controller
  2021-12-17 14:20   ` Miquel Raynal
@ 2021-12-17 21:38     ` Wolfram Sang
  -1 siblings, 0 replies; 32+ messages in thread
From: Wolfram Sang @ 2021-12-17 21:38 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd, Rob Herring,
	devicetree, Milan Stevanovic, Jimmy Lalande, Thomas Petazzoni,
	linux-renesas-soc, Magnus Damm, Gareth Williams, Phil Edworthy,
	Geert Uytterhoeven, Chris Brandt, Ralph Siemsen,
	Geert Uytterhoeven

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

On Fri, Dec 17, 2021 at 03:20:33PM +0100, Miquel Raynal wrote:
> Describe the NAND controller embedded in r9a06g032 SoCs.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>

Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

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

* Re: [PATCH v6 4/4] ARM: dts: r9a06g032: Describe the NAND controller
@ 2021-12-17 21:38     ` Wolfram Sang
  0 siblings, 0 replies; 32+ messages in thread
From: Wolfram Sang @ 2021-12-17 21:38 UTC (permalink / raw)
  To: Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd, Rob Herring,
	devicetree, Milan Stevanovic, Jimmy Lalande, Thomas Petazzoni,
	linux-renesas-soc, Magnus Damm, Gareth Williams, Phil Edworthy,
	Geert Uytterhoeven, Chris Brandt, Ralph Siemsen,
	Geert Uytterhoeven


[-- Attachment #1.1: Type: text/plain, Size: 312 bytes --]

On Fri, Dec 17, 2021 at 03:20:33PM +0100, Miquel Raynal wrote:
> Describe the NAND controller embedded in r9a06g032 SoCs.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>

Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>


[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

[-- Attachment #2: Type: text/plain, Size: 144 bytes --]

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

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

* Re: [PATCH v6 3/4] MAINTAINERS: Add an entry for Renesas NAND controller
  2021-12-17 21:34     ` Wolfram Sang
@ 2021-12-21  9:16       ` Miquel Raynal
  -1 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-21  9:16 UTC (permalink / raw)
  To: Wolfram Sang
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd, Rob Herring,
	devicetree, Milan Stevanovic, Jimmy Lalande, Thomas Petazzoni,
	linux-renesas-soc, Magnus Damm, Gareth Williams, Phil Edworthy,
	Geert Uytterhoeven, Chris Brandt, Ralph Siemsen

Hi Wolfram,

wsa@kernel.org wrote on Fri, 17 Dec 2021 22:34:26 +0100:

> > +RENESAS R-CAR GEN3 & RZ/N1 NAND CONTROLLER DRIVER
> > +M:	Miquel Raynal <miquel.raynal@bootlin.com>
> > +L:	linux-mtd@lists.infradead.org  
> 
> Could you add
> 
> L:      linux-renesas-soc@vger.kernel.org
> 
> as well please?

Sure! I'll add this line when applying.

Thanks for the feedback and for the overall review!

> 
> > +S:	Maintained
> > +F:	Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
> > +F:	drivers/mtd/nand/raw/renesas-nand-controller.c  

Cheers,
Miquèl

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

* Re: [PATCH v6 3/4] MAINTAINERS: Add an entry for Renesas NAND controller
@ 2021-12-21  9:16       ` Miquel Raynal
  0 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-21  9:16 UTC (permalink / raw)
  To: Wolfram Sang
  Cc: Richard Weinberger, Vignesh Raghavendra, Tudor Ambarus,
	Pratyush Yadav, Michael Walle, linux-mtd, Rob Herring,
	devicetree, Milan Stevanovic, Jimmy Lalande, Thomas Petazzoni,
	linux-renesas-soc, Magnus Damm, Gareth Williams, Phil Edworthy,
	Geert Uytterhoeven, Chris Brandt, Ralph Siemsen

Hi Wolfram,

wsa@kernel.org wrote on Fri, 17 Dec 2021 22:34:26 +0100:

> > +RENESAS R-CAR GEN3 & RZ/N1 NAND CONTROLLER DRIVER
> > +M:	Miquel Raynal <miquel.raynal@bootlin.com>
> > +L:	linux-mtd@lists.infradead.org  
> 
> Could you add
> 
> L:      linux-renesas-soc@vger.kernel.org
> 
> as well please?

Sure! I'll add this line when applying.

Thanks for the feedback and for the overall review!

> 
> > +S:	Maintained
> > +F:	Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
> > +F:	drivers/mtd/nand/raw/renesas-nand-controller.c  

Cheers,
Miquèl

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

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

* Re: [PATCH v6 3/4] MAINTAINERS: Add an entry for Renesas NAND controller
  2021-12-17 14:20   ` Miquel Raynal
@ 2021-12-21 17:10     ` Miquel Raynal
  -1 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-21 17:10 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Tudor Ambarus, Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Rob Herring, devicetree, Milan Stevanovic, Jimmy Lalande,
	Thomas Petazzoni, linux-renesas-soc, Magnus Damm,
	Gareth Williams, Phil Edworthy, Geert Uytterhoeven, Wolfram Sang,
	Chris Brandt, Ralph Siemsen

On Fri, 2021-12-17 at 14:20:32 UTC, Miquel Raynal wrote:
> Point to the driver and the bindings.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>

Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next.

Miquel

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

* Re: [PATCH v6 3/4] MAINTAINERS: Add an entry for Renesas NAND controller
@ 2021-12-21 17:10     ` Miquel Raynal
  0 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-21 17:10 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Tudor Ambarus, Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Rob Herring, devicetree, Milan Stevanovic, Jimmy Lalande,
	Thomas Petazzoni, linux-renesas-soc, Magnus Damm,
	Gareth Williams, Phil Edworthy, Geert Uytterhoeven, Wolfram Sang,
	Chris Brandt, Ralph Siemsen

On Fri, 2021-12-17 at 14:20:32 UTC, Miquel Raynal wrote:
> Point to the driver and the bindings.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>

Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next.

Miquel

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

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

* Re: [PATCH v6 2/4] mtd: rawnand: renesas: Add new NAND controller driver
  2021-12-17 14:20   ` Miquel Raynal
@ 2021-12-21 17:10     ` Miquel Raynal
  -1 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-21 17:10 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Tudor Ambarus, Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Rob Herring, devicetree, Milan Stevanovic, Jimmy Lalande,
	Thomas Petazzoni, linux-renesas-soc, Magnus Damm,
	Gareth Williams, Phil Edworthy, Geert Uytterhoeven, Wolfram Sang,
	Chris Brandt, Ralph Siemsen

On Fri, 2021-12-17 at 14:20:31 UTC, Miquel Raynal wrote:
> Introduce Renesas NAND controller driver which currently supports the
> following features on R-Car Gen3 and RZ/N1 SoCs:
> - All ONFI timing modes
> - Different configurations of its internal ECC controller
> - On-die (not tested) and software ECC support
> - Several chips (not tested)
> - Subpage accesses
> - DMA and PIO
> 
> This controller was originally provided by Evatronix before being bought
> by Cadence.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Tested-by: Ralph Siemsen <ralph.siemsen@linaro.org>
> Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>

Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next.

Miquel

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

* Re: [PATCH v6 2/4] mtd: rawnand: renesas: Add new NAND controller driver
@ 2021-12-21 17:10     ` Miquel Raynal
  0 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-21 17:10 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Tudor Ambarus, Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Rob Herring, devicetree, Milan Stevanovic, Jimmy Lalande,
	Thomas Petazzoni, linux-renesas-soc, Magnus Damm,
	Gareth Williams, Phil Edworthy, Geert Uytterhoeven, Wolfram Sang,
	Chris Brandt, Ralph Siemsen

On Fri, 2021-12-17 at 14:20:31 UTC, Miquel Raynal wrote:
> Introduce Renesas NAND controller driver which currently supports the
> following features on R-Car Gen3 and RZ/N1 SoCs:
> - All ONFI timing modes
> - Different configurations of its internal ECC controller
> - On-die (not tested) and software ECC support
> - Several chips (not tested)
> - Subpage accesses
> - DMA and PIO
> 
> This controller was originally provided by Evatronix before being bought
> by Cadence.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Tested-by: Ralph Siemsen <ralph.siemsen@linaro.org>
> Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>

Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next.

Miquel

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

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

* Re: [PATCH v6 1/4] dt-bindings: mtd: renesas: Describe Renesas R-Car Gen3 & RZ/N1 NAND controller
  2021-12-17 14:20   ` Miquel Raynal
@ 2021-12-21 17:10     ` Miquel Raynal
  -1 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-21 17:10 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Tudor Ambarus, Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Rob Herring, devicetree, Milan Stevanovic, Jimmy Lalande,
	Thomas Petazzoni, linux-renesas-soc, Magnus Damm,
	Gareth Williams, Phil Edworthy, Geert Uytterhoeven, Wolfram Sang,
	Chris Brandt, Ralph Siemsen, Geert Uytterhoeven, Rob Herring

On Fri, 2021-12-17 at 14:20:30 UTC, Miquel Raynal wrote:
> Add a Yaml description for this Renesas NAND controller.
> 
> As this controller is embedded on different SoC families, provide:
> * a family-specific "r-car-gen3" compatible and a more specific
>   "r8a77951" one
> * a family-specific "rzn1" compatible and a more specific "r9a06g032"
>   one
> 
> More compatibles can be added later if new SoCs with this controller
> must be supported.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> Reviewed-by: Rob Herring <robh@kernel.org>
> Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>

Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next.

Miquel

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

* Re: [PATCH v6 1/4] dt-bindings: mtd: renesas: Describe Renesas R-Car Gen3 & RZ/N1 NAND controller
@ 2021-12-21 17:10     ` Miquel Raynal
  0 siblings, 0 replies; 32+ messages in thread
From: Miquel Raynal @ 2021-12-21 17:10 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Tudor Ambarus, Pratyush Yadav, Michael Walle, linux-mtd
  Cc: Rob Herring, devicetree, Milan Stevanovic, Jimmy Lalande,
	Thomas Petazzoni, linux-renesas-soc, Magnus Damm,
	Gareth Williams, Phil Edworthy, Geert Uytterhoeven, Wolfram Sang,
	Chris Brandt, Ralph Siemsen, Geert Uytterhoeven, Rob Herring

On Fri, 2021-12-17 at 14:20:30 UTC, Miquel Raynal wrote:
> Add a Yaml description for this Renesas NAND controller.
> 
> As this controller is embedded on different SoC families, provide:
> * a family-specific "r-car-gen3" compatible and a more specific
>   "r8a77951" one
> * a family-specific "rzn1" compatible and a more specific "r9a06g032"
>   one
> 
> More compatibles can be added later if new SoCs with this controller
> must be supported.
> 
> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
> Reviewed-by: Rob Herring <robh@kernel.org>
> Acked-by: Wolfram Sang <wsa+renesas@sang-engineering.com>

Applied to https://git.kernel.org/pub/scm/linux/kernel/git/mtd/linux.git nand/next.

Miquel

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

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

end of thread, other threads:[~2021-12-21 17:17 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-17 14:20 [PATCH v6 0/4] Renesas NAND controller support Miquel Raynal
2021-12-17 14:20 ` Miquel Raynal
2021-12-17 14:20 ` [PATCH v6 1/4] dt-bindings: mtd: renesas: Describe Renesas R-Car Gen3 & RZ/N1 NAND controller Miquel Raynal
2021-12-17 14:20   ` Miquel Raynal
2021-12-17 15:44   ` Geert Uytterhoeven
2021-12-17 15:44     ` Geert Uytterhoeven
2021-12-17 15:51     ` Miquel Raynal
2021-12-17 15:51       ` Miquel Raynal
2021-12-17 15:52       ` Geert Uytterhoeven
2021-12-17 15:52         ` Geert Uytterhoeven
2021-12-17 21:38   ` Wolfram Sang
2021-12-17 21:38     ` Wolfram Sang
2021-12-21 17:10   ` Miquel Raynal
2021-12-21 17:10     ` Miquel Raynal
2021-12-17 14:20 ` [PATCH v6 2/4] mtd: rawnand: renesas: Add new NAND controller driver Miquel Raynal
2021-12-17 14:20   ` Miquel Raynal
2021-12-17 21:37   ` Wolfram Sang
2021-12-17 21:37     ` Wolfram Sang
2021-12-21 17:10   ` Miquel Raynal
2021-12-21 17:10     ` Miquel Raynal
2021-12-17 14:20 ` [PATCH v6 3/4] MAINTAINERS: Add an entry for Renesas NAND controller Miquel Raynal
2021-12-17 14:20   ` Miquel Raynal
2021-12-17 21:34   ` Wolfram Sang
2021-12-17 21:34     ` Wolfram Sang
2021-12-21  9:16     ` Miquel Raynal
2021-12-21  9:16       ` Miquel Raynal
2021-12-21 17:10   ` Miquel Raynal
2021-12-21 17:10     ` Miquel Raynal
2021-12-17 14:20 ` [PATCH v6 4/4] ARM: dts: r9a06g032: Describe the " Miquel Raynal
2021-12-17 14:20   ` Miquel Raynal
2021-12-17 21:38   ` Wolfram Sang
2021-12-17 21:38     ` Wolfram Sang

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.