Merge tag 'nand/for-5.17' into mtd/next
authorMiquel Raynal <miquel.raynal@bootlin.com>
Fri, 31 Dec 2021 12:31:23 +0000 (13:31 +0100)
committerMiquel Raynal <miquel.raynal@bootlin.com>
Fri, 31 Dec 2021 12:31:34 +0000 (13:31 +0100)
Raw NAND core:
* Export nand_read_page_hwecc_oob_first()

GPMC memory controller for OMAP2 NAND controller:
* GPMC:
  - Add support for AM64 SoC and allow build on K3 platforms
  - Use a compatible match table when checking for NAND controller
  - Use platform_get_irq() to get the interrupt

Raw NAND controller drivers:
* OMAP2 NAND controller:
  - Document the missing 'rb-gpios' DT property
  - Drop unused variable
  - Fix force_8bit flag behaviour for DMA mode
  - Move to exec_op interface
  - Use platform_get_irq() to get the interrupt
* Renesas:
  - Add new NAND controller driver with its bindings and MAINTAINERS entry
* Onenand:
  - Remove redundant variable ooblen
* MPC5121:
  - Remove unused variable in ads5121_select_chip()
* GPMI:
  - Add ERR007117 protection for nfc_apply_timings
  - Remove explicit default gpmi clock setting for i.MX6
  - Use platform_get_irq_byname() to get the interrupt
  - Remove unneeded variable
* Ingenic:
  - JZ4740 needs 'oob_first' read page function
* Davinci:
  - Rewrite function description
  - Avoid duplicated page read
  - Don't calculate ECC when reading page

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
18 files changed:
Documentation/devicetree/bindings/memory-controllers/ti,gpmc.yaml
Documentation/devicetree/bindings/mtd/renesas-nandc.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/mtd/ti,gpmc-nand.yaml
MAINTAINERS
drivers/memory/omap-gpmc.c
drivers/mtd/nand/onenand/onenand_bbt.c
drivers/mtd/nand/raw/Kconfig
drivers/mtd/nand/raw/Makefile
drivers/mtd/nand/raw/davinci_nand.c
drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
drivers/mtd/nand/raw/ingenic/ingenic_nand_drv.c
drivers/mtd/nand/raw/mpc5121_nfc.c
drivers/mtd/nand/raw/nand_base.c
drivers/mtd/nand/raw/omap2.c
drivers/mtd/nand/raw/omap_elm.c
drivers/mtd/nand/raw/renesas-nand-controller.c [new file with mode: 0644]
include/linux/mtd/rawnand.h
include/linux/platform_data/mtd-nand-omap2.h

index 25b42d6..64dc9d3 100644 (file)
@@ -23,13 +23,20 @@ properties:
     items:
       - enum:
           - ti,am3352-gpmc
+          - ti,am64-gpmc
           - ti,omap2420-gpmc
           - ti,omap2430-gpmc
           - ti,omap3430-gpmc
           - ti,omap4430-gpmc
 
   reg:
-    maxItems: 1
+    minItems: 1
+    maxItems: 2
+
+  reg-names:
+    items:
+      - const: cfg
+      - const: data
 
   interrupts:
     maxItems: 1
@@ -44,6 +51,9 @@ properties:
     items:
       - const: fck
 
+  power-domains:
+    maxItems: 1
+
   dmas:
     items:
       - description: DMA channel for GPMC NAND prefetch
@@ -133,6 +143,17 @@ required:
   - "#address-cells"
   - "#size-cells"
 
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: ti,am64-gpmc
+    then:
+      required:
+        - reg-names
+        - power-domains
+
 additionalProperties: false
 
 examples:
diff --git a/Documentation/devicetree/bindings/mtd/renesas-nandc.yaml b/Documentation/devicetree/bindings/mtd/renesas-nandc.yaml
new file mode 100644 (file)
index 0000000..2870d36
--- /dev/null
@@ -0,0 +1,61 @@
+# 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,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>;
+    };
index beb26b9..4ac1988 100644 (file)
@@ -16,7 +16,10 @@ description:
 
 properties:
   compatible:
-    const: ti,omap2-nand
+    items:
+      - enum:
+          - ti,am64-nand
+          - ti,omap2-nand
 
   reg:
     maxItems: 1
@@ -53,6 +56,11 @@ properties:
     enum: [8, 16]
     default: 8
 
+  rb-gpios:
+    description:
+      GPIO connection to R/B signal from NAND chip
+    maxItems: 1
+
 patternProperties:
   "@[0-9a-f]+$":
     $ref: "/schemas/mtd/partitions/partition.yaml"
index 352a781..db56ade 100644 (file)
@@ -16275,6 +16275,14 @@ 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
+L:     linux-renesas-soc@vger.kernel.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
index be0858b..ed11887 100644 (file)
@@ -237,6 +237,7 @@ struct gpmc_device {
        struct omap3_gpmc_regs context;
        int nirqs;
        unsigned int is_suspended:1;
+       struct resource *data;
 };
 
 static struct irq_domain *gpmc_irq_domain;
@@ -1456,12 +1457,18 @@ static void gpmc_mem_exit(void)
        }
 }
 
-static void gpmc_mem_init(void)
+static void gpmc_mem_init(struct gpmc_device *gpmc)
 {
        int cs;
 
-       gpmc_mem_root.start = GPMC_MEM_START;
-       gpmc_mem_root.end = GPMC_MEM_END;
+       if (!gpmc->data) {
+               /* All legacy devices have same data IO window */
+               gpmc_mem_root.start = GPMC_MEM_START;
+               gpmc_mem_root.end = GPMC_MEM_END;
+       } else {
+               gpmc_mem_root.start = gpmc->data->start;
+               gpmc_mem_root.end = gpmc->data->end;
+       }
 
        /* Reserve all regions that has been set up by bootloader */
        for (cs = 0; cs < gpmc_cs_num; cs++) {
@@ -1888,6 +1895,7 @@ static const struct of_device_id gpmc_dt_ids[] = {
        { .compatible = "ti,omap3430-gpmc" },   /* omap3430 & omap3630 */
        { .compatible = "ti,omap4430-gpmc" },   /* omap4430 & omap4460 & omap543x */
        { .compatible = "ti,am3352-gpmc" },     /* am335x devices */
+       { .compatible = "ti,am64-gpmc" },
        { }
 };
 
@@ -2175,7 +2183,7 @@ static int gpmc_probe_generic_child(struct platform_device *pdev,
                }
        }
 
-       if (of_device_is_compatible(child, "ti,omap2-nand")) {
+       if (of_match_node(omap_nand_ids, child)) {
                /* NAND specific setup */
                val = 8;
                of_property_read_u32(child, "nand-bus-width", &val);
@@ -2502,21 +2510,29 @@ static int gpmc_probe(struct platform_device *pdev)
        gpmc->dev = &pdev->dev;
        platform_set_drvdata(pdev, gpmc);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res)
-               return -ENOENT;
-
-       gpmc_base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(gpmc_base))
-               return PTR_ERR(gpmc_base);
-
-       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg");
        if (!res) {
-               dev_err(&pdev->dev, "Failed to get resource: irq\n");
-               return -ENOENT;
+               /* legacy DT */
+               gpmc_base = devm_platform_ioremap_resource(pdev, 0);
+               if (IS_ERR(gpmc_base))
+                       return PTR_ERR(gpmc_base);
+       } else {
+               gpmc_base = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(gpmc_base))
+                       return PTR_ERR(gpmc_base);
+
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "data");
+               if (!res) {
+                       dev_err(&pdev->dev, "couldn't get data reg resource\n");
+                       return -ENOENT;
+               }
+
+               gpmc->data = res;
        }
 
-       gpmc->irq = res->start;
+       gpmc->irq = platform_get_irq(pdev, 0);
+       if (gpmc->irq < 0)
+               return gpmc->irq;
 
        gpmc_l3_clk = devm_clk_get(&pdev->dev, "fck");
        if (IS_ERR(gpmc_l3_clk)) {
@@ -2562,7 +2578,7 @@ static int gpmc_probe(struct platform_device *pdev)
        dev_info(gpmc->dev, "GPMC revision %d.%d\n", GPMC_REVISION_MAJOR(l),
                 GPMC_REVISION_MINOR(l));
 
-       gpmc_mem_init();
+       gpmc_mem_init(gpmc);
        rc = gpmc_gpio_init(gpmc);
        if (rc)
                goto gpio_init_failed;
index def89f1..b17315f 100644 (file)
@@ -60,7 +60,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
        int i, j, numblocks, len, scanlen;
        int startblock;
        loff_t from;
-       size_t readlen, ooblen;
+       size_t readlen;
        struct mtd_oob_ops ops;
        int rgn;
 
@@ -69,7 +69,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
        len = 2;
 
        /* We need only read few bytes from the OOB area */
-       scanlen = ooblen = 0;
+       scanlen = 0;
        readlen = bd->len;
 
        /* chip == -1 case only */
index 67b7cb6..0cd5ebf 100644 (file)
@@ -40,8 +40,9 @@ config MTD_NAND_AMS_DELTA
 
 config MTD_NAND_OMAP2
        tristate "OMAP2, OMAP3, OMAP4 and Keystone NAND controller"
-       depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST
+       depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST
        depends on HAS_IOMEM
+       select OMAP_GPMC if ARCH_K3
        help
          Support for NAND flash on Texas Instruments OMAP2, OMAP3, OMAP4
          and Keystone platforms.
@@ -461,6 +462,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
index 2f97958..88a5665 100644 (file)
@@ -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
index 118da99..45fec8c 100644 (file)
@@ -371,77 +371,6 @@ correct:
        return corrected;
 }
 
-/**
- * nand_read_page_hwecc_oob_first - hw ecc, read oob first
- * @chip: nand chip info structure
- * @buf: buffer to store read data
- * @oob_required: caller requires OOB data read to chip->oob_poi
- * @page: page number to read
- *
- * Hardware ECC for large page chips, require OOB to be read first. For this
- * ECC mode, the write_page method is re-used from ECC_HW. These methods
- * read/write ECC from the OOB area, unlike the ECC_HW_SYNDROME support with
- * multiple ECC steps, follows the "infix ECC" scheme and reads/writes ECC from
- * the data area, by overwriting the NAND manufacturer bad block markings.
- */
-static int nand_davinci_read_page_hwecc_oob_first(struct nand_chip *chip,
-                                                 uint8_t *buf,
-                                                 int oob_required, int page)
-{
-       struct mtd_info *mtd = nand_to_mtd(chip);
-       int i, eccsize = chip->ecc.size, ret;
-       int eccbytes = chip->ecc.bytes;
-       int eccsteps = chip->ecc.steps;
-       uint8_t *p = buf;
-       uint8_t *ecc_code = chip->ecc.code_buf;
-       uint8_t *ecc_calc = chip->ecc.calc_buf;
-       unsigned int max_bitflips = 0;
-
-       /* Read the OOB area first */
-       ret = nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
-       if (ret)
-               return ret;
-
-       ret = nand_read_page_op(chip, page, 0, NULL, 0);
-       if (ret)
-               return ret;
-
-       ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
-                                        chip->ecc.total);
-       if (ret)
-               return ret;
-
-       for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
-               int stat;
-
-               chip->ecc.hwctl(chip, NAND_ECC_READ);
-
-               ret = nand_read_data_op(chip, p, eccsize, false, false);
-               if (ret)
-                       return ret;
-
-               chip->ecc.calculate(chip, p, &ecc_calc[i]);
-
-               stat = chip->ecc.correct(chip, p, &ecc_code[i], NULL);
-               if (stat == -EBADMSG &&
-                   (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
-                       /* check for empty pages with bitflips */
-                       stat = nand_check_erased_ecc_chunk(p, eccsize,
-                                                          &ecc_code[i],
-                                                          eccbytes, NULL, 0,
-                                                          chip->ecc.strength);
-               }
-
-               if (stat < 0) {
-                       mtd->ecc_stats.failed++;
-               } else {
-                       mtd->ecc_stats.corrected += stat;
-                       max_bitflips = max_t(unsigned int, max_bitflips, stat);
-               }
-       }
-       return max_bitflips;
-}
-
 /*----------------------------------------------------------------------*/
 
 /* An ECC layout for using 4-bit ECC with small-page flash, storing
@@ -651,7 +580,7 @@ static int davinci_nand_attach_chip(struct nand_chip *chip)
                        } else if (chunks == 4 || chunks == 8) {
                                mtd_set_ooblayout(mtd,
                                                  nand_get_large_page_ooblayout());
-                               chip->ecc.read_page = nand_davinci_read_page_hwecc_oob_first;
+                               chip->ecc.read_page = nand_read_page_hwecc_oob_first;
                        } else {
                                return -EIO;
                        }
index 10cc718..1b64c5a 100644 (file)
@@ -713,14 +713,32 @@ static void gpmi_nfc_compute_timings(struct gpmi_nand_data *this,
                              (use_half_period ? BM_GPMI_CTRL1_HALF_PERIOD : 0);
 }
 
-static void gpmi_nfc_apply_timings(struct gpmi_nand_data *this)
+static int gpmi_nfc_apply_timings(struct gpmi_nand_data *this)
 {
        struct gpmi_nfc_hardware_timing *hw = &this->hw;
        struct resources *r = &this->resources;
        void __iomem *gpmi_regs = r->gpmi_regs;
        unsigned int dll_wait_time_us;
+       int ret;
+
+       /* Clock dividers do NOT guarantee a clean clock signal on its output
+        * during the change of the divide factor on i.MX6Q/UL/SX. On i.MX7/8,
+        * all clock dividers provide these guarantee.
+        */
+       if (GPMI_IS_MX6Q(this) || GPMI_IS_MX6SX(this))
+               clk_disable_unprepare(r->clock[0]);
 
-       clk_set_rate(r->clock[0], hw->clk_rate);
+       ret = clk_set_rate(r->clock[0], hw->clk_rate);
+       if (ret) {
+               dev_err(this->dev, "cannot set clock rate to %lu Hz: %d\n", hw->clk_rate, ret);
+               return ret;
+       }
+
+       if (GPMI_IS_MX6Q(this) || GPMI_IS_MX6SX(this)) {
+               ret = clk_prepare_enable(r->clock[0]);
+               if (ret)
+                       return ret;
+       }
 
        writel(hw->timing0, gpmi_regs + HW_GPMI_TIMING0);
        writel(hw->timing1, gpmi_regs + HW_GPMI_TIMING1);
@@ -739,6 +757,8 @@ static void gpmi_nfc_apply_timings(struct gpmi_nand_data *this)
 
        /* Wait for the DLL to settle. */
        udelay(dll_wait_time_us);
+
+       return 0;
 }
 
 static int gpmi_setup_interface(struct nand_chip *chip, int chipnr,
@@ -971,16 +991,13 @@ static int acquire_bch_irq(struct gpmi_nand_data *this, irq_handler_t irq_h)
 {
        struct platform_device *pdev = this->pdev;
        const char *res_name = GPMI_NAND_BCH_INTERRUPT_RES_NAME;
-       struct resource *r;
        int err;
 
-       r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name);
-       if (!r) {
-               dev_err(this->dev, "Can't get resource for %s\n", res_name);
-               return -ENODEV;
-       }
+       err = platform_get_irq_byname(pdev, res_name);
+       if (err < 0)
+               return err;
 
-       err = devm_request_irq(this->dev, r->start, irq_h, 0, res_name, this);
+       err = devm_request_irq(this->dev, err, irq_h, 0, res_name, this);
        if (err)
                dev_err(this->dev, "error requesting BCH IRQ\n");
 
@@ -1032,15 +1049,6 @@ static int gpmi_get_clks(struct gpmi_nand_data *this)
                r->clock[i] = clk;
        }
 
-       if (GPMI_IS_MX6(this))
-               /*
-                * Set the default value for the gpmi clock.
-                *
-                * If you want to use the ONFI nand which is in the
-                * Synchronous Mode, you should change the clock as you need.
-                */
-               clk_set_rate(r->clock[0], 22000000);
-
        return 0;
 
 err_clock:
@@ -1425,7 +1433,6 @@ static int gpmi_ecc_write_page(struct nand_chip *chip, const uint8_t *buf,
        struct mtd_info *mtd = nand_to_mtd(chip);
        struct gpmi_nand_data *this = nand_get_controller_data(chip);
        struct bch_geometry *nfc_geo = &this->bch_geometry;
-       int ret;
 
        dev_dbg(this->dev, "ecc write page.\n");
 
@@ -1445,9 +1452,7 @@ static int gpmi_ecc_write_page(struct nand_chip *chip, const uint8_t *buf,
                                    this->auxiliary_virt);
        }
 
-       ret = nand_prog_page_op(chip, page, 0, buf, nfc_geo->page_size);
-
-       return ret;
+       return nand_prog_page_op(chip, page, 0, buf, nfc_geo->page_size);
 }
 
 /*
@@ -2278,7 +2283,9 @@ static int gpmi_nfc_exec_op(struct nand_chip *chip,
         */
        if (this->hw.must_apply_timings) {
                this->hw.must_apply_timings = false;
-               gpmi_nfc_apply_timings(this);
+               ret = gpmi_nfc_apply_timings(this);
+               if (ret)
+                       return ret;
        }
 
        dev_dbg(this->dev, "%s: %d instructions\n", __func__, op->ninstrs);
index 0e9d426..b18861b 100644 (file)
@@ -32,6 +32,7 @@ struct jz_soc_info {
        unsigned long addr_offset;
        unsigned long cmd_offset;
        const struct mtd_ooblayout_ops *oob_layout;
+       bool oob_first;
 };
 
 struct ingenic_nand_cs {
@@ -240,6 +241,9 @@ static int ingenic_nand_attach_chip(struct nand_chip *chip)
        if (chip->bbt_options & NAND_BBT_USE_FLASH)
                chip->bbt_options |= NAND_BBT_NO_OOB;
 
+       if (nfc->soc_info->oob_first)
+               chip->ecc.read_page = nand_read_page_hwecc_oob_first;
+
        /* For legacy reasons we use a different layout on the qi,lb60 board. */
        if (of_machine_is_compatible("qi,lb60"))
                mtd_set_ooblayout(mtd, &qi_lb60_ooblayout_ops);
@@ -534,6 +538,7 @@ static const struct jz_soc_info jz4740_soc_info = {
        .data_offset = 0x00000000,
        .cmd_offset = 0x00008000,
        .addr_offset = 0x00010000,
+       .oob_first = true,
 };
 
 static const struct jz_soc_info jz4725b_soc_info = {
index cb293c5..5b9271b 100644 (file)
@@ -291,7 +291,6 @@ static int ads5121_chipselect_init(struct mtd_info *mtd)
 /* Control chips select signal on ADS5121 board */
 static void ads5121_select_chip(struct nand_chip *nand, int chip)
 {
-       struct mtd_info *mtd = nand_to_mtd(nand);
        struct mpc5121_nfc_prv *prv = nand_get_controller_data(nand);
        u8 v;
 
index b3a9bc0..9f48b4e 100644 (file)
@@ -3163,6 +3163,73 @@ static int nand_read_page_hwecc(struct nand_chip *chip, uint8_t *buf,
        return max_bitflips;
 }
 
+/**
+ * nand_read_page_hwecc_oob_first - Hardware ECC page read with ECC
+ *                                  data read from OOB area
+ * @chip: nand chip info structure
+ * @buf: buffer to store read data
+ * @oob_required: caller requires OOB data read to chip->oob_poi
+ * @page: page number to read
+ *
+ * Hardware ECC for large page chips, which requires the ECC data to be
+ * extracted from the OOB before the actual data is read.
+ */
+int nand_read_page_hwecc_oob_first(struct nand_chip *chip, uint8_t *buf,
+                                  int oob_required, int page)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       int i, eccsize = chip->ecc.size, ret;
+       int eccbytes = chip->ecc.bytes;
+       int eccsteps = chip->ecc.steps;
+       uint8_t *p = buf;
+       uint8_t *ecc_code = chip->ecc.code_buf;
+       unsigned int max_bitflips = 0;
+
+       /* Read the OOB area first */
+       ret = nand_read_oob_op(chip, page, 0, chip->oob_poi, mtd->oobsize);
+       if (ret)
+               return ret;
+
+       /* Move read cursor to start of page */
+       ret = nand_change_read_column_op(chip, 0, NULL, 0, false);
+       if (ret)
+               return ret;
+
+       ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+                                        chip->ecc.total);
+       if (ret)
+               return ret;
+
+       for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+               int stat;
+
+               chip->ecc.hwctl(chip, NAND_ECC_READ);
+
+               ret = nand_read_data_op(chip, p, eccsize, false, false);
+               if (ret)
+                       return ret;
+
+               stat = chip->ecc.correct(chip, p, &ecc_code[i], NULL);
+               if (stat == -EBADMSG &&
+                   (chip->ecc.options & NAND_ECC_GENERIC_ERASED_CHECK)) {
+                       /* check for empty pages with bitflips */
+                       stat = nand_check_erased_ecc_chunk(p, eccsize,
+                                                          &ecc_code[i],
+                                                          eccbytes, NULL, 0,
+                                                          chip->ecc.strength);
+               }
+
+               if (stat < 0) {
+                       mtd->ecc_stats.failed++;
+               } else {
+                       mtd->ecc_stats.corrected += stat;
+                       max_bitflips = max_t(unsigned int, max_bitflips, stat);
+               }
+       }
+       return max_bitflips;
+}
+EXPORT_SYMBOL_GPL(nand_read_page_hwecc_oob_first);
+
 /**
  * nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read
  * @chip: nand chip info structure
index b26d494..f0bbbe4 100644 (file)
@@ -19,7 +19,7 @@
 #include <linux/mtd/rawnand.h>
 #include <linux/mtd/partitions.h>
 #include <linux/omap-dma.h>
-#include <linux/io.h>
+#include <linux/iopoll.h>
 #include <linux/slab.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
@@ -148,7 +148,6 @@ struct omap_nand_info {
        int                             gpmc_cs;
        bool                            dev_ready;
        enum nand_io                    xfer_type;
-       int                             devsize;
        enum omap_ecc                   ecc_opt;
        struct device_node              *elm_of_node;
 
@@ -164,6 +163,7 @@ struct omap_nand_info {
        u_char                          *buf;
        int                                     buf_len;
        /* Interface to GPMC */
+       void __iomem                    *fifo;
        struct gpmc_nand_regs           reg;
        struct gpmc_nand_ops            *ops;
        bool                            flash_bbt;
@@ -175,6 +175,11 @@ struct omap_nand_info {
        unsigned int                    nsteps_per_eccpg;
        unsigned int                    eccpg_size;
        unsigned int                    eccpg_bytes;
+       void (*data_in)(struct nand_chip *chip, void *buf,
+                       unsigned int len, bool force_8bit);
+       void (*data_out)(struct nand_chip *chip,
+                        const void *buf, unsigned int len,
+                        bool force_8bit);
 };
 
 static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
@@ -182,6 +187,13 @@ static inline struct omap_nand_info *mtd_to_omap(struct mtd_info *mtd)
        return container_of(mtd_to_nand(mtd), struct omap_nand_info, nand);
 }
 
+static void omap_nand_data_in(struct nand_chip *chip, void *buf,
+                             unsigned int len, bool force_8bit);
+
+static void omap_nand_data_out(struct nand_chip *chip,
+                              const void *buf, unsigned int len,
+                              bool force_8bit);
+
 /**
  * omap_prefetch_enable - configures and starts prefetch transfer
  * @cs: cs (chip select) number
@@ -241,169 +253,70 @@ static int omap_prefetch_reset(int cs, struct omap_nand_info *info)
 }
 
 /**
- * omap_hwcontrol - hardware specific access to control-lines
- * @chip: NAND chip object
- * @cmd: command to device
- * @ctrl:
- * NAND_NCE: bit 0 -> don't care
- * NAND_CLE: bit 1 -> Command Latch
- * NAND_ALE: bit 2 -> Address Latch
- *
- * NOTE: boards may use different bits for these!!
+ * omap_nand_data_in_pref - NAND data in using prefetch engine
  */
-static void omap_hwcontrol(struct nand_chip *chip, int cmd, unsigned int ctrl)
+static void omap_nand_data_in_pref(struct nand_chip *chip, void *buf,
+                                  unsigned int len, bool force_8bit)
 {
        struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
-
-       if (cmd != NAND_CMD_NONE) {
-               if (ctrl & NAND_CLE)
-                       writeb(cmd, info->reg.gpmc_nand_command);
-
-               else if (ctrl & NAND_ALE)
-                       writeb(cmd, info->reg.gpmc_nand_address);
-
-               else /* NAND_NCE */
-                       writeb(cmd, info->reg.gpmc_nand_data);
-       }
-}
-
-/**
- * omap_read_buf8 - read data from NAND controller into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
- */
-static void omap_read_buf8(struct mtd_info *mtd, u_char *buf, int len)
-{
-       struct nand_chip *nand = mtd_to_nand(mtd);
-
-       ioread8_rep(nand->legacy.IO_ADDR_R, buf, len);
-}
-
-/**
- * omap_write_buf8 - write buffer to NAND controller
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- */
-static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len)
-{
-       struct omap_nand_info *info = mtd_to_omap(mtd);
-       u_char *p = (u_char *)buf;
-       bool status;
-
-       while (len--) {
-               iowrite8(*p++, info->nand.legacy.IO_ADDR_W);
-               /* wait until buffer is available for write */
-               do {
-                       status = info->ops->nand_writebuffer_empty();
-               } while (!status);
-       }
-}
-
-/**
- * omap_read_buf16 - read data from NAND controller into buffer
- * @mtd: MTD device structure
- * @buf: buffer to store date
- * @len: number of bytes to read
- */
-static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
-{
-       struct nand_chip *nand = mtd_to_nand(mtd);
-
-       ioread16_rep(nand->legacy.IO_ADDR_R, buf, len / 2);
-}
-
-/**
- * omap_write_buf16 - write buffer to NAND controller
- * @mtd: MTD device structure
- * @buf: data buffer
- * @len: number of bytes to write
- */
-static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
-{
-       struct omap_nand_info *info = mtd_to_omap(mtd);
-       u16 *p = (u16 *) buf;
-       bool status;
-       /* FIXME try bursts of writesw() or DMA ... */
-       len >>= 1;
-
-       while (len--) {
-               iowrite16(*p++, info->nand.legacy.IO_ADDR_W);
-               /* wait until buffer is available for write */
-               do {
-                       status = info->ops->nand_writebuffer_empty();
-               } while (!status);
-       }
-}
-
-/**
- * omap_read_buf_pref - read data from NAND controller into buffer
- * @chip: NAND chip object
- * @buf: buffer to store date
- * @len: number of bytes to read
- */
-static void omap_read_buf_pref(struct nand_chip *chip, u_char *buf, int len)
-{
-       struct mtd_info *mtd = nand_to_mtd(chip);
-       struct omap_nand_info *info = mtd_to_omap(mtd);
        uint32_t r_count = 0;
        int ret = 0;
        u32 *p = (u32 *)buf;
+       unsigned int pref_len;
 
-       /* take care of subpage reads */
-       if (len % 4) {
-               if (info->nand.options & NAND_BUSWIDTH_16)
-                       omap_read_buf16(mtd, buf, len % 4);
-               else
-                       omap_read_buf8(mtd, buf, len % 4);
-               p = (u32 *) (buf + len % 4);
-               len -= len % 4;
+       if (force_8bit) {
+               omap_nand_data_in(chip, buf, len, force_8bit);
+               return;
        }
 
+       /* read 32-bit words using prefetch and remaining bytes normally */
+
        /* configure and start prefetch transfer */
+       pref_len = len - (len & 3);
        ret = omap_prefetch_enable(info->gpmc_cs,
-                       PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x0, info);
+                       PREFETCH_FIFOTHRESHOLD_MAX, 0x0, pref_len, 0x0, info);
        if (ret) {
-               /* PFPW engine is busy, use cpu copy method */
-               if (info->nand.options & NAND_BUSWIDTH_16)
-                       omap_read_buf16(mtd, (u_char *)p, len);
-               else
-                       omap_read_buf8(mtd, (u_char *)p, len);
+               /* prefetch engine is busy, use CPU copy method */
+               omap_nand_data_in(chip, buf, len, false);
        } else {
                do {
                        r_count = readl(info->reg.gpmc_prefetch_status);
                        r_count = PREFETCH_STATUS_FIFO_CNT(r_count);
                        r_count = r_count >> 2;
-                       ioread32_rep(info->nand.legacy.IO_ADDR_R, p, r_count);
+                       ioread32_rep(info->fifo, p, r_count);
                        p += r_count;
-                       len -= r_count << 2;
-               } while (len);
-               /* disable and stop the PFPW engine */
+                       pref_len -= r_count << 2;
+               } while (pref_len);
+               /* disable and stop the Prefetch engine */
                omap_prefetch_reset(info->gpmc_cs, info);
+               /* fetch any remaining bytes */
+               if (len & 3)
+                       omap_nand_data_in(chip, p, len & 3, false);
        }
 }
 
 /**
- * omap_write_buf_pref - write buffer to NAND controller
- * @chip: NAND chip object
- * @buf: data buffer
- * @len: number of bytes to write
+ * omap_nand_data_out_pref - NAND data out using Write Posting engine
  */
-static void omap_write_buf_pref(struct nand_chip *chip, const u_char *buf,
-                               int len)
+static void omap_nand_data_out_pref(struct nand_chip *chip,
+                                   const void *buf, unsigned int len,
+                                   bool force_8bit)
 {
-       struct mtd_info *mtd = nand_to_mtd(chip);
-       struct omap_nand_info *info = mtd_to_omap(mtd);
+       struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
        uint32_t w_count = 0;
        int i = 0, ret = 0;
        u16 *p = (u16 *)buf;
        unsigned long tim, limit;
        u32 val;
 
+       if (force_8bit) {
+               omap_nand_data_out(chip, buf, len, force_8bit);
+               return;
+       }
+
        /* take care of subpage writes */
        if (len % 2 != 0) {
-               writeb(*buf, info->nand.legacy.IO_ADDR_W);
+               writeb(*(u8 *)buf, info->fifo);
                p = (u16 *)(buf + 1);
                len--;
        }
@@ -412,18 +325,15 @@ static void omap_write_buf_pref(struct nand_chip *chip, const u_char *buf,
        ret = omap_prefetch_enable(info->gpmc_cs,
                        PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1, info);
        if (ret) {
-               /* PFPW engine is busy, use cpu copy method */
-               if (info->nand.options & NAND_BUSWIDTH_16)
-                       omap_write_buf16(mtd, (u_char *)p, len);
-               else
-                       omap_write_buf8(mtd, (u_char *)p, len);
+               /* write posting engine is busy, use CPU copy method */
+               omap_nand_data_out(chip, buf, len, false);
        } else {
                while (len) {
                        w_count = readl(info->reg.gpmc_prefetch_status);
                        w_count = PREFETCH_STATUS_FIFO_CNT(w_count);
                        w_count = w_count >> 1;
                        for (i = 0; (i < w_count) && len; i++, len -= 2)
-                               iowrite16(*p++, info->nand.legacy.IO_ADDR_W);
+                               iowrite16(*p++, info->fifo);
                }
                /* wait for data to flushed-out before reset the prefetch */
                tim = 0;
@@ -451,15 +361,16 @@ static void omap_nand_dma_callback(void *data)
 
 /*
  * omap_nand_dma_transfer: configure and start dma transfer
- * @mtd: MTD device structure
+ * @chip: nand chip structure
  * @addr: virtual address in RAM of source/destination
  * @len: number of data bytes to be transferred
  * @is_write: flag for read/write operation
  */
-static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
-                                       unsigned int len, int is_write)
+static inline int omap_nand_dma_transfer(struct nand_chip *chip,
+                                        const void *addr, unsigned int len,
+                                        int is_write)
 {
-       struct omap_nand_info *info = mtd_to_omap(mtd);
+       struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
        struct dma_async_tx_descriptor *tx;
        enum dma_data_direction dir = is_write ? DMA_TO_DEVICE :
                                                        DMA_FROM_DEVICE;
@@ -521,49 +432,51 @@ static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
 out_copy_unmap:
        dma_unmap_sg(info->dma->device->dev, &sg, 1, dir);
 out_copy:
-       if (info->nand.options & NAND_BUSWIDTH_16)
-               is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len)
-                       : omap_write_buf16(mtd, (u_char *) addr, len);
-       else
-               is_write == 0 ? omap_read_buf8(mtd, (u_char *) addr, len)
-                       : omap_write_buf8(mtd, (u_char *) addr, len);
+       is_write == 0 ? omap_nand_data_in(chip, (void *)addr, len, false)
+                     : omap_nand_data_out(chip, addr, len, false);
+
        return 0;
 }
 
 /**
- * omap_read_buf_dma_pref - read data from NAND controller into buffer
- * @chip: NAND chip object
- * @buf: buffer to store date
- * @len: number of bytes to read
+ * omap_nand_data_in_dma_pref - NAND data in using DMA and Prefetch
  */
-static void omap_read_buf_dma_pref(struct nand_chip *chip, u_char *buf,
-                                  int len)
+static void omap_nand_data_in_dma_pref(struct nand_chip *chip, void *buf,
+                                      unsigned int len, bool force_8bit)
 {
        struct mtd_info *mtd = nand_to_mtd(chip);
 
+       if (force_8bit) {
+               omap_nand_data_in(chip, buf, len, force_8bit);
+               return;
+       }
+
        if (len <= mtd->oobsize)
-               omap_read_buf_pref(chip, buf, len);
+               omap_nand_data_in_pref(chip, buf, len, false);
        else
                /* start transfer in DMA mode */
-               omap_nand_dma_transfer(mtd, buf, len, 0x0);
+               omap_nand_dma_transfer(chip, buf, len, 0x0);
 }
 
 /**
- * omap_write_buf_dma_pref - write buffer to NAND controller
- * @chip: NAND chip object
- * @buf: data buffer
- * @len: number of bytes to write
+ * omap_nand_data_out_dma_pref - NAND data out using DMA and write posting
  */
-static void omap_write_buf_dma_pref(struct nand_chip *chip, const u_char *buf,
-                                   int len)
+static void omap_nand_data_out_dma_pref(struct nand_chip *chip,
+                                       const void *buf, unsigned int len,
+                                       bool force_8bit)
 {
        struct mtd_info *mtd = nand_to_mtd(chip);
 
+       if (force_8bit) {
+               omap_nand_data_out(chip, buf, len, force_8bit);
+               return;
+       }
+
        if (len <= mtd->oobsize)
-               omap_write_buf_pref(chip, buf, len);
+               omap_nand_data_out_pref(chip, buf, len, false);
        else
                /* start transfer in DMA mode */
-               omap_nand_dma_transfer(mtd, (u_char *)buf, len, 0x1);
+               omap_nand_dma_transfer(chip, buf, len, 0x1);
 }
 
 /*
@@ -587,13 +500,13 @@ static irqreturn_t omap_nand_irq(int this_irq, void *dev)
                        bytes = info->buf_len;
                else if (!info->buf_len)
                        bytes = 0;
-               iowrite32_rep(info->nand.legacy.IO_ADDR_W, (u32 *)info->buf,
+               iowrite32_rep(info->fifo, (u32 *)info->buf,
                              bytes >> 2);
                info->buf = info->buf + bytes;
                info->buf_len -= bytes;
 
        } else {
-               ioread32_rep(info->nand.legacy.IO_ADDR_R, (u32 *)info->buf,
+               ioread32_rep(info->fifo, (u32 *)info->buf,
                             bytes >> 2);
                info->buf = info->buf + bytes;
 
@@ -613,20 +526,17 @@ done:
 }
 
 /*
- * omap_read_buf_irq_pref - read data from NAND controller into buffer
- * @chip: NAND chip object
- * @buf: buffer to store date
- * @len: number of bytes to read
+ * omap_nand_data_in_irq_pref - NAND data in using Prefetch and IRQ
  */
-static void omap_read_buf_irq_pref(struct nand_chip *chip, u_char *buf,
-                                  int len)
+static void omap_nand_data_in_irq_pref(struct nand_chip *chip, void *buf,
+                                      unsigned int len, bool force_8bit)
 {
-       struct mtd_info *mtd = nand_to_mtd(chip);
-       struct omap_nand_info *info = mtd_to_omap(mtd);
+       struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
+       struct mtd_info *mtd = nand_to_mtd(&info->nand);
        int ret = 0;
 
-       if (len <= mtd->oobsize) {
-               omap_read_buf_pref(chip, buf, len);
+       if (len <= mtd->oobsize || force_8bit) {
+               omap_nand_data_in(chip, buf, len, force_8bit);
                return;
        }
 
@@ -637,9 +547,11 @@ static void omap_read_buf_irq_pref(struct nand_chip *chip, u_char *buf,
        /*  configure and start prefetch transfer */
        ret = omap_prefetch_enable(info->gpmc_cs,
                        PREFETCH_FIFOTHRESHOLD_MAX/2, 0x0, len, 0x0, info);
-       if (ret)
+       if (ret) {
                /* PFPW engine is busy, use cpu copy method */
-               goto out_copy;
+               omap_nand_data_in(chip, buf, len, false);
+               return;
+       }
 
        info->buf_len = len;
 
@@ -652,31 +564,23 @@ static void omap_read_buf_irq_pref(struct nand_chip *chip, u_char *buf,
        /* disable and stop the PFPW engine */
        omap_prefetch_reset(info->gpmc_cs, info);
        return;
-
-out_copy:
-       if (info->nand.options & NAND_BUSWIDTH_16)
-               omap_read_buf16(mtd, buf, len);
-       else
-               omap_read_buf8(mtd, buf, len);
 }
 
 /*
- * omap_write_buf_irq_pref - write buffer to NAND controller
- * @chip: NAND chip object
- * @buf: data buffer
- * @len: number of bytes to write
+ * omap_nand_data_out_irq_pref - NAND out using write posting and IRQ
  */
-static void omap_write_buf_irq_pref(struct nand_chip *chip, const u_char *buf,
-                                   int len)
+static void omap_nand_data_out_irq_pref(struct nand_chip *chip,
+                                       const void *buf, unsigned int len,
+                                       bool force_8bit)
 {
-       struct mtd_info *mtd = nand_to_mtd(chip);
-       struct omap_nand_info *info = mtd_to_omap(mtd);
+       struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
+       struct mtd_info *mtd = nand_to_mtd(&info->nand);
        int ret = 0;
        unsigned long tim, limit;
        u32 val;
 
-       if (len <= mtd->oobsize) {
-               omap_write_buf_pref(chip, buf, len);
+       if (len <= mtd->oobsize || force_8bit) {
+               omap_nand_data_out(chip, buf, len, force_8bit);
                return;
        }
 
@@ -687,9 +591,11 @@ static void omap_write_buf_irq_pref(struct nand_chip *chip, const u_char *buf,
        /* configure and start prefetch transfer : size=24 */
        ret = omap_prefetch_enable(info->gpmc_cs,
                (PREFETCH_FIFOTHRESHOLD_MAX * 3) / 8, 0x0, len, 0x1, info);
-       if (ret)
+       if (ret) {
                /* PFPW engine is busy, use cpu copy method */
-               goto out_copy;
+               omap_nand_data_out(chip, buf, len, false);
+               return;
+       }
 
        info->buf_len = len;
 
@@ -711,12 +617,6 @@ static void omap_write_buf_irq_pref(struct nand_chip *chip, const u_char *buf,
        /* disable and stop the PFPW engine */
        omap_prefetch_reset(info->gpmc_cs, info);
        return;
-
-out_copy:
-       if (info->nand.options & NAND_BUSWIDTH_16)
-               omap_write_buf16(mtd, buf, len);
-       else
-               omap_write_buf8(mtd, buf, len);
 }
 
 /**
@@ -981,50 +881,6 @@ static void omap_enable_hwecc(struct nand_chip *chip, int mode)
        writel(val, info->reg.gpmc_ecc_config);
 }
 
-/**
- * omap_wait - wait until the command is done
- * @this: NAND Chip structure
- *
- * Wait function is called during Program and erase operations and
- * the way it is called from MTD layer, we should wait till the NAND
- * chip is ready after the programming/erase operation has completed.
- *
- * Erase can take up to 400ms and program up to 20ms according to
- * general NAND and SmartMedia specs
- */
-static int omap_wait(struct nand_chip *this)
-{
-       struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(this));
-       unsigned long timeo = jiffies;
-       int status;
-
-       timeo += msecs_to_jiffies(400);
-
-       writeb(NAND_CMD_STATUS & 0xFF, info->reg.gpmc_nand_command);
-       while (time_before(jiffies, timeo)) {
-               status = readb(info->reg.gpmc_nand_data);
-               if (status & NAND_STATUS_READY)
-                       break;
-               cond_resched();
-       }
-
-       status = readb(info->reg.gpmc_nand_data);
-       return status;
-}
-
-/**
- * omap_dev_ready - checks the NAND Ready GPIO line
- * @chip: NAND chip object
- *
- * Returns true if ready and false if busy.
- */
-static int omap_dev_ready(struct nand_chip *chip)
-{
-       struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
-
-       return gpiod_get_value(info->ready_gpiod);
-}
-
 /**
  * omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation
  * @chip: NAND chip object
@@ -1543,8 +1399,8 @@ static int omap_write_page_bch(struct nand_chip *chip, const uint8_t *buf,
                chip->ecc.hwctl(chip, NAND_ECC_WRITE);
 
                /* Write data */
-               chip->legacy.write_buf(chip, buf + (eccpg * info->eccpg_size),
-                                      info->eccpg_size);
+               info->data_out(chip, buf + (eccpg * info->eccpg_size),
+                              info->eccpg_size, false);
 
                /* Update ecc vector from GPMC result registers */
                ret = omap_calculate_ecc_bch_multi(mtd,
@@ -1562,7 +1418,7 @@ static int omap_write_page_bch(struct nand_chip *chip, const uint8_t *buf,
        }
 
        /* Write ecc vector to OOB area */
-       chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize);
+       info->data_out(chip, chip->oob_poi, mtd->oobsize, false);
 
        return nand_prog_page_end_op(chip);
 }
@@ -1607,8 +1463,8 @@ static int omap_write_subpage_bch(struct nand_chip *chip, u32 offset,
                chip->ecc.hwctl(chip, NAND_ECC_WRITE);
 
                /* Write data */
-               chip->legacy.write_buf(chip, buf + (eccpg * info->eccpg_size),
-                                      info->eccpg_size);
+               info->data_out(chip, buf + (eccpg * info->eccpg_size),
+                              info->eccpg_size, false);
 
                for (step = 0; step < info->nsteps_per_eccpg; step++) {
                        unsigned int base_step = eccpg * info->nsteps_per_eccpg;
@@ -1641,7 +1497,7 @@ static int omap_write_subpage_bch(struct nand_chip *chip, u32 offset,
        }
 
        /* write OOB buffer to NAND device */
-       chip->legacy.write_buf(chip, chip->oob_poi, mtd->oobsize);
+       info->data_out(chip, chip->oob_poi, mtd->oobsize, false);
 
        return nand_prog_page_end_op(chip);
 }
@@ -1984,8 +1840,8 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
        /* Re-populate low-level callbacks based on xfer modes */
        switch (info->xfer_type) {
        case NAND_OMAP_PREFETCH_POLLED:
-               chip->legacy.read_buf = omap_read_buf_pref;
-               chip->legacy.write_buf = omap_write_buf_pref;
+               info->data_in = omap_nand_data_in_pref;
+               info->data_out = omap_nand_data_out_pref;
                break;
 
        case NAND_OMAP_POLLED:
@@ -2017,8 +1873,9 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
                                        err);
                                return err;
                        }
-                       chip->legacy.read_buf = omap_read_buf_dma_pref;
-                       chip->legacy.write_buf = omap_write_buf_dma_pref;
+
+                       info->data_in = omap_nand_data_in_dma_pref;
+                       info->data_out = omap_nand_data_out_dma_pref;
                }
                break;
 
@@ -2049,9 +1906,8 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
                        return err;
                }
 
-               chip->legacy.read_buf = omap_read_buf_irq_pref;
-               chip->legacy.write_buf = omap_write_buf_irq_pref;
-
+               info->data_in = omap_nand_data_in_irq_pref;
+               info->data_out = omap_nand_data_out_irq_pref;
                break;
 
        default:
@@ -2217,8 +2073,105 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
        return 0;
 }
 
+static void omap_nand_data_in(struct nand_chip *chip, void *buf,
+                             unsigned int len, bool force_8bit)
+{
+       struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
+       u32 alignment = ((uintptr_t)buf | len) & 3;
+
+       if (force_8bit || (alignment & 1))
+               ioread8_rep(info->fifo, buf, len);
+       else if (alignment & 3)
+               ioread16_rep(info->fifo, buf, len >> 1);
+       else
+               ioread32_rep(info->fifo, buf, len >> 2);
+}
+
+static void omap_nand_data_out(struct nand_chip *chip,
+                              const void *buf, unsigned int len,
+                              bool force_8bit)
+{
+       struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
+       u32 alignment = ((uintptr_t)buf | len) & 3;
+
+       if (force_8bit || (alignment & 1))
+               iowrite8_rep(info->fifo, buf, len);
+       else if (alignment & 3)
+               iowrite16_rep(info->fifo, buf, len >> 1);
+       else
+               iowrite32_rep(info->fifo, buf, len >> 2);
+}
+
+static int omap_nand_exec_instr(struct nand_chip *chip,
+                               const struct nand_op_instr *instr)
+{
+       struct omap_nand_info *info = mtd_to_omap(nand_to_mtd(chip));
+       unsigned int i;
+       int ret;
+
+       switch (instr->type) {
+       case NAND_OP_CMD_INSTR:
+               iowrite8(instr->ctx.cmd.opcode,
+                        info->reg.gpmc_nand_command);
+               break;
+
+       case NAND_OP_ADDR_INSTR:
+               for (i = 0; i < instr->ctx.addr.naddrs; i++) {
+                       iowrite8(instr->ctx.addr.addrs[i],
+                                info->reg.gpmc_nand_address);
+               }
+               break;
+
+       case NAND_OP_DATA_IN_INSTR:
+               info->data_in(chip, instr->ctx.data.buf.in,
+                             instr->ctx.data.len,
+                             instr->ctx.data.force_8bit);
+               break;
+
+       case NAND_OP_DATA_OUT_INSTR:
+               info->data_out(chip, instr->ctx.data.buf.out,
+                              instr->ctx.data.len,
+                              instr->ctx.data.force_8bit);
+               break;
+
+       case NAND_OP_WAITRDY_INSTR:
+               ret = info->ready_gpiod ?
+                       nand_gpio_waitrdy(chip, info->ready_gpiod, instr->ctx.waitrdy.timeout_ms) :
+                       nand_soft_waitrdy(chip, instr->ctx.waitrdy.timeout_ms);
+               if (ret)
+                       return ret;
+               break;
+       }
+
+       if (instr->delay_ns)
+               ndelay(instr->delay_ns);
+
+       return 0;
+}
+
+static int omap_nand_exec_op(struct nand_chip *chip,
+                            const struct nand_operation *op,
+                            bool check_only)
+{
+       unsigned int i;
+
+       if (check_only)
+               return 0;
+
+       for (i = 0; i < op->ninstrs; i++) {
+               int ret;
+
+               ret = omap_nand_exec_instr(chip, &op->instrs[i]);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
 static const struct nand_controller_ops omap_nand_controller_ops = {
        .attach_chip = omap_nand_attach_chip,
+       .exec_op = omap_nand_exec_op,
 };
 
 /* Shared among all NAND instances to synchronize access to the ECC Engine */
@@ -2233,6 +2186,7 @@ static int omap_nand_probe(struct platform_device *pdev)
        int                             err;
        struct resource                 *res;
        struct device                   *dev = &pdev->dev;
+       void __iomem *vaddr;
 
        info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info),
                                GFP_KERNEL);
@@ -2266,10 +2220,11 @@ static int omap_nand_probe(struct platform_device *pdev)
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       nand_chip->legacy.IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(nand_chip->legacy.IO_ADDR_R))
-               return PTR_ERR(nand_chip->legacy.IO_ADDR_R);
+       vaddr = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(vaddr))
+               return PTR_ERR(vaddr);
 
+       info->fifo = vaddr;
        info->phys_base = res->start;
 
        if (!omap_gpmc_controller_initialized) {
@@ -2280,9 +2235,6 @@ static int omap_nand_probe(struct platform_device *pdev)
 
        nand_chip->controller = &omap_gpmc_controller;
 
-       nand_chip->legacy.IO_ADDR_W = nand_chip->legacy.IO_ADDR_R;
-       nand_chip->legacy.cmd_ctrl  = omap_hwcontrol;
-
        info->ready_gpiod = devm_gpiod_get_optional(&pdev->dev, "rb",
                                                    GPIOD_IN);
        if (IS_ERR(info->ready_gpiod)) {
@@ -2290,26 +2242,12 @@ static int omap_nand_probe(struct platform_device *pdev)
                return PTR_ERR(info->ready_gpiod);
        }
 
-       /*
-        * If RDY/BSY line is connected to OMAP then use the omap ready
-        * function and the generic nand_wait function which reads the status
-        * register after monitoring the RDY/BSY line. Otherwise use a standard
-        * chip delay which is slightly more than tR (AC Timing) of the NAND
-        * device and read status register until you get a failure or success
-        */
-       if (info->ready_gpiod) {
-               nand_chip->legacy.dev_ready = omap_dev_ready;
-               nand_chip->legacy.chip_delay = 0;
-       } else {
-               nand_chip->legacy.waitfunc = omap_wait;
-               nand_chip->legacy.chip_delay = 50;
-       }
-
        if (info->flash_bbt)
                nand_chip->bbt_options |= NAND_BBT_USE_FLASH;
 
-       /* scan NAND device connected to chip controller */
-       nand_chip->options |= info->devsize & NAND_BUSWIDTH_16;
+       /* default operations */
+       info->data_in = omap_nand_data_in;
+       info->data_out = omap_nand_data_out;
 
        err = nand_scan(nand_chip, 1);
        if (err)
@@ -2352,10 +2290,7 @@ static int omap_nand_remove(struct platform_device *pdev)
        return ret;
 }
 
-static const struct of_device_id omap_nand_ids[] = {
-       { .compatible = "ti,omap2-nand", },
-       {},
-};
+/* omap_nand_ids defined in linux/platform_data/mtd-nand-omap2.h */
 MODULE_DEVICE_TABLE(of, omap_nand_ids);
 
 static struct platform_driver omap_nand_driver = {
index 8bab753..db105d9 100644 (file)
@@ -384,8 +384,8 @@ static irqreturn_t elm_isr(int this_irq, void *dev_id)
 static int elm_probe(struct platform_device *pdev)
 {
        int ret = 0;
-       struct resource *irq;
        struct elm_info *info;
+       int irq;
 
        info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
        if (!info)
@@ -393,20 +393,18 @@ static int elm_probe(struct platform_device *pdev)
 
        info->dev = &pdev->dev;
 
-       irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-       if (!irq) {
-               dev_err(&pdev->dev, "no irq resource defined\n");
-               return -ENODEV;
-       }
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
 
        info->elm_base = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(info->elm_base))
                return PTR_ERR(info->elm_base);
 
-       ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0,
-                       pdev->name, info);
+       ret = devm_request_irq(&pdev->dev, irq, elm_isr, 0,
+                              pdev->name, info);
        if (ret) {
-               dev_err(&pdev->dev, "failure requesting %pr\n", irq);
+               dev_err(&pdev->dev, "failure requesting %d\n", irq);
                return ret;
        }
 
diff --git a/drivers/mtd/nand/raw/renesas-nand-controller.c b/drivers/mtd/nand/raw/renesas-nand-controller.c
new file mode 100644 (file)
index 0000000..428e083
--- /dev/null
@@ -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");
index b2f9dd3..5b88cd5 100644 (file)
@@ -1539,6 +1539,8 @@ int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len,
                      bool force_8bit, bool check_only);
 int nand_write_data_op(struct nand_chip *chip, const void *buf,
                       unsigned int len, bool force_8bit);
+int nand_read_page_hwecc_oob_first(struct nand_chip *chip, uint8_t *buf,
+                                  int oob_required, int page);
 
 /* Scan and identify a NAND device */
 int nand_scan_with_ids(struct nand_chip *chip, unsigned int max_chips,
index de6ada7..8c2f1f1 100644 (file)
@@ -7,6 +7,7 @@
 #define        _MTD_NAND_OMAP2_H
 
 #include <linux/mtd/partitions.h>
+#include <linux/mod_devicetable.h>
 
 #define        GPMC_BCH_NUM_REMAINDER  8
 
@@ -61,4 +62,11 @@ struct gpmc_nand_regs {
        void __iomem    *gpmc_bch_result5[GPMC_BCH_NUM_REMAINDER];
        void __iomem    *gpmc_bch_result6[GPMC_BCH_NUM_REMAINDER];
 };
-#endif
+
+static const struct of_device_id omap_nand_ids[] = {
+       { .compatible = "ti,omap2-nand", },
+       { .compatible = "ti,am64-nand", },
+       {},
+};
+
+#endif /* _MTD_NAND_OMAP2_H */