Merge tag 'for-linus-20170510' of git://git.infradead.org/linux-mtd
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 11 May 2017 17:44:22 +0000 (10:44 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 11 May 2017 17:44:22 +0000 (10:44 -0700)
Pull MTD updates from Brian Norris:
 "NAND, from Boris:
   - some minor fixes/improvements on existing drivers (fsmc, gpio, ifc,
     davinci, brcmnand, omap)
   - a huge cleanup/rework of the denali driver accompanied with core
     fixes/improvements to simplify the driver code
   - a complete rewrite of the atmel driver to support new DT bindings
     make future evolution easier
   - the addition of per-vendor detection/initialization steps to avoid
     extending the nand_ids table with more extended-id entries

  SPI NOR, from Cyrille:
   - fixes in the hisi, intel and Mediatek SPI controller drivers
   - fixes to some SPI flash memories not supporting the Chip Erase
     command.
   - add support to some new memory parts (Winbond, Macronix, Micron,
     ESMT).
   - add new driver for the STM32 QSPI controller

  And a few fixes for Gemini and Versatile platforms on physmap-of"

* tag 'for-linus-20170510' of git://git.infradead.org/linux-mtd: (100 commits)
  MAINTAINERS: Update NAND subsystem git repositories
  mtd: nand: gpio: update binding
  mtd: nand: add ooblayout for old hamming layout
  mtd: oxnas_nand: Allocating more than necessary in probe()
  dt-bindings: mtd: Document the STM32 QSPI bindings
  mtd: mtk-nor: set controller's address width according to nor flash
  mtd: spi-nor: add driver for STM32 quad spi flash controller
  mtd: nand: brcmnand: Check flash #WP pin status before nand erase/program
  mtd: nand: davinci: add comment on NAND subpage write status on keystone
  mtd: nand: omap2: Fix partition creation via cmdline mtdparts
  mtd: nand: NULL terminate a of_device_id table
  mtd: nand: Fix a couple error codes
  mtd: nand: allow drivers to request minimum alignment for passed buffer
  mtd: nand: allocate aligned buffers if NAND_OWN_BUFFERS is unset
  mtd: nand: denali: allow to override revision number
  mtd: nand: denali_dt: use pdev instead of ofdev for platform_device
  mtd: nand: denali_dt: remove dma-mask DT property
  mtd: nand: denali: support 64bit capable DMA engine
  mtd: nand: denali_dt: enable HW_ECC_FIXUP for Altera SOCFPGA variant
  mtd: nand: denali: support HW_ECC_FIXUP capability
  ...

54 files changed:
Documentation/devicetree/bindings/mtd/atmel-nand.txt
Documentation/devicetree/bindings/mtd/denali-nand.txt
Documentation/devicetree/bindings/mtd/gpio-control-nand.txt
Documentation/devicetree/bindings/mtd/stm32-quadspi.txt [new file with mode: 0644]
MAINTAINERS
arch/cris/arch-v32/drivers/Kconfig
drivers/memory/Kconfig
drivers/mtd/chips/cfi_cmdset_0002.c
drivers/mtd/maps/Makefile
drivers/mtd/maps/physmap_of.c [deleted file]
drivers/mtd/maps/physmap_of_core.c [new file with mode: 0644]
drivers/mtd/mtdswap.c
drivers/mtd/nand/Kconfig
drivers/mtd/nand/Makefile
drivers/mtd/nand/atmel/Makefile [new file with mode: 0644]
drivers/mtd/nand/atmel/nand-controller.c [new file with mode: 0644]
drivers/mtd/nand/atmel/pmecc.c [new file with mode: 0644]
drivers/mtd/nand/atmel/pmecc.h [new file with mode: 0644]
drivers/mtd/nand/atmel_nand.c [deleted file]
drivers/mtd/nand/atmel_nand_ecc.h [deleted file]
drivers/mtd/nand/atmel_nand_nfc.h [deleted file]
drivers/mtd/nand/brcmnand/brcmnand.c
drivers/mtd/nand/cmx270_nand.c
drivers/mtd/nand/davinci_nand.c
drivers/mtd/nand/denali.c
drivers/mtd/nand/denali.h
drivers/mtd/nand/denali_dt.c
drivers/mtd/nand/fsmc_nand.c
drivers/mtd/nand/gpio.c
drivers/mtd/nand/nand_amd.c [new file with mode: 0644]
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/nand_hynix.c [new file with mode: 0644]
drivers/mtd/nand/nand_ids.c
drivers/mtd/nand/nand_macronix.c [new file with mode: 0644]
drivers/mtd/nand/nand_micron.c [new file with mode: 0644]
drivers/mtd/nand/nand_samsung.c [new file with mode: 0644]
drivers/mtd/nand/nand_toshiba.c [new file with mode: 0644]
drivers/mtd/nand/nandsim.c
drivers/mtd/nand/omap2.c
drivers/mtd/nand/orion_nand.c
drivers/mtd/nand/oxnas_nand.c
drivers/mtd/nand/sunxi_nand.c
drivers/mtd/nand/tango_nand.c
drivers/mtd/ofpart.c
drivers/mtd/spi-nor/Kconfig
drivers/mtd/spi-nor/Makefile
drivers/mtd/spi-nor/hisi-sfc.c
drivers/mtd/spi-nor/intel-spi.c
drivers/mtd/spi-nor/mtk-quadspi.c
drivers/mtd/spi-nor/spi-nor.c
drivers/mtd/spi-nor/stm32-quadspi.c [new file with mode: 0644]
fs/jffs2/readinode.c
include/linux/mtd/mtd.h
include/linux/mtd/nand.h

index 3e7ee99..f6bee57 100644 (file)
@@ -1,4 +1,109 @@
-Atmel NAND flash
+Atmel NAND flash controller bindings
+
+The NAND flash controller node should be defined under the EBI bus (see
+Documentation/devicetree/bindings/memory-controllers/atmel,ebi.txt).
+One or several NAND devices can be defined under this NAND controller.
+The NAND controller might be connected to an ECC engine.
+
+* NAND controller bindings:
+
+Required properties:
+- compatible: should be one of the following
+       "atmel,at91rm9200-nand-controller"
+       "atmel,at91sam9260-nand-controller"
+       "atmel,at91sam9261-nand-controller"
+       "atmel,at91sam9g45-nand-controller"
+       "atmel,sama5d3-nand-controller"
+- ranges: empty ranges property to forward EBI ranges definitions.
+- #address-cells: should be set to 2.
+- #size-cells: should be set to 1.
+- atmel,nfc-io: phandle to the NFC IO block. Only required for sama5d3
+               controllers.
+- atmel,nfc-sram: phandle to the NFC SRAM block. Only required for sama5d3
+                 controllers.
+
+Optional properties:
+- ecc-engine: phandle to the PMECC block. Only meaningful if the SoC embeds
+             a PMECC engine.
+
+* NAND device/chip bindings:
+
+Required properties:
+- reg: describes the CS lines assigned to the NAND device. If the NAND device
+       exposes multiple CS lines (multi-dies chips), your reg property will
+       contain X tuples of 3 entries.
+       1st entry: the CS line this NAND chip is connected to
+       2nd entry: the base offset of the memory region assigned to this
+                 device (always 0)
+       3rd entry: the memory region size (always 0x800000)
+
+Optional properties:
+- rb-gpios: the GPIO(s) used to check the Ready/Busy status of the NAND.
+- cs-gpios: the GPIO(s) used to control the CS line.
+- det-gpios: the GPIO used to detect if a Smartmedia Card is present.
+- atmel,rb: an integer identifying the native Ready/Busy pin. Only meaningful
+           on sama5 SoCs.
+
+All generic properties described in
+Documentation/devicetree/bindings/mtd/{common,nand}.txt also apply to the NAND
+device node, and NAND partitions should be defined under the NAND node as
+described in Documentation/devicetree/bindings/mtd/partition.txt.
+
+* ECC engine (PMECC) bindings:
+
+Required properties:
+- compatible: should be one of the following
+       "atmel,at91sam9g45-pmecc"
+       "atmel,sama5d4-pmecc"
+       "atmel,sama5d2-pmecc"
+- reg: should contain 2 register ranges. The first one is pointing to the PMECC
+       block, and the second one to the PMECC_ERRLOC block.
+
+Example:
+
+       pmecc: ecc-engine@ffffc070 {
+               compatible = "atmel,at91sam9g45-pmecc";
+                reg = <0xffffc070 0x490>,
+                      <0xffffc500 0x100>;
+       };
+
+       ebi: ebi@10000000 {
+               compatible = "atmel,sama5d3-ebi";
+               #address-cells = <2>;
+               #size-cells = <1>;
+               atmel,smc = <&hsmc>;
+               reg = <0x10000000 0x10000000
+                      0x40000000 0x30000000>;
+               ranges = <0x0 0x0 0x10000000 0x10000000
+                         0x1 0x0 0x40000000 0x10000000
+                         0x2 0x0 0x50000000 0x10000000
+                         0x3 0x0 0x60000000 0x10000000>;
+               clocks = <&mck>;
+
+                nand_controller: nand-controller {
+                       compatible = "atmel,sama5d3-nand-controller";
+                       atmel,nfc-sram = <&nfc_sram>;
+                       atmel,nfc-io = <&nfc_io>;
+                       ecc-engine = <&pmecc>;
+                       #address-cells = <2>;
+                       #size-cells = <1>;
+                       ranges;
+
+                       nand@3 {
+                               reg = <0x3 0x0 0x800000>;
+                               atmel,rb = <0>;
+
+                               /*
+                                * Put generic NAND/MTD properties and
+                                * subnodes here.
+                                */
+                       };
+               };
+       };
+
+-----------------------------------------------------------------------
+
+Deprecated bindings (should not be used in new device trees):
 
 Required properties:
 - compatible: The possible values are:
index b04d03a..e593bbe 100644 (file)
@@ -1,11 +1,11 @@
 * Denali NAND controller
 
 Required properties:
-  - compatible : should be "denali,denali-nand-dt"
+  - compatible : should be one of the following:
+      "altr,socfpga-denali-nand"            - for Altera SOCFPGA
   - reg : should contain registers location and length for data and reg.
   - reg-names: Should contain the reg names "nand_data" and "denali_reg"
   - interrupts : The interrupt number.
-  - dm-mask : DMA bit mask
 
 The device tree may optionally contain sub-nodes describing partitions of the
 address space. See partition.txt for more detail.
@@ -15,9 +15,8 @@ Examples:
 nand: nand@ff900000 {
        #address-cells = <1>;
        #size-cells = <1>;
-       compatible = "denali,denali-nand-dt";
+       compatible = "altr,socfpga-denali-nand";
        reg = <0xff900000 0x100000>, <0xffb80000 0x10000>;
        reg-names = "nand_data", "denali_reg";
        interrupts = <0 144 4>;
-       dma-mask = <0xffffffff>;
 };
index af8915b..486a17d 100644 (file)
@@ -12,7 +12,7 @@ Required properties:
 - #address-cells, #size-cells : Must be present if the device has sub-nodes
   representing partitions.
 - gpios : Specifies the GPIO pins to control the NAND device.  The order of
-  GPIO references is:  RDY, nCE, ALE, CLE, and an optional nWP.
+  GPIO references is:  RDY, nCE, ALE, CLE, and nWP. nCE and nWP are optional.
 
 Optional properties:
 - bank-width : Width (in bytes) of the device.  If not present, the width
@@ -36,7 +36,7 @@ gpio-nand@1,0 {
        #address-cells = <1>;
        #size-cells = <1>;
        gpios = <&banka 1 0>,   /* RDY */
-               <&banka 2 0>,   /* nCE */
+               <0>,            /* nCE */
                <&banka 3 0>,   /* ALE */
                <&banka 4 0>,   /* CLE */
                <0>;            /* nWP */
diff --git a/Documentation/devicetree/bindings/mtd/stm32-quadspi.txt b/Documentation/devicetree/bindings/mtd/stm32-quadspi.txt
new file mode 100644 (file)
index 0000000..ddd18c1
--- /dev/null
@@ -0,0 +1,43 @@
+* STMicroelectronics Quad Serial Peripheral Interface(QuadSPI)
+
+Required properties:
+- compatible: should be "st,stm32f469-qspi"
+- reg: the first contains the register location and length.
+       the second contains the memory mapping address and length
+- reg-names: should contain the reg names "qspi" "qspi_mm"
+- interrupts: should contain the interrupt for the device
+- clocks: the phandle of the clock needed by the QSPI controller
+- A pinctrl must be defined to set pins in mode of operation for QSPI transfer
+
+Optional properties:
+- resets: must contain the phandle to the reset controller.
+
+A spi flash must be a child of the nor_flash node and could have some
+properties. Also see jedec,spi-nor.txt.
+
+Required properties:
+- reg: chip-Select number (QSPI controller may connect 2 nor flashes)
+- spi-max-frequency: max frequency of spi bus
+
+Optional property:
+- spi-rx-bus-width: see ../spi/spi-bus.txt for the description
+
+Example:
+
+qspi: spi@a0001000 {
+       compatible = "st,stm32f469-qspi";
+       reg = <0xa0001000 0x1000>, <0x90000000 0x10000000>;
+       reg-names = "qspi", "qspi_mm";
+       interrupts = <91>;
+       resets = <&rcc STM32F4_AHB3_RESET(QSPI)>;
+       clocks = <&rcc 0 STM32F4_AHB3_CLOCK(QSPI)>;
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_qspi0>;
+
+       flash@0 {
+               reg = <0>;
+               spi-rx-bus-width = <4>;
+               spi-max-frequency = <108000000>;
+               ...
+       };
+};
index ce3d04d..12a0b41 100644 (file)
@@ -2274,7 +2274,7 @@ M:        Wenyou Yang <wenyou.yang@atmel.com>
 M:     Josh Wu <rainyfeeling@outlook.com>
 L:     linux-mtd@lists.infradead.org
 S:     Supported
-F:     drivers/mtd/nand/atmel_nand*
+F:     drivers/mtd/nand/atmel/*
 
 ATMEL SDMMC DRIVER
 M:     Ludovic Desroches <ludovic.desroches@microchip.com>
@@ -8376,12 +8376,12 @@ M:      Brian Norris <computersforpeace@gmail.com>
 M:     Boris Brezillon <boris.brezillon@free-electrons.com>
 M:     Marek Vasut <marek.vasut@gmail.com>
 M:     Richard Weinberger <richard@nod.at>
-M:     Cyrille Pitchen <cyrille.pitchen@atmel.com>
+M:     Cyrille Pitchen <cyrille.pitchen@wedev4u.fr>
 L:     linux-mtd@lists.infradead.org
 W:     http://www.linux-mtd.infradead.org/
 Q:     http://patchwork.ozlabs.org/project/linux-mtd/list/
-T:     git git://git.infradead.org/linux-mtd.git
-T:     git git://git.infradead.org/l2-mtd.git
+T:     git git://git.infradead.org/linux-mtd.git master
+T:     git git://git.infradead.org/l2-mtd.git master
 S:     Maintained
 F:     Documentation/devicetree/bindings/mtd/
 F:     drivers/mtd/
@@ -8756,7 +8756,8 @@ R:        Richard Weinberger <richard@nod.at>
 L:     linux-mtd@lists.infradead.org
 W:     http://www.linux-mtd.infradead.org/
 Q:     http://patchwork.ozlabs.org/project/linux-mtd/list/
-T:     git git://github.com/linux-nand/linux.git
+T:     git git://git.infradead.org/linux-mtd.git nand/fixes
+T:     git git://git.infradead.org/l2-mtd.git nand/next
 S:     Maintained
 F:     drivers/mtd/nand/
 F:     include/linux/mtd/nand*.h
@@ -12113,7 +12114,7 @@ S:      Maintained
 F:     drivers/clk/spear/
 
 SPI NOR SUBSYSTEM
-M:     Cyrille Pitchen <cyrille.pitchen@atmel.com>
+M:     Cyrille Pitchen <cyrille.pitchen@wedev4u.fr>
 M:     Marek Vasut <marek.vasut@gmail.com>
 L:     linux-mtd@lists.infradead.org
 W:     http://www.linux-mtd.infradead.org/
index 2735eb7..b7cd6b9 100644 (file)
@@ -136,7 +136,6 @@ config ETRAX_NANDFLASH
        bool "NAND flash support"
        depends on ETRAX_ARCH_V32
        select MTD_NAND
-       select MTD_NAND_IDS
        help
          This option enables MTD mapping of NAND flash devices.  Needed to use
          NAND flash memories.  If unsure, say Y.
index 3ecc429..ffc3502 100644 (file)
@@ -116,7 +116,7 @@ config FSL_CORENET_CF
 
 config FSL_IFC
        bool
-       depends on FSL_SOC || ARCH_LAYERSCAPE
+       depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A
 
 config JZ4780_NEMC
        bool "Ingenic JZ4780 SoC NEMC driver"
index 9dca881..56aa6b7 100644 (file)
@@ -323,7 +323,8 @@ static void fixup_sst38vf640x_sectorsize(struct mtd_info *mtd)
         * it should report a size of 8KBytes (0x0020*256).
         */
        cfi->cfiq->EraseRegionInfo[0] = 0x002003ff;
-       pr_warning("%s: Bad 38VF640x CFI data; adjusting sector size from 64 to 8KiB\n", mtd->name);
+       pr_warn("%s: Bad 38VF640x CFI data; adjusting sector size from 64 to 8KiB\n",
+               mtd->name);
 }
 
 static void fixup_s29gl064n_sectors(struct mtd_info *mtd)
@@ -333,7 +334,8 @@ static void fixup_s29gl064n_sectors(struct mtd_info *mtd)
 
        if ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0x003f) {
                cfi->cfiq->EraseRegionInfo[0] |= 0x0040;
-               pr_warning("%s: Bad S29GL064N CFI data; adjust from 64 to 128 sectors\n", mtd->name);
+               pr_warn("%s: Bad S29GL064N CFI data; adjust from 64 to 128 sectors\n",
+                       mtd->name);
        }
 }
 
@@ -344,7 +346,8 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd)
 
        if ((cfi->cfiq->EraseRegionInfo[1] & 0xffff) == 0x007e) {
                cfi->cfiq->EraseRegionInfo[1] &= ~0x0040;
-               pr_warning("%s: Bad S29GL032N CFI data; adjust from 127 to 63 sectors\n", mtd->name);
+               pr_warn("%s: Bad S29GL032N CFI data; adjust from 127 to 63 sectors\n",
+                       mtd->name);
        }
 }
 
@@ -358,7 +361,8 @@ static void fixup_s29ns512p_sectors(struct mtd_info *mtd)
         * which is not permitted by CFI.
         */
        cfi->cfiq->EraseRegionInfo[0] = 0x020001ff;
-       pr_warning("%s: Bad S29NS512P CFI data; adjust to 512 sectors\n", mtd->name);
+       pr_warn("%s: Bad S29NS512P CFI data; adjust to 512 sectors\n",
+               mtd->name);
 }
 
 /* Used to fix CFI-Tables of chips without Extended Query Tables */
index aef1846..5a09a72 100644 (file)
@@ -17,12 +17,10 @@ obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o
 obj-$(CONFIG_MTD_TSUNAMI)      += tsunami_flash.o
 obj-$(CONFIG_MTD_PXA2XX)       += pxa2xx-flash.o
 obj-$(CONFIG_MTD_PHYSMAP)      += physmap.o
-ifdef CONFIG_MTD_PHYSMAP_OF_VERSATILE
-physmap_of-objs += physmap_of_versatile.o
-endif
-ifdef CONFIG_MTD_PHYSMAP_OF_GEMINI
-physmap_of-objs += physmap_of_gemini.o
-endif
+physmap_of-objs-y              += physmap_of_core.o
+physmap_of-objs-$(CONFIG_MTD_PHYSMAP_OF_VERSATILE) += physmap_of_versatile.o
+physmap_of-objs-$(CONFIG_MTD_PHYSMAP_OF_GEMINI) += physmap_of_gemini.o
+physmap_of-objs                        := $(physmap_of-objs-y)
 obj-$(CONFIG_MTD_PHYSMAP_OF)   += physmap_of.o
 obj-$(CONFIG_MTD_PISMO)                += pismo.o
 obj-$(CONFIG_MTD_PMC_MSP_EVM)   += pmcmsp-flash.o
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
deleted file mode 100644 (file)
index 14e8909..0000000
+++ /dev/null
@@ -1,389 +0,0 @@
-/*
- * Flash mappings described by the OF (or flattened) device tree
- *
- * Copyright (C) 2006 MontaVista Software Inc.
- * Author: Vitaly Wool <vwool@ru.mvista.com>
- *
- * Revised to handle newer style flash binding by:
- *   Copyright (C) 2007 David Gibson, IBM Corporation.
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of  the GNU General  Public License as published by the
- * Free Software Foundation;  either version 2 of the  License, or (at your
- * option) any later version.
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/device.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/concat.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
-#include <linux/slab.h>
-#include "physmap_of_gemini.h"
-#include "physmap_of_versatile.h"
-
-struct of_flash_list {
-       struct mtd_info *mtd;
-       struct map_info map;
-       struct resource *res;
-};
-
-struct of_flash {
-       struct mtd_info         *cmtd;
-       int list_size; /* number of elements in of_flash_list */
-       struct of_flash_list    list[0];
-};
-
-static int of_flash_remove(struct platform_device *dev)
-{
-       struct of_flash *info;
-       int i;
-
-       info = dev_get_drvdata(&dev->dev);
-       if (!info)
-               return 0;
-       dev_set_drvdata(&dev->dev, NULL);
-
-       if (info->cmtd) {
-               mtd_device_unregister(info->cmtd);
-               if (info->cmtd != info->list[0].mtd)
-                       mtd_concat_destroy(info->cmtd);
-       }
-
-       for (i = 0; i < info->list_size; i++) {
-               if (info->list[i].mtd)
-                       map_destroy(info->list[i].mtd);
-
-               if (info->list[i].map.virt)
-                       iounmap(info->list[i].map.virt);
-
-               if (info->list[i].res) {
-                       release_resource(info->list[i].res);
-                       kfree(info->list[i].res);
-               }
-       }
-       return 0;
-}
-
-static const char * const rom_probe_types[] = {
-       "cfi_probe", "jedec_probe", "map_rom" };
-
-/* Helper function to handle probing of the obsolete "direct-mapped"
- * compatible binding, which has an extra "probe-type" property
- * describing the type of flash probe necessary. */
-static struct mtd_info *obsolete_probe(struct platform_device *dev,
-                                      struct map_info *map)
-{
-       struct device_node *dp = dev->dev.of_node;
-       const char *of_probe;
-       struct mtd_info *mtd;
-       int i;
-
-       dev_warn(&dev->dev, "Device tree uses obsolete \"direct-mapped\" "
-                "flash binding\n");
-
-       of_probe = of_get_property(dp, "probe-type", NULL);
-       if (!of_probe) {
-               for (i = 0; i < ARRAY_SIZE(rom_probe_types); i++) {
-                       mtd = do_map_probe(rom_probe_types[i], map);
-                       if (mtd)
-                               return mtd;
-               }
-               return NULL;
-       } else if (strcmp(of_probe, "CFI") == 0) {
-               return do_map_probe("cfi_probe", map);
-       } else if (strcmp(of_probe, "JEDEC") == 0) {
-               return do_map_probe("jedec_probe", map);
-       } else {
-               if (strcmp(of_probe, "ROM") != 0)
-                       dev_warn(&dev->dev, "obsolete_probe: don't know probe "
-                                "type '%s', mapping as rom\n", of_probe);
-               return do_map_probe("map_rom", map);
-       }
-}
-
-/* When partitions are set we look for a linux,part-probe property which
-   specifies the list of partition probers to use. If none is given then the
-   default is use. These take precedence over other device tree
-   information. */
-static const char * const part_probe_types_def[] = {
-       "cmdlinepart", "RedBoot", "ofpart", "ofoldpart", NULL };
-
-static const char * const *of_get_probes(struct device_node *dp)
-{
-       const char *cp;
-       int cplen;
-       unsigned int l;
-       unsigned int count;
-       const char **res;
-
-       cp = of_get_property(dp, "linux,part-probe", &cplen);
-       if (cp == NULL)
-               return part_probe_types_def;
-
-       count = 0;
-       for (l = 0; l != cplen; l++)
-               if (cp[l] == 0)
-                       count++;
-
-       res = kzalloc((count + 1)*sizeof(*res), GFP_KERNEL);
-       if (!res)
-               return NULL;
-       count = 0;
-       while (cplen > 0) {
-               res[count] = cp;
-               l = strlen(cp) + 1;
-               cp += l;
-               cplen -= l;
-               count++;
-       }
-       return res;
-}
-
-static void of_free_probes(const char * const *probes)
-{
-       if (probes != part_probe_types_def)
-               kfree(probes);
-}
-
-static const struct of_device_id of_flash_match[];
-static int of_flash_probe(struct platform_device *dev)
-{
-       const char * const *part_probe_types;
-       const struct of_device_id *match;
-       struct device_node *dp = dev->dev.of_node;
-       struct resource res;
-       struct of_flash *info;
-       const char *probe_type;
-       const __be32 *width;
-       int err;
-       int i;
-       int count;
-       const __be32 *p;
-       int reg_tuple_size;
-       struct mtd_info **mtd_list = NULL;
-       resource_size_t res_size;
-       bool map_indirect;
-       const char *mtd_name = NULL;
-
-       match = of_match_device(of_flash_match, &dev->dev);
-       if (!match)
-               return -EINVAL;
-       probe_type = match->data;
-
-       reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32);
-
-       of_property_read_string(dp, "linux,mtd-name", &mtd_name);
-
-       /*
-        * Get number of "reg" tuples. Scan for MTD devices on area's
-        * described by each "reg" region. This makes it possible (including
-        * the concat support) to support the Intel P30 48F4400 chips which
-        * consists internally of 2 non-identical NOR chips on one die.
-        */
-       p = of_get_property(dp, "reg", &count);
-       if (!p || count % reg_tuple_size != 0) {
-               dev_err(&dev->dev, "Malformed reg property on %s\n",
-                               dev->dev.of_node->full_name);
-               err = -EINVAL;
-               goto err_flash_remove;
-       }
-       count /= reg_tuple_size;
-
-       map_indirect = of_property_read_bool(dp, "no-unaligned-direct-access");
-
-       err = -ENOMEM;
-       info = devm_kzalloc(&dev->dev,
-                           sizeof(struct of_flash) +
-                           sizeof(struct of_flash_list) * count, GFP_KERNEL);
-       if (!info)
-               goto err_flash_remove;
-
-       dev_set_drvdata(&dev->dev, info);
-
-       mtd_list = kzalloc(sizeof(*mtd_list) * count, GFP_KERNEL);
-       if (!mtd_list)
-               goto err_flash_remove;
-
-       for (i = 0; i < count; i++) {
-               err = -ENXIO;
-               if (of_address_to_resource(dp, i, &res)) {
-                       /*
-                        * Continue with next register tuple if this
-                        * one is not mappable
-                        */
-                       continue;
-               }
-
-               dev_dbg(&dev->dev, "of_flash device: %pR\n", &res);
-
-               err = -EBUSY;
-               res_size = resource_size(&res);
-               info->list[i].res = request_mem_region(res.start, res_size,
-                                                      dev_name(&dev->dev));
-               if (!info->list[i].res)
-                       goto err_out;
-
-               err = -ENXIO;
-               width = of_get_property(dp, "bank-width", NULL);
-               if (!width) {
-                       dev_err(&dev->dev, "Can't get bank width from device"
-                               " tree\n");
-                       goto err_out;
-               }
-
-               info->list[i].map.name = mtd_name ?: dev_name(&dev->dev);
-               info->list[i].map.phys = res.start;
-               info->list[i].map.size = res_size;
-               info->list[i].map.bankwidth = be32_to_cpup(width);
-               info->list[i].map.device_node = dp;
-
-               err = of_flash_probe_gemini(dev, dp, &info->list[i].map);
-               if (err)
-                       return err;
-               err = of_flash_probe_versatile(dev, dp, &info->list[i].map);
-               if (err)
-                       return err;
-
-               err = -ENOMEM;
-               info->list[i].map.virt = ioremap(info->list[i].map.phys,
-                                                info->list[i].map.size);
-               if (!info->list[i].map.virt) {
-                       dev_err(&dev->dev, "Failed to ioremap() flash"
-                               " region\n");
-                       goto err_out;
-               }
-
-               simple_map_init(&info->list[i].map);
-
-               /*
-                * On some platforms (e.g. MPC5200) a direct 1:1 mapping
-                * may cause problems with JFFS2 usage, as the local bus (LPB)
-                * doesn't support unaligned accesses as implemented in the
-                * JFFS2 code via memcpy(). By setting NO_XIP, the
-                * flash will not be exposed directly to the MTD users
-                * (e.g. JFFS2) any more.
-                */
-               if (map_indirect)
-                       info->list[i].map.phys = NO_XIP;
-
-               if (probe_type) {
-                       info->list[i].mtd = do_map_probe(probe_type,
-                                                        &info->list[i].map);
-               } else {
-                       info->list[i].mtd = obsolete_probe(dev,
-                                                          &info->list[i].map);
-               }
-
-               /* Fall back to mapping region as ROM */
-               if (!info->list[i].mtd) {
-                       dev_warn(&dev->dev,
-                               "do_map_probe() failed for type %s\n",
-                                probe_type);
-
-                       info->list[i].mtd = do_map_probe("map_rom",
-                                                        &info->list[i].map);
-               }
-               mtd_list[i] = info->list[i].mtd;
-
-               err = -ENXIO;
-               if (!info->list[i].mtd) {
-                       dev_err(&dev->dev, "do_map_probe() failed\n");
-                       goto err_out;
-               } else {
-                       info->list_size++;
-               }
-               info->list[i].mtd->dev.parent = &dev->dev;
-       }
-
-       err = 0;
-       info->cmtd = NULL;
-       if (info->list_size == 1) {
-               info->cmtd = info->list[0].mtd;
-       } else if (info->list_size > 1) {
-               /*
-                * We detected multiple devices. Concatenate them together.
-                */
-               info->cmtd = mtd_concat_create(mtd_list, info->list_size,
-                                              dev_name(&dev->dev));
-       }
-       if (info->cmtd == NULL)
-               err = -ENXIO;
-
-       if (err)
-               goto err_out;
-
-       info->cmtd->dev.parent = &dev->dev;
-       mtd_set_of_node(info->cmtd, dp);
-       part_probe_types = of_get_probes(dp);
-       if (!part_probe_types) {
-               err = -ENOMEM;
-               goto err_out;
-       }
-       mtd_device_parse_register(info->cmtd, part_probe_types, NULL,
-                       NULL, 0);
-       of_free_probes(part_probe_types);
-
-       kfree(mtd_list);
-
-       return 0;
-
-err_out:
-       kfree(mtd_list);
-err_flash_remove:
-       of_flash_remove(dev);
-
-       return err;
-}
-
-static const struct of_device_id of_flash_match[] = {
-       {
-               .compatible     = "cfi-flash",
-               .data           = (void *)"cfi_probe",
-       },
-       {
-               /* FIXME: JEDEC chips can't be safely and reliably
-                * probed, although the mtd code gets it right in
-                * practice most of the time.  We should use the
-                * vendor and device ids specified by the binding to
-                * bypass the heuristic probe code, but the mtd layer
-                * provides, at present, no interface for doing so
-                * :(. */
-               .compatible     = "jedec-flash",
-               .data           = (void *)"jedec_probe",
-       },
-       {
-               .compatible     = "mtd-ram",
-               .data           = (void *)"map_ram",
-       },
-       {
-               .compatible     = "mtd-rom",
-               .data           = (void *)"map_rom",
-       },
-       {
-               .type           = "rom",
-               .compatible     = "direct-mapped"
-       },
-       { },
-};
-MODULE_DEVICE_TABLE(of, of_flash_match);
-
-static struct platform_driver of_flash_driver = {
-       .driver = {
-               .name = "of-flash",
-               .of_match_table = of_flash_match,
-       },
-       .probe          = of_flash_probe,
-       .remove         = of_flash_remove,
-};
-
-module_platform_driver(of_flash_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Vitaly Wool <vwool@ru.mvista.com>");
-MODULE_DESCRIPTION("Device tree based MTD map driver");
diff --git a/drivers/mtd/maps/physmap_of_core.c b/drivers/mtd/maps/physmap_of_core.c
new file mode 100644 (file)
index 0000000..62fa683
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * Flash mappings described by the OF (or flattened) device tree
+ *
+ * Copyright (C) 2006 MontaVista Software Inc.
+ * Author: Vitaly Wool <vwool@ru.mvista.com>
+ *
+ * Revised to handle newer style flash binding by:
+ *   Copyright (C) 2007 David Gibson, IBM Corporation.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/concat.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include "physmap_of_gemini.h"
+#include "physmap_of_versatile.h"
+
+struct of_flash_list {
+       struct mtd_info *mtd;
+       struct map_info map;
+       struct resource *res;
+};
+
+struct of_flash {
+       struct mtd_info         *cmtd;
+       int list_size; /* number of elements in of_flash_list */
+       struct of_flash_list    list[0];
+};
+
+static int of_flash_remove(struct platform_device *dev)
+{
+       struct of_flash *info;
+       int i;
+
+       info = dev_get_drvdata(&dev->dev);
+       if (!info)
+               return 0;
+       dev_set_drvdata(&dev->dev, NULL);
+
+       if (info->cmtd) {
+               mtd_device_unregister(info->cmtd);
+               if (info->cmtd != info->list[0].mtd)
+                       mtd_concat_destroy(info->cmtd);
+       }
+
+       for (i = 0; i < info->list_size; i++) {
+               if (info->list[i].mtd)
+                       map_destroy(info->list[i].mtd);
+
+               if (info->list[i].map.virt)
+                       iounmap(info->list[i].map.virt);
+
+               if (info->list[i].res) {
+                       release_resource(info->list[i].res);
+                       kfree(info->list[i].res);
+               }
+       }
+       return 0;
+}
+
+static const char * const rom_probe_types[] = {
+       "cfi_probe", "jedec_probe", "map_rom" };
+
+/* Helper function to handle probing of the obsolete "direct-mapped"
+ * compatible binding, which has an extra "probe-type" property
+ * describing the type of flash probe necessary. */
+static struct mtd_info *obsolete_probe(struct platform_device *dev,
+                                      struct map_info *map)
+{
+       struct device_node *dp = dev->dev.of_node;
+       const char *of_probe;
+       struct mtd_info *mtd;
+       int i;
+
+       dev_warn(&dev->dev, "Device tree uses obsolete \"direct-mapped\" "
+                "flash binding\n");
+
+       of_probe = of_get_property(dp, "probe-type", NULL);
+       if (!of_probe) {
+               for (i = 0; i < ARRAY_SIZE(rom_probe_types); i++) {
+                       mtd = do_map_probe(rom_probe_types[i], map);
+                       if (mtd)
+                               return mtd;
+               }
+               return NULL;
+       } else if (strcmp(of_probe, "CFI") == 0) {
+               return do_map_probe("cfi_probe", map);
+       } else if (strcmp(of_probe, "JEDEC") == 0) {
+               return do_map_probe("jedec_probe", map);
+       } else {
+               if (strcmp(of_probe, "ROM") != 0)
+                       dev_warn(&dev->dev, "obsolete_probe: don't know probe "
+                                "type '%s', mapping as rom\n", of_probe);
+               return do_map_probe("map_rom", map);
+       }
+}
+
+/* When partitions are set we look for a linux,part-probe property which
+   specifies the list of partition probers to use. If none is given then the
+   default is use. These take precedence over other device tree
+   information. */
+static const char * const part_probe_types_def[] = {
+       "cmdlinepart", "RedBoot", "ofpart", "ofoldpart", NULL };
+
+static const char * const *of_get_probes(struct device_node *dp)
+{
+       const char **res;
+       int count;
+
+       count = of_property_count_strings(dp, "linux,part-probe");
+       if (count < 0)
+               return part_probe_types_def;
+
+       res = kzalloc((count + 1) * sizeof(*res), GFP_KERNEL);
+       if (!res)
+               return NULL;
+
+       count = of_property_read_string_array(dp, "linux,part-probe", res,
+                                             count);
+       if (count < 0)
+               return NULL;
+
+       return res;
+}
+
+static void of_free_probes(const char * const *probes)
+{
+       if (probes != part_probe_types_def)
+               kfree(probes);
+}
+
+static const struct of_device_id of_flash_match[];
+static int of_flash_probe(struct platform_device *dev)
+{
+       const char * const *part_probe_types;
+       const struct of_device_id *match;
+       struct device_node *dp = dev->dev.of_node;
+       struct resource res;
+       struct of_flash *info;
+       const char *probe_type;
+       const __be32 *width;
+       int err;
+       int i;
+       int count;
+       const __be32 *p;
+       int reg_tuple_size;
+       struct mtd_info **mtd_list = NULL;
+       resource_size_t res_size;
+       bool map_indirect;
+       const char *mtd_name = NULL;
+
+       match = of_match_device(of_flash_match, &dev->dev);
+       if (!match)
+               return -EINVAL;
+       probe_type = match->data;
+
+       reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32);
+
+       of_property_read_string(dp, "linux,mtd-name", &mtd_name);
+
+       /*
+        * Get number of "reg" tuples. Scan for MTD devices on area's
+        * described by each "reg" region. This makes it possible (including
+        * the concat support) to support the Intel P30 48F4400 chips which
+        * consists internally of 2 non-identical NOR chips on one die.
+        */
+       p = of_get_property(dp, "reg", &count);
+       if (!p || count % reg_tuple_size != 0) {
+               dev_err(&dev->dev, "Malformed reg property on %s\n",
+                               dev->dev.of_node->full_name);
+               err = -EINVAL;
+               goto err_flash_remove;
+       }
+       count /= reg_tuple_size;
+
+       map_indirect = of_property_read_bool(dp, "no-unaligned-direct-access");
+
+       err = -ENOMEM;
+       info = devm_kzalloc(&dev->dev,
+                           sizeof(struct of_flash) +
+                           sizeof(struct of_flash_list) * count, GFP_KERNEL);
+       if (!info)
+               goto err_flash_remove;
+
+       dev_set_drvdata(&dev->dev, info);
+
+       mtd_list = kzalloc(sizeof(*mtd_list) * count, GFP_KERNEL);
+       if (!mtd_list)
+               goto err_flash_remove;
+
+       for (i = 0; i < count; i++) {
+               err = -ENXIO;
+               if (of_address_to_resource(dp, i, &res)) {
+                       /*
+                        * Continue with next register tuple if this
+                        * one is not mappable
+                        */
+                       continue;
+               }
+
+               dev_dbg(&dev->dev, "of_flash device: %pR\n", &res);
+
+               err = -EBUSY;
+               res_size = resource_size(&res);
+               info->list[i].res = request_mem_region(res.start, res_size,
+                                                      dev_name(&dev->dev));
+               if (!info->list[i].res)
+                       goto err_out;
+
+               err = -ENXIO;
+               width = of_get_property(dp, "bank-width", NULL);
+               if (!width) {
+                       dev_err(&dev->dev, "Can't get bank width from device"
+                               " tree\n");
+                       goto err_out;
+               }
+
+               info->list[i].map.name = mtd_name ?: dev_name(&dev->dev);
+               info->list[i].map.phys = res.start;
+               info->list[i].map.size = res_size;
+               info->list[i].map.bankwidth = be32_to_cpup(width);
+               info->list[i].map.device_node = dp;
+
+               err = of_flash_probe_gemini(dev, dp, &info->list[i].map);
+               if (err)
+                       return err;
+               err = of_flash_probe_versatile(dev, dp, &info->list[i].map);
+               if (err)
+                       return err;
+
+               err = -ENOMEM;
+               info->list[i].map.virt = ioremap(info->list[i].map.phys,
+                                                info->list[i].map.size);
+               if (!info->list[i].map.virt) {
+                       dev_err(&dev->dev, "Failed to ioremap() flash"
+                               " region\n");
+                       goto err_out;
+               }
+
+               simple_map_init(&info->list[i].map);
+
+               /*
+                * On some platforms (e.g. MPC5200) a direct 1:1 mapping
+                * may cause problems with JFFS2 usage, as the local bus (LPB)
+                * doesn't support unaligned accesses as implemented in the
+                * JFFS2 code via memcpy(). By setting NO_XIP, the
+                * flash will not be exposed directly to the MTD users
+                * (e.g. JFFS2) any more.
+                */
+               if (map_indirect)
+                       info->list[i].map.phys = NO_XIP;
+
+               if (probe_type) {
+                       info->list[i].mtd = do_map_probe(probe_type,
+                                                        &info->list[i].map);
+               } else {
+                       info->list[i].mtd = obsolete_probe(dev,
+                                                          &info->list[i].map);
+               }
+
+               /* Fall back to mapping region as ROM */
+               if (!info->list[i].mtd) {
+                       dev_warn(&dev->dev,
+                               "do_map_probe() failed for type %s\n",
+                                probe_type);
+
+                       info->list[i].mtd = do_map_probe("map_rom",
+                                                        &info->list[i].map);
+               }
+               mtd_list[i] = info->list[i].mtd;
+
+               err = -ENXIO;
+               if (!info->list[i].mtd) {
+                       dev_err(&dev->dev, "do_map_probe() failed\n");
+                       goto err_out;
+               } else {
+                       info->list_size++;
+               }
+               info->list[i].mtd->dev.parent = &dev->dev;
+       }
+
+       err = 0;
+       info->cmtd = NULL;
+       if (info->list_size == 1) {
+               info->cmtd = info->list[0].mtd;
+       } else if (info->list_size > 1) {
+               /*
+                * We detected multiple devices. Concatenate them together.
+                */
+               info->cmtd = mtd_concat_create(mtd_list, info->list_size,
+                                              dev_name(&dev->dev));
+       }
+       if (info->cmtd == NULL)
+               err = -ENXIO;
+
+       if (err)
+               goto err_out;
+
+       info->cmtd->dev.parent = &dev->dev;
+       mtd_set_of_node(info->cmtd, dp);
+       part_probe_types = of_get_probes(dp);
+       if (!part_probe_types) {
+               err = -ENOMEM;
+               goto err_out;
+       }
+       mtd_device_parse_register(info->cmtd, part_probe_types, NULL,
+                       NULL, 0);
+       of_free_probes(part_probe_types);
+
+       kfree(mtd_list);
+
+       return 0;
+
+err_out:
+       kfree(mtd_list);
+err_flash_remove:
+       of_flash_remove(dev);
+
+       return err;
+}
+
+static const struct of_device_id of_flash_match[] = {
+       {
+               .compatible     = "cfi-flash",
+               .data           = (void *)"cfi_probe",
+       },
+       {
+               /* FIXME: JEDEC chips can't be safely and reliably
+                * probed, although the mtd code gets it right in
+                * practice most of the time.  We should use the
+                * vendor and device ids specified by the binding to
+                * bypass the heuristic probe code, but the mtd layer
+                * provides, at present, no interface for doing so
+                * :(. */
+               .compatible     = "jedec-flash",
+               .data           = (void *)"jedec_probe",
+       },
+       {
+               .compatible     = "mtd-ram",
+               .data           = (void *)"map_ram",
+       },
+       {
+               .compatible     = "mtd-rom",
+               .data           = (void *)"map_rom",
+       },
+       {
+               .type           = "rom",
+               .compatible     = "direct-mapped"
+       },
+       { },
+};
+MODULE_DEVICE_TABLE(of, of_flash_match);
+
+static struct platform_driver of_flash_driver = {
+       .driver = {
+               .name = "of-flash",
+               .of_match_table = of_flash_match,
+       },
+       .probe          = of_flash_probe,
+       .remove         = of_flash_remove,
+};
+
+module_platform_driver(of_flash_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vitaly Wool <vwool@ru.mvista.com>");
+MODULE_DESCRIPTION("Device tree based MTD map driver");
index c40e2c9..f12879a 100644 (file)
@@ -1235,10 +1235,8 @@ static int mtdswap_show(struct seq_file *s, void *data)
 
                if (root->rb_node) {
                        count[i] = d->trees[i].count;
-                       min[i] = rb_entry(rb_first(root), struct swap_eb,
-                                       rb)->erase_count;
-                       max[i] = rb_entry(rb_last(root), struct swap_eb,
-                                       rb)->erase_count;
+                       min[i] = MTDSWAP_ECNT_MIN(root);
+                       max[i] = MTDSWAP_ECNT_MAX(root);
                } else
                        count[i] = 0;
        }
index 6d4d567..c302952 100644 (file)
@@ -13,7 +13,6 @@ config MTD_NAND_ECC_SMC
 menuconfig MTD_NAND
        tristate "NAND Device Support"
        depends on MTD
-       select MTD_NAND_IDS
        select MTD_NAND_ECC
        help
          This enables support for accessing all type of NAND flash
@@ -60,17 +59,6 @@ config MTD_NAND_DENALI_DT
          Enable the driver for NAND flash on platforms using a Denali NAND
          controller as a DT device.
 
-config MTD_NAND_DENALI_SCRATCH_REG_ADDR
-        hex "Denali NAND size scratch register address"
-        default "0xFF108018"
-        depends on MTD_NAND_DENALI_PCI
-        help
-          Some platforms place the NAND chip size in a scratch register
-          because (some versions of) the driver aren't able to automatically
-          determine the size of certain chips. Set the address of the
-          scratch register here to enable this feature. On Intel Moorestown
-          boards, the scratch register is at 0xFF108018.
-
 config MTD_NAND_GPIO
        tristate "GPIO assisted NAND Flash driver"
        depends on GPIOLIB || COMPILE_TEST
@@ -109,9 +97,6 @@ config MTD_NAND_OMAP_BCH
 config MTD_NAND_OMAP_BCH_BUILD
        def_tristate MTD_NAND_OMAP2 && MTD_NAND_OMAP_BCH
 
-config MTD_NAND_IDS
-       tristate
-
 config MTD_NAND_RICOH
        tristate "Ricoh xD card reader"
        default n
@@ -321,11 +306,11 @@ config MTD_NAND_CS553X
          If you say "m", the module will be called cs553x_nand.
 
 config MTD_NAND_ATMEL
-       tristate "Support for NAND Flash / SmartMedia on AT91 and AVR32"
-       depends on ARCH_AT91 || AVR32
+       tristate "Support for NAND Flash / SmartMedia on AT91"
+       depends on ARCH_AT91
        help
          Enables support for NAND Flash / Smart Media Card interface
-         on Atmel AT91 and AVR32 processors.
+         on Atmel AT91 processors.
 
 config MTD_NAND_PXA3xx
        tristate "NAND support on PXA3xx and Armada 370/XP"
@@ -443,7 +428,7 @@ config MTD_NAND_FSL_ELBC
 
 config MTD_NAND_FSL_IFC
        tristate "NAND support for Freescale IFC controller"
-       depends on FSL_SOC || ARCH_LAYERSCAPE
+       depends on FSL_SOC || ARCH_LAYERSCAPE || SOC_LS1021A
        select FSL_IFC
        select MEMORY
        help
index 19a66e4..ade5fc4 100644 (file)
@@ -5,7 +5,6 @@
 obj-$(CONFIG_MTD_NAND)                 += nand.o
 obj-$(CONFIG_MTD_NAND_ECC)             += nand_ecc.o
 obj-$(CONFIG_MTD_NAND_BCH)             += nand_bch.o
-obj-$(CONFIG_MTD_NAND_IDS)             += nand_ids.o
 obj-$(CONFIG_MTD_SM_COMMON)            += sm_common.o
 
 obj-$(CONFIG_MTD_NAND_CAFE)            += cafe_nand.o
@@ -25,7 +24,7 @@ obj-$(CONFIG_MTD_NAND_SHARPSL)                += sharpsl.o
 obj-$(CONFIG_MTD_NAND_NANDSIM)         += nandsim.o
 obj-$(CONFIG_MTD_NAND_CS553X)          += cs553x_nand.o
 obj-$(CONFIG_MTD_NAND_NDFC)            += ndfc.o
-obj-$(CONFIG_MTD_NAND_ATMEL)           += atmel_nand.o
+obj-$(CONFIG_MTD_NAND_ATMEL)           += atmel/
 obj-$(CONFIG_MTD_NAND_GPIO)            += gpio.o
 omap2_nand-objs := omap2.o
 obj-$(CONFIG_MTD_NAND_OMAP2)           += omap2_nand.o
@@ -61,4 +60,10 @@ obj-$(CONFIG_MTD_NAND_BRCMNAND)              += brcmnand/
 obj-$(CONFIG_MTD_NAND_QCOM)            += qcom_nandc.o
 obj-$(CONFIG_MTD_NAND_MTK)             += mtk_nand.o mtk_ecc.o
 
-nand-objs := nand_base.o nand_bbt.o nand_timings.o
+nand-objs := nand_base.o nand_bbt.o nand_timings.o nand_ids.o
+nand-objs += nand_amd.o
+nand-objs += nand_hynix.o
+nand-objs += nand_macronix.o
+nand-objs += nand_micron.o
+nand-objs += nand_samsung.o
+nand-objs += nand_toshiba.o
diff --git a/drivers/mtd/nand/atmel/Makefile b/drivers/mtd/nand/atmel/Makefile
new file mode 100644 (file)
index 0000000..288db4f
--- /dev/null
@@ -0,0 +1,4 @@
+obj-$(CONFIG_MTD_NAND_ATMEL)   += atmel-nand-controller.o atmel-pmecc.o
+
+atmel-nand-controller-objs     := nand-controller.o
+atmel-pmecc-objs               := pmecc.o
diff --git a/drivers/mtd/nand/atmel/nand-controller.c b/drivers/mtd/nand/atmel/nand-controller.c
new file mode 100644 (file)
index 0000000..3b24468
--- /dev/null
@@ -0,0 +1,2197 @@
+/*
+ * Copyright 2017 ATMEL
+ * Copyright 2017 Free Electrons
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ *
+ * Derived from the atmel_nand.c driver which contained the following
+ * copyrights:
+ *
+ *   Copyright 2003 Rick Bronson
+ *
+ *   Derived from drivers/mtd/nand/autcpu12.c
+ *     Copyright 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ *   Derived from drivers/mtd/spia.c
+ *     Copyright 2000 Steven J. Hill (sjhill@cotw.com)
+ *
+ *
+ *   Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
+ *     Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007
+ *
+ *   Derived from Das U-Boot source code
+ *     (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
+ *     Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
+ *
+ *   Add Programmable Multibit ECC support for various AT91 SoC
+ *     Copyright 2012 ATMEL, Hong Xu
+ *
+ *   Add Nand Flash Controller support for SAMA5 SoC
+ *     Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * A few words about the naming convention in this file. This convention
+ * applies to structure and function names.
+ *
+ * Prefixes:
+ *
+ * - atmel_nand_: all generic structures/functions
+ * - atmel_smc_nand_: all structures/functions specific to the SMC interface
+ *                   (at91sam9 and avr32 SoCs)
+ * - atmel_hsmc_nand_: all structures/functions specific to the HSMC interface
+ *                    (sama5 SoCs and later)
+ * - atmel_nfc_: all structures/functions used to manipulate the NFC sub-block
+ *              that is available in the HSMC block
+ * - <soc>_nand_: all SoC specific structures/functions
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/genalloc.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/atmel-matrix.h>
+#include <linux/module.h>
+#include <linux/mtd/nand.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/iopoll.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/atmel.h>
+#include <linux/regmap.h>
+
+#include "pmecc.h"
+
+#define ATMEL_HSMC_NFC_CFG                     0x0
+#define ATMEL_HSMC_NFC_CFG_SPARESIZE(x)                (((x) / 4) << 24)
+#define ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK      GENMASK(30, 24)
+#define ATMEL_HSMC_NFC_CFG_DTO(cyc, mul)       (((cyc) << 16) | ((mul) << 20))
+#define ATMEL_HSMC_NFC_CFG_DTO_MAX             GENMASK(22, 16)
+#define ATMEL_HSMC_NFC_CFG_RBEDGE              BIT(13)
+#define ATMEL_HSMC_NFC_CFG_FALLING_EDGE                BIT(12)
+#define ATMEL_HSMC_NFC_CFG_RSPARE              BIT(9)
+#define ATMEL_HSMC_NFC_CFG_WSPARE              BIT(8)
+#define ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK       GENMASK(2, 0)
+#define ATMEL_HSMC_NFC_CFG_PAGESIZE(x)         (fls((x) / 512) - 1)
+
+#define ATMEL_HSMC_NFC_CTRL                    0x4
+#define ATMEL_HSMC_NFC_CTRL_EN                 BIT(0)
+#define ATMEL_HSMC_NFC_CTRL_DIS                        BIT(1)
+
+#define ATMEL_HSMC_NFC_SR                      0x8
+#define ATMEL_HSMC_NFC_IER                     0xc
+#define ATMEL_HSMC_NFC_IDR                     0x10
+#define ATMEL_HSMC_NFC_IMR                     0x14
+#define ATMEL_HSMC_NFC_SR_ENABLED              BIT(1)
+#define ATMEL_HSMC_NFC_SR_RB_RISE              BIT(4)
+#define ATMEL_HSMC_NFC_SR_RB_FALL              BIT(5)
+#define ATMEL_HSMC_NFC_SR_BUSY                 BIT(8)
+#define ATMEL_HSMC_NFC_SR_WR                   BIT(11)
+#define ATMEL_HSMC_NFC_SR_CSID                 GENMASK(14, 12)
+#define ATMEL_HSMC_NFC_SR_XFRDONE              BIT(16)
+#define ATMEL_HSMC_NFC_SR_CMDDONE              BIT(17)
+#define ATMEL_HSMC_NFC_SR_DTOE                 BIT(20)
+#define ATMEL_HSMC_NFC_SR_UNDEF                        BIT(21)
+#define ATMEL_HSMC_NFC_SR_AWB                  BIT(22)
+#define ATMEL_HSMC_NFC_SR_NFCASE               BIT(23)
+#define ATMEL_HSMC_NFC_SR_ERRORS               (ATMEL_HSMC_NFC_SR_DTOE | \
+                                                ATMEL_HSMC_NFC_SR_UNDEF | \
+                                                ATMEL_HSMC_NFC_SR_AWB | \
+                                                ATMEL_HSMC_NFC_SR_NFCASE)
+#define ATMEL_HSMC_NFC_SR_RBEDGE(x)            BIT((x) + 24)
+
+#define ATMEL_HSMC_NFC_ADDR                    0x18
+#define ATMEL_HSMC_NFC_BANK                    0x1c
+
+#define ATMEL_NFC_MAX_RB_ID                    7
+
+#define ATMEL_NFC_SRAM_SIZE                    0x2400
+
+#define ATMEL_NFC_CMD(pos, cmd)                        ((cmd) << (((pos) * 8) + 2))
+#define ATMEL_NFC_VCMD2                                BIT(18)
+#define ATMEL_NFC_ACYCLE(naddrs)               ((naddrs) << 19)
+#define ATMEL_NFC_CSID(cs)                     ((cs) << 22)
+#define ATMEL_NFC_DATAEN                       BIT(25)
+#define ATMEL_NFC_NFCWR                                BIT(26)
+
+#define ATMEL_NFC_MAX_ADDR_CYCLES              5
+
+#define ATMEL_NAND_ALE_OFFSET                  BIT(21)
+#define ATMEL_NAND_CLE_OFFSET                  BIT(22)
+
+#define DEFAULT_TIMEOUT_MS                     1000
+#define MIN_DMA_LEN                            128
+
+enum atmel_nand_rb_type {
+       ATMEL_NAND_NO_RB,
+       ATMEL_NAND_NATIVE_RB,
+       ATMEL_NAND_GPIO_RB,
+};
+
+struct atmel_nand_rb {
+       enum atmel_nand_rb_type type;
+       union {
+               struct gpio_desc *gpio;
+               int id;
+       };
+};
+
+struct atmel_nand_cs {
+       int id;
+       struct atmel_nand_rb rb;
+       struct gpio_desc *csgpio;
+       struct {
+               void __iomem *virt;
+               dma_addr_t dma;
+       } io;
+};
+
+struct atmel_nand {
+       struct list_head node;
+       struct device *dev;
+       struct nand_chip base;
+       struct atmel_nand_cs *activecs;
+       struct atmel_pmecc_user *pmecc;
+       struct gpio_desc *cdgpio;
+       int numcs;
+       struct atmel_nand_cs cs[];
+};
+
+static inline struct atmel_nand *to_atmel_nand(struct nand_chip *chip)
+{
+       return container_of(chip, struct atmel_nand, base);
+}
+
+enum atmel_nfc_data_xfer {
+       ATMEL_NFC_NO_DATA,
+       ATMEL_NFC_READ_DATA,
+       ATMEL_NFC_WRITE_DATA,
+};
+
+struct atmel_nfc_op {
+       u8 cs;
+       u8 ncmds;
+       u8 cmds[2];
+       u8 naddrs;
+       u8 addrs[5];
+       enum atmel_nfc_data_xfer data;
+       u32 wait;
+       u32 errors;
+};
+
+struct atmel_nand_controller;
+struct atmel_nand_controller_caps;
+
+struct atmel_nand_controller_ops {
+       int (*probe)(struct platform_device *pdev,
+                    const struct atmel_nand_controller_caps *caps);
+       int (*remove)(struct atmel_nand_controller *nc);
+       void (*nand_init)(struct atmel_nand_controller *nc,
+                         struct atmel_nand *nand);
+       int (*ecc_init)(struct atmel_nand *nand);
+};
+
+struct atmel_nand_controller_caps {
+       bool has_dma;
+       bool legacy_of_bindings;
+       u32 ale_offs;
+       u32 cle_offs;
+       const struct atmel_nand_controller_ops *ops;
+};
+
+struct atmel_nand_controller {
+       struct nand_hw_control base;
+       const struct atmel_nand_controller_caps *caps;
+       struct device *dev;
+       struct regmap *smc;
+       struct dma_chan *dmac;
+       struct atmel_pmecc *pmecc;
+       struct list_head chips;
+       struct clk *mck;
+};
+
+static inline struct atmel_nand_controller *
+to_nand_controller(struct nand_hw_control *ctl)
+{
+       return container_of(ctl, struct atmel_nand_controller, base);
+}
+
+struct atmel_smc_nand_controller {
+       struct atmel_nand_controller base;
+       struct regmap *matrix;
+       unsigned int ebi_csa_offs;
+};
+
+static inline struct atmel_smc_nand_controller *
+to_smc_nand_controller(struct nand_hw_control *ctl)
+{
+       return container_of(to_nand_controller(ctl),
+                           struct atmel_smc_nand_controller, base);
+}
+
+struct atmel_hsmc_nand_controller {
+       struct atmel_nand_controller base;
+       struct {
+               struct gen_pool *pool;
+               void __iomem *virt;
+               dma_addr_t dma;
+       } sram;
+       struct regmap *io;
+       struct atmel_nfc_op op;
+       struct completion complete;
+       int irq;
+
+       /* Only used when instantiating from legacy DT bindings. */
+       struct clk *clk;
+};
+
+static inline struct atmel_hsmc_nand_controller *
+to_hsmc_nand_controller(struct nand_hw_control *ctl)
+{
+       return container_of(to_nand_controller(ctl),
+                           struct atmel_hsmc_nand_controller, base);
+}
+
+static bool atmel_nfc_op_done(struct atmel_nfc_op *op, u32 status)
+{
+       op->errors |= status & ATMEL_HSMC_NFC_SR_ERRORS;
+       op->wait ^= status & op->wait;
+
+       return !op->wait || op->errors;
+}
+
+static irqreturn_t atmel_nfc_interrupt(int irq, void *data)
+{
+       struct atmel_hsmc_nand_controller *nc = data;
+       u32 sr, rcvd;
+       bool done;
+
+       regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &sr);
+
+       rcvd = sr & (nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS);
+       done = atmel_nfc_op_done(&nc->op, sr);
+
+       if (rcvd)
+               regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, rcvd);
+
+       if (done)
+               complete(&nc->complete);
+
+       return rcvd ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int atmel_nfc_wait(struct atmel_hsmc_nand_controller *nc, bool poll,
+                         unsigned int timeout_ms)
+{
+       int ret;
+
+       if (!timeout_ms)
+               timeout_ms = DEFAULT_TIMEOUT_MS;
+
+       if (poll) {
+               u32 status;
+
+               ret = regmap_read_poll_timeout(nc->base.smc,
+                                              ATMEL_HSMC_NFC_SR, status,
+                                              atmel_nfc_op_done(&nc->op,
+                                                                status),
+                                              0, timeout_ms * 1000);
+       } else {
+               init_completion(&nc->complete);
+               regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IER,
+                            nc->op.wait | ATMEL_HSMC_NFC_SR_ERRORS);
+               ret = wait_for_completion_timeout(&nc->complete,
+                                               msecs_to_jiffies(timeout_ms));
+               if (!ret)
+                       ret = -ETIMEDOUT;
+               else
+                       ret = 0;
+
+               regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff);
+       }
+
+       if (nc->op.errors & ATMEL_HSMC_NFC_SR_DTOE) {
+               dev_err(nc->base.dev, "Waiting NAND R/B Timeout\n");
+               ret = -ETIMEDOUT;
+       }
+
+       if (nc->op.errors & ATMEL_HSMC_NFC_SR_UNDEF) {
+               dev_err(nc->base.dev, "Access to an undefined area\n");
+               ret = -EIO;
+       }
+
+       if (nc->op.errors & ATMEL_HSMC_NFC_SR_AWB) {
+               dev_err(nc->base.dev, "Access while busy\n");
+               ret = -EIO;
+       }
+
+       if (nc->op.errors & ATMEL_HSMC_NFC_SR_NFCASE) {
+               dev_err(nc->base.dev, "Wrong access size\n");
+               ret = -EIO;
+       }
+
+       return ret;
+}
+
+static void atmel_nand_dma_transfer_finished(void *data)
+{
+       struct completion *finished = data;
+
+       complete(finished);
+}
+
+static int atmel_nand_dma_transfer(struct atmel_nand_controller *nc,
+                                  void *buf, dma_addr_t dev_dma, size_t len,
+                                  enum dma_data_direction dir)
+{
+       DECLARE_COMPLETION_ONSTACK(finished);
+       dma_addr_t src_dma, dst_dma, buf_dma;
+       struct dma_async_tx_descriptor *tx;
+       dma_cookie_t cookie;
+
+       buf_dma = dma_map_single(nc->dev, buf, len, dir);
+       if (dma_mapping_error(nc->dev, dev_dma)) {
+               dev_err(nc->dev,
+                       "Failed to prepare a buffer for DMA access\n");
+               goto err;
+       }
+
+       if (dir == DMA_FROM_DEVICE) {
+               src_dma = dev_dma;
+               dst_dma = buf_dma;
+       } else {
+               src_dma = buf_dma;
+               dst_dma = dev_dma;
+       }
+
+       tx = dmaengine_prep_dma_memcpy(nc->dmac, dst_dma, src_dma, len,
+                                      DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
+       if (!tx) {
+               dev_err(nc->dev, "Failed to prepare DMA memcpy\n");
+               goto err_unmap;
+       }
+
+       tx->callback = atmel_nand_dma_transfer_finished;
+       tx->callback_param = &finished;
+
+       cookie = dmaengine_submit(tx);
+       if (dma_submit_error(cookie)) {
+               dev_err(nc->dev, "Failed to do DMA tx_submit\n");
+               goto err_unmap;
+       }
+
+       dma_async_issue_pending(nc->dmac);
+       wait_for_completion(&finished);
+
+       return 0;
+
+err_unmap:
+       dma_unmap_single(nc->dev, buf_dma, len, dir);
+
+err:
+       dev_dbg(nc->dev, "Fall back to CPU I/O\n");
+
+       return -EIO;
+}
+
+static u8 atmel_nand_read_byte(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct atmel_nand *nand = to_atmel_nand(chip);
+
+       return ioread8(nand->activecs->io.virt);
+}
+
+static u16 atmel_nand_read_word(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct atmel_nand *nand = to_atmel_nand(chip);
+
+       return ioread16(nand->activecs->io.virt);
+}
+
+static void atmel_nand_write_byte(struct mtd_info *mtd, u8 byte)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct atmel_nand *nand = to_atmel_nand(chip);
+
+       if (chip->options & NAND_BUSWIDTH_16)
+               iowrite16(byte | (byte << 8), nand->activecs->io.virt);
+       else
+               iowrite8(byte, nand->activecs->io.virt);
+}
+
+static void atmel_nand_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct atmel_nand *nand = to_atmel_nand(chip);
+       struct atmel_nand_controller *nc;
+
+       nc = to_nand_controller(chip->controller);
+
+       /*
+        * If the controller supports DMA, the buffer address is DMA-able and
+        * len is long enough to make DMA transfers profitable, let's trigger
+        * a DMA transfer. If it fails, fallback to PIO mode.
+        */
+       if (nc->dmac && virt_addr_valid(buf) &&
+           len >= MIN_DMA_LEN &&
+           !atmel_nand_dma_transfer(nc, buf, nand->activecs->io.dma, len,
+                                    DMA_FROM_DEVICE))
+               return;
+
+       if (chip->options & NAND_BUSWIDTH_16)
+               ioread16_rep(nand->activecs->io.virt, buf, len / 2);
+       else
+               ioread8_rep(nand->activecs->io.virt, buf, len);
+}
+
+static void atmel_nand_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct atmel_nand *nand = to_atmel_nand(chip);
+       struct atmel_nand_controller *nc;
+
+       nc = to_nand_controller(chip->controller);
+
+       /*
+        * If the controller supports DMA, the buffer address is DMA-able and
+        * len is long enough to make DMA transfers profitable, let's trigger
+        * a DMA transfer. If it fails, fallback to PIO mode.
+        */
+       if (nc->dmac && virt_addr_valid(buf) &&
+           len >= MIN_DMA_LEN &&
+           !atmel_nand_dma_transfer(nc, (void *)buf, nand->activecs->io.dma,
+                                    len, DMA_TO_DEVICE))
+               return;
+
+       if (chip->options & NAND_BUSWIDTH_16)
+               iowrite16_rep(nand->activecs->io.virt, buf, len / 2);
+       else
+               iowrite8_rep(nand->activecs->io.virt, buf, len);
+}
+
+static int atmel_nand_dev_ready(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct atmel_nand *nand = to_atmel_nand(chip);
+
+       return gpiod_get_value(nand->activecs->rb.gpio);
+}
+
+static void atmel_nand_select_chip(struct mtd_info *mtd, int cs)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct atmel_nand *nand = to_atmel_nand(chip);
+
+       if (cs < 0 || cs >= nand->numcs) {
+               nand->activecs = NULL;
+               chip->dev_ready = NULL;
+               return;
+       }
+
+       nand->activecs = &nand->cs[cs];
+
+       if (nand->activecs->rb.type == ATMEL_NAND_GPIO_RB)
+               chip->dev_ready = atmel_nand_dev_ready;
+}
+
+static int atmel_hsmc_nand_dev_ready(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct atmel_nand *nand = to_atmel_nand(chip);
+       struct atmel_hsmc_nand_controller *nc;
+       u32 status;
+
+       nc = to_hsmc_nand_controller(chip->controller);
+
+       regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &status);
+
+       return status & ATMEL_HSMC_NFC_SR_RBEDGE(nand->activecs->rb.id);
+}
+
+static void atmel_hsmc_nand_select_chip(struct mtd_info *mtd, int cs)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct atmel_nand *nand = to_atmel_nand(chip);
+       struct atmel_hsmc_nand_controller *nc;
+
+       nc = to_hsmc_nand_controller(chip->controller);
+
+       atmel_nand_select_chip(mtd, cs);
+
+       if (!nand->activecs) {
+               regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL,
+                            ATMEL_HSMC_NFC_CTRL_DIS);
+               return;
+       }
+
+       if (nand->activecs->rb.type == ATMEL_NAND_NATIVE_RB)
+               chip->dev_ready = atmel_hsmc_nand_dev_ready;
+
+       regmap_update_bits(nc->base.smc, ATMEL_HSMC_NFC_CFG,
+                          ATMEL_HSMC_NFC_CFG_PAGESIZE_MASK |
+                          ATMEL_HSMC_NFC_CFG_SPARESIZE_MASK |
+                          ATMEL_HSMC_NFC_CFG_RSPARE |
+                          ATMEL_HSMC_NFC_CFG_WSPARE,
+                          ATMEL_HSMC_NFC_CFG_PAGESIZE(mtd->writesize) |
+                          ATMEL_HSMC_NFC_CFG_SPARESIZE(mtd->oobsize) |
+                          ATMEL_HSMC_NFC_CFG_RSPARE);
+       regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CTRL,
+                    ATMEL_HSMC_NFC_CTRL_EN);
+}
+
+static int atmel_nfc_exec_op(struct atmel_hsmc_nand_controller *nc, bool poll)
+{
+       u8 *addrs = nc->op.addrs;
+       unsigned int op = 0;
+       u32 addr, val;
+       int i, ret;
+
+       nc->op.wait = ATMEL_HSMC_NFC_SR_CMDDONE;
+
+       for (i = 0; i < nc->op.ncmds; i++)
+               op |= ATMEL_NFC_CMD(i, nc->op.cmds[i]);
+
+       if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES)
+               regmap_write(nc->base.smc, ATMEL_HSMC_NFC_ADDR, *addrs++);
+
+       op |= ATMEL_NFC_CSID(nc->op.cs) |
+             ATMEL_NFC_ACYCLE(nc->op.naddrs);
+
+       if (nc->op.ncmds > 1)
+               op |= ATMEL_NFC_VCMD2;
+
+       addr = addrs[0] | (addrs[1] << 8) | (addrs[2] << 16) |
+              (addrs[3] << 24);
+
+       if (nc->op.data != ATMEL_NFC_NO_DATA) {
+               op |= ATMEL_NFC_DATAEN;
+               nc->op.wait |= ATMEL_HSMC_NFC_SR_XFRDONE;
+
+               if (nc->op.data == ATMEL_NFC_WRITE_DATA)
+                       op |= ATMEL_NFC_NFCWR;
+       }
+
+       /* Clear all flags. */
+       regmap_read(nc->base.smc, ATMEL_HSMC_NFC_SR, &val);
+
+       /* Send the command. */
+       regmap_write(nc->io, op, addr);
+
+       ret = atmel_nfc_wait(nc, poll, 0);
+       if (ret)
+               dev_err(nc->base.dev,
+                       "Failed to send NAND command (err = %d)!",
+                       ret);
+
+       /* Reset the op state. */
+       memset(&nc->op, 0, sizeof(nc->op));
+
+       return ret;
+}
+
+static void atmel_hsmc_nand_cmd_ctrl(struct mtd_info *mtd, int dat,
+                                    unsigned int ctrl)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct atmel_nand *nand = to_atmel_nand(chip);
+       struct atmel_hsmc_nand_controller *nc;
+
+       nc = to_hsmc_nand_controller(chip->controller);
+
+       if (ctrl & NAND_ALE) {
+               if (nc->op.naddrs == ATMEL_NFC_MAX_ADDR_CYCLES)
+                       return;
+
+               nc->op.addrs[nc->op.naddrs++] = dat;
+       } else if (ctrl & NAND_CLE) {
+               if (nc->op.ncmds > 1)
+                       return;
+
+               nc->op.cmds[nc->op.ncmds++] = dat;
+       }
+
+       if (dat == NAND_CMD_NONE) {
+               nc->op.cs = nand->activecs->id;
+               atmel_nfc_exec_op(nc, true);
+       }
+}
+
+static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
+                               unsigned int ctrl)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct atmel_nand *nand = to_atmel_nand(chip);
+       struct atmel_nand_controller *nc;
+
+       nc = to_nand_controller(chip->controller);
+
+       if ((ctrl & NAND_CTRL_CHANGE) && nand->activecs->csgpio) {
+               if (ctrl & NAND_NCE)
+                       gpiod_set_value(nand->activecs->csgpio, 0);
+               else
+                       gpiod_set_value(nand->activecs->csgpio, 1);
+       }
+
+       if (ctrl & NAND_ALE)
+               writeb(cmd, nand->activecs->io.virt + nc->caps->ale_offs);
+       else if (ctrl & NAND_CLE)
+               writeb(cmd, nand->activecs->io.virt + nc->caps->cle_offs);
+}
+
+static void atmel_nfc_copy_to_sram(struct nand_chip *chip, const u8 *buf,
+                                  bool oob_required)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       struct atmel_hsmc_nand_controller *nc;
+       int ret = -EIO;
+
+       nc = to_hsmc_nand_controller(chip->controller);
+
+       if (nc->base.dmac)
+               ret = atmel_nand_dma_transfer(&nc->base, (void *)buf,
+                                             nc->sram.dma, mtd->writesize,
+                                             DMA_TO_DEVICE);
+
+       /* Falling back to CPU copy. */
+       if (ret)
+               memcpy_toio(nc->sram.virt, buf, mtd->writesize);
+
+       if (oob_required)
+               memcpy_toio(nc->sram.virt + mtd->writesize, chip->oob_poi,
+                           mtd->oobsize);
+}
+
+static void atmel_nfc_copy_from_sram(struct nand_chip *chip, u8 *buf,
+                                    bool oob_required)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       struct atmel_hsmc_nand_controller *nc;
+       int ret = -EIO;
+
+       nc = to_hsmc_nand_controller(chip->controller);
+
+       if (nc->base.dmac)
+               ret = atmel_nand_dma_transfer(&nc->base, buf, nc->sram.dma,
+                                             mtd->writesize, DMA_FROM_DEVICE);
+
+       /* Falling back to CPU copy. */
+       if (ret)
+               memcpy_fromio(buf, nc->sram.virt, mtd->writesize);
+
+       if (oob_required)
+               memcpy_fromio(chip->oob_poi, nc->sram.virt + mtd->writesize,
+                             mtd->oobsize);
+}
+
+static void atmel_nfc_set_op_addr(struct nand_chip *chip, int page, int column)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       struct atmel_hsmc_nand_controller *nc;
+
+       nc = to_hsmc_nand_controller(chip->controller);
+
+       if (column >= 0) {
+               nc->op.addrs[nc->op.naddrs++] = column;
+
+               /*
+                * 2 address cycles for the column offset on large page NANDs.
+                */
+               if (mtd->writesize > 512)
+                       nc->op.addrs[nc->op.naddrs++] = column >> 8;
+       }
+
+       if (page >= 0) {
+               nc->op.addrs[nc->op.naddrs++] = page;
+               nc->op.addrs[nc->op.naddrs++] = page >> 8;
+
+               if ((mtd->writesize > 512 && chip->chipsize > SZ_128M) ||
+                   (mtd->writesize <= 512 && chip->chipsize > SZ_32M))
+                       nc->op.addrs[nc->op.naddrs++] = page >> 16;
+       }
+}
+
+static int atmel_nand_pmecc_enable(struct nand_chip *chip, int op, bool raw)
+{
+       struct atmel_nand *nand = to_atmel_nand(chip);
+       struct atmel_nand_controller *nc;
+       int ret;
+
+       nc = to_nand_controller(chip->controller);
+
+       if (raw)
+               return 0;
+
+       ret = atmel_pmecc_enable(nand->pmecc, op);
+       if (ret)
+               dev_err(nc->dev,
+                       "Failed to enable ECC engine (err = %d)\n", ret);
+
+       return ret;
+}
+
+static void atmel_nand_pmecc_disable(struct nand_chip *chip, bool raw)
+{
+       struct atmel_nand *nand = to_atmel_nand(chip);
+
+       if (!raw)
+               atmel_pmecc_disable(nand->pmecc);
+}
+
+static int atmel_nand_pmecc_generate_eccbytes(struct nand_chip *chip, bool raw)
+{
+       struct atmel_nand *nand = to_atmel_nand(chip);
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       struct atmel_nand_controller *nc;
+       struct mtd_oob_region oobregion;
+       void *eccbuf;
+       int ret, i;
+
+       nc = to_nand_controller(chip->controller);
+
+       if (raw)
+               return 0;
+
+       ret = atmel_pmecc_wait_rdy(nand->pmecc);
+       if (ret) {
+               dev_err(nc->dev,
+                       "Failed to transfer NAND page data (err = %d)\n",
+                       ret);
+               return ret;
+       }
+
+       mtd_ooblayout_ecc(mtd, 0, &oobregion);
+       eccbuf = chip->oob_poi + oobregion.offset;
+
+       for (i = 0; i < chip->ecc.steps; i++) {
+               atmel_pmecc_get_generated_eccbytes(nand->pmecc, i,
+                                                  eccbuf);
+               eccbuf += chip->ecc.bytes;
+       }
+
+       return 0;
+}
+
+static int atmel_nand_pmecc_correct_data(struct nand_chip *chip, void *buf,
+                                        bool raw)
+{
+       struct atmel_nand *nand = to_atmel_nand(chip);
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       struct atmel_nand_controller *nc;
+       struct mtd_oob_region oobregion;
+       int ret, i, max_bitflips = 0;
+       void *databuf, *eccbuf;
+
+       nc = to_nand_controller(chip->controller);
+
+       if (raw)
+               return 0;
+
+       ret = atmel_pmecc_wait_rdy(nand->pmecc);
+       if (ret) {
+               dev_err(nc->dev,
+                       "Failed to read NAND page data (err = %d)\n",
+                       ret);
+               return ret;
+       }
+
+       mtd_ooblayout_ecc(mtd, 0, &oobregion);
+       eccbuf = chip->oob_poi + oobregion.offset;
+       databuf = buf;
+
+       for (i = 0; i < chip->ecc.steps; i++) {
+               ret = atmel_pmecc_correct_sector(nand->pmecc, i, databuf,
+                                                eccbuf);
+               if (ret < 0 && !atmel_pmecc_correct_erased_chunks(nand->pmecc))
+                       ret = nand_check_erased_ecc_chunk(databuf,
+                                                         chip->ecc.size,
+                                                         eccbuf,
+                                                         chip->ecc.bytes,
+                                                         NULL, 0,
+                                                         chip->ecc.strength);
+
+               if (ret >= 0)
+                       max_bitflips = max(ret, max_bitflips);
+               else
+                       mtd->ecc_stats.failed++;
+
+               databuf += chip->ecc.size;
+               eccbuf += chip->ecc.bytes;
+       }
+
+       return max_bitflips;
+}
+
+static int atmel_nand_pmecc_write_pg(struct nand_chip *chip, const u8 *buf,
+                                    bool oob_required, int page, bool raw)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       struct atmel_nand *nand = to_atmel_nand(chip);
+       int ret;
+
+       ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw);
+       if (ret)
+               return ret;
+
+       atmel_nand_write_buf(mtd, buf, mtd->writesize);
+
+       ret = atmel_nand_pmecc_generate_eccbytes(chip, raw);
+       if (ret) {
+               atmel_pmecc_disable(nand->pmecc);
+               return ret;
+       }
+
+       atmel_nand_pmecc_disable(chip, raw);
+
+       atmel_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+       return 0;
+}
+
+static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
+                                      struct nand_chip *chip, const u8 *buf,
+                                      int oob_required, int page)
+{
+       return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, false);
+}
+
+static int atmel_nand_pmecc_write_page_raw(struct mtd_info *mtd,
+                                          struct nand_chip *chip,
+                                          const u8 *buf, int oob_required,
+                                          int page)
+{
+       return atmel_nand_pmecc_write_pg(chip, buf, oob_required, page, true);
+}
+
+static int atmel_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf,
+                                   bool oob_required, int page, bool raw)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       int ret;
+
+       ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw);
+       if (ret)
+               return ret;
+
+       atmel_nand_read_buf(mtd, buf, mtd->writesize);
+       atmel_nand_read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+       ret = atmel_nand_pmecc_correct_data(chip, buf, raw);
+
+       atmel_nand_pmecc_disable(chip, raw);
+
+       return ret;
+}
+
+static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
+                                     struct nand_chip *chip, u8 *buf,
+                                     int oob_required, int page)
+{
+       return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, false);
+}
+
+static int atmel_nand_pmecc_read_page_raw(struct mtd_info *mtd,
+                                         struct nand_chip *chip, u8 *buf,
+                                         int oob_required, int page)
+{
+       return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page, true);
+}
+
+static int atmel_hsmc_nand_pmecc_write_pg(struct nand_chip *chip,
+                                         const u8 *buf, bool oob_required,
+                                         int page, bool raw)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       struct atmel_nand *nand = to_atmel_nand(chip);
+       struct atmel_hsmc_nand_controller *nc;
+       int ret;
+
+       nc = to_hsmc_nand_controller(chip->controller);
+
+       atmel_nfc_copy_to_sram(chip, buf, false);
+
+       nc->op.cmds[0] = NAND_CMD_SEQIN;
+       nc->op.ncmds = 1;
+       atmel_nfc_set_op_addr(chip, page, 0x0);
+       nc->op.cs = nand->activecs->id;
+       nc->op.data = ATMEL_NFC_WRITE_DATA;
+
+       ret = atmel_nand_pmecc_enable(chip, NAND_ECC_WRITE, raw);
+       if (ret)
+               return ret;
+
+       ret = atmel_nfc_exec_op(nc, false);
+       if (ret) {
+               atmel_nand_pmecc_disable(chip, raw);
+               dev_err(nc->base.dev,
+                       "Failed to transfer NAND page data (err = %d)\n",
+                       ret);
+               return ret;
+       }
+
+       ret = atmel_nand_pmecc_generate_eccbytes(chip, raw);
+
+       atmel_nand_pmecc_disable(chip, raw);
+
+       if (ret)
+               return ret;
+
+       atmel_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+       nc->op.cmds[0] = NAND_CMD_PAGEPROG;
+       nc->op.ncmds = 1;
+       nc->op.cs = nand->activecs->id;
+       ret = atmel_nfc_exec_op(nc, false);
+       if (ret)
+               dev_err(nc->base.dev, "Failed to program NAND page (err = %d)\n",
+                       ret);
+
+       return ret;
+}
+
+static int atmel_hsmc_nand_pmecc_write_page(struct mtd_info *mtd,
+                                           struct nand_chip *chip,
+                                           const u8 *buf, int oob_required,
+                                           int page)
+{
+       return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page,
+                                             false);
+}
+
+static int atmel_hsmc_nand_pmecc_write_page_raw(struct mtd_info *mtd,
+                                               struct nand_chip *chip,
+                                               const u8 *buf,
+                                               int oob_required, int page)
+{
+       return atmel_hsmc_nand_pmecc_write_pg(chip, buf, oob_required, page,
+                                             true);
+}
+
+static int atmel_hsmc_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf,
+                                        bool oob_required, int page,
+                                        bool raw)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       struct atmel_nand *nand = to_atmel_nand(chip);
+       struct atmel_hsmc_nand_controller *nc;
+       int ret;
+
+       nc = to_hsmc_nand_controller(chip->controller);
+
+       /*
+        * Optimized read page accessors only work when the NAND R/B pin is
+        * connected to a native SoC R/B pin. If that's not the case, fallback
+        * to the non-optimized one.
+        */
+       if (nand->activecs->rb.type != ATMEL_NAND_NATIVE_RB) {
+               chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+
+               return atmel_nand_pmecc_read_pg(chip, buf, oob_required, page,
+                                               raw);
+       }
+
+       nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READ0;
+
+       if (mtd->writesize > 512)
+               nc->op.cmds[nc->op.ncmds++] = NAND_CMD_READSTART;
+
+       atmel_nfc_set_op_addr(chip, page, 0x0);
+       nc->op.cs = nand->activecs->id;
+       nc->op.data = ATMEL_NFC_READ_DATA;
+
+       ret = atmel_nand_pmecc_enable(chip, NAND_ECC_READ, raw);
+       if (ret)
+               return ret;
+
+       ret = atmel_nfc_exec_op(nc, false);
+       if (ret) {
+               atmel_nand_pmecc_disable(chip, raw);
+               dev_err(nc->base.dev,
+                       "Failed to load NAND page data (err = %d)\n",
+                       ret);
+               return ret;
+       }
+
+       atmel_nfc_copy_from_sram(chip, buf, true);
+
+       ret = atmel_nand_pmecc_correct_data(chip, buf, raw);
+
+       atmel_nand_pmecc_disable(chip, raw);
+
+       return ret;
+}
+
+static int atmel_hsmc_nand_pmecc_read_page(struct mtd_info *mtd,
+                                          struct nand_chip *chip, u8 *buf,
+                                          int oob_required, int page)
+{
+       return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page,
+                                            false);
+}
+
+static int atmel_hsmc_nand_pmecc_read_page_raw(struct mtd_info *mtd,
+                                              struct nand_chip *chip,
+                                              u8 *buf, int oob_required,
+                                              int page)
+{
+       return atmel_hsmc_nand_pmecc_read_pg(chip, buf, oob_required, page,
+                                            true);
+}
+
+static int atmel_nand_pmecc_init(struct nand_chip *chip)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       struct atmel_nand *nand = to_atmel_nand(chip);
+       struct atmel_nand_controller *nc;
+       struct atmel_pmecc_user_req req;
+
+       nc = to_nand_controller(chip->controller);
+
+       if (!nc->pmecc) {
+               dev_err(nc->dev, "HW ECC not supported\n");
+               return -ENOTSUPP;
+       }
+
+       if (nc->caps->legacy_of_bindings) {
+               u32 val;
+
+               if (!of_property_read_u32(nc->dev->of_node, "atmel,pmecc-cap",
+                                         &val))
+                       chip->ecc.strength = val;
+
+               if (!of_property_read_u32(nc->dev->of_node,
+                                         "atmel,pmecc-sector-size",
+                                         &val))
+                       chip->ecc.size = val;
+       }
+
+       if (chip->ecc.options & NAND_ECC_MAXIMIZE)
+               req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH;
+       else if (chip->ecc.strength)
+               req.ecc.strength = chip->ecc.strength;
+       else if (chip->ecc_strength_ds)
+               req.ecc.strength = chip->ecc_strength_ds;
+       else
+               req.ecc.strength = ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH;
+
+       if (chip->ecc.size)
+               req.ecc.sectorsize = chip->ecc.size;
+       else if (chip->ecc_step_ds)
+               req.ecc.sectorsize = chip->ecc_step_ds;
+       else
+               req.ecc.sectorsize = ATMEL_PMECC_SECTOR_SIZE_AUTO;
+
+       req.pagesize = mtd->writesize;
+       req.oobsize = mtd->oobsize;
+
+       if (mtd->writesize <= 512) {
+               req.ecc.bytes = 4;
+               req.ecc.ooboffset = 0;
+       } else {
+               req.ecc.bytes = mtd->oobsize - 2;
+               req.ecc.ooboffset = ATMEL_PMECC_OOBOFFSET_AUTO;
+       }
+
+       nand->pmecc = atmel_pmecc_create_user(nc->pmecc, &req);
+       if (IS_ERR(nand->pmecc))
+               return PTR_ERR(nand->pmecc);
+
+       chip->ecc.algo = NAND_ECC_BCH;
+       chip->ecc.size = req.ecc.sectorsize;
+       chip->ecc.bytes = req.ecc.bytes / req.ecc.nsectors;
+       chip->ecc.strength = req.ecc.strength;
+
+       chip->options |= NAND_NO_SUBPAGE_WRITE;
+
+       mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
+
+       return 0;
+}
+
+static int atmel_nand_ecc_init(struct atmel_nand *nand)
+{
+       struct nand_chip *chip = &nand->base;
+       struct atmel_nand_controller *nc;
+       int ret;
+
+       nc = to_nand_controller(chip->controller);
+
+       switch (chip->ecc.mode) {
+       case NAND_ECC_NONE:
+       case NAND_ECC_SOFT:
+               /*
+                * Nothing to do, the core will initialize everything for us.
+                */
+               break;
+
+       case NAND_ECC_HW:
+               ret = atmel_nand_pmecc_init(chip);
+               if (ret)
+                       return ret;
+
+               chip->ecc.read_page = atmel_nand_pmecc_read_page;
+               chip->ecc.write_page = atmel_nand_pmecc_write_page;
+               chip->ecc.read_page_raw = atmel_nand_pmecc_read_page_raw;
+               chip->ecc.write_page_raw = atmel_nand_pmecc_write_page_raw;
+               break;
+
+       default:
+               /* Other modes are not supported. */
+               dev_err(nc->dev, "Unsupported ECC mode: %d\n",
+                       chip->ecc.mode);
+               return -ENOTSUPP;
+       }
+
+       return 0;
+}
+
+static int atmel_hsmc_nand_ecc_init(struct atmel_nand *nand)
+{
+       struct nand_chip *chip = &nand->base;
+       int ret;
+
+       ret = atmel_nand_ecc_init(nand);
+       if (ret)
+               return ret;
+
+       if (chip->ecc.mode != NAND_ECC_HW)
+               return 0;
+
+       /* Adjust the ECC operations for the HSMC IP. */
+       chip->ecc.read_page = atmel_hsmc_nand_pmecc_read_page;
+       chip->ecc.write_page = atmel_hsmc_nand_pmecc_write_page;
+       chip->ecc.read_page_raw = atmel_hsmc_nand_pmecc_read_page_raw;
+       chip->ecc.write_page_raw = atmel_hsmc_nand_pmecc_write_page_raw;
+       chip->ecc.options |= NAND_ECC_CUSTOM_PAGE_ACCESS;
+
+       return 0;
+}
+
+static void atmel_nand_init(struct atmel_nand_controller *nc,
+                           struct atmel_nand *nand)
+{
+       struct nand_chip *chip = &nand->base;
+       struct mtd_info *mtd = nand_to_mtd(chip);
+
+       mtd->dev.parent = nc->dev;
+       nand->base.controller = &nc->base;
+
+       chip->cmd_ctrl = atmel_nand_cmd_ctrl;
+       chip->read_byte = atmel_nand_read_byte;
+       chip->read_word = atmel_nand_read_word;
+       chip->write_byte = atmel_nand_write_byte;
+       chip->read_buf = atmel_nand_read_buf;
+       chip->write_buf = atmel_nand_write_buf;
+       chip->select_chip = atmel_nand_select_chip;
+
+       /* Some NANDs require a longer delay than the default one (20us). */
+       chip->chip_delay = 40;
+
+       /*
+        * Use a bounce buffer when the buffer passed by the MTD user is not
+        * suitable for DMA.
+        */
+       if (nc->dmac)
+               chip->options |= NAND_USE_BOUNCE_BUFFER;
+
+       /* Default to HW ECC if pmecc is available. */
+       if (nc->pmecc)
+               chip->ecc.mode = NAND_ECC_HW;
+}
+
+static void atmel_smc_nand_init(struct atmel_nand_controller *nc,
+                               struct atmel_nand *nand)
+{
+       struct nand_chip *chip = &nand->base;
+       struct atmel_smc_nand_controller *smc_nc;
+       int i;
+
+       atmel_nand_init(nc, nand);
+
+       smc_nc = to_smc_nand_controller(chip->controller);
+       if (!smc_nc->matrix)
+               return;
+
+       /* Attach the CS to the NAND Flash logic. */
+       for (i = 0; i < nand->numcs; i++)
+               regmap_update_bits(smc_nc->matrix, smc_nc->ebi_csa_offs,
+                                  BIT(nand->cs[i].id), BIT(nand->cs[i].id));
+}
+
+static void atmel_hsmc_nand_init(struct atmel_nand_controller *nc,
+                                struct atmel_nand *nand)
+{
+       struct nand_chip *chip = &nand->base;
+
+       atmel_nand_init(nc, nand);
+
+       /* Overload some methods for the HSMC controller. */
+       chip->cmd_ctrl = atmel_hsmc_nand_cmd_ctrl;
+       chip->select_chip = atmel_hsmc_nand_select_chip;
+}
+
+static int atmel_nand_detect(struct atmel_nand *nand)
+{
+       struct nand_chip *chip = &nand->base;
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       struct atmel_nand_controller *nc;
+       int ret;
+
+       nc = to_nand_controller(chip->controller);
+
+       ret = nand_scan_ident(mtd, nand->numcs, NULL);
+       if (ret)
+               dev_err(nc->dev, "nand_scan_ident() failed: %d\n", ret);
+
+       return ret;
+}
+
+static int atmel_nand_unregister(struct atmel_nand *nand)
+{
+       struct nand_chip *chip = &nand->base;
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       int ret;
+
+       ret = mtd_device_unregister(mtd);
+       if (ret)
+               return ret;
+
+       nand_cleanup(chip);
+       list_del(&nand->node);
+
+       return 0;
+}
+
+static int atmel_nand_register(struct atmel_nand *nand)
+{
+       struct nand_chip *chip = &nand->base;
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       struct atmel_nand_controller *nc;
+       int ret;
+
+       nc = to_nand_controller(chip->controller);
+
+       if (nc->caps->legacy_of_bindings || !nc->dev->of_node) {
+               /*
+                * We keep the MTD name unchanged to avoid breaking platforms
+                * where the MTD cmdline parser is used and the bootloader
+                * has not been updated to use the new naming scheme.
+                */
+               mtd->name = "atmel_nand";
+       } else if (!mtd->name) {
+               /*
+                * If the new bindings are used and the bootloader has not been
+                * updated to pass a new mtdparts parameter on the cmdline, you
+                * should define the following property in your nand node:
+                *
+                *      label = "atmel_nand";
+                *
+                * This way, mtd->name will be set by the core when
+                * nand_set_flash_node() is called.
+                */
+               mtd->name = devm_kasprintf(nc->dev, GFP_KERNEL,
+                                          "%s:nand.%d", dev_name(nc->dev),
+                                          nand->cs[0].id);
+               if (!mtd->name) {
+                       dev_err(nc->dev, "Failed to allocate mtd->name\n");
+                       return -ENOMEM;
+               }
+       }
+
+       ret = nand_scan_tail(mtd);
+       if (ret) {
+               dev_err(nc->dev, "nand_scan_tail() failed: %d\n", ret);
+               return ret;
+       }
+
+       ret = mtd_device_register(mtd, NULL, 0);
+       if (ret) {
+               dev_err(nc->dev, "Failed to register mtd device: %d\n", ret);
+               nand_cleanup(chip);
+               return ret;
+       }
+
+       list_add_tail(&nand->node, &nc->chips);
+
+       return 0;
+}
+
+static struct atmel_nand *atmel_nand_create(struct atmel_nand_controller *nc,
+                                           struct device_node *np,
+                                           int reg_cells)
+{
+       struct atmel_nand *nand;
+       struct gpio_desc *gpio;
+       int numcs, ret, i;
+
+       numcs = of_property_count_elems_of_size(np, "reg",
+                                               reg_cells * sizeof(u32));
+       if (numcs < 1) {
+               dev_err(nc->dev, "Missing or invalid reg property\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       nand = devm_kzalloc(nc->dev,
+                           sizeof(*nand) + (numcs * sizeof(*nand->cs)),
+                           GFP_KERNEL);
+       if (!nand) {
+               dev_err(nc->dev, "Failed to allocate NAND object\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       nand->numcs = numcs;
+
+       gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev, "det", 0,
+                                                     &np->fwnode, GPIOD_IN,
+                                                     "nand-det");
+       if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) {
+               dev_err(nc->dev,
+                       "Failed to get detect gpio (err = %ld)\n",
+                       PTR_ERR(gpio));
+               return ERR_CAST(gpio);
+       }
+
+       if (!IS_ERR(gpio))
+               nand->cdgpio = gpio;
+
+       for (i = 0; i < numcs; i++) {
+               struct resource res;
+               u32 val;
+
+               ret = of_address_to_resource(np, 0, &res);
+               if (ret) {
+                       dev_err(nc->dev, "Invalid reg property (err = %d)\n",
+                               ret);
+                       return ERR_PTR(ret);
+               }
+
+               ret = of_property_read_u32_index(np, "reg", i * reg_cells,
+                                                &val);
+               if (ret) {
+                       dev_err(nc->dev, "Invalid reg property (err = %d)\n",
+                               ret);
+                       return ERR_PTR(ret);
+               }
+
+               nand->cs[i].id = val;
+
+               nand->cs[i].io.dma = res.start;
+               nand->cs[i].io.virt = devm_ioremap_resource(nc->dev, &res);
+               if (IS_ERR(nand->cs[i].io.virt))
+                       return ERR_CAST(nand->cs[i].io.virt);
+
+               if (!of_property_read_u32(np, "atmel,rb", &val)) {
+                       if (val > ATMEL_NFC_MAX_RB_ID)
+                               return ERR_PTR(-EINVAL);
+
+                       nand->cs[i].rb.type = ATMEL_NAND_NATIVE_RB;
+                       nand->cs[i].rb.id = val;
+               } else {
+                       gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev,
+                                                       "rb", i, &np->fwnode,
+                                                       GPIOD_IN, "nand-rb");
+                       if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) {
+                               dev_err(nc->dev,
+                                       "Failed to get R/B gpio (err = %ld)\n",
+                                       PTR_ERR(gpio));
+                               return ERR_CAST(gpio);
+                       }
+
+                       if (!IS_ERR(gpio)) {
+                               nand->cs[i].rb.type = ATMEL_NAND_GPIO_RB;
+                               nand->cs[i].rb.gpio = gpio;
+                       }
+               }
+
+               gpio = devm_fwnode_get_index_gpiod_from_child(nc->dev, "cs",
+                                                             i, &np->fwnode,
+                                                             GPIOD_OUT_HIGH,
+                                                             "nand-cs");
+               if (IS_ERR(gpio) && PTR_ERR(gpio) != -ENOENT) {
+                       dev_err(nc->dev,
+                               "Failed to get CS gpio (err = %ld)\n",
+                               PTR_ERR(gpio));
+                       return ERR_CAST(gpio);
+               }
+
+               if (!IS_ERR(gpio))
+                       nand->cs[i].csgpio = gpio;
+       }
+
+       nand_set_flash_node(&nand->base, np);
+
+       return nand;
+}
+
+static int
+atmel_nand_controller_add_nand(struct atmel_nand_controller *nc,
+                              struct atmel_nand *nand)
+{
+       int ret;
+
+       /* No card inserted, skip this NAND. */
+       if (nand->cdgpio && gpiod_get_value(nand->cdgpio)) {
+               dev_info(nc->dev, "No SmartMedia card inserted.\n");
+               return 0;
+       }
+
+       nc->caps->ops->nand_init(nc, nand);
+
+       ret = atmel_nand_detect(nand);
+       if (ret)
+               return ret;
+
+       ret = nc->caps->ops->ecc_init(nand);
+       if (ret)
+               return ret;
+
+       return atmel_nand_register(nand);
+}
+
+static int
+atmel_nand_controller_remove_nands(struct atmel_nand_controller *nc)
+{
+       struct atmel_nand *nand, *tmp;
+       int ret;
+
+       list_for_each_entry_safe(nand, tmp, &nc->chips, node) {
+               ret = atmel_nand_unregister(nand);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int
+atmel_nand_controller_legacy_add_nands(struct atmel_nand_controller *nc)
+{
+       struct device *dev = nc->dev;
+       struct platform_device *pdev = to_platform_device(dev);
+       struct atmel_nand *nand;
+       struct gpio_desc *gpio;
+       struct resource *res;
+
+       /*
+        * Legacy bindings only allow connecting a single NAND with a unique CS
+        * line to the controller.
+        */
+       nand = devm_kzalloc(nc->dev, sizeof(*nand) + sizeof(*nand->cs),
+                           GFP_KERNEL);
+       if (!nand)
+               return -ENOMEM;
+
+       nand->numcs = 1;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       nand->cs[0].io.virt = devm_ioremap_resource(dev, res);
+       if (IS_ERR(nand->cs[0].io.virt))
+               return PTR_ERR(nand->cs[0].io.virt);
+
+       nand->cs[0].io.dma = res->start;
+
+       /*
+        * The old driver was hardcoding the CS id to 3 for all sama5
+        * controllers. Since this id is only meaningful for the sama5
+        * controller we can safely assign this id to 3 no matter the
+        * controller.
+        * If one wants to connect a NAND to a different CS line, he will
+        * have to use the new bindings.
+        */
+       nand->cs[0].id = 3;
+
+       /* R/B GPIO. */
+       gpio = devm_gpiod_get_index_optional(dev, NULL, 0,  GPIOD_IN);
+       if (IS_ERR(gpio)) {
+               dev_err(dev, "Failed to get R/B gpio (err = %ld)\n",
+                       PTR_ERR(gpio));
+               return PTR_ERR(gpio);
+       }
+
+       if (gpio) {
+               nand->cs[0].rb.type = ATMEL_NAND_GPIO_RB;
+               nand->cs[0].rb.gpio = gpio;
+       }
+
+       /* CS GPIO. */
+       gpio = devm_gpiod_get_index_optional(dev, NULL, 1, GPIOD_OUT_HIGH);
+       if (IS_ERR(gpio)) {
+               dev_err(dev, "Failed to get CS gpio (err = %ld)\n",
+                       PTR_ERR(gpio));
+               return PTR_ERR(gpio);
+       }
+
+       nand->cs[0].csgpio = gpio;
+
+       /* Card detect GPIO. */
+       gpio = devm_gpiod_get_index_optional(nc->dev, NULL, 2, GPIOD_IN);
+       if (IS_ERR(gpio)) {
+               dev_err(dev,
+                       "Failed to get detect gpio (err = %ld)\n",
+                       PTR_ERR(gpio));
+               return PTR_ERR(gpio);
+       }
+
+       nand->cdgpio = gpio;
+
+       nand_set_flash_node(&nand->base, nc->dev->of_node);
+
+       return atmel_nand_controller_add_nand(nc, nand);
+}
+
+static int atmel_nand_controller_add_nands(struct atmel_nand_controller *nc)
+{
+       struct device_node *np, *nand_np;
+       struct device *dev = nc->dev;
+       int ret, reg_cells;
+       u32 val;
+
+       /* We do not retrieve the SMC syscon when parsing old DTs. */
+       if (nc->caps->legacy_of_bindings)
+               return atmel_nand_controller_legacy_add_nands(nc);
+
+       np = dev->of_node;
+
+       ret = of_property_read_u32(np, "#address-cells", &val);
+       if (ret) {
+               dev_err(dev, "missing #address-cells property\n");
+               return ret;
+       }
+
+       reg_cells = val;
+
+       ret = of_property_read_u32(np, "#size-cells", &val);
+       if (ret) {
+               dev_err(dev, "missing #address-cells property\n");
+               return ret;
+       }
+
+       reg_cells += val;
+
+       for_each_child_of_node(np, nand_np) {
+               struct atmel_nand *nand;
+
+               nand = atmel_nand_create(nc, nand_np, reg_cells);
+               if (IS_ERR(nand)) {
+                       ret = PTR_ERR(nand);
+                       goto err;
+               }
+
+               ret = atmel_nand_controller_add_nand(nc, nand);
+               if (ret)
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       atmel_nand_controller_remove_nands(nc);
+
+       return ret;
+}
+
+static void atmel_nand_controller_cleanup(struct atmel_nand_controller *nc)
+{
+       if (nc->dmac)
+               dma_release_channel(nc->dmac);
+
+       clk_put(nc->mck);
+}
+
+static const struct of_device_id atmel_matrix_of_ids[] = {
+       {
+               .compatible = "atmel,at91sam9260-matrix",
+               .data = (void *)AT91SAM9260_MATRIX_EBICSA,
+       },
+       {
+               .compatible = "atmel,at91sam9261-matrix",
+               .data = (void *)AT91SAM9261_MATRIX_EBICSA,
+       },
+       {
+               .compatible = "atmel,at91sam9263-matrix",
+               .data = (void *)AT91SAM9263_MATRIX_EBI0CSA,
+       },
+       {
+               .compatible = "atmel,at91sam9rl-matrix",
+               .data = (void *)AT91SAM9RL_MATRIX_EBICSA,
+       },
+       {
+               .compatible = "atmel,at91sam9g45-matrix",
+               .data = (void *)AT91SAM9G45_MATRIX_EBICSA,
+       },
+       {
+               .compatible = "atmel,at91sam9n12-matrix",
+               .data = (void *)AT91SAM9N12_MATRIX_EBICSA,
+       },
+       {
+               .compatible = "atmel,at91sam9x5-matrix",
+               .data = (void *)AT91SAM9X5_MATRIX_EBICSA,
+       },
+       { /* sentinel */ },
+};
+
+static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
+                               struct platform_device *pdev,
+                               const struct atmel_nand_controller_caps *caps)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       int ret;
+
+       nand_hw_control_init(&nc->base);
+       INIT_LIST_HEAD(&nc->chips);
+       nc->dev = dev;
+       nc->caps = caps;
+
+       platform_set_drvdata(pdev, nc);
+
+       nc->pmecc = devm_atmel_pmecc_get(dev);
+       if (IS_ERR(nc->pmecc)) {
+               ret = PTR_ERR(nc->pmecc);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "Could not get PMECC object (err = %d)\n",
+                               ret);
+               return ret;
+       }
+
+       if (nc->caps->has_dma) {
+               dma_cap_mask_t mask;
+
+               dma_cap_zero(mask);
+               dma_cap_set(DMA_MEMCPY, mask);
+
+               nc->dmac = dma_request_channel(mask, NULL, NULL);
+               if (!nc->dmac)
+                       dev_err(nc->dev, "Failed to request DMA channel\n");
+       }
+
+       /* We do not retrieve the SMC syscon when parsing old DTs. */
+       if (nc->caps->legacy_of_bindings)
+               return 0;
+
+       np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0);
+       if (!np) {
+               dev_err(dev, "Missing or invalid atmel,smc property\n");
+               return -EINVAL;
+       }
+
+       nc->smc = syscon_node_to_regmap(np);
+       of_node_put(np);
+       if (IS_ERR(nc->smc)) {
+               ret = PTR_ERR(nc->smc);
+               dev_err(dev, "Could not get SMC regmap (err = %d)\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int
+atmel_smc_nand_controller_init(struct atmel_smc_nand_controller *nc)
+{
+       struct device *dev = nc->base.dev;
+       const struct of_device_id *match;
+       struct device_node *np;
+       int ret;
+
+       /* We do not retrieve the matrix syscon when parsing old DTs. */
+       if (nc->base.caps->legacy_of_bindings)
+               return 0;
+
+       np = of_parse_phandle(dev->parent->of_node, "atmel,matrix", 0);
+       if (!np)
+               return 0;
+
+       match = of_match_node(atmel_matrix_of_ids, np);
+       if (!match) {
+               of_node_put(np);
+               return 0;
+       }
+
+       nc->matrix = syscon_node_to_regmap(np);
+       of_node_put(np);
+       if (IS_ERR(nc->matrix)) {
+               ret = PTR_ERR(nc->matrix);
+               dev_err(dev, "Could not get Matrix regmap (err = %d)\n", ret);
+               return ret;
+       }
+
+       nc->ebi_csa_offs = (unsigned int)match->data;
+
+       /*
+        * The at91sam9263 has 2 EBIs, if the NAND controller is under EBI1
+        * add 4 to ->ebi_csa_offs.
+        */
+       if (of_device_is_compatible(dev->parent->of_node,
+                                   "atmel,at91sam9263-ebi1"))
+               nc->ebi_csa_offs += 4;
+
+       return 0;
+}
+
+static int
+atmel_hsmc_nand_controller_legacy_init(struct atmel_hsmc_nand_controller *nc)
+{
+       struct regmap_config regmap_conf = {
+               .reg_bits = 32,
+               .val_bits = 32,
+               .reg_stride = 4,
+       };
+
+       struct device *dev = nc->base.dev;
+       struct device_node *nand_np, *nfc_np;
+       void __iomem *iomem;
+       struct resource res;
+       int ret;
+
+       nand_np = dev->of_node;
+       nfc_np = of_find_compatible_node(dev->of_node, NULL,
+                                        "atmel,sama5d3-nfc");
+
+       nc->clk = of_clk_get(nfc_np, 0);
+       if (IS_ERR(nc->clk)) {
+               ret = PTR_ERR(nc->clk);
+               dev_err(dev, "Failed to retrieve HSMC clock (err = %d)\n",
+                       ret);
+               goto out;
+       }
+
+       ret = clk_prepare_enable(nc->clk);
+       if (ret) {
+               dev_err(dev, "Failed to enable the HSMC clock (err = %d)\n",
+                       ret);
+               goto out;
+       }
+
+       nc->irq = of_irq_get(nand_np, 0);
+       if (nc->irq < 0) {
+               ret = nc->irq;
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "Failed to get IRQ number (err = %d)\n",
+                               ret);
+               goto out;
+       }
+
+       ret = of_address_to_resource(nfc_np, 0, &res);
+       if (ret) {
+               dev_err(dev, "Invalid or missing NFC IO resource (err = %d)\n",
+                       ret);
+               goto out;
+       }
+
+       iomem = devm_ioremap_resource(dev, &res);
+       if (IS_ERR(iomem)) {
+               ret = PTR_ERR(iomem);
+               goto out;
+       }
+
+       regmap_conf.name = "nfc-io";
+       regmap_conf.max_register = resource_size(&res) - 4;
+       nc->io = devm_regmap_init_mmio(dev, iomem, &regmap_conf);
+       if (IS_ERR(nc->io)) {
+               ret = PTR_ERR(nc->io);
+               dev_err(dev, "Could not create NFC IO regmap (err = %d)\n",
+                       ret);
+               goto out;
+       }
+
+       ret = of_address_to_resource(nfc_np, 1, &res);
+       if (ret) {
+               dev_err(dev, "Invalid or missing HSMC resource (err = %d)\n",
+                       ret);
+               goto out;
+       }
+
+       iomem = devm_ioremap_resource(dev, &res);
+       if (IS_ERR(iomem)) {
+               ret = PTR_ERR(iomem);
+               goto out;
+       }
+
+       regmap_conf.name = "smc";
+       regmap_conf.max_register = resource_size(&res) - 4;
+       nc->base.smc = devm_regmap_init_mmio(dev, iomem, &regmap_conf);
+       if (IS_ERR(nc->base.smc)) {
+               ret = PTR_ERR(nc->base.smc);
+               dev_err(dev, "Could not create NFC IO regmap (err = %d)\n",
+                       ret);
+               goto out;
+       }
+
+       ret = of_address_to_resource(nfc_np, 2, &res);
+       if (ret) {
+               dev_err(dev, "Invalid or missing SRAM resource (err = %d)\n",
+                       ret);
+               goto out;
+       }
+
+       nc->sram.virt = devm_ioremap_resource(dev, &res);
+       if (IS_ERR(nc->sram.virt)) {
+               ret = PTR_ERR(nc->sram.virt);
+               goto out;
+       }
+
+       nc->sram.dma = res.start;
+
+out:
+       of_node_put(nfc_np);
+
+       return ret;
+}
+
+static int
+atmel_hsmc_nand_controller_init(struct atmel_hsmc_nand_controller *nc)
+{
+       struct device *dev = nc->base.dev;
+       struct device_node *np;
+       int ret;
+
+       np = of_parse_phandle(dev->parent->of_node, "atmel,smc", 0);
+       if (!np) {
+               dev_err(dev, "Missing or invalid atmel,smc property\n");
+               return -EINVAL;
+       }
+
+       nc->irq = of_irq_get(np, 0);
+       of_node_put(np);
+       if (nc->irq < 0) {
+               if (nc->irq != -EPROBE_DEFER)
+                       dev_err(dev, "Failed to get IRQ number (err = %d)\n",
+                               nc->irq);
+               return nc->irq;
+       }
+
+       np = of_parse_phandle(dev->of_node, "atmel,nfc-io", 0);
+       if (!np) {
+               dev_err(dev, "Missing or invalid atmel,nfc-io property\n");
+               return -EINVAL;
+       }
+
+       nc->io = syscon_node_to_regmap(np);
+       of_node_put(np);
+       if (IS_ERR(nc->io)) {
+               ret = PTR_ERR(nc->io);
+               dev_err(dev, "Could not get NFC IO regmap (err = %d)\n", ret);
+               return ret;
+       }
+
+       nc->sram.pool = of_gen_pool_get(nc->base.dev->of_node,
+                                        "atmel,nfc-sram", 0);
+       if (!nc->sram.pool) {
+               dev_err(nc->base.dev, "Missing SRAM\n");
+               return -ENOMEM;
+       }
+
+       nc->sram.virt = gen_pool_dma_alloc(nc->sram.pool,
+                                           ATMEL_NFC_SRAM_SIZE,
+                                           &nc->sram.dma);
+       if (!nc->sram.virt) {
+               dev_err(nc->base.dev,
+                       "Could not allocate memory from the NFC SRAM pool\n");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static int
+atmel_hsmc_nand_controller_remove(struct atmel_nand_controller *nc)
+{
+       struct atmel_hsmc_nand_controller *hsmc_nc;
+       int ret;
+
+       ret = atmel_nand_controller_remove_nands(nc);
+       if (ret)
+               return ret;
+
+       hsmc_nc = container_of(nc, struct atmel_hsmc_nand_controller, base);
+       if (hsmc_nc->sram.pool)
+               gen_pool_free(hsmc_nc->sram.pool,
+                             (unsigned long)hsmc_nc->sram.virt,
+                             ATMEL_NFC_SRAM_SIZE);
+
+       if (hsmc_nc->clk) {
+               clk_disable_unprepare(hsmc_nc->clk);
+               clk_put(hsmc_nc->clk);
+       }
+
+       atmel_nand_controller_cleanup(nc);
+
+       return 0;
+}
+
+static int atmel_hsmc_nand_controller_probe(struct platform_device *pdev,
+                               const struct atmel_nand_controller_caps *caps)
+{
+       struct device *dev = &pdev->dev;
+       struct atmel_hsmc_nand_controller *nc;
+       int ret;
+
+       nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL);
+       if (!nc)
+               return -ENOMEM;
+
+       ret = atmel_nand_controller_init(&nc->base, pdev, caps);
+       if (ret)
+               return ret;
+
+       if (caps->legacy_of_bindings)
+               ret = atmel_hsmc_nand_controller_legacy_init(nc);
+       else
+               ret = atmel_hsmc_nand_controller_init(nc);
+
+       if (ret)
+               return ret;
+
+       /* Make sure all irqs are masked before registering our IRQ handler. */
+       regmap_write(nc->base.smc, ATMEL_HSMC_NFC_IDR, 0xffffffff);
+       ret = devm_request_irq(dev, nc->irq, atmel_nfc_interrupt,
+                              IRQF_SHARED, "nfc", nc);
+       if (ret) {
+               dev_err(dev,
+                       "Could not get register NFC interrupt handler (err = %d)\n",
+                       ret);
+               goto err;
+       }
+
+       /* Initial NFC configuration. */
+       regmap_write(nc->base.smc, ATMEL_HSMC_NFC_CFG,
+                    ATMEL_HSMC_NFC_CFG_DTO_MAX);
+
+       ret = atmel_nand_controller_add_nands(&nc->base);
+       if (ret)
+               goto err;
+
+       return 0;
+
+err:
+       atmel_hsmc_nand_controller_remove(&nc->base);
+
+       return ret;
+}
+
+static const struct atmel_nand_controller_ops atmel_hsmc_nc_ops = {
+       .probe = atmel_hsmc_nand_controller_probe,
+       .remove = atmel_hsmc_nand_controller_remove,
+       .ecc_init = atmel_hsmc_nand_ecc_init,
+       .nand_init = atmel_hsmc_nand_init,
+};
+
+static const struct atmel_nand_controller_caps atmel_sama5_nc_caps = {
+       .has_dma = true,
+       .ale_offs = BIT(21),
+       .cle_offs = BIT(22),
+       .ops = &atmel_hsmc_nc_ops,
+};
+
+/* Only used to parse old bindings. */
+static const struct atmel_nand_controller_caps atmel_sama5_nand_caps = {
+       .has_dma = true,
+       .ale_offs = BIT(21),
+       .cle_offs = BIT(22),
+       .ops = &atmel_hsmc_nc_ops,
+       .legacy_of_bindings = true,
+};
+
+static int atmel_smc_nand_controller_probe(struct platform_device *pdev,
+                               const struct atmel_nand_controller_caps *caps)
+{
+       struct device *dev = &pdev->dev;
+       struct atmel_smc_nand_controller *nc;
+       int ret;
+
+       nc = devm_kzalloc(dev, sizeof(*nc), GFP_KERNEL);
+       if (!nc)
+               return -ENOMEM;
+
+       ret = atmel_nand_controller_init(&nc->base, pdev, caps);
+       if (ret)
+               return ret;
+
+       ret = atmel_smc_nand_controller_init(nc);
+       if (ret)
+               return ret;
+
+       return atmel_nand_controller_add_nands(&nc->base);
+}
+
+static int
+atmel_smc_nand_controller_remove(struct atmel_nand_controller *nc)
+{
+       int ret;
+
+       ret = atmel_nand_controller_remove_nands(nc);
+       if (ret)
+               return ret;
+
+       atmel_nand_controller_cleanup(nc);
+
+       return 0;
+}
+
+static const struct atmel_nand_controller_ops atmel_smc_nc_ops = {
+       .probe = atmel_smc_nand_controller_probe,
+       .remove = atmel_smc_nand_controller_remove,
+       .ecc_init = atmel_nand_ecc_init,
+       .nand_init = atmel_smc_nand_init,
+};
+
+static const struct atmel_nand_controller_caps atmel_rm9200_nc_caps = {
+       .ale_offs = BIT(21),
+       .cle_offs = BIT(22),
+       .ops = &atmel_smc_nc_ops,
+};
+
+static const struct atmel_nand_controller_caps atmel_sam9261_nc_caps = {
+       .ale_offs = BIT(22),
+       .cle_offs = BIT(21),
+       .ops = &atmel_smc_nc_ops,
+};
+
+static const struct atmel_nand_controller_caps atmel_sam9g45_nc_caps = {
+       .has_dma = true,
+       .ale_offs = BIT(21),
+       .cle_offs = BIT(22),
+       .ops = &atmel_smc_nc_ops,
+};
+
+/* Only used to parse old bindings. */
+static const struct atmel_nand_controller_caps atmel_rm9200_nand_caps = {
+       .ale_offs = BIT(21),
+       .cle_offs = BIT(22),
+       .ops = &atmel_smc_nc_ops,
+       .legacy_of_bindings = true,
+};
+
+static const struct atmel_nand_controller_caps atmel_sam9261_nand_caps = {
+       .ale_offs = BIT(22),
+       .cle_offs = BIT(21),
+       .ops = &atmel_smc_nc_ops,
+       .legacy_of_bindings = true,
+};
+
+static const struct atmel_nand_controller_caps atmel_sam9g45_nand_caps = {
+       .has_dma = true,
+       .ale_offs = BIT(21),
+       .cle_offs = BIT(22),
+       .ops = &atmel_smc_nc_ops,
+       .legacy_of_bindings = true,
+};
+
+static const struct of_device_id atmel_nand_controller_of_ids[] = {
+       {
+               .compatible = "atmel,at91rm9200-nand-controller",
+               .data = &atmel_rm9200_nc_caps,
+       },
+       {
+               .compatible = "atmel,at91sam9260-nand-controller",
+               .data = &atmel_rm9200_nc_caps,
+       },
+       {
+               .compatible = "atmel,at91sam9261-nand-controller",
+               .data = &atmel_sam9261_nc_caps,
+       },
+       {
+               .compatible = "atmel,at91sam9g45-nand-controller",
+               .data = &atmel_sam9g45_nc_caps,
+       },
+       {
+               .compatible = "atmel,sama5d3-nand-controller",
+               .data = &atmel_sama5_nc_caps,
+       },
+       /* Support for old/deprecated bindings: */
+       {
+               .compatible = "atmel,at91rm9200-nand",
+               .data = &atmel_rm9200_nand_caps,
+       },
+       {
+               .compatible = "atmel,sama5d4-nand",
+               .data = &atmel_rm9200_nand_caps,
+       },
+       {
+               .compatible = "atmel,sama5d2-nand",
+               .data = &atmel_rm9200_nand_caps,
+       },
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, atmel_nand_controller_of_ids);
+
+static int atmel_nand_controller_probe(struct platform_device *pdev)
+{
+       const struct atmel_nand_controller_caps *caps;
+
+       if (pdev->id_entry)
+               caps = (void *)pdev->id_entry->driver_data;
+       else
+               caps = of_device_get_match_data(&pdev->dev);
+
+       if (!caps) {
+               dev_err(&pdev->dev, "Could not retrieve NFC caps\n");
+               return -EINVAL;
+       }
+
+       if (caps->legacy_of_bindings) {
+               u32 ale_offs = 21;
+
+               /*
+                * If we are parsing legacy DT props and the DT contains a
+                * valid NFC node, forward the request to the sama5 logic.
+                */
+               if (of_find_compatible_node(pdev->dev.of_node, NULL,
+                                           "atmel,sama5d3-nfc"))
+                       caps = &atmel_sama5_nand_caps;
+
+               /*
+                * Even if the compatible says we are dealing with an
+                * at91rm9200 controller, the atmel,nand-has-dma specify that
+                * this controller supports DMA, which means we are in fact
+                * dealing with an at91sam9g45+ controller.
+                */
+               if (!caps->has_dma &&
+                   of_property_read_bool(pdev->dev.of_node,
+                                         "atmel,nand-has-dma"))
+                       caps = &atmel_sam9g45_nand_caps;
+
+               /*
+                * All SoCs except the at91sam9261 are assigning ALE to A21 and
+                * CLE to A22. If atmel,nand-addr-offset != 21 this means we're
+                * actually dealing with an at91sam9261 controller.
+                */
+               of_property_read_u32(pdev->dev.of_node,
+                                    "atmel,nand-addr-offset", &ale_offs);
+               if (ale_offs != 21)
+                       caps = &atmel_sam9261_nand_caps;
+       }
+
+       return caps->ops->probe(pdev, caps);
+}
+
+static int atmel_nand_controller_remove(struct platform_device *pdev)
+{
+       struct atmel_nand_controller *nc = platform_get_drvdata(pdev);
+
+       return nc->caps->ops->remove(nc);
+}
+
+static struct platform_driver atmel_nand_controller_driver = {
+       .driver = {
+               .name = "atmel-nand-controller",
+               .of_match_table = of_match_ptr(atmel_nand_controller_of_ids),
+       },
+       .probe = atmel_nand_controller_probe,
+       .remove = atmel_nand_controller_remove,
+};
+module_platform_driver(atmel_nand_controller_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("NAND Flash Controller driver for Atmel SoCs");
+MODULE_ALIAS("platform:atmel-nand-controller");
diff --git a/drivers/mtd/nand/atmel/pmecc.c b/drivers/mtd/nand/atmel/pmecc.c
new file mode 100644 (file)
index 0000000..55a8ee5
--- /dev/null
@@ -0,0 +1,1020 @@
+/*
+ * Copyright 2017 ATMEL
+ * Copyright 2017 Free Electrons
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ *
+ * Derived from the atmel_nand.c driver which contained the following
+ * copyrights:
+ *
+ *   Copyright 2003 Rick Bronson
+ *
+ *   Derived from drivers/mtd/nand/autcpu12.c
+ *     Copyright 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ *   Derived from drivers/mtd/spia.c
+ *     Copyright 2000 Steven J. Hill (sjhill@cotw.com)
+ *
+ *   Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
+ *     Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright 2007
+ *
+ *   Derived from Das U-Boot source code
+ *     (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
+ *      Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
+ *
+ *   Add Programmable Multibit ECC support for various AT91 SoC
+ *     Copyright 2012 ATMEL, Hong Xu
+ *
+ *   Add Nand Flash Controller support for SAMA5 SoC
+ *     Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * The PMECC is an hardware assisted BCH engine, which means part of the
+ * ECC algorithm is left to the software. The hardware/software repartition
+ * is explained in the "PMECC Controller Functional Description" chapter in
+ * Atmel datasheets, and some of the functions in this file are directly
+ * implementing the algorithms described in the "Software Implementation"
+ * sub-section.
+ *
+ * TODO: it seems that the software BCH implementation in lib/bch.c is already
+ * providing some of the logic we are implementing here. It would be smart
+ * to expose the needed lib/bch.c helpers/functions and re-use them here.
+ */
+
+#include <linux/genalloc.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mtd/nand.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "pmecc.h"
+
+/* Galois field dimension */
+#define PMECC_GF_DIMENSION_13                  13
+#define PMECC_GF_DIMENSION_14                  14
+
+/* Primitive Polynomial used by PMECC */
+#define PMECC_GF_13_PRIMITIVE_POLY             0x201b
+#define PMECC_GF_14_PRIMITIVE_POLY             0x4443
+
+#define PMECC_LOOKUP_TABLE_SIZE_512            0x2000
+#define PMECC_LOOKUP_TABLE_SIZE_1024           0x4000
+
+/* Time out value for reading PMECC status register */
+#define PMECC_MAX_TIMEOUT_MS                   100
+
+/* PMECC Register Definitions */
+#define ATMEL_PMECC_CFG                                0x0
+#define PMECC_CFG_BCH_STRENGTH(x)              (x)
+#define PMECC_CFG_BCH_STRENGTH_MASK            GENMASK(2, 0)
+#define PMECC_CFG_SECTOR512                    (0 << 4)
+#define PMECC_CFG_SECTOR1024                   (1 << 4)
+#define PMECC_CFG_NSECTORS(x)                  ((fls(x) - 1) << 8)
+#define PMECC_CFG_READ_OP                      (0 << 12)
+#define PMECC_CFG_WRITE_OP                     (1 << 12)
+#define PMECC_CFG_SPARE_ENABLE                 BIT(16)
+#define PMECC_CFG_AUTO_ENABLE                  BIT(20)
+
+#define ATMEL_PMECC_SAREA                      0x4
+#define ATMEL_PMECC_SADDR                      0x8
+#define ATMEL_PMECC_EADDR                      0xc
+
+#define ATMEL_PMECC_CLK                                0x10
+#define PMECC_CLK_133MHZ                       (2 << 0)
+
+#define ATMEL_PMECC_CTRL                       0x14
+#define PMECC_CTRL_RST                         BIT(0)
+#define PMECC_CTRL_DATA                                BIT(1)
+#define PMECC_CTRL_USER                                BIT(2)
+#define PMECC_CTRL_ENABLE                      BIT(4)
+#define PMECC_CTRL_DISABLE                     BIT(5)
+
+#define ATMEL_PMECC_SR                         0x18
+#define PMECC_SR_BUSY                          BIT(0)
+#define PMECC_SR_ENABLE                                BIT(4)
+
+#define ATMEL_PMECC_IER                                0x1c
+#define ATMEL_PMECC_IDR                                0x20
+#define ATMEL_PMECC_IMR                                0x24
+#define ATMEL_PMECC_ISR                                0x28
+#define PMECC_ERROR_INT                                BIT(0)
+
+#define ATMEL_PMECC_ECC(sector, n)             \
+       ((((sector) + 1) * 0x40) + (n))
+
+#define ATMEL_PMECC_REM(sector, n)             \
+       ((((sector) + 1) * 0x40) + ((n) * 4) + 0x200)
+
+/* PMERRLOC Register Definitions */
+#define ATMEL_PMERRLOC_ELCFG                   0x0
+#define PMERRLOC_ELCFG_SECTOR_512              (0 << 0)
+#define PMERRLOC_ELCFG_SECTOR_1024             (1 << 0)
+#define PMERRLOC_ELCFG_NUM_ERRORS(n)           ((n) << 16)
+
+#define ATMEL_PMERRLOC_ELPRIM                  0x4
+#define ATMEL_PMERRLOC_ELEN                    0x8
+#define ATMEL_PMERRLOC_ELDIS                   0xc
+#define PMERRLOC_DISABLE                       BIT(0)
+
+#define ATMEL_PMERRLOC_ELSR                    0x10
+#define PMERRLOC_ELSR_BUSY                     BIT(0)
+
+#define ATMEL_PMERRLOC_ELIER                   0x14
+#define ATMEL_PMERRLOC_ELIDR                   0x18
+#define ATMEL_PMERRLOC_ELIMR                   0x1c
+#define ATMEL_PMERRLOC_ELISR                   0x20
+#define PMERRLOC_ERR_NUM_MASK                  GENMASK(12, 8)
+#define PMERRLOC_CALC_DONE                     BIT(0)
+
+#define ATMEL_PMERRLOC_SIGMA(x)                        (((x) * 0x4) + 0x28)
+
+#define ATMEL_PMERRLOC_EL(offs, x)             (((x) * 0x4) + (offs))
+
+struct atmel_pmecc_gf_tables {
+       u16 *alpha_to;
+       u16 *index_of;
+};
+
+struct atmel_pmecc_caps {
+       const int *strengths;
+       int nstrengths;
+       int el_offset;
+       bool correct_erased_chunks;
+};
+
+struct atmel_pmecc {
+       struct device *dev;
+       const struct atmel_pmecc_caps *caps;
+
+       struct {
+               void __iomem *base;
+               void __iomem *errloc;
+       } regs;
+
+       struct mutex lock;
+};
+
+struct atmel_pmecc_user_conf_cache {
+       u32 cfg;
+       u32 sarea;
+       u32 saddr;
+       u32 eaddr;
+};
+
+struct atmel_pmecc_user {
+       struct atmel_pmecc_user_conf_cache cache;
+       struct atmel_pmecc *pmecc;
+       const struct atmel_pmecc_gf_tables *gf_tables;
+       int eccbytes;
+       s16 *partial_syn;
+       s16 *si;
+       s16 *lmu;
+       s16 *smu;
+       s32 *mu;
+       s32 *dmu;
+       s32 *delta;
+       u32 isr;
+};
+
+static DEFINE_MUTEX(pmecc_gf_tables_lock);
+static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_512;
+static const struct atmel_pmecc_gf_tables *pmecc_gf_tables_1024;
+
+static inline int deg(unsigned int poly)
+{
+       /* polynomial degree is the most-significant bit index */
+       return fls(poly) - 1;
+}
+
+static int atmel_pmecc_build_gf_tables(int mm, unsigned int poly,
+                                      struct atmel_pmecc_gf_tables *gf_tables)
+{
+       unsigned int i, x = 1;
+       const unsigned int k = BIT(deg(poly));
+       unsigned int nn = BIT(mm) - 1;
+
+       /* primitive polynomial must be of degree m */
+       if (k != (1u << mm))
+               return -EINVAL;
+
+       for (i = 0; i < nn; i++) {
+               gf_tables->alpha_to[i] = x;
+               gf_tables->index_of[x] = i;
+               if (i && (x == 1))
+                       /* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */
+                       return -EINVAL;
+               x <<= 1;
+               if (x & k)
+                       x ^= poly;
+       }
+       gf_tables->alpha_to[nn] = 1;
+       gf_tables->index_of[0] = 0;
+
+       return 0;
+}
+
+static const struct atmel_pmecc_gf_tables *
+atmel_pmecc_create_gf_tables(const struct atmel_pmecc_user_req *req)
+{
+       struct atmel_pmecc_gf_tables *gf_tables;
+       unsigned int poly, degree, table_size;
+       int ret;
+
+       if (req->ecc.sectorsize == 512) {
+               degree = PMECC_GF_DIMENSION_13;
+               poly = PMECC_GF_13_PRIMITIVE_POLY;
+               table_size = PMECC_LOOKUP_TABLE_SIZE_512;
+       } else {
+               degree = PMECC_GF_DIMENSION_14;
+               poly = PMECC_GF_14_PRIMITIVE_POLY;
+               table_size = PMECC_LOOKUP_TABLE_SIZE_1024;
+       }
+
+       gf_tables = kzalloc(sizeof(*gf_tables) +
+                           (2 * table_size * sizeof(u16)),
+                           GFP_KERNEL);
+       if (!gf_tables)
+               return ERR_PTR(-ENOMEM);
+
+       gf_tables->alpha_to = (void *)(gf_tables + 1);
+       gf_tables->index_of = gf_tables->alpha_to + table_size;
+
+       ret = atmel_pmecc_build_gf_tables(degree, poly, gf_tables);
+       if (ret) {
+               kfree(gf_tables);
+               return ERR_PTR(ret);
+       }
+
+       return gf_tables;
+}
+
+static const struct atmel_pmecc_gf_tables *
+atmel_pmecc_get_gf_tables(const struct atmel_pmecc_user_req *req)
+{
+       const struct atmel_pmecc_gf_tables **gf_tables, *ret;
+
+       mutex_lock(&pmecc_gf_tables_lock);
+       if (req->ecc.sectorsize == 512)
+               gf_tables = &pmecc_gf_tables_512;
+       else
+               gf_tables = &pmecc_gf_tables_1024;
+
+       ret = *gf_tables;
+
+       if (!ret) {
+               ret = atmel_pmecc_create_gf_tables(req);
+               if (!IS_ERR(ret))
+                       *gf_tables = ret;
+       }
+       mutex_unlock(&pmecc_gf_tables_lock);
+
+       return ret;
+}
+
+static int atmel_pmecc_prepare_user_req(struct atmel_pmecc *pmecc,
+                                       struct atmel_pmecc_user_req *req)
+{
+       int i, max_eccbytes, eccbytes = 0, eccstrength = 0;
+
+       if (req->pagesize <= 0 || req->oobsize <= 0 || req->ecc.bytes <= 0)
+               return -EINVAL;
+
+       if (req->ecc.ooboffset >= 0 &&
+           req->ecc.ooboffset + req->ecc.bytes > req->oobsize)
+               return -EINVAL;
+
+       if (req->ecc.sectorsize == ATMEL_PMECC_SECTOR_SIZE_AUTO) {
+               if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH)
+                       return -EINVAL;
+
+               if (req->pagesize > 512)
+                       req->ecc.sectorsize = 1024;
+               else
+                       req->ecc.sectorsize = 512;
+       }
+
+       if (req->ecc.sectorsize != 512 && req->ecc.sectorsize != 1024)
+               return -EINVAL;
+
+       if (req->pagesize % req->ecc.sectorsize)
+               return -EINVAL;
+
+       req->ecc.nsectors = req->pagesize / req->ecc.sectorsize;
+
+       max_eccbytes = req->ecc.bytes;
+
+       for (i = 0; i < pmecc->caps->nstrengths; i++) {
+               int nbytes, strength = pmecc->caps->strengths[i];
+
+               if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH &&
+                   strength < req->ecc.strength)
+                       continue;
+
+               nbytes = DIV_ROUND_UP(strength * fls(8 * req->ecc.sectorsize),
+                                     8);
+               nbytes *= req->ecc.nsectors;
+
+               if (nbytes > max_eccbytes)
+                       break;
+
+               eccstrength = strength;
+               eccbytes = nbytes;
+
+               if (req->ecc.strength != ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH)
+                       break;
+       }
+
+       if (!eccstrength)
+               return -EINVAL;
+
+       req->ecc.bytes = eccbytes;
+       req->ecc.strength = eccstrength;
+
+       if (req->ecc.ooboffset < 0)
+               req->ecc.ooboffset = req->oobsize - eccbytes;
+
+       return 0;
+}
+
+struct atmel_pmecc_user *
+atmel_pmecc_create_user(struct atmel_pmecc *pmecc,
+                       struct atmel_pmecc_user_req *req)
+{
+       struct atmel_pmecc_user *user;
+       const struct atmel_pmecc_gf_tables *gf_tables;
+       int strength, size, ret;
+
+       ret = atmel_pmecc_prepare_user_req(pmecc, req);
+       if (ret)
+               return ERR_PTR(ret);
+
+       size = sizeof(*user);
+       size = ALIGN(size, sizeof(u16));
+       /* Reserve space for partial_syn, si and smu */
+       size += ((2 * req->ecc.strength) + 1) * sizeof(u16) *
+               (2 + req->ecc.strength + 2);
+       /* Reserve space for lmu. */
+       size += (req->ecc.strength + 1) * sizeof(u16);
+       /* Reserve space for mu, dmu and delta. */
+       size = ALIGN(size, sizeof(s32));
+       size += (req->ecc.strength + 1) * sizeof(s32);
+
+       user = kzalloc(size, GFP_KERNEL);
+       if (!user)
+               return ERR_PTR(-ENOMEM);
+
+       user->pmecc = pmecc;
+
+       user->partial_syn = (s16 *)PTR_ALIGN(user + 1, sizeof(u16));
+       user->si = user->partial_syn + ((2 * req->ecc.strength) + 1);
+       user->lmu = user->si + ((2 * req->ecc.strength) + 1);
+       user->smu = user->lmu + (req->ecc.strength + 1);
+       user->mu = (s32 *)PTR_ALIGN(user->smu +
+                                   (((2 * req->ecc.strength) + 1) *
+                                    (req->ecc.strength + 2)),
+                                   sizeof(s32));
+       user->dmu = user->mu + req->ecc.strength + 1;
+       user->delta = user->dmu + req->ecc.strength + 1;
+
+       gf_tables = atmel_pmecc_get_gf_tables(req);
+       if (IS_ERR(gf_tables)) {
+               kfree(user);
+               return ERR_CAST(gf_tables);
+       }
+
+       user->gf_tables = gf_tables;
+
+       user->eccbytes = req->ecc.bytes / req->ecc.nsectors;
+
+       for (strength = 0; strength < pmecc->caps->nstrengths; strength++) {
+               if (pmecc->caps->strengths[strength] == req->ecc.strength)
+                       break;
+       }
+
+       user->cache.cfg = PMECC_CFG_BCH_STRENGTH(strength) |
+                         PMECC_CFG_NSECTORS(req->ecc.nsectors);
+
+       if (req->ecc.sectorsize == 1024)
+               user->cache.cfg |= PMECC_CFG_SECTOR1024;
+
+       user->cache.sarea = req->oobsize - 1;
+       user->cache.saddr = req->ecc.ooboffset;
+       user->cache.eaddr = req->ecc.ooboffset + req->ecc.bytes - 1;
+
+       return user;
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_create_user);
+
+void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user)
+{
+       kfree(user);
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_destroy_user);
+
+static int get_strength(struct atmel_pmecc_user *user)
+{
+       const int *strengths = user->pmecc->caps->strengths;
+
+       return strengths[user->cache.cfg & PMECC_CFG_BCH_STRENGTH_MASK];
+}
+
+static int get_sectorsize(struct atmel_pmecc_user *user)
+{
+       return user->cache.cfg & PMECC_LOOKUP_TABLE_SIZE_1024 ? 1024 : 512;
+}
+
+static void atmel_pmecc_gen_syndrome(struct atmel_pmecc_user *user, int sector)
+{
+       int strength = get_strength(user);
+       u32 value;
+       int i;
+
+       /* Fill odd syndromes */
+       for (i = 0; i < strength; i++) {
+               value = readl_relaxed(user->pmecc->regs.base +
+                                     ATMEL_PMECC_REM(sector, i / 2));
+               if (i & 1)
+                       value >>= 16;
+
+               user->partial_syn[(2 * i) + 1] = value;
+       }
+}
+
+static void atmel_pmecc_substitute(struct atmel_pmecc_user *user)
+{
+       int degree = get_sectorsize(user) == 512 ? 13 : 14;
+       int cw_len = BIT(degree) - 1;
+       int strength = get_strength(user);
+       s16 *alpha_to = user->gf_tables->alpha_to;
+       s16 *index_of = user->gf_tables->index_of;
+       s16 *partial_syn = user->partial_syn;
+       s16 *si;
+       int i, j;
+
+       /*
+        * si[] is a table that holds the current syndrome value,
+        * an element of that table belongs to the field
+        */
+       si = user->si;
+
+       memset(&si[1], 0, sizeof(s16) * ((2 * strength) - 1));
+
+       /* Computation 2t syndromes based on S(x) */
+       /* Odd syndromes */
+       for (i = 1; i < 2 * strength; i += 2) {
+               for (j = 0; j < degree; j++) {
+                       if (partial_syn[i] & BIT(j))
+                               si[i] = alpha_to[i * j] ^ si[i];
+               }
+       }
+       /* Even syndrome = (Odd syndrome) ** 2 */
+       for (i = 2, j = 1; j <= strength; i = ++j << 1) {
+               if (si[j] == 0) {
+                       si[i] = 0;
+               } else {
+                       s16 tmp;
+
+                       tmp = index_of[si[j]];
+                       tmp = (tmp * 2) % cw_len;
+                       si[i] = alpha_to[tmp];
+               }
+       }
+}
+
+static void atmel_pmecc_get_sigma(struct atmel_pmecc_user *user)
+{
+       s16 *lmu = user->lmu;
+       s16 *si = user->si;
+       s32 *mu = user->mu;
+       s32 *dmu = user->dmu;
+       s32 *delta = user->delta;
+       int degree = get_sectorsize(user) == 512 ? 13 : 14;
+       int cw_len = BIT(degree) - 1;
+       int strength = get_strength(user);
+       int num = 2 * strength + 1;
+       s16 *index_of = user->gf_tables->index_of;
+       s16 *alpha_to = user->gf_tables->alpha_to;
+       int i, j, k;
+       u32 dmu_0_count, tmp;
+       s16 *smu = user->smu;
+
+       /* index of largest delta */
+       int ro;
+       int largest;
+       int diff;
+
+       dmu_0_count = 0;
+
+       /* First Row */
+
+       /* Mu */
+       mu[0] = -1;
+
+       memset(smu, 0, sizeof(s16) * num);
+       smu[0] = 1;
+
+       /* discrepancy set to 1 */
+       dmu[0] = 1;
+       /* polynom order set to 0 */
+       lmu[0] = 0;
+       delta[0] = (mu[0] * 2 - lmu[0]) >> 1;
+
+       /* Second Row */
+
+       /* Mu */
+       mu[1] = 0;
+       /* Sigma(x) set to 1 */
+       memset(&smu[num], 0, sizeof(s16) * num);
+       smu[num] = 1;
+
+       /* discrepancy set to S1 */
+       dmu[1] = si[1];
+
+       /* polynom order set to 0 */
+       lmu[1] = 0;
+
+       delta[1] = (mu[1] * 2 - lmu[1]) >> 1;
+
+       /* Init the Sigma(x) last row */
+       memset(&smu[(strength + 1) * num], 0, sizeof(s16) * num);
+
+       for (i = 1; i <= strength; i++) {
+               mu[i + 1] = i << 1;
+               /* Begin Computing Sigma (Mu+1) and L(mu) */
+               /* check if discrepancy is set to 0 */
+               if (dmu[i] == 0) {
+                       dmu_0_count++;
+
+                       tmp = ((strength - (lmu[i] >> 1) - 1) / 2);
+                       if ((strength - (lmu[i] >> 1) - 1) & 0x1)
+                               tmp += 2;
+                       else
+                               tmp += 1;
+
+                       if (dmu_0_count == tmp) {
+                               for (j = 0; j <= (lmu[i] >> 1) + 1; j++)
+                                       smu[(strength + 1) * num + j] =
+                                                       smu[i * num + j];
+
+                               lmu[strength + 1] = lmu[i];
+                               return;
+                       }
+
+                       /* copy polynom */
+                       for (j = 0; j <= lmu[i] >> 1; j++)
+                               smu[(i + 1) * num + j] = smu[i * num + j];
+
+                       /* copy previous polynom order to the next */
+                       lmu[i + 1] = lmu[i];
+               } else {
+                       ro = 0;
+                       largest = -1;
+                       /* find largest delta with dmu != 0 */
+                       for (j = 0; j < i; j++) {
+                               if ((dmu[j]) && (delta[j] > largest)) {
+                                       largest = delta[j];
+                                       ro = j;
+                               }
+                       }
+
+                       /* compute difference */
+                       diff = (mu[i] - mu[ro]);
+
+                       /* Compute degree of the new smu polynomial */
+                       if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff))
+                               lmu[i + 1] = lmu[i];
+                       else
+                               lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2;
+
+                       /* Init smu[i+1] with 0 */
+                       for (k = 0; k < num; k++)
+                               smu[(i + 1) * num + k] = 0;
+
+                       /* Compute smu[i+1] */
+                       for (k = 0; k <= lmu[ro] >> 1; k++) {
+                               s16 a, b, c;
+
+                               if (!(smu[ro * num + k] && dmu[i]))
+                                       continue;
+
+                               a = index_of[dmu[i]];
+                               b = index_of[dmu[ro]];
+                               c = index_of[smu[ro * num + k]];
+                               tmp = a + (cw_len - b) + c;
+                               a = alpha_to[tmp % cw_len];
+                               smu[(i + 1) * num + (k + diff)] = a;
+                       }
+
+                       for (k = 0; k <= lmu[i] >> 1; k++)
+                               smu[(i + 1) * num + k] ^= smu[i * num + k];
+               }
+
+               /* End Computing Sigma (Mu+1) and L(mu) */
+               /* In either case compute delta */
+               delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1;
+
+               /* Do not compute discrepancy for the last iteration */
+               if (i >= strength)
+                       continue;
+
+               for (k = 0; k <= (lmu[i + 1] >> 1); k++) {
+                       tmp = 2 * (i - 1);
+                       if (k == 0) {
+                               dmu[i + 1] = si[tmp + 3];
+                       } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) {
+                               s16 a, b, c;
+
+                               a = index_of[smu[(i + 1) * num + k]];
+                               b = si[2 * (i - 1) + 3 - k];
+                               c = index_of[b];
+                               tmp = a + c;
+                               tmp %= cw_len;
+                               dmu[i + 1] = alpha_to[tmp] ^ dmu[i + 1];
+                       }
+               }
+       }
+}
+
+static int atmel_pmecc_err_location(struct atmel_pmecc_user *user)
+{
+       int sector_size = get_sectorsize(user);
+       int degree = sector_size == 512 ? 13 : 14;
+       struct atmel_pmecc *pmecc = user->pmecc;
+       int strength = get_strength(user);
+       int ret, roots_nbr, i, err_nbr = 0;
+       int num = (2 * strength) + 1;
+       s16 *smu = user->smu;
+       u32 val;
+
+       writel(PMERRLOC_DISABLE, pmecc->regs.errloc + ATMEL_PMERRLOC_ELDIS);
+
+       for (i = 0; i <= user->lmu[strength + 1] >> 1; i++) {
+               writel_relaxed(smu[(strength + 1) * num + i],
+                              pmecc->regs.errloc + ATMEL_PMERRLOC_SIGMA(i));
+               err_nbr++;
+       }
+
+       val = (err_nbr - 1) << 16;
+       if (sector_size == 1024)
+               val |= 1;
+
+       writel(val, pmecc->regs.errloc + ATMEL_PMERRLOC_ELCFG);
+       writel((sector_size * 8) + (degree * strength),
+              pmecc->regs.errloc + ATMEL_PMERRLOC_ELEN);
+
+       ret = readl_relaxed_poll_timeout(pmecc->regs.errloc +
+                                        ATMEL_PMERRLOC_ELISR,
+                                        val, val & PMERRLOC_CALC_DONE, 0,
+                                        PMECC_MAX_TIMEOUT_MS * 1000);
+       if (ret) {
+               dev_err(pmecc->dev,
+                       "PMECC: Timeout to calculate error location.\n");
+               return ret;
+       }
+
+       roots_nbr = (val & PMERRLOC_ERR_NUM_MASK) >> 8;
+       /* Number of roots == degree of smu hence <= cap */
+       if (roots_nbr == user->lmu[strength + 1] >> 1)
+               return err_nbr - 1;
+
+       /*
+        * Number of roots does not match the degree of smu
+        * unable to correct error.
+        */
+       return -EBADMSG;
+}
+
+int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector,
+                              void *data, void *ecc)
+{
+       struct atmel_pmecc *pmecc = user->pmecc;
+       int sectorsize = get_sectorsize(user);
+       int eccbytes = user->eccbytes;
+       int i, nerrors;
+
+       if (!(user->isr & BIT(sector)))
+               return 0;
+
+       atmel_pmecc_gen_syndrome(user, sector);
+       atmel_pmecc_substitute(user);
+       atmel_pmecc_get_sigma(user);
+
+       nerrors = atmel_pmecc_err_location(user);
+       if (nerrors < 0)
+               return nerrors;
+
+       for (i = 0; i < nerrors; i++) {
+               const char *area;
+               int byte, bit;
+               u32 errpos;
+               u8 *ptr;
+
+               errpos = readl_relaxed(pmecc->regs.errloc +
+                               ATMEL_PMERRLOC_EL(pmecc->caps->el_offset, i));
+               errpos--;
+
+               byte = errpos / 8;
+               bit = errpos % 8;
+
+               if (byte < sectorsize) {
+                       ptr = data + byte;
+                       area = "data";
+               } else if (byte < sectorsize + eccbytes) {
+                       ptr = ecc + byte - sectorsize;
+                       area = "ECC";
+               } else {
+                       dev_dbg(pmecc->dev,
+                               "Invalid errpos value (%d, max is %d)\n",
+                               errpos, (sectorsize + eccbytes) * 8);
+                       return -EINVAL;
+               }
+
+               dev_dbg(pmecc->dev,
+                       "Bit flip in %s area, byte %d: 0x%02x -> 0x%02x\n",
+                       area, byte, *ptr, (unsigned int)(*ptr ^ BIT(bit)));
+
+               *ptr ^= BIT(bit);
+       }
+
+       return nerrors;
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_correct_sector);
+
+bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user)
+{
+       return user->pmecc->caps->correct_erased_chunks;
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_correct_erased_chunks);
+
+void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user,
+                                       int sector, void *ecc)
+{
+       struct atmel_pmecc *pmecc = user->pmecc;
+       u8 *ptr = ecc;
+       int i;
+
+       for (i = 0; i < user->eccbytes; i++)
+               ptr[i] = readb_relaxed(pmecc->regs.base +
+                                      ATMEL_PMECC_ECC(sector, i));
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_get_generated_eccbytes);
+
+int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op)
+{
+       struct atmel_pmecc *pmecc = user->pmecc;
+       u32 cfg;
+
+       if (op != NAND_ECC_READ && op != NAND_ECC_WRITE) {
+               dev_err(pmecc->dev, "Bad ECC operation!");
+               return -EINVAL;
+       }
+
+       mutex_lock(&user->pmecc->lock);
+
+       cfg = user->cache.cfg;
+       if (op == NAND_ECC_WRITE)
+               cfg |= PMECC_CFG_WRITE_OP;
+       else
+               cfg |= PMECC_CFG_AUTO_ENABLE;
+
+       writel(cfg, pmecc->regs.base + ATMEL_PMECC_CFG);
+       writel(user->cache.sarea, pmecc->regs.base + ATMEL_PMECC_SAREA);
+       writel(user->cache.saddr, pmecc->regs.base + ATMEL_PMECC_SADDR);
+       writel(user->cache.eaddr, pmecc->regs.base + ATMEL_PMECC_EADDR);
+
+       writel(PMECC_CTRL_ENABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+       writel(PMECC_CTRL_DATA, pmecc->regs.base + ATMEL_PMECC_CTRL);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_enable);
+
+void atmel_pmecc_disable(struct atmel_pmecc_user *user)
+{
+       struct atmel_pmecc *pmecc = user->pmecc;
+
+       writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
+       writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+       mutex_unlock(&user->pmecc->lock);
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_disable);
+
+int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user)
+{
+       struct atmel_pmecc *pmecc = user->pmecc;
+       u32 status;
+       int ret;
+
+       ret = readl_relaxed_poll_timeout(pmecc->regs.base +
+                                        ATMEL_PMECC_SR,
+                                        status, !(status & PMECC_SR_BUSY), 0,
+                                        PMECC_MAX_TIMEOUT_MS * 1000);
+       if (ret) {
+               dev_err(pmecc->dev,
+                       "Timeout while waiting for PMECC ready.\n");
+               return ret;
+       }
+
+       user->isr = readl_relaxed(pmecc->regs.base + ATMEL_PMECC_ISR);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(atmel_pmecc_wait_rdy);
+
+static struct atmel_pmecc *atmel_pmecc_create(struct platform_device *pdev,
+                                       const struct atmel_pmecc_caps *caps,
+                                       int pmecc_res_idx, int errloc_res_idx)
+{
+       struct device *dev = &pdev->dev;
+       struct atmel_pmecc *pmecc;
+       struct resource *res;
+
+       pmecc = devm_kzalloc(dev, sizeof(*pmecc), GFP_KERNEL);
+       if (!pmecc)
+               return ERR_PTR(-ENOMEM);
+
+       pmecc->caps = caps;
+       pmecc->dev = dev;
+       mutex_init(&pmecc->lock);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, pmecc_res_idx);
+       pmecc->regs.base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(pmecc->regs.base))
+               return ERR_CAST(pmecc->regs.base);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, errloc_res_idx);
+       pmecc->regs.errloc = devm_ioremap_resource(dev, res);
+       if (IS_ERR(pmecc->regs.errloc))
+               return ERR_CAST(pmecc->regs.errloc);
+
+       /* Disable all interrupts before registering the PMECC handler. */
+       writel(0xffffffff, pmecc->regs.base + ATMEL_PMECC_IDR);
+
+       /* Reset the ECC engine */
+       writel(PMECC_CTRL_RST, pmecc->regs.base + ATMEL_PMECC_CTRL);
+       writel(PMECC_CTRL_DISABLE, pmecc->regs.base + ATMEL_PMECC_CTRL);
+
+       return pmecc;
+}
+
+static void devm_atmel_pmecc_put(struct device *dev, void *res)
+{
+       struct atmel_pmecc **pmecc = res;
+
+       put_device((*pmecc)->dev);
+}
+
+static struct atmel_pmecc *atmel_pmecc_get_by_node(struct device *userdev,
+                                                  struct device_node *np)
+{
+       struct platform_device *pdev;
+       struct atmel_pmecc *pmecc, **ptr;
+
+       pdev = of_find_device_by_node(np);
+       if (!pdev || !platform_get_drvdata(pdev))
+               return ERR_PTR(-EPROBE_DEFER);
+
+       ptr = devres_alloc(devm_atmel_pmecc_put, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       get_device(&pdev->dev);
+       pmecc = platform_get_drvdata(pdev);
+
+       *ptr = pmecc;
+
+       devres_add(userdev, ptr);
+
+       return pmecc;
+}
+
+static const int atmel_pmecc_strengths[] = { 2, 4, 8, 12, 24, 32 };
+
+static struct atmel_pmecc_caps at91sam9g45_caps = {
+       .strengths = atmel_pmecc_strengths,
+       .nstrengths = 5,
+       .el_offset = 0x8c,
+};
+
+static struct atmel_pmecc_caps sama5d4_caps = {
+       .strengths = atmel_pmecc_strengths,
+       .nstrengths = 5,
+       .el_offset = 0x8c,
+       .correct_erased_chunks = true,
+};
+
+static struct atmel_pmecc_caps sama5d2_caps = {
+       .strengths = atmel_pmecc_strengths,
+       .nstrengths = 6,
+       .el_offset = 0xac,
+       .correct_erased_chunks = true,
+};
+
+static const struct of_device_id atmel_pmecc_legacy_match[] = {
+       { .compatible = "atmel,sama5d4-nand", &sama5d4_caps },
+       { .compatible = "atmel,sama5d2-nand", &sama5d2_caps },
+       { /* sentinel */ }
+};
+
+struct atmel_pmecc *devm_atmel_pmecc_get(struct device *userdev)
+{
+       struct atmel_pmecc *pmecc;
+       struct device_node *np;
+
+       if (!userdev)
+               return ERR_PTR(-EINVAL);
+
+       if (!userdev->of_node)
+               return NULL;
+
+       np = of_parse_phandle(userdev->of_node, "ecc-engine", 0);
+       if (np) {
+               pmecc = atmel_pmecc_get_by_node(userdev, np);
+               of_node_put(np);
+       } else {
+               /*
+                * Support old DT bindings: in this case the PMECC iomem
+                * resources are directly defined in the user pdev at position
+                * 1 and 2. Extract all relevant information from there.
+                */
+               struct platform_device *pdev = to_platform_device(userdev);
+               const struct atmel_pmecc_caps *caps;
+
+               /* No PMECC engine available. */
+               if (!of_property_read_bool(userdev->of_node,
+                                          "atmel,has-pmecc"))
+                       return NULL;
+
+               caps = &at91sam9g45_caps;
+
+               /*
+                * Try to find the NFC subnode and extract the associated caps
+                * from there.
+                */
+               np = of_find_compatible_node(userdev->of_node, NULL,
+                                            "atmel,sama5d3-nfc");
+               if (np) {
+                       const struct of_device_id *match;
+
+                       match = of_match_node(atmel_pmecc_legacy_match, np);
+                       if (match && match->data)
+                               caps = match->data;
+
+                       of_node_put(np);
+               }
+
+               pmecc = atmel_pmecc_create(pdev, caps, 1, 2);
+       }
+
+       return pmecc;
+}
+EXPORT_SYMBOL(devm_atmel_pmecc_get);
+
+static const struct of_device_id atmel_pmecc_match[] = {
+       { .compatible = "atmel,at91sam9g45-pmecc", &at91sam9g45_caps },
+       { .compatible = "atmel,sama5d4-pmecc", &sama5d4_caps },
+       { .compatible = "atmel,sama5d2-pmecc", &sama5d2_caps },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, atmel_pmecc_match);
+
+static int atmel_pmecc_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       const struct atmel_pmecc_caps *caps;
+       struct atmel_pmecc *pmecc;
+
+       caps = of_device_get_match_data(&pdev->dev);
+       if (!caps) {
+               dev_err(dev, "Invalid caps\n");
+               return -EINVAL;
+       }
+
+       pmecc = atmel_pmecc_create(pdev, caps, 0, 1);
+       if (IS_ERR(pmecc))
+               return PTR_ERR(pmecc);
+
+       platform_set_drvdata(pdev, pmecc);
+
+       return 0;
+}
+
+static struct platform_driver atmel_pmecc_driver = {
+       .driver = {
+               .name = "atmel-pmecc",
+               .of_match_table = of_match_ptr(atmel_pmecc_match),
+       },
+       .probe = atmel_pmecc_probe,
+};
+module_platform_driver(atmel_pmecc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>");
+MODULE_DESCRIPTION("PMECC engine driver");
+MODULE_ALIAS("platform:atmel_pmecc");
diff --git a/drivers/mtd/nand/atmel/pmecc.h b/drivers/mtd/nand/atmel/pmecc.h
new file mode 100644 (file)
index 0000000..a8ddbfc
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * © Copyright 2016 ATMEL
+ * © Copyright 2016 Free Electrons
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ *
+ * Derived from the atmel_nand.c driver which contained the following
+ * copyrights:
+ *
+ *    Copyright © 2003 Rick Bronson
+ *
+ *    Derived from drivers/mtd/nand/autcpu12.c
+ *        Copyright © 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ *    Derived from drivers/mtd/spia.c
+ *        Copyright © 2000 Steven J. Hill (sjhill@cotw.com)
+ *
+ *
+ *    Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
+ *        Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright © 2007
+ *
+ *        Derived from Das U-Boot source code
+ *              (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
+ *        © Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
+ *
+ *    Add Programmable Multibit ECC support for various AT91 SoC
+ *        © Copyright 2012 ATMEL, Hong Xu
+ *
+ *    Add Nand Flash Controller support for SAMA5 SoC
+ *        © Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef ATMEL_PMECC_H
+#define ATMEL_PMECC_H
+
+#define ATMEL_PMECC_MAXIMIZE_ECC_STRENGTH      0
+#define ATMEL_PMECC_SECTOR_SIZE_AUTO           0
+#define ATMEL_PMECC_OOBOFFSET_AUTO             -1
+
+struct atmel_pmecc_user_req {
+       int pagesize;
+       int oobsize;
+       struct {
+               int strength;
+               int bytes;
+               int sectorsize;
+               int nsectors;
+               int ooboffset;
+       } ecc;
+};
+
+struct atmel_pmecc *devm_atmel_pmecc_get(struct device *dev);
+
+struct atmel_pmecc_user *
+atmel_pmecc_create_user(struct atmel_pmecc *pmecc,
+                       struct atmel_pmecc_user_req *req);
+void atmel_pmecc_destroy_user(struct atmel_pmecc_user *user);
+
+int atmel_pmecc_enable(struct atmel_pmecc_user *user, int op);
+void atmel_pmecc_disable(struct atmel_pmecc_user *user);
+int atmel_pmecc_wait_rdy(struct atmel_pmecc_user *user);
+int atmel_pmecc_correct_sector(struct atmel_pmecc_user *user, int sector,
+                              void *data, void *ecc);
+bool atmel_pmecc_correct_erased_chunks(struct atmel_pmecc_user *user);
+void atmel_pmecc_get_generated_eccbytes(struct atmel_pmecc_user *user,
+                                       int sector, void *ecc);
+
+#endif /* ATMEL_PMECC_H */
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
deleted file mode 100644 (file)
index 9ebd5ec..0000000
+++ /dev/null
@@ -1,2479 +0,0 @@
-/*
- *  Copyright © 2003 Rick Bronson
- *
- *  Derived from drivers/mtd/nand/autcpu12.c
- *      Copyright © 2001 Thomas Gleixner (gleixner@autronix.de)
- *
- *  Derived from drivers/mtd/spia.c
- *      Copyright © 2000 Steven J. Hill (sjhill@cotw.com)
- *
- *
- *  Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
- *     Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright © 2007
- *
- *     Derived from Das U-Boot source code
- *                     (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
- *     © Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
- *
- *  Add Programmable Multibit ECC support for various AT91 SoC
- *     © Copyright 2012 ATMEL, Hong Xu
- *
- *  Add Nand Flash Controller support for SAMA5 SoC
- *     © Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/clk.h>
-#include <linux/dma-mapping.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/platform_device.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
-#include <linux/mtd/partitions.h>
-
-#include <linux/delay.h>
-#include <linux/dmaengine.h>
-#include <linux/gpio.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/platform_data/atmel.h>
-
-static int use_dma = 1;
-module_param(use_dma, int, 0);
-
-static int on_flash_bbt = 0;
-module_param(on_flash_bbt, int, 0);
-
-/* Register access macros */
-#define ecc_readl(add, reg)                            \
-       __raw_readl(add + ATMEL_ECC_##reg)
-#define ecc_writel(add, reg, value)                    \
-       __raw_writel((value), add + ATMEL_ECC_##reg)
-
-#include "atmel_nand_ecc.h"    /* Hardware ECC registers */
-#include "atmel_nand_nfc.h"    /* Nand Flash Controller definition */
-
-struct atmel_nand_caps {
-       bool pmecc_correct_erase_page;
-       uint8_t pmecc_max_correction;
-};
-
-/*
- * oob layout for large page size
- * bad block info is on bytes 0 and 1
- * the bytes have to be consecutives to avoid
- * several NAND_CMD_RNDOUT during read
- *
- * oob layout for small page size
- * bad block info is on bytes 4 and 5
- * the bytes have to be consecutives to avoid
- * several NAND_CMD_RNDOUT during read
- */
-static int atmel_ooblayout_ecc_sp(struct mtd_info *mtd, int section,
-                                 struct mtd_oob_region *oobregion)
-{
-       if (section)
-               return -ERANGE;
-
-       oobregion->length = 4;
-       oobregion->offset = 0;
-
-       return 0;
-}
-
-static int atmel_ooblayout_free_sp(struct mtd_info *mtd, int section,
-                                  struct mtd_oob_region *oobregion)
-{
-       if (section)
-               return -ERANGE;
-
-       oobregion->offset = 6;
-       oobregion->length = mtd->oobsize - oobregion->offset;
-
-       return 0;
-}
-
-static const struct mtd_ooblayout_ops atmel_ooblayout_sp_ops = {
-       .ecc = atmel_ooblayout_ecc_sp,
-       .free = atmel_ooblayout_free_sp,
-};
-
-struct atmel_nfc {
-       void __iomem            *base_cmd_regs;
-       void __iomem            *hsmc_regs;
-       void                    *sram_bank0;
-       dma_addr_t              sram_bank0_phys;
-       bool                    use_nfc_sram;
-       bool                    write_by_sram;
-
-       struct clk              *clk;
-
-       bool                    is_initialized;
-       struct completion       comp_ready;
-       struct completion       comp_cmd_done;
-       struct completion       comp_xfer_done;
-
-       /* Point to the sram bank which include readed data via NFC */
-       void                    *data_in_sram;
-       bool                    will_write_sram;
-};
-static struct atmel_nfc        nand_nfc;
-
-struct atmel_nand_host {
-       struct nand_chip        nand_chip;
-       void __iomem            *io_base;
-       dma_addr_t              io_phys;
-       struct atmel_nand_data  board;
-       struct device           *dev;
-       void __iomem            *ecc;
-
-       struct completion       comp;
-       struct dma_chan         *dma_chan;
-
-       struct atmel_nfc        *nfc;
-
-       const struct atmel_nand_caps    *caps;
-       bool                    has_pmecc;
-       u8                      pmecc_corr_cap;
-       u16                     pmecc_sector_size;
-       bool                    has_no_lookup_table;
-       u32                     pmecc_lookup_table_offset;
-       u32                     pmecc_lookup_table_offset_512;
-       u32                     pmecc_lookup_table_offset_1024;
-
-       int                     pmecc_degree;   /* Degree of remainders */
-       int                     pmecc_cw_len;   /* Length of codeword */
-
-       void __iomem            *pmerrloc_base;
-       void __iomem            *pmerrloc_el_base;
-       void __iomem            *pmecc_rom_base;
-
-       /* lookup table for alpha_to and index_of */
-       void __iomem            *pmecc_alpha_to;
-       void __iomem            *pmecc_index_of;
-
-       /* data for pmecc computation */
-       int16_t                 *pmecc_partial_syn;
-       int16_t                 *pmecc_si;
-       int16_t                 *pmecc_smu;     /* Sigma table */
-       int16_t                 *pmecc_lmu;     /* polynomal order */
-       int                     *pmecc_mu;
-       int                     *pmecc_dmu;
-       int                     *pmecc_delta;
-};
-
-/*
- * Enable NAND.
- */
-static void atmel_nand_enable(struct atmel_nand_host *host)
-{
-       if (gpio_is_valid(host->board.enable_pin))
-               gpio_set_value(host->board.enable_pin, 0);
-}
-
-/*
- * Disable NAND.
- */
-static void atmel_nand_disable(struct atmel_nand_host *host)
-{
-       if (gpio_is_valid(host->board.enable_pin))
-               gpio_set_value(host->board.enable_pin, 1);
-}
-
-/*
- * Hardware specific access to control-lines
- */
-static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
-{
-       struct nand_chip *nand_chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
-
-       if (ctrl & NAND_CTRL_CHANGE) {
-               if (ctrl & NAND_NCE)
-                       atmel_nand_enable(host);
-               else
-                       atmel_nand_disable(host);
-       }
-       if (cmd == NAND_CMD_NONE)
-               return;
-
-       if (ctrl & NAND_CLE)
-               writeb(cmd, host->io_base + (1 << host->board.cle));
-       else
-               writeb(cmd, host->io_base + (1 << host->board.ale));
-}
-
-/*
- * Read the Device Ready pin.
- */
-static int atmel_nand_device_ready(struct mtd_info *mtd)
-{
-       struct nand_chip *nand_chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
-
-       return gpio_get_value(host->board.rdy_pin) ^
-                !!host->board.rdy_pin_active_low;
-}
-
-/* Set up for hardware ready pin and enable pin. */
-static int atmel_nand_set_enable_ready_pins(struct mtd_info *mtd)
-{
-       struct nand_chip *chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(chip);
-       int res = 0;
-
-       if (gpio_is_valid(host->board.rdy_pin)) {
-               res = devm_gpio_request(host->dev,
-                               host->board.rdy_pin, "nand_rdy");
-               if (res < 0) {
-                       dev_err(host->dev,
-                               "can't request rdy gpio %d\n",
-                               host->board.rdy_pin);
-                       return res;
-               }
-
-               res = gpio_direction_input(host->board.rdy_pin);
-               if (res < 0) {
-                       dev_err(host->dev,
-                               "can't request input direction rdy gpio %d\n",
-                               host->board.rdy_pin);
-                       return res;
-               }
-
-               chip->dev_ready = atmel_nand_device_ready;
-       }
-
-       if (gpio_is_valid(host->board.enable_pin)) {
-               res = devm_gpio_request(host->dev,
-                               host->board.enable_pin, "nand_enable");
-               if (res < 0) {
-                       dev_err(host->dev,
-                               "can't request enable gpio %d\n",
-                               host->board.enable_pin);
-                       return res;
-               }
-
-               res = gpio_direction_output(host->board.enable_pin, 1);
-               if (res < 0) {
-                       dev_err(host->dev,
-                               "can't request output direction enable gpio %d\n",
-                               host->board.enable_pin);
-                       return res;
-               }
-       }
-
-       return res;
-}
-
-/*
- * Minimal-overhead PIO for data access.
- */
-static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
-{
-       struct nand_chip        *nand_chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
-
-       if (host->nfc && host->nfc->use_nfc_sram && host->nfc->data_in_sram) {
-               memcpy(buf, host->nfc->data_in_sram, len);
-               host->nfc->data_in_sram += len;
-       } else {
-               __raw_readsb(nand_chip->IO_ADDR_R, buf, len);
-       }
-}
-
-static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
-{
-       struct nand_chip        *nand_chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
-
-       if (host->nfc && host->nfc->use_nfc_sram && host->nfc->data_in_sram) {
-               memcpy(buf, host->nfc->data_in_sram, len);
-               host->nfc->data_in_sram += len;
-       } else {
-               __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
-       }
-}
-
-static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
-{
-       struct nand_chip        *nand_chip = mtd_to_nand(mtd);
-
-       __raw_writesb(nand_chip->IO_ADDR_W, buf, len);
-}
-
-static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
-{
-       struct nand_chip        *nand_chip = mtd_to_nand(mtd);
-
-       __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
-}
-
-static void dma_complete_func(void *completion)
-{
-       complete(completion);
-}
-
-static int nfc_set_sram_bank(struct atmel_nand_host *host, unsigned int bank)
-{
-       /* NFC only has two banks. Must be 0 or 1 */
-       if (bank > 1)
-               return -EINVAL;
-
-       if (bank) {
-               struct mtd_info *mtd = nand_to_mtd(&host->nand_chip);
-
-               /* Only for a 2k-page or lower flash, NFC can handle 2 banks */
-               if (mtd->writesize > 2048)
-                       return -EINVAL;
-               nfc_writel(host->nfc->hsmc_regs, BANK, ATMEL_HSMC_NFC_BANK1);
-       } else {
-               nfc_writel(host->nfc->hsmc_regs, BANK, ATMEL_HSMC_NFC_BANK0);
-       }
-
-       return 0;
-}
-
-static uint nfc_get_sram_off(struct atmel_nand_host *host)
-{
-       if (nfc_readl(host->nfc->hsmc_regs, BANK) & ATMEL_HSMC_NFC_BANK1)
-               return NFC_SRAM_BANK1_OFFSET;
-       else
-               return 0;
-}
-
-static dma_addr_t nfc_sram_phys(struct atmel_nand_host *host)
-{
-       if (nfc_readl(host->nfc->hsmc_regs, BANK) & ATMEL_HSMC_NFC_BANK1)
-               return host->nfc->sram_bank0_phys + NFC_SRAM_BANK1_OFFSET;
-       else
-               return host->nfc->sram_bank0_phys;
-}
-
-static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
-                              int is_read)
-{
-       struct dma_device *dma_dev;
-       enum dma_ctrl_flags flags;
-       dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
-       struct dma_async_tx_descriptor *tx = NULL;
-       dma_cookie_t cookie;
-       struct nand_chip *chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(chip);
-       void *p = buf;
-       int err = -EIO;
-       enum dma_data_direction dir = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
-       struct atmel_nfc *nfc = host->nfc;
-
-       if (buf >= high_memory)
-               goto err_buf;
-
-       dma_dev = host->dma_chan->device;
-
-       flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
-
-       phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
-       if (dma_mapping_error(dma_dev->dev, phys_addr)) {
-               dev_err(host->dev, "Failed to dma_map_single\n");
-               goto err_buf;
-       }
-
-       if (is_read) {
-               if (nfc && nfc->data_in_sram)
-                       dma_src_addr = nfc_sram_phys(host) + (nfc->data_in_sram
-                               - (nfc->sram_bank0 + nfc_get_sram_off(host)));
-               else
-                       dma_src_addr = host->io_phys;
-
-               dma_dst_addr = phys_addr;
-       } else {
-               dma_src_addr = phys_addr;
-
-               if (nfc && nfc->write_by_sram)
-                       dma_dst_addr = nfc_sram_phys(host);
-               else
-                       dma_dst_addr = host->io_phys;
-       }
-
-       tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
-                                            dma_src_addr, len, flags);
-       if (!tx) {
-               dev_err(host->dev, "Failed to prepare DMA memcpy\n");
-               goto err_dma;
-       }
-
-       init_completion(&host->comp);
-       tx->callback = dma_complete_func;
-       tx->callback_param = &host->comp;
-
-       cookie = tx->tx_submit(tx);
-       if (dma_submit_error(cookie)) {
-               dev_err(host->dev, "Failed to do DMA tx_submit\n");
-               goto err_dma;
-       }
-
-       dma_async_issue_pending(host->dma_chan);
-       wait_for_completion(&host->comp);
-
-       if (is_read && nfc && nfc->data_in_sram)
-               /* After read data from SRAM, need to increase the position */
-               nfc->data_in_sram += len;
-
-       err = 0;
-
-err_dma:
-       dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
-err_buf:
-       if (err != 0)
-               dev_dbg(host->dev, "Fall back to CPU I/O\n");
-       return err;
-}
-
-static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
-{
-       struct nand_chip *chip = mtd_to_nand(mtd);
-
-       if (use_dma && len > mtd->oobsize)
-               /* only use DMA for bigger than oob size: better performances */
-               if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
-                       return;
-
-       if (chip->options & NAND_BUSWIDTH_16)
-               atmel_read_buf16(mtd, buf, len);
-       else
-               atmel_read_buf8(mtd, buf, len);
-}
-
-static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
-{
-       struct nand_chip *chip = mtd_to_nand(mtd);
-
-       if (use_dma && len > mtd->oobsize)
-               /* only use DMA for bigger than oob size: better performances */
-               if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
-                       return;
-
-       if (chip->options & NAND_BUSWIDTH_16)
-               atmel_write_buf16(mtd, buf, len);
-       else
-               atmel_write_buf8(mtd, buf, len);
-}
-
-/*
- * Return number of ecc bytes per sector according to sector size and
- * correction capability
- *
- * Following table shows what at91 PMECC supported:
- * Correction Capability       Sector_512_bytes        Sector_1024_bytes
- * =====================       ================        =================
- *                2-bits                 4-bytes                  4-bytes
- *                4-bits                 7-bytes                  7-bytes
- *                8-bits                13-bytes                 14-bytes
- *               12-bits                20-bytes                 21-bytes
- *               24-bits                39-bytes                 42-bytes
- *               32-bits                52-bytes                 56-bytes
- */
-static int pmecc_get_ecc_bytes(int cap, int sector_size)
-{
-       int m = 12 + sector_size / 512;
-       return (m * cap + 7) / 8;
-}
-
-static void __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host)
-{
-       int table_size;
-
-       table_size = host->pmecc_sector_size == 512 ?
-               PMECC_LOOKUP_TABLE_SIZE_512 : PMECC_LOOKUP_TABLE_SIZE_1024;
-
-       return host->pmecc_rom_base + host->pmecc_lookup_table_offset +
-                       table_size * sizeof(int16_t);
-}
-
-static int pmecc_data_alloc(struct atmel_nand_host *host)
-{
-       const int cap = host->pmecc_corr_cap;
-       int size;
-
-       size = (2 * cap + 1) * sizeof(int16_t);
-       host->pmecc_partial_syn = devm_kzalloc(host->dev, size, GFP_KERNEL);
-       host->pmecc_si = devm_kzalloc(host->dev, size, GFP_KERNEL);
-       host->pmecc_lmu = devm_kzalloc(host->dev,
-                       (cap + 1) * sizeof(int16_t), GFP_KERNEL);
-       host->pmecc_smu = devm_kzalloc(host->dev,
-                       (cap + 2) * size, GFP_KERNEL);
-
-       size = (cap + 1) * sizeof(int);
-       host->pmecc_mu = devm_kzalloc(host->dev, size, GFP_KERNEL);
-       host->pmecc_dmu = devm_kzalloc(host->dev, size, GFP_KERNEL);
-       host->pmecc_delta = devm_kzalloc(host->dev, size, GFP_KERNEL);
-
-       if (!host->pmecc_partial_syn ||
-               !host->pmecc_si ||
-               !host->pmecc_lmu ||
-               !host->pmecc_smu ||
-               !host->pmecc_mu ||
-               !host->pmecc_dmu ||
-               !host->pmecc_delta)
-               return -ENOMEM;
-
-       return 0;
-}
-
-static void pmecc_gen_syndrome(struct mtd_info *mtd, int sector)
-{
-       struct nand_chip *nand_chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
-       int i;
-       uint32_t value;
-
-       /* Fill odd syndromes */
-       for (i = 0; i < host->pmecc_corr_cap; i++) {
-               value = pmecc_readl_rem_relaxed(host->ecc, sector, i / 2);
-               if (i & 1)
-                       value >>= 16;
-               value &= 0xffff;
-               host->pmecc_partial_syn[(2 * i) + 1] = (int16_t)value;
-       }
-}
-
-static void pmecc_substitute(struct mtd_info *mtd)
-{
-       struct nand_chip *nand_chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
-       int16_t __iomem *alpha_to = host->pmecc_alpha_to;
-       int16_t __iomem *index_of = host->pmecc_index_of;
-       int16_t *partial_syn = host->pmecc_partial_syn;
-       const int cap = host->pmecc_corr_cap;
-       int16_t *si;
-       int i, j;
-
-       /* si[] is a table that holds the current syndrome value,
-        * an element of that table belongs to the field
-        */
-       si = host->pmecc_si;
-
-       memset(&si[1], 0, sizeof(int16_t) * (2 * cap - 1));
-
-       /* Computation 2t syndromes based on S(x) */
-       /* Odd syndromes */
-       for (i = 1; i < 2 * cap; i += 2) {
-               for (j = 0; j < host->pmecc_degree; j++) {
-                       if (partial_syn[i] & ((unsigned short)0x1 << j))
-                               si[i] = readw_relaxed(alpha_to + i * j) ^ si[i];
-               }
-       }
-       /* Even syndrome = (Odd syndrome) ** 2 */
-       for (i = 2, j = 1; j <= cap; i = ++j << 1) {
-               if (si[j] == 0) {
-                       si[i] = 0;
-               } else {
-                       int16_t tmp;
-
-                       tmp = readw_relaxed(index_of + si[j]);
-                       tmp = (tmp * 2) % host->pmecc_cw_len;
-                       si[i] = readw_relaxed(alpha_to + tmp);
-               }
-       }
-
-       return;
-}
-
-static void pmecc_get_sigma(struct mtd_info *mtd)
-{
-       struct nand_chip *nand_chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
-
-       int16_t *lmu = host->pmecc_lmu;
-       int16_t *si = host->pmecc_si;
-       int *mu = host->pmecc_mu;
-       int *dmu = host->pmecc_dmu;     /* Discrepancy */
-       int *delta = host->pmecc_delta; /* Delta order */
-       int cw_len = host->pmecc_cw_len;
-       const int16_t cap = host->pmecc_corr_cap;
-       const int num = 2 * cap + 1;
-       int16_t __iomem *index_of = host->pmecc_index_of;
-       int16_t __iomem *alpha_to = host->pmecc_alpha_to;
-       int i, j, k;
-       uint32_t dmu_0_count, tmp;
-       int16_t *smu = host->pmecc_smu;
-
-       /* index of largest delta */
-       int ro;
-       int largest;
-       int diff;
-
-       dmu_0_count = 0;
-
-       /* First Row */
-
-       /* Mu */
-       mu[0] = -1;
-
-       memset(smu, 0, sizeof(int16_t) * num);
-       smu[0] = 1;
-
-       /* discrepancy set to 1 */
-       dmu[0] = 1;
-       /* polynom order set to 0 */
-       lmu[0] = 0;
-       delta[0] = (mu[0] * 2 - lmu[0]) >> 1;
-
-       /* Second Row */
-
-       /* Mu */
-       mu[1] = 0;
-       /* Sigma(x) set to 1 */
-       memset(&smu[num], 0, sizeof(int16_t) * num);
-       smu[num] = 1;
-
-       /* discrepancy set to S1 */
-       dmu[1] = si[1];
-
-       /* polynom order set to 0 */
-       lmu[1] = 0;
-
-       delta[1] = (mu[1] * 2 - lmu[1]) >> 1;
-
-       /* Init the Sigma(x) last row */
-       memset(&smu[(cap + 1) * num], 0, sizeof(int16_t) * num);
-
-       for (i = 1; i <= cap; i++) {
-               mu[i + 1] = i << 1;
-               /* Begin Computing Sigma (Mu+1) and L(mu) */
-               /* check if discrepancy is set to 0 */
-               if (dmu[i] == 0) {
-                       dmu_0_count++;
-
-                       tmp = ((cap - (lmu[i] >> 1) - 1) / 2);
-                       if ((cap - (lmu[i] >> 1) - 1) & 0x1)
-                               tmp += 2;
-                       else
-                               tmp += 1;
-
-                       if (dmu_0_count == tmp) {
-                               for (j = 0; j <= (lmu[i] >> 1) + 1; j++)
-                                       smu[(cap + 1) * num + j] =
-                                                       smu[i * num + j];
-
-                               lmu[cap + 1] = lmu[i];
-                               return;
-                       }
-
-                       /* copy polynom */
-                       for (j = 0; j <= lmu[i] >> 1; j++)
-                               smu[(i + 1) * num + j] = smu[i * num + j];
-
-                       /* copy previous polynom order to the next */
-                       lmu[i + 1] = lmu[i];
-               } else {
-                       ro = 0;
-                       largest = -1;
-                       /* find largest delta with dmu != 0 */
-                       for (j = 0; j < i; j++) {
-                               if ((dmu[j]) && (delta[j] > largest)) {
-                                       largest = delta[j];
-                                       ro = j;
-                               }
-                       }
-
-                       /* compute difference */
-                       diff = (mu[i] - mu[ro]);
-
-                       /* Compute degree of the new smu polynomial */
-                       if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff))
-                               lmu[i + 1] = lmu[i];
-                       else
-                               lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2;
-
-                       /* Init smu[i+1] with 0 */
-                       for (k = 0; k < num; k++)
-                               smu[(i + 1) * num + k] = 0;
-
-                       /* Compute smu[i+1] */
-                       for (k = 0; k <= lmu[ro] >> 1; k++) {
-                               int16_t a, b, c;
-
-                               if (!(smu[ro * num + k] && dmu[i]))
-                                       continue;
-                               a = readw_relaxed(index_of + dmu[i]);
-                               b = readw_relaxed(index_of + dmu[ro]);
-                               c = readw_relaxed(index_of + smu[ro * num + k]);
-                               tmp = a + (cw_len - b) + c;
-                               a = readw_relaxed(alpha_to + tmp % cw_len);
-                               smu[(i + 1) * num + (k + diff)] = a;
-                       }
-
-                       for (k = 0; k <= lmu[i] >> 1; k++)
-                               smu[(i + 1) * num + k] ^= smu[i * num + k];
-               }
-
-               /* End Computing Sigma (Mu+1) and L(mu) */
-               /* In either case compute delta */
-               delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1;
-
-               /* Do not compute discrepancy for the last iteration */
-               if (i >= cap)
-                       continue;
-
-               for (k = 0; k <= (lmu[i + 1] >> 1); k++) {
-                       tmp = 2 * (i - 1);
-                       if (k == 0) {
-                               dmu[i + 1] = si[tmp + 3];
-                       } else if (smu[(i + 1) * num + k] && si[tmp + 3 - k]) {
-                               int16_t a, b, c;
-                               a = readw_relaxed(index_of +
-                                               smu[(i + 1) * num + k]);
-                               b = si[2 * (i - 1) + 3 - k];
-                               c = readw_relaxed(index_of + b);
-                               tmp = a + c;
-                               tmp %= cw_len;
-                               dmu[i + 1] = readw_relaxed(alpha_to + tmp) ^
-                                       dmu[i + 1];
-                       }
-               }
-       }
-
-       return;
-}
-
-static int pmecc_err_location(struct mtd_info *mtd)
-{
-       struct nand_chip *nand_chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
-       unsigned long end_time;
-       const int cap = host->pmecc_corr_cap;
-       const int num = 2 * cap + 1;
-       int sector_size = host->pmecc_sector_size;
-       int err_nbr = 0;        /* number of error */
-       int roots_nbr;          /* number of roots */
-       int i;
-       uint32_t val;
-       int16_t *smu = host->pmecc_smu;
-
-       pmerrloc_writel(host->pmerrloc_base, ELDIS, PMERRLOC_DISABLE);
-
-       for (i = 0; i <= host->pmecc_lmu[cap + 1] >> 1; i++) {
-               pmerrloc_writel_sigma_relaxed(host->pmerrloc_base, i,
-                                     smu[(cap + 1) * num + i]);
-               err_nbr++;
-       }
-
-       val = (err_nbr - 1) << 16;
-       if (sector_size == 1024)
-               val |= 1;
-
-       pmerrloc_writel(host->pmerrloc_base, ELCFG, val);
-       pmerrloc_writel(host->pmerrloc_base, ELEN,
-                       sector_size * 8 + host->pmecc_degree * cap);
-
-       end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS);
-       while (!(pmerrloc_readl_relaxed(host->pmerrloc_base, ELISR)
-                & PMERRLOC_CALC_DONE)) {
-               if (unlikely(time_after(jiffies, end_time))) {
-                       dev_err(host->dev, "PMECC: Timeout to calculate error location.\n");
-                       return -1;
-               }
-               cpu_relax();
-       }
-
-       roots_nbr = (pmerrloc_readl_relaxed(host->pmerrloc_base, ELISR)
-               & PMERRLOC_ERR_NUM_MASK) >> 8;
-       /* Number of roots == degree of smu hence <= cap */
-       if (roots_nbr == host->pmecc_lmu[cap + 1] >> 1)
-               return err_nbr - 1;
-
-       /* Number of roots does not match the degree of smu
-        * unable to correct error */
-       return -1;
-}
-
-static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
-               int sector_num, int extra_bytes, int err_nbr)
-{
-       struct nand_chip *nand_chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
-       int i = 0;
-       int byte_pos, bit_pos, sector_size, pos;
-       uint32_t tmp;
-       uint8_t err_byte;
-
-       sector_size = host->pmecc_sector_size;
-
-       while (err_nbr) {
-               tmp = pmerrloc_readl_el_relaxed(host->pmerrloc_el_base, i) - 1;
-               byte_pos = tmp / 8;
-               bit_pos  = tmp % 8;
-
-               if (byte_pos >= (sector_size + extra_bytes))
-                       BUG();  /* should never happen */
-
-               if (byte_pos < sector_size) {
-                       err_byte = *(buf + byte_pos);
-                       *(buf + byte_pos) ^= (1 << bit_pos);
-
-                       pos = sector_num * host->pmecc_sector_size + byte_pos;
-                       dev_dbg(host->dev, "Bit flip in data area, byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
-                               pos, bit_pos, err_byte, *(buf + byte_pos));
-               } else {
-                       struct mtd_oob_region oobregion;
-
-                       /* Bit flip in OOB area */
-                       tmp = sector_num * nand_chip->ecc.bytes
-                                       + (byte_pos - sector_size);
-                       err_byte = ecc[tmp];
-                       ecc[tmp] ^= (1 << bit_pos);
-
-                       mtd_ooblayout_ecc(mtd, 0, &oobregion);
-                       pos = tmp + oobregion.offset;
-                       dev_dbg(host->dev, "Bit flip in OOB, oob_byte_pos: %d, bit_pos: %d, 0x%02x -> 0x%02x\n",
-                               pos, bit_pos, err_byte, ecc[tmp]);
-               }
-
-               i++;
-               err_nbr--;
-       }
-
-       return;
-}
-
-static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf,
-       u8 *ecc)
-{
-       struct nand_chip *nand_chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
-       int i, err_nbr;
-       uint8_t *buf_pos;
-       int max_bitflips = 0;
-
-       for (i = 0; i < nand_chip->ecc.steps; i++) {
-               err_nbr = 0;
-               if (pmecc_stat & 0x1) {
-                       buf_pos = buf + i * host->pmecc_sector_size;
-
-                       pmecc_gen_syndrome(mtd, i);
-                       pmecc_substitute(mtd);
-                       pmecc_get_sigma(mtd);
-
-                       err_nbr = pmecc_err_location(mtd);
-                       if (err_nbr >= 0) {
-                               pmecc_correct_data(mtd, buf_pos, ecc, i,
-                                                  nand_chip->ecc.bytes,
-                                                  err_nbr);
-                       } else if (!host->caps->pmecc_correct_erase_page) {
-                               u8 *ecc_pos = ecc + (i * nand_chip->ecc.bytes);
-
-                               /* Try to detect erased pages */
-                               err_nbr = nand_check_erased_ecc_chunk(buf_pos,
-                                                       host->pmecc_sector_size,
-                                                       ecc_pos,
-                                                       nand_chip->ecc.bytes,
-                                                       NULL, 0,
-                                                       nand_chip->ecc.strength);
-                       }
-
-                       if (err_nbr < 0) {
-                               dev_err(host->dev, "PMECC: Too many errors\n");
-                               mtd->ecc_stats.failed++;
-                               return -EIO;
-                       }
-
-                       mtd->ecc_stats.corrected += err_nbr;
-                       max_bitflips = max_t(int, max_bitflips, err_nbr);
-               }
-               pmecc_stat >>= 1;
-       }
-
-       return max_bitflips;
-}
-
-static void pmecc_enable(struct atmel_nand_host *host, int ecc_op)
-{
-       u32 val;
-
-       if (ecc_op != NAND_ECC_READ && ecc_op != NAND_ECC_WRITE) {
-               dev_err(host->dev, "atmel_nand: wrong pmecc operation type!");
-               return;
-       }
-
-       pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST);
-       pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
-       val = pmecc_readl_relaxed(host->ecc, CFG);
-
-       if (ecc_op == NAND_ECC_READ)
-               pmecc_writel(host->ecc, CFG, (val & ~PMECC_CFG_WRITE_OP)
-                       | PMECC_CFG_AUTO_ENABLE);
-       else
-               pmecc_writel(host->ecc, CFG, (val | PMECC_CFG_WRITE_OP)
-                       & ~PMECC_CFG_AUTO_ENABLE);
-
-       pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE);
-       pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DATA);
-}
-
-static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
-       struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
-{
-       struct atmel_nand_host *host = nand_get_controller_data(chip);
-       int eccsize = chip->ecc.size * chip->ecc.steps;
-       uint8_t *oob = chip->oob_poi;
-       uint32_t stat;
-       unsigned long end_time;
-       int bitflips = 0;
-
-       if (!host->nfc || !host->nfc->use_nfc_sram)
-               pmecc_enable(host, NAND_ECC_READ);
-
-       chip->read_buf(mtd, buf, eccsize);
-       chip->read_buf(mtd, oob, mtd->oobsize);
-
-       end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS);
-       while ((pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY)) {
-               if (unlikely(time_after(jiffies, end_time))) {
-                       dev_err(host->dev, "PMECC: Timeout to get error status.\n");
-                       return -EIO;
-               }
-               cpu_relax();
-       }
-
-       stat = pmecc_readl_relaxed(host->ecc, ISR);
-       if (stat != 0) {
-               struct mtd_oob_region oobregion;
-
-               mtd_ooblayout_ecc(mtd, 0, &oobregion);
-               bitflips = pmecc_correction(mtd, stat, buf,
-                                           &oob[oobregion.offset]);
-               if (bitflips < 0)
-                       /* uncorrectable errors */
-                       return 0;
-       }
-
-       return bitflips;
-}
-
-static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
-               struct nand_chip *chip, const uint8_t *buf, int oob_required,
-               int page)
-{
-       struct atmel_nand_host *host = nand_get_controller_data(chip);
-       struct mtd_oob_region oobregion = { };
-       int i, j, section = 0;
-       unsigned long end_time;
-
-       if (!host->nfc || !host->nfc->write_by_sram) {
-               pmecc_enable(host, NAND_ECC_WRITE);
-               chip->write_buf(mtd, (u8 *)buf, mtd->writesize);
-       }
-
-       end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS);
-       while ((pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY)) {
-               if (unlikely(time_after(jiffies, end_time))) {
-                       dev_err(host->dev, "PMECC: Timeout to get ECC value.\n");
-                       return -EIO;
-               }
-               cpu_relax();
-       }
-
-       for (i = 0; i < chip->ecc.steps; i++) {
-               for (j = 0; j < chip->ecc.bytes; j++) {
-                       if (!oobregion.length)
-                               mtd_ooblayout_ecc(mtd, section, &oobregion);
-
-                       chip->oob_poi[oobregion.offset] =
-                               pmecc_readb_ecc_relaxed(host->ecc, i, j);
-                       oobregion.length--;
-                       oobregion.offset++;
-                       section++;
-               }
-       }
-       chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
-
-       return 0;
-}
-
-static void atmel_pmecc_core_init(struct mtd_info *mtd)
-{
-       struct nand_chip *nand_chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
-       int eccbytes = mtd_ooblayout_count_eccbytes(mtd);
-       uint32_t val = 0;
-       struct mtd_oob_region oobregion;
-
-       pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST);
-       pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
-
-       switch (host->pmecc_corr_cap) {
-       case 2:
-               val = PMECC_CFG_BCH_ERR2;
-               break;
-       case 4:
-               val = PMECC_CFG_BCH_ERR4;
-               break;
-       case 8:
-               val = PMECC_CFG_BCH_ERR8;
-               break;
-       case 12:
-               val = PMECC_CFG_BCH_ERR12;
-               break;
-       case 24:
-               val = PMECC_CFG_BCH_ERR24;
-               break;
-       case 32:
-               val = PMECC_CFG_BCH_ERR32;
-               break;
-       }
-
-       if (host->pmecc_sector_size == 512)
-               val |= PMECC_CFG_SECTOR512;
-       else if (host->pmecc_sector_size == 1024)
-               val |= PMECC_CFG_SECTOR1024;
-
-       switch (nand_chip->ecc.steps) {
-       case 1:
-               val |= PMECC_CFG_PAGE_1SECTOR;
-               break;
-       case 2:
-               val |= PMECC_CFG_PAGE_2SECTORS;
-               break;
-       case 4:
-               val |= PMECC_CFG_PAGE_4SECTORS;
-               break;
-       case 8:
-               val |= PMECC_CFG_PAGE_8SECTORS;
-               break;
-       }
-
-       val |= (PMECC_CFG_READ_OP | PMECC_CFG_SPARE_DISABLE
-               | PMECC_CFG_AUTO_DISABLE);
-       pmecc_writel(host->ecc, CFG, val);
-
-       pmecc_writel(host->ecc, SAREA, mtd->oobsize - 1);
-       mtd_ooblayout_ecc(mtd, 0, &oobregion);
-       pmecc_writel(host->ecc, SADDR, oobregion.offset);
-       pmecc_writel(host->ecc, EADDR,
-                    oobregion.offset + eccbytes - 1);
-       /* See datasheet about PMECC Clock Control Register */
-       pmecc_writel(host->ecc, CLK, 2);
-       pmecc_writel(host->ecc, IDR, 0xff);
-       pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE);
-}
-
-/*
- * Get minimum ecc requirements from NAND.
- * If pmecc-cap, pmecc-sector-size in DTS are not specified, this function
- * will set them according to minimum ecc requirement. Otherwise, use the
- * value in DTS file.
- * return 0 if success. otherwise return error code.
- */
-static int pmecc_choose_ecc(struct atmel_nand_host *host,
-               int *cap, int *sector_size)
-{
-       /* Get minimum ECC requirements */
-       if (host->nand_chip.ecc_strength_ds) {
-               *cap = host->nand_chip.ecc_strength_ds;
-               *sector_size = host->nand_chip.ecc_step_ds;
-               dev_info(host->dev, "minimum ECC: %d bits in %d bytes\n",
-                               *cap, *sector_size);
-       } else {
-               *cap = 2;
-               *sector_size = 512;
-               dev_info(host->dev, "can't detect min. ECC, assume 2 bits in 512 bytes\n");
-       }
-
-       /* If device tree doesn't specify, use NAND's minimum ECC parameters */
-       if (host->pmecc_corr_cap == 0) {
-               if (*cap > host->caps->pmecc_max_correction)
-                       return -EINVAL;
-
-               /* use the most fitable ecc bits (the near bigger one ) */
-               if (*cap <= 2)
-                       host->pmecc_corr_cap = 2;
-               else if (*cap <= 4)
-                       host->pmecc_corr_cap = 4;
-               else if (*cap <= 8)
-                       host->pmecc_corr_cap = 8;
-               else if (*cap <= 12)
-                       host->pmecc_corr_cap = 12;
-               else if (*cap <= 24)
-                       host->pmecc_corr_cap = 24;
-               else if (*cap <= 32)
-                       host->pmecc_corr_cap = 32;
-               else
-                       return -EINVAL;
-       }
-       if (host->pmecc_sector_size == 0) {
-               /* use the most fitable sector size (the near smaller one ) */
-               if (*sector_size >= 1024)
-                       host->pmecc_sector_size = 1024;
-               else if (*sector_size >= 512)
-                       host->pmecc_sector_size = 512;
-               else
-                       return -EINVAL;
-       }
-       return 0;
-}
-
-static inline int deg(unsigned int poly)
-{
-       /* polynomial degree is the most-significant bit index */
-       return fls(poly) - 1;
-}
-
-static int build_gf_tables(int mm, unsigned int poly,
-               int16_t *index_of, int16_t *alpha_to)
-{
-       unsigned int i, x = 1;
-       const unsigned int k = 1 << deg(poly);
-       unsigned int nn = (1 << mm) - 1;
-
-       /* primitive polynomial must be of degree m */
-       if (k != (1u << mm))
-               return -EINVAL;
-
-       for (i = 0; i < nn; i++) {
-               alpha_to[i] = x;
-               index_of[x] = i;
-               if (i && (x == 1))
-                       /* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */
-                       return -EINVAL;
-               x <<= 1;
-               if (x & k)
-                       x ^= poly;
-       }
-       alpha_to[nn] = 1;
-       index_of[0] = 0;
-
-       return 0;
-}
-
-static uint16_t *create_lookup_table(struct device *dev, int sector_size)
-{
-       int degree = (sector_size == 512) ?
-                       PMECC_GF_DIMENSION_13 :
-                       PMECC_GF_DIMENSION_14;
-       unsigned int poly = (sector_size == 512) ?
-                       PMECC_GF_13_PRIMITIVE_POLY :
-                       PMECC_GF_14_PRIMITIVE_POLY;
-       int table_size = (sector_size == 512) ?
-                       PMECC_LOOKUP_TABLE_SIZE_512 :
-                       PMECC_LOOKUP_TABLE_SIZE_1024;
-
-       int16_t *addr = devm_kzalloc(dev, 2 * table_size * sizeof(uint16_t),
-                       GFP_KERNEL);
-       if (addr && build_gf_tables(degree, poly, addr, addr + table_size))
-               return NULL;
-
-       return addr;
-}
-
-static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
-                                        struct atmel_nand_host *host)
-{
-       struct nand_chip *nand_chip = &host->nand_chip;
-       struct mtd_info *mtd = nand_to_mtd(nand_chip);
-       struct resource *regs, *regs_pmerr, *regs_rom;
-       uint16_t *galois_table;
-       int cap, sector_size, err_no;
-
-       err_no = pmecc_choose_ecc(host, &cap, &sector_size);
-       if (err_no) {
-               dev_err(host->dev, "The NAND flash's ECC requirement are not support!");
-               return err_no;
-       }
-
-       if (cap > host->pmecc_corr_cap ||
-                       sector_size != host->pmecc_sector_size)
-               dev_info(host->dev, "WARNING: Be Caution! Using different PMECC parameters from Nand ONFI ECC reqirement.\n");
-
-       cap = host->pmecc_corr_cap;
-       sector_size = host->pmecc_sector_size;
-       host->pmecc_lookup_table_offset = (sector_size == 512) ?
-                       host->pmecc_lookup_table_offset_512 :
-                       host->pmecc_lookup_table_offset_1024;
-
-       dev_info(host->dev, "Initialize PMECC params, cap: %d, sector: %d\n",
-                cap, sector_size);
-
-       regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       if (!regs) {
-               dev_warn(host->dev,
-                       "Can't get I/O resource regs for PMECC controller, rolling back on software ECC\n");
-               nand_chip->ecc.mode = NAND_ECC_SOFT;
-               nand_chip->ecc.algo = NAND_ECC_HAMMING;
-               return 0;
-       }
-
-       host->ecc = devm_ioremap_resource(&pdev->dev, regs);
-       if (IS_ERR(host->ecc)) {
-               err_no = PTR_ERR(host->ecc);
-               goto err;
-       }
-
-       regs_pmerr = platform_get_resource(pdev, IORESOURCE_MEM, 2);
-       host->pmerrloc_base = devm_ioremap_resource(&pdev->dev, regs_pmerr);
-       if (IS_ERR(host->pmerrloc_base)) {
-               err_no = PTR_ERR(host->pmerrloc_base);
-               goto err;
-       }
-       host->pmerrloc_el_base = host->pmerrloc_base + ATMEL_PMERRLOC_SIGMAx +
-               (host->caps->pmecc_max_correction + 1) * 4;
-
-       if (!host->has_no_lookup_table) {
-               regs_rom = platform_get_resource(pdev, IORESOURCE_MEM, 3);
-               host->pmecc_rom_base = devm_ioremap_resource(&pdev->dev,
-                                                               regs_rom);
-               if (IS_ERR(host->pmecc_rom_base)) {
-                       dev_err(host->dev, "Can not get I/O resource for ROM, will build a lookup table in runtime!\n");
-                       host->has_no_lookup_table = true;
-               }
-       }
-
-       if (host->has_no_lookup_table) {
-               /* Build the look-up table in runtime */
-               galois_table = create_lookup_table(host->dev, sector_size);
-               if (!galois_table) {
-                       dev_err(host->dev, "Failed to build a lookup table in runtime!\n");
-                       err_no = -EINVAL;
-                       goto err;
-               }
-
-               host->pmecc_rom_base = (void __iomem *)galois_table;
-               host->pmecc_lookup_table_offset = 0;
-       }
-
-       nand_chip->ecc.size = sector_size;
-
-       /* set ECC page size and oob layout */
-       switch (mtd->writesize) {
-       case 512:
-       case 1024:
-       case 2048:
-       case 4096:
-       case 8192:
-               if (sector_size > mtd->writesize) {
-                       dev_err(host->dev, "pmecc sector size is bigger than the page size!\n");
-                       err_no = -EINVAL;
-                       goto err;
-               }
-
-               host->pmecc_degree = (sector_size == 512) ?
-                       PMECC_GF_DIMENSION_13 : PMECC_GF_DIMENSION_14;
-               host->pmecc_cw_len = (1 << host->pmecc_degree) - 1;
-               host->pmecc_alpha_to = pmecc_get_alpha_to(host);
-               host->pmecc_index_of = host->pmecc_rom_base +
-                       host->pmecc_lookup_table_offset;
-
-               nand_chip->ecc.strength = cap;
-               nand_chip->ecc.bytes = pmecc_get_ecc_bytes(cap, sector_size);
-               nand_chip->ecc.steps = mtd->writesize / sector_size;
-               nand_chip->ecc.total = nand_chip->ecc.bytes *
-                       nand_chip->ecc.steps;
-               if (nand_chip->ecc.total >
-                               mtd->oobsize - PMECC_OOB_RESERVED_BYTES) {
-                       dev_err(host->dev, "No room for ECC bytes\n");
-                       err_no = -EINVAL;
-                       goto err;
-               }
-
-               mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
-               break;
-       default:
-               dev_warn(host->dev,
-                       "Unsupported page size for PMECC, use Software ECC\n");
-               /* page size not handled by HW ECC */
-               /* switching back to soft ECC */
-               nand_chip->ecc.mode = NAND_ECC_SOFT;
-               nand_chip->ecc.algo = NAND_ECC_HAMMING;
-               return 0;
-       }
-
-       /* Allocate data for PMECC computation */
-       err_no = pmecc_data_alloc(host);
-       if (err_no) {
-               dev_err(host->dev,
-                               "Cannot allocate memory for PMECC computation!\n");
-               goto err;
-       }
-
-       nand_chip->options |= NAND_NO_SUBPAGE_WRITE;
-       nand_chip->ecc.read_page = atmel_nand_pmecc_read_page;
-       nand_chip->ecc.write_page = atmel_nand_pmecc_write_page;
-
-       atmel_pmecc_core_init(mtd);
-
-       return 0;
-
-err:
-       return err_no;
-}
-
-/*
- * Calculate HW ECC
- *
- * function called after a write
- *
- * mtd:        MTD block structure
- * dat:        raw data (unused)
- * ecc_code:   buffer for ECC
- */
-static int atmel_nand_calculate(struct mtd_info *mtd,
-               const u_char *dat, unsigned char *ecc_code)
-{
-       struct nand_chip *nand_chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
-       unsigned int ecc_value;
-
-       /* get the first 2 ECC bytes */
-       ecc_value = ecc_readl(host->ecc, PR);
-
-       ecc_code[0] = ecc_value & 0xFF;
-       ecc_code[1] = (ecc_value >> 8) & 0xFF;
-
-       /* get the last 2 ECC bytes */
-       ecc_value = ecc_readl(host->ecc, NPR) & ATMEL_ECC_NPARITY;
-
-       ecc_code[2] = ecc_value & 0xFF;
-       ecc_code[3] = (ecc_value >> 8) & 0xFF;
-
-       return 0;
-}
-
-/*
- * HW ECC read page function
- *
- * mtd:        mtd info structure
- * chip:       nand chip info structure
- * buf:        buffer to store read data
- * oob_required:    caller expects OOB data read to chip->oob_poi
- */
-static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
-                               uint8_t *buf, int oob_required, int page)
-{
-       int eccsize = chip->ecc.size;
-       int eccbytes = chip->ecc.bytes;
-       uint8_t *p = buf;
-       uint8_t *oob = chip->oob_poi;
-       uint8_t *ecc_pos;
-       int stat;
-       unsigned int max_bitflips = 0;
-       struct mtd_oob_region oobregion = {};
-
-       /*
-        * Errata: ALE is incorrectly wired up to the ECC controller
-        * on the AP7000, so it will include the address cycles in the
-        * ECC calculation.
-        *
-        * Workaround: Reset the parity registers before reading the
-        * actual data.
-        */
-       struct atmel_nand_host *host = nand_get_controller_data(chip);
-       if (host->board.need_reset_workaround)
-               ecc_writel(host->ecc, CR, ATMEL_ECC_RST);
-
-       /* read the page */
-       chip->read_buf(mtd, p, eccsize);
-
-       /* move to ECC position if needed */
-       mtd_ooblayout_ecc(mtd, 0, &oobregion);
-       if (oobregion.offset != 0) {
-               /*
-                * This only works on large pages because the ECC controller
-                * waits for NAND_CMD_RNDOUTSTART after the NAND_CMD_RNDOUT.
-                * Anyway, for small pages, the first ECC byte is at offset
-                * 0 in the OOB area.
-                */
-               chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
-                             mtd->writesize + oobregion.offset, -1);
-       }
-
-       /* the ECC controller needs to read the ECC just after the data */
-       ecc_pos = oob + oobregion.offset;
-       chip->read_buf(mtd, ecc_pos, eccbytes);
-
-       /* check if there's an error */
-       stat = chip->ecc.correct(mtd, p, oob, NULL);
-
-       if (stat < 0) {
-               mtd->ecc_stats.failed++;
-       } else {
-               mtd->ecc_stats.corrected += stat;
-               max_bitflips = max_t(unsigned int, max_bitflips, stat);
-       }
-
-       /* get back to oob start (end of page) */
-       chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
-
-       /* read the oob */
-       chip->read_buf(mtd, oob, mtd->oobsize);
-
-       return max_bitflips;
-}
-
-/*
- * HW ECC Correction
- *
- * function called after a read
- *
- * mtd:        MTD block structure
- * dat:        raw data read from the chip
- * read_ecc:   ECC from the chip (unused)
- * isnull:     unused
- *
- * Detect and correct a 1 bit error for a page
- */
-static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,
-               u_char *read_ecc, u_char *isnull)
-{
-       struct nand_chip *nand_chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
-       unsigned int ecc_status;
-       unsigned int ecc_word, ecc_bit;
-
-       /* get the status from the Status Register */
-       ecc_status = ecc_readl(host->ecc, SR);
-
-       /* if there's no error */
-       if (likely(!(ecc_status & ATMEL_ECC_RECERR)))
-               return 0;
-
-       /* get error bit offset (4 bits) */
-       ecc_bit = ecc_readl(host->ecc, PR) & ATMEL_ECC_BITADDR;
-       /* get word address (12 bits) */
-       ecc_word = ecc_readl(host->ecc, PR) & ATMEL_ECC_WORDADDR;
-       ecc_word >>= 4;
-
-       /* if there are multiple errors */
-       if (ecc_status & ATMEL_ECC_MULERR) {
-               /* check if it is a freshly erased block
-                * (filled with 0xff) */
-               if ((ecc_bit == ATMEL_ECC_BITADDR)
-                               && (ecc_word == (ATMEL_ECC_WORDADDR >> 4))) {
-                       /* the block has just been erased, return OK */
-                       return 0;
-               }
-               /* it doesn't seems to be a freshly
-                * erased block.
-                * We can't correct so many errors */
-               dev_dbg(host->dev, "atmel_nand : multiple errors detected."
-                               " Unable to correct.\n");
-               return -EBADMSG;
-       }
-
-       /* if there's a single bit error : we can correct it */
-       if (ecc_status & ATMEL_ECC_ECCERR) {
-               /* there's nothing much to do here.
-                * the bit error is on the ECC itself.
-                */
-               dev_dbg(host->dev, "atmel_nand : one bit error on ECC code."
-                               " Nothing to correct\n");
-               return 0;
-       }
-
-       dev_dbg(host->dev, "atmel_nand : one bit error on data."
-                       " (word offset in the page :"
-                       " 0x%x bit offset : 0x%x)\n",
-                       ecc_word, ecc_bit);
-       /* correct the error */
-       if (nand_chip->options & NAND_BUSWIDTH_16) {
-               /* 16 bits words */
-               ((unsigned short *) dat)[ecc_word] ^= (1 << ecc_bit);
-       } else {
-               /* 8 bits words */
-               dat[ecc_word] ^= (1 << ecc_bit);
-       }
-       dev_dbg(host->dev, "atmel_nand : error corrected\n");
-       return 1;
-}
-
-/*
- * Enable HW ECC : unused on most chips
- */
-static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
-{
-       struct nand_chip *nand_chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
-
-       if (host->board.need_reset_workaround)
-               ecc_writel(host->ecc, CR, ATMEL_ECC_RST);
-}
-
-static int atmel_of_init_ecc(struct atmel_nand_host *host,
-                            struct device_node *np)
-{
-       u32 offset[2];
-       u32 val;
-
-       host->has_pmecc = of_property_read_bool(np, "atmel,has-pmecc");
-
-       /* Not using PMECC */
-       if (!(host->nand_chip.ecc.mode == NAND_ECC_HW) || !host->has_pmecc)
-               return 0;
-
-       /* use PMECC, get correction capability, sector size and lookup
-        * table offset.
-        * If correction bits and sector size are not specified, then find
-        * them from NAND ONFI parameters.
-        */
-       if (of_property_read_u32(np, "atmel,pmecc-cap", &val) == 0) {
-               if (val > host->caps->pmecc_max_correction) {
-                       dev_err(host->dev,
-                               "Required ECC strength too high: %u max %u\n",
-                               val, host->caps->pmecc_max_correction);
-                       return -EINVAL;
-               }
-               if ((val != 2)  && (val != 4)  && (val != 8) &&
-                   (val != 12) && (val != 24) && (val != 32)) {
-                       dev_err(host->dev,
-                               "Required ECC strength not supported: %u\n",
-                               val);
-                       return -EINVAL;
-               }
-               host->pmecc_corr_cap = (u8)val;
-       }
-
-       if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) == 0) {
-               if ((val != 512) && (val != 1024)) {
-                       dev_err(host->dev,
-                               "Required ECC sector size not supported: %u\n",
-                               val);
-                       return -EINVAL;
-               }
-               host->pmecc_sector_size = (u16)val;
-       }
-
-       if (of_property_read_u32_array(np, "atmel,pmecc-lookup-table-offset",
-                       offset, 2) != 0) {
-               dev_err(host->dev, "Cannot get PMECC lookup table offset, will build a lookup table in runtime.\n");
-               host->has_no_lookup_table = true;
-               /* Will build a lookup table and initialize the offset later */
-               return 0;
-       }
-
-       if (!offset[0] && !offset[1]) {
-               dev_err(host->dev, "Invalid PMECC lookup table offset\n");
-               return -EINVAL;
-       }
-
-       host->pmecc_lookup_table_offset_512 = offset[0];
-       host->pmecc_lookup_table_offset_1024 = offset[1];
-
-       return 0;
-}
-
-static int atmel_of_init_port(struct atmel_nand_host *host,
-                             struct device_node *np)
-{
-       u32 val;
-       struct atmel_nand_data *board = &host->board;
-       enum of_gpio_flags flags = 0;
-
-       host->caps = (struct atmel_nand_caps *)
-               of_device_get_match_data(host->dev);
-
-       if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) {
-               if (val >= 32) {
-                       dev_err(host->dev, "invalid addr-offset %u\n", val);
-                       return -EINVAL;
-               }
-               board->ale = val;
-       }
-
-       if (of_property_read_u32(np, "atmel,nand-cmd-offset", &val) == 0) {
-               if (val >= 32) {
-                       dev_err(host->dev, "invalid cmd-offset %u\n", val);
-                       return -EINVAL;
-               }
-               board->cle = val;
-       }
-
-       board->has_dma = of_property_read_bool(np, "atmel,nand-has-dma");
-
-       board->rdy_pin = of_get_gpio_flags(np, 0, &flags);
-       board->rdy_pin_active_low = (flags == OF_GPIO_ACTIVE_LOW);
-
-       board->enable_pin = of_get_gpio(np, 1);
-       board->det_pin = of_get_gpio(np, 2);
-
-       /* load the nfc driver if there is */
-       of_platform_populate(np, NULL, NULL, host->dev);
-
-       /*
-        * Initialize ECC mode to NAND_ECC_SOFT so that we have a correct value
-        * even if the nand-ecc-mode property is not defined.
-        */
-       host->nand_chip.ecc.mode = NAND_ECC_SOFT;
-       host->nand_chip.ecc.algo = NAND_ECC_HAMMING;
-
-       return 0;
-}
-
-static int atmel_hw_nand_init_params(struct platform_device *pdev,
-                                        struct atmel_nand_host *host)
-{
-       struct nand_chip *nand_chip = &host->nand_chip;
-       struct mtd_info *mtd = nand_to_mtd(nand_chip);
-       struct resource         *regs;
-
-       regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       if (!regs) {
-               dev_err(host->dev,
-                       "Can't get I/O resource regs, use software ECC\n");
-               nand_chip->ecc.mode = NAND_ECC_SOFT;
-               nand_chip->ecc.algo = NAND_ECC_HAMMING;
-               return 0;
-       }
-
-       host->ecc = devm_ioremap_resource(&pdev->dev, regs);
-       if (IS_ERR(host->ecc))
-               return PTR_ERR(host->ecc);
-
-       /* ECC is calculated for the whole page (1 step) */
-       nand_chip->ecc.size = mtd->writesize;
-
-       /* set ECC page size and oob layout */
-       switch (mtd->writesize) {
-       case 512:
-               mtd_set_ooblayout(mtd, &atmel_ooblayout_sp_ops);
-               ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_528);
-               break;
-       case 1024:
-               mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
-               ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_1056);
-               break;
-       case 2048:
-               mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
-               ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_2112);
-               break;
-       case 4096:
-               mtd_set_ooblayout(mtd, &nand_ooblayout_lp_ops);
-               ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_4224);
-               break;
-       default:
-               /* page size not handled by HW ECC */
-               /* switching back to soft ECC */
-               nand_chip->ecc.mode = NAND_ECC_SOFT;
-               nand_chip->ecc.algo = NAND_ECC_HAMMING;
-               return 0;
-       }
-
-       /* set up for HW ECC */
-       nand_chip->ecc.calculate = atmel_nand_calculate;
-       nand_chip->ecc.correct = atmel_nand_correct;
-       nand_chip->ecc.hwctl = atmel_nand_hwctl;
-       nand_chip->ecc.read_page = atmel_nand_read_page;
-       nand_chip->ecc.bytes = 4;
-       nand_chip->ecc.strength = 1;
-
-       return 0;
-}
-
-static inline u32 nfc_read_status(struct atmel_nand_host *host)
-{
-       u32 err_flags = NFC_SR_DTOE | NFC_SR_UNDEF | NFC_SR_AWB | NFC_SR_ASE;
-       u32 nfc_status = nfc_readl(host->nfc->hsmc_regs, SR);
-
-       if (unlikely(nfc_status & err_flags)) {
-               if (nfc_status & NFC_SR_DTOE)
-                       dev_err(host->dev, "NFC: Waiting Nand R/B Timeout Error\n");
-               else if (nfc_status & NFC_SR_UNDEF)
-                       dev_err(host->dev, "NFC: Access Undefined Area Error\n");
-               else if (nfc_status & NFC_SR_AWB)
-                       dev_err(host->dev, "NFC: Access memory While NFC is busy\n");
-               else if (nfc_status & NFC_SR_ASE)
-                       dev_err(host->dev, "NFC: Access memory Size Error\n");
-       }
-
-       return nfc_status;
-}
-
-/* SMC interrupt service routine */
-static irqreturn_t hsmc_interrupt(int irq, void *dev_id)
-{
-       struct atmel_nand_host *host = dev_id;
-       u32 status, mask, pending;
-       irqreturn_t ret = IRQ_NONE;
-
-       status = nfc_read_status(host);
-       mask = nfc_readl(host->nfc->hsmc_regs, IMR);
-       pending = status & mask;
-
-       if (pending & NFC_SR_XFR_DONE) {
-               complete(&host->nfc->comp_xfer_done);
-               nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_XFR_DONE);
-               ret = IRQ_HANDLED;
-       }
-       if (pending & NFC_SR_RB_EDGE) {
-               complete(&host->nfc->comp_ready);
-               nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_RB_EDGE);
-               ret = IRQ_HANDLED;
-       }
-       if (pending & NFC_SR_CMD_DONE) {
-               complete(&host->nfc->comp_cmd_done);
-               nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_CMD_DONE);
-               ret = IRQ_HANDLED;
-       }
-
-       return ret;
-}
-
-/* NFC(Nand Flash Controller) related functions */
-static void nfc_prepare_interrupt(struct atmel_nand_host *host, u32 flag)
-{
-       if (flag & NFC_SR_XFR_DONE)
-               init_completion(&host->nfc->comp_xfer_done);
-
-       if (flag & NFC_SR_RB_EDGE)
-               init_completion(&host->nfc->comp_ready);
-
-       if (flag & NFC_SR_CMD_DONE)
-               init_completion(&host->nfc->comp_cmd_done);
-
-       /* Enable interrupt that need to wait for */
-       nfc_writel(host->nfc->hsmc_regs, IER, flag);
-}
-
-static int nfc_wait_interrupt(struct atmel_nand_host *host, u32 flag)
-{
-       int i, index = 0;
-       struct completion *comp[3];     /* Support 3 interrupt completion */
-
-       if (flag & NFC_SR_XFR_DONE)
-               comp[index++] = &host->nfc->comp_xfer_done;
-
-       if (flag & NFC_SR_RB_EDGE)
-               comp[index++] = &host->nfc->comp_ready;
-
-       if (flag & NFC_SR_CMD_DONE)
-               comp[index++] = &host->nfc->comp_cmd_done;
-
-       if (index == 0) {
-               dev_err(host->dev, "Unknown interrupt flag: 0x%08x\n", flag);
-               return -EINVAL;
-       }
-
-       for (i = 0; i < index; i++) {
-               if (wait_for_completion_timeout(comp[i],
-                               msecs_to_jiffies(NFC_TIME_OUT_MS)))
-                       continue;       /* wait for next completion */
-               else
-                       goto err_timeout;
-       }
-
-       return 0;
-
-err_timeout:
-       dev_err(host->dev, "Time out to wait for interrupt: 0x%08x\n", flag);
-       /* Disable the interrupt as it is not handled by interrupt handler */
-       nfc_writel(host->nfc->hsmc_regs, IDR, flag);
-       return -ETIMEDOUT;
-}
-
-static int nfc_send_command(struct atmel_nand_host *host,
-       unsigned int cmd, unsigned int addr, unsigned char cycle0)
-{
-       unsigned long timeout;
-       u32 flag = NFC_SR_CMD_DONE;
-       flag |= cmd & NFCADDR_CMD_DATAEN ? NFC_SR_XFR_DONE : 0;
-
-       dev_dbg(host->dev,
-               "nfc_cmd: 0x%08x, addr1234: 0x%08x, cycle0: 0x%02x\n",
-               cmd, addr, cycle0);
-
-       timeout = jiffies + msecs_to_jiffies(NFC_TIME_OUT_MS);
-       while (nfc_readl(host->nfc->hsmc_regs, SR) & NFC_SR_BUSY) {
-               if (time_after(jiffies, timeout)) {
-                       dev_err(host->dev,
-                               "Time out to wait for NFC ready!\n");
-                       return -ETIMEDOUT;
-               }
-       }
-
-       nfc_prepare_interrupt(host, flag);
-       nfc_writel(host->nfc->hsmc_regs, CYCLE0, cycle0);
-       nfc_cmd_addr1234_writel(cmd, addr, host->nfc->base_cmd_regs);
-       return nfc_wait_interrupt(host, flag);
-}
-
-static int nfc_device_ready(struct mtd_info *mtd)
-{
-       u32 status, mask;
-       struct nand_chip *nand_chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
-
-       status = nfc_read_status(host);
-       mask = nfc_readl(host->nfc->hsmc_regs, IMR);
-
-       /* The mask should be 0. If not we may lost interrupts */
-       if (unlikely(mask & status))
-               dev_err(host->dev, "Lost the interrupt flags: 0x%08x\n",
-                               mask & status);
-
-       return status & NFC_SR_RB_EDGE;
-}
-
-static void nfc_select_chip(struct mtd_info *mtd, int chip)
-{
-       struct nand_chip *nand_chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(nand_chip);
-
-       if (chip == -1)
-               nfc_writel(host->nfc->hsmc_regs, CTRL, NFC_CTRL_DISABLE);
-       else
-               nfc_writel(host->nfc->hsmc_regs, CTRL, NFC_CTRL_ENABLE);
-}
-
-static int nfc_make_addr(struct mtd_info *mtd, int command, int column,
-               int page_addr, unsigned int *addr1234, unsigned int *cycle0)
-{
-       struct nand_chip *chip = mtd_to_nand(mtd);
-
-       int acycle = 0;
-       unsigned char addr_bytes[8];
-       int index = 0, bit_shift;
-
-       BUG_ON(addr1234 == NULL || cycle0 == NULL);
-
-       *cycle0 = 0;
-       *addr1234 = 0;
-
-       if (column != -1) {
-               if (chip->options & NAND_BUSWIDTH_16 &&
-                               !nand_opcode_8bits(command))
-                       column >>= 1;
-               addr_bytes[acycle++] = column & 0xff;
-               if (mtd->writesize > 512)
-                       addr_bytes[acycle++] = (column >> 8) & 0xff;
-       }
-
-       if (page_addr != -1) {
-               addr_bytes[acycle++] = page_addr & 0xff;
-               addr_bytes[acycle++] = (page_addr >> 8) & 0xff;
-               if (chip->chipsize > (128 << 20))
-                       addr_bytes[acycle++] = (page_addr >> 16) & 0xff;
-       }
-
-       if (acycle > 4)
-               *cycle0 = addr_bytes[index++];
-
-       for (bit_shift = 0; index < acycle; bit_shift += 8)
-               *addr1234 += addr_bytes[index++] << bit_shift;
-
-       /* return acycle in cmd register */
-       return acycle << NFCADDR_CMD_ACYCLE_BIT_POS;
-}
-
-static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
-                               int column, int page_addr)
-{
-       struct nand_chip *chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(chip);
-       unsigned long timeout;
-       unsigned int nfc_addr_cmd = 0;
-
-       unsigned int cmd1 = command << NFCADDR_CMD_CMD1_BIT_POS;
-
-       /* Set default settings: no cmd2, no addr cycle. read from nand */
-       unsigned int cmd2 = 0;
-       unsigned int vcmd2 = 0;
-       int acycle = NFCADDR_CMD_ACYCLE_NONE;
-       int csid = NFCADDR_CMD_CSID_3;
-       int dataen = NFCADDR_CMD_DATADIS;
-       int nfcwr = NFCADDR_CMD_NFCRD;
-       unsigned int addr1234 = 0;
-       unsigned int cycle0 = 0;
-       bool do_addr = true;
-       host->nfc->data_in_sram = NULL;
-
-       dev_dbg(host->dev, "%s: cmd = 0x%02x, col = 0x%08x, page = 0x%08x\n",
-            __func__, command, column, page_addr);
-
-       switch (command) {
-       case NAND_CMD_RESET:
-               nfc_addr_cmd = cmd1 | acycle | csid | dataen | nfcwr;
-               nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0);
-               udelay(chip->chip_delay);
-
-               nfc_nand_command(mtd, NAND_CMD_STATUS, -1, -1);
-               timeout = jiffies + msecs_to_jiffies(NFC_TIME_OUT_MS);
-               while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) {
-                       if (time_after(jiffies, timeout)) {
-                               dev_err(host->dev,
-                                       "Time out to wait status ready!\n");
-                               break;
-                       }
-               }
-               return;
-       case NAND_CMD_STATUS:
-               do_addr = false;
-               break;
-       case NAND_CMD_PARAM:
-       case NAND_CMD_READID:
-               do_addr = false;
-               acycle = NFCADDR_CMD_ACYCLE_1;
-               if (column != -1)
-                       addr1234 = column;
-               break;
-       case NAND_CMD_RNDOUT:
-               cmd2 = NAND_CMD_RNDOUTSTART << NFCADDR_CMD_CMD2_BIT_POS;
-               vcmd2 = NFCADDR_CMD_VCMD2;
-               break;
-       case NAND_CMD_READ0:
-       case NAND_CMD_READOOB:
-               if (command == NAND_CMD_READOOB) {
-                       column += mtd->writesize;
-                       command = NAND_CMD_READ0; /* only READ0 is valid */
-                       cmd1 = command << NFCADDR_CMD_CMD1_BIT_POS;
-               }
-               if (host->nfc->use_nfc_sram) {
-                       /* Enable Data transfer to sram */
-                       dataen = NFCADDR_CMD_DATAEN;
-
-                       /* Need enable PMECC now, since NFC will transfer
-                        * data in bus after sending nfc read command.
-                        */
-                       if (chip->ecc.mode == NAND_ECC_HW && host->has_pmecc)
-                               pmecc_enable(host, NAND_ECC_READ);
-               }
-
-               cmd2 = NAND_CMD_READSTART << NFCADDR_CMD_CMD2_BIT_POS;
-               vcmd2 = NFCADDR_CMD_VCMD2;
-               break;
-       /* For prgramming command, the cmd need set to write enable */
-       case NAND_CMD_PAGEPROG:
-       case NAND_CMD_SEQIN:
-       case NAND_CMD_RNDIN:
-               nfcwr = NFCADDR_CMD_NFCWR;
-               if (host->nfc->will_write_sram && command == NAND_CMD_SEQIN)
-                       dataen = NFCADDR_CMD_DATAEN;
-               break;
-       default:
-               break;
-       }
-
-       if (do_addr)
-               acycle = nfc_make_addr(mtd, command, column, page_addr,
-                               &addr1234, &cycle0);
-
-       nfc_addr_cmd = cmd1 | cmd2 | vcmd2 | acycle | csid | dataen | nfcwr;
-       nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0);
-
-       /*
-        * Program and erase have their own busy handlers status, sequential
-        * in, and deplete1 need no delay.
-        */
-       switch (command) {
-       case NAND_CMD_CACHEDPROG:
-       case NAND_CMD_PAGEPROG:
-       case NAND_CMD_ERASE1:
-       case NAND_CMD_ERASE2:
-       case NAND_CMD_RNDIN:
-       case NAND_CMD_STATUS:
-       case NAND_CMD_RNDOUT:
-       case NAND_CMD_SEQIN:
-       case NAND_CMD_READID:
-               return;
-
-       case NAND_CMD_READ0:
-               if (dataen == NFCADDR_CMD_DATAEN) {
-                       host->nfc->data_in_sram = host->nfc->sram_bank0 +
-                               nfc_get_sram_off(host);
-                       return;
-               }
-               /* fall through */
-       default:
-               nfc_prepare_interrupt(host, NFC_SR_RB_EDGE);
-               nfc_wait_interrupt(host, NFC_SR_RB_EDGE);
-       }
-}
-
-static int nfc_sram_write_page(struct mtd_info *mtd, struct nand_chip *chip,
-                       uint32_t offset, int data_len, const uint8_t *buf,
-                       int oob_required, int page, int cached, int raw)
-{
-       int cfg, len;
-       int status = 0;
-       struct atmel_nand_host *host = nand_get_controller_data(chip);
-       void *sram = host->nfc->sram_bank0 + nfc_get_sram_off(host);
-
-       /* Subpage write is not supported */
-       if (offset || (data_len < mtd->writesize))
-               return -EINVAL;
-
-       len = mtd->writesize;
-       /* Copy page data to sram that will write to nand via NFC */
-       if (use_dma) {
-               if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) != 0)
-                       /* Fall back to use cpu copy */
-                       memcpy(sram, buf, len);
-       } else {
-               memcpy(sram, buf, len);
-       }
-
-       cfg = nfc_readl(host->nfc->hsmc_regs, CFG);
-       if (unlikely(raw) && oob_required) {
-               memcpy(sram + len, chip->oob_poi, mtd->oobsize);
-               len += mtd->oobsize;
-               nfc_writel(host->nfc->hsmc_regs, CFG, cfg | NFC_CFG_WSPARE);
-       } else {
-               nfc_writel(host->nfc->hsmc_regs, CFG, cfg & ~NFC_CFG_WSPARE);
-       }
-
-       if (chip->ecc.mode == NAND_ECC_HW && host->has_pmecc)
-               /*
-                * When use NFC sram, need set up PMECC before send
-                * NAND_CMD_SEQIN command. Since when the nand command
-                * is sent, nfc will do transfer from sram and nand.
-                */
-               pmecc_enable(host, NAND_ECC_WRITE);
-
-       host->nfc->will_write_sram = true;
-       chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
-       host->nfc->will_write_sram = false;
-
-       if (likely(!raw))
-               /* Need to write ecc into oob */
-               status = chip->ecc.write_page(mtd, chip, buf, oob_required,
-                                             page);
-
-       if (status < 0)
-               return status;
-
-       chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
-       status = chip->waitfunc(mtd, chip);
-
-       if ((status & NAND_STATUS_FAIL) && (chip->errstat))
-               status = chip->errstat(mtd, chip, FL_WRITING, status, page);
-
-       if (status & NAND_STATUS_FAIL)
-               return -EIO;
-
-       return 0;
-}
-
-static int nfc_sram_init(struct mtd_info *mtd)
-{
-       struct nand_chip *chip = mtd_to_nand(mtd);
-       struct atmel_nand_host *host = nand_get_controller_data(chip);
-       int res = 0;
-
-       /* Initialize the NFC CFG register */
-       unsigned int cfg_nfc = 0;
-
-       /* set page size and oob layout */
-       switch (mtd->writesize) {
-       case 512:
-               cfg_nfc = NFC_CFG_PAGESIZE_512;
-               break;
-       case 1024:
-               cfg_nfc = NFC_CFG_PAGESIZE_1024;
-               break;
-       case 2048:
-               cfg_nfc = NFC_CFG_PAGESIZE_2048;
-               break;
-       case 4096:
-               cfg_nfc = NFC_CFG_PAGESIZE_4096;
-               break;
-       case 8192:
-               cfg_nfc = NFC_CFG_PAGESIZE_8192;
-               break;
-       default:
-               dev_err(host->dev, "Unsupported page size for NFC.\n");
-               res = -ENXIO;
-               return res;
-       }
-
-       /* oob bytes size = (NFCSPARESIZE + 1) * 4
-        * Max support spare size is 512 bytes. */
-       cfg_nfc |= (((mtd->oobsize / 4) - 1) << NFC_CFG_NFC_SPARESIZE_BIT_POS
-               & NFC_CFG_NFC_SPARESIZE);
-       /* default set a max timeout */
-       cfg_nfc |= NFC_CFG_RSPARE |
-                       NFC_CFG_NFC_DTOCYC | NFC_CFG_NFC_DTOMUL;
-
-       nfc_writel(host->nfc->hsmc_regs, CFG, cfg_nfc);
-
-       host->nfc->will_write_sram = false;
-       nfc_set_sram_bank(host, 0);
-
-       /* Use Write page with NFC SRAM only for PMECC or ECC NONE. */
-       if (host->nfc->write_by_sram) {
-               if ((chip->ecc.mode == NAND_ECC_HW && host->has_pmecc) ||
-                               chip->ecc.mode == NAND_ECC_NONE)
-                       chip->write_page = nfc_sram_write_page;
-               else
-                       host->nfc->write_by_sram = false;
-       }
-
-       dev_info(host->dev, "Using NFC Sram read %s\n",
-                       host->nfc->write_by_sram ? "and write" : "");
-       return 0;
-}
-
-static struct platform_driver atmel_nand_nfc_driver;
-/*
- * Probe for the NAND device.
- */
-static int atmel_nand_probe(struct platform_device *pdev)
-{
-       struct atmel_nand_host *host;
-       struct mtd_info *mtd;
-       struct nand_chip *nand_chip;
-       struct resource *mem;
-       int res, irq;
-
-       /* Allocate memory for the device structure (and zero it) */
-       host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
-       if (!host)
-               return -ENOMEM;
-
-       res = platform_driver_register(&atmel_nand_nfc_driver);
-       if (res)
-               dev_err(&pdev->dev, "atmel_nand: can't register NFC driver\n");
-
-       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       host->io_base = devm_ioremap_resource(&pdev->dev, mem);
-       if (IS_ERR(host->io_base)) {
-               res = PTR_ERR(host->io_base);
-               goto err_nand_ioremap;
-       }
-       host->io_phys = (dma_addr_t)mem->start;
-
-       nand_chip = &host->nand_chip;
-       mtd = nand_to_mtd(nand_chip);
-       host->dev = &pdev->dev;
-       if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
-               nand_set_flash_node(nand_chip, pdev->dev.of_node);
-               /* Only when CONFIG_OF is enabled of_node can be parsed */
-               res = atmel_of_init_port(host, pdev->dev.of_node);
-               if (res)
-                       goto err_nand_ioremap;
-       } else {
-               memcpy(&host->board, dev_get_platdata(&pdev->dev),
-                      sizeof(struct atmel_nand_data));
-               nand_chip->ecc.mode = host->board.ecc_mode;
-
-               /*
-                * When using software ECC every supported avr32 board means
-                * Hamming algorithm. If that ever changes we'll need to add
-                * ecc_algo field to the struct atmel_nand_data.
-                */
-               if (nand_chip->ecc.mode == NAND_ECC_SOFT)
-                       nand_chip->ecc.algo = NAND_ECC_HAMMING;
-
-               /* 16-bit bus width */
-               if (host->board.bus_width_16)
-                       nand_chip->options |= NAND_BUSWIDTH_16;
-       }
-
-        /* link the private data structures */
-       nand_set_controller_data(nand_chip, host);
-       mtd->dev.parent = &pdev->dev;
-
-       /* Set address of NAND IO lines */
-       nand_chip->IO_ADDR_R = host->io_base;
-       nand_chip->IO_ADDR_W = host->io_base;
-
-       if (nand_nfc.is_initialized) {
-               /* NFC driver is probed and initialized */
-               host->nfc = &nand_nfc;
-
-               nand_chip->select_chip = nfc_select_chip;
-               nand_chip->dev_ready = nfc_device_ready;
-               nand_chip->cmdfunc = nfc_nand_command;
-
-               /* Initialize the interrupt for NFC */
-               irq = platform_get_irq(pdev, 0);
-               if (irq < 0) {
-                       dev_err(host->dev, "Cannot get HSMC irq!\n");
-                       res = irq;
-                       goto err_nand_ioremap;
-               }
-
-               res = devm_request_irq(&pdev->dev, irq, hsmc_interrupt,
-                               0, "hsmc", host);
-               if (res) {
-                       dev_err(&pdev->dev, "Unable to request HSMC irq %d\n",
-                               irq);
-                       goto err_nand_ioremap;
-               }
-       } else {
-               res = atmel_nand_set_enable_ready_pins(mtd);
-               if (res)
-                       goto err_nand_ioremap;
-
-               nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl;
-       }
-
-       nand_chip->chip_delay = 40;             /* 40us command delay time */
-
-
-       nand_chip->read_buf = atmel_read_buf;
-       nand_chip->write_buf = atmel_write_buf;
-
-       platform_set_drvdata(pdev, host);
-       atmel_nand_enable(host);
-
-       if (gpio_is_valid(host->board.det_pin)) {
-               res = devm_gpio_request(&pdev->dev,
-                               host->board.det_pin, "nand_det");
-               if (res < 0) {
-                       dev_err(&pdev->dev,
-                               "can't request det gpio %d\n",
-                               host->board.det_pin);
-                       goto err_no_card;
-               }
-
-               res = gpio_direction_input(host->board.det_pin);
-               if (res < 0) {
-                       dev_err(&pdev->dev,
-                               "can't request input direction det gpio %d\n",
-                               host->board.det_pin);
-                       goto err_no_card;
-               }
-
-               if (gpio_get_value(host->board.det_pin)) {
-                       dev_info(&pdev->dev, "No SmartMedia card inserted.\n");
-                       res = -ENXIO;
-                       goto err_no_card;
-               }
-       }
-
-       if (!host->board.has_dma)
-               use_dma = 0;
-
-       if (use_dma) {
-               dma_cap_mask_t mask;
-
-               dma_cap_zero(mask);
-               dma_cap_set(DMA_MEMCPY, mask);
-               host->dma_chan = dma_request_channel(mask, NULL, NULL);
-               if (!host->dma_chan) {
-                       dev_err(host->dev, "Failed to request DMA channel\n");
-                       use_dma = 0;
-               }
-       }
-       if (use_dma)
-               dev_info(host->dev, "Using %s for DMA transfers.\n",
-                                       dma_chan_name(host->dma_chan));
-       else
-               dev_info(host->dev, "No DMA support for NAND access.\n");
-
-       /* first scan to find the device and get the page size */
-       res = nand_scan_ident(mtd, 1, NULL);
-       if (res)
-               goto err_scan_ident;
-
-       if (host->board.on_flash_bbt || on_flash_bbt)
-               nand_chip->bbt_options |= NAND_BBT_USE_FLASH;
-
-       if (nand_chip->bbt_options & NAND_BBT_USE_FLASH)
-               dev_info(&pdev->dev, "Use On Flash BBT\n");
-
-       if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
-               res = atmel_of_init_ecc(host, pdev->dev.of_node);
-               if (res)
-                       goto err_hw_ecc;
-       }
-
-       if (nand_chip->ecc.mode == NAND_ECC_HW) {
-               if (host->has_pmecc)
-                       res = atmel_pmecc_nand_init_params(pdev, host);
-               else
-                       res = atmel_hw_nand_init_params(pdev, host);
-
-               if (res != 0)
-                       goto err_hw_ecc;
-       }
-
-       /* initialize the nfc configuration register */
-       if (host->nfc && host->nfc->use_nfc_sram) {
-               res = nfc_sram_init(mtd);
-               if (res) {
-                       host->nfc->use_nfc_sram = false;
-                       dev_err(host->dev, "Disable use nfc sram for data transfer.\n");
-               }
-       }
-
-       /* second phase scan */
-       res = nand_scan_tail(mtd);
-       if (res)
-               goto err_scan_tail;
-
-       mtd->name = "atmel_nand";
-       res = mtd_device_register(mtd, host->board.parts,
-                                 host->board.num_parts);
-       if (!res)
-               return res;
-
-err_scan_tail:
-       if (host->has_pmecc && host->nand_chip.ecc.mode == NAND_ECC_HW)
-               pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
-err_hw_ecc:
-err_scan_ident:
-err_no_card:
-       atmel_nand_disable(host);
-       if (host->dma_chan)
-               dma_release_channel(host->dma_chan);
-err_nand_ioremap:
-       return res;
-}
-
-/*
- * Remove a NAND device.
- */
-static int atmel_nand_remove(struct platform_device *pdev)
-{
-       struct atmel_nand_host *host = platform_get_drvdata(pdev);
-       struct mtd_info *mtd = nand_to_mtd(&host->nand_chip);
-
-       nand_release(mtd);
-
-       atmel_nand_disable(host);
-
-       if (host->has_pmecc && host->nand_chip.ecc.mode == NAND_ECC_HW) {
-               pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
-               pmerrloc_writel(host->pmerrloc_base, ELDIS,
-                               PMERRLOC_DISABLE);
-       }
-
-       if (host->dma_chan)
-               dma_release_channel(host->dma_chan);
-
-       platform_driver_unregister(&atmel_nand_nfc_driver);
-
-       return 0;
-}
-
-/*
- * AT91RM9200 does not have PMECC or PMECC Errloc peripherals for
- * BCH ECC. Combined with the "atmel,has-pmecc", it is used to describe
- * devices from the SAM9 family that have those.
- */
-static const struct atmel_nand_caps at91rm9200_caps = {
-       .pmecc_correct_erase_page = false,
-       .pmecc_max_correction = 24,
-};
-
-static const struct atmel_nand_caps sama5d4_caps = {
-       .pmecc_correct_erase_page = true,
-       .pmecc_max_correction = 24,
-};
-
-/*
- * The PMECC Errloc controller starting in SAMA5D2 is not compatible,
- * as the increased correction strength requires more registers.
- */
-static const struct atmel_nand_caps sama5d2_caps = {
-       .pmecc_correct_erase_page = true,
-       .pmecc_max_correction = 32,
-};
-
-static const struct of_device_id atmel_nand_dt_ids[] = {
-       { .compatible = "atmel,at91rm9200-nand", .data = &at91rm9200_caps },
-       { .compatible = "atmel,sama5d4-nand", .data = &sama5d4_caps },
-       { .compatible = "atmel,sama5d2-nand", .data = &sama5d2_caps },
-       { /* sentinel */ }
-};
-
-MODULE_DEVICE_TABLE(of, atmel_nand_dt_ids);
-
-static int atmel_nand_nfc_probe(struct platform_device *pdev)
-{
-       struct atmel_nfc *nfc = &nand_nfc;
-       struct resource *nfc_cmd_regs, *nfc_hsmc_regs, *nfc_sram;
-       int ret;
-
-       nfc_cmd_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       nfc->base_cmd_regs = devm_ioremap_resource(&pdev->dev, nfc_cmd_regs);
-       if (IS_ERR(nfc->base_cmd_regs))
-               return PTR_ERR(nfc->base_cmd_regs);
-
-       nfc_hsmc_regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       nfc->hsmc_regs = devm_ioremap_resource(&pdev->dev, nfc_hsmc_regs);
-       if (IS_ERR(nfc->hsmc_regs))
-               return PTR_ERR(nfc->hsmc_regs);
-
-       nfc_sram = platform_get_resource(pdev, IORESOURCE_MEM, 2);
-       if (nfc_sram) {
-               nfc->sram_bank0 = (void * __force)
-                               devm_ioremap_resource(&pdev->dev, nfc_sram);
-               if (IS_ERR(nfc->sram_bank0)) {
-                       dev_warn(&pdev->dev, "Fail to ioremap the NFC sram with error: %ld. So disable NFC sram.\n",
-                                       PTR_ERR(nfc->sram_bank0));
-               } else {
-                       nfc->use_nfc_sram = true;
-                       nfc->sram_bank0_phys = (dma_addr_t)nfc_sram->start;
-
-                       if (pdev->dev.of_node)
-                               nfc->write_by_sram = of_property_read_bool(
-                                               pdev->dev.of_node,
-                                               "atmel,write-by-sram");
-               }
-       }
-
-       nfc_writel(nfc->hsmc_regs, IDR, 0xffffffff);
-       nfc_readl(nfc->hsmc_regs, SR);  /* clear the NFC_SR */
-
-       nfc->clk = devm_clk_get(&pdev->dev, NULL);
-       if (!IS_ERR(nfc->clk)) {
-               ret = clk_prepare_enable(nfc->clk);
-               if (ret)
-                       return ret;
-       } else {
-               dev_warn(&pdev->dev, "NFC clock missing, update your Device Tree");
-       }
-
-       nfc->is_initialized = true;
-       dev_info(&pdev->dev, "NFC is probed.\n");
-
-       return 0;
-}
-
-static int atmel_nand_nfc_remove(struct platform_device *pdev)
-{
-       struct atmel_nfc *nfc = &nand_nfc;
-
-       if (!IS_ERR(nfc->clk))
-               clk_disable_unprepare(nfc->clk);
-
-       return 0;
-}
-
-static const struct of_device_id atmel_nand_nfc_match[] = {
-       { .compatible = "atmel,sama5d3-nfc" },
-       { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, atmel_nand_nfc_match);
-
-static struct platform_driver atmel_nand_nfc_driver = {
-       .driver = {
-               .name = "atmel_nand_nfc",
-               .of_match_table = of_match_ptr(atmel_nand_nfc_match),
-       },
-       .probe = atmel_nand_nfc_probe,
-       .remove = atmel_nand_nfc_remove,
-};
-
-static struct platform_driver atmel_nand_driver = {
-       .probe          = atmel_nand_probe,
-       .remove         = atmel_nand_remove,
-       .driver         = {
-               .name   = "atmel_nand",
-               .of_match_table = of_match_ptr(atmel_nand_dt_ids),
-       },
-};
-
-module_platform_driver(atmel_nand_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Rick Bronson");
-MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91 / AVR32");
-MODULE_ALIAS("platform:atmel_nand");
diff --git a/drivers/mtd/nand/atmel_nand_ecc.h b/drivers/mtd/nand/atmel_nand_ecc.h
deleted file mode 100644 (file)
index 834d694..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * Error Corrected Code Controller (ECC) - System peripherals regsters.
- * Based on AT91SAM9260 datasheet revision B.
- *
- * Copyright (C) 2007 Andrew Victor
- * Copyright (C) 2007 - 2012 Atmel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#ifndef ATMEL_NAND_ECC_H
-#define ATMEL_NAND_ECC_H
-
-#define ATMEL_ECC_CR           0x00                    /* Control register */
-#define                ATMEL_ECC_RST           (1 << 0)                /* Reset parity */
-
-#define ATMEL_ECC_MR           0x04                    /* Mode register */
-#define                ATMEL_ECC_PAGESIZE      (3 << 0)                /* Page Size */
-#define                        ATMEL_ECC_PAGESIZE_528          (0)
-#define                        ATMEL_ECC_PAGESIZE_1056         (1)
-#define                        ATMEL_ECC_PAGESIZE_2112         (2)
-#define                        ATMEL_ECC_PAGESIZE_4224         (3)
-
-#define ATMEL_ECC_SR           0x08                    /* Status register */
-#define                ATMEL_ECC_RECERR                (1 << 0)                /* Recoverable Error */
-#define                ATMEL_ECC_ECCERR                (1 << 1)                /* ECC Single Bit Error */
-#define                ATMEL_ECC_MULERR                (1 << 2)                /* Multiple Errors */
-
-#define ATMEL_ECC_PR           0x0c                    /* Parity register */
-#define                ATMEL_ECC_BITADDR       (0xf << 0)              /* Bit Error Address */
-#define                ATMEL_ECC_WORDADDR      (0xfff << 4)            /* Word Error Address */
-
-#define ATMEL_ECC_NPR          0x10                    /* NParity register */
-#define                ATMEL_ECC_NPARITY       (0xffff << 0)           /* NParity */
-
-/* PMECC Register Definitions */
-#define ATMEL_PMECC_CFG                        0x000   /* Configuration Register */
-#define                PMECC_CFG_BCH_ERR2              (0 << 0)
-#define                PMECC_CFG_BCH_ERR4              (1 << 0)
-#define                PMECC_CFG_BCH_ERR8              (2 << 0)
-#define                PMECC_CFG_BCH_ERR12             (3 << 0)
-#define                PMECC_CFG_BCH_ERR24             (4 << 0)
-#define                PMECC_CFG_BCH_ERR32             (5 << 0)
-
-#define                PMECC_CFG_SECTOR512             (0 << 4)
-#define                PMECC_CFG_SECTOR1024            (1 << 4)
-
-#define                PMECC_CFG_PAGE_1SECTOR          (0 << 8)
-#define                PMECC_CFG_PAGE_2SECTORS         (1 << 8)
-#define                PMECC_CFG_PAGE_4SECTORS         (2 << 8)
-#define                PMECC_CFG_PAGE_8SECTORS         (3 << 8)
-
-#define                PMECC_CFG_READ_OP               (0 << 12)
-#define                PMECC_CFG_WRITE_OP              (1 << 12)
-
-#define                PMECC_CFG_SPARE_ENABLE          (1 << 16)
-#define                PMECC_CFG_SPARE_DISABLE         (0 << 16)
-
-#define                PMECC_CFG_AUTO_ENABLE           (1 << 20)
-#define                PMECC_CFG_AUTO_DISABLE          (0 << 20)
-
-#define ATMEL_PMECC_SAREA              0x004   /* Spare area size */
-#define ATMEL_PMECC_SADDR              0x008   /* PMECC starting address */
-#define ATMEL_PMECC_EADDR              0x00c   /* PMECC ending address */
-#define ATMEL_PMECC_CLK                        0x010   /* PMECC clock control */
-#define                PMECC_CLK_133MHZ                (2 << 0)
-
-#define ATMEL_PMECC_CTRL               0x014   /* PMECC control register */
-#define                PMECC_CTRL_RST                  (1 << 0)
-#define                PMECC_CTRL_DATA                 (1 << 1)
-#define                PMECC_CTRL_USER                 (1 << 2)
-#define                PMECC_CTRL_ENABLE               (1 << 4)
-#define                PMECC_CTRL_DISABLE              (1 << 5)
-
-#define ATMEL_PMECC_SR                 0x018   /* PMECC status register */
-#define                PMECC_SR_BUSY                   (1 << 0)
-#define                PMECC_SR_ENABLE                 (1 << 4)
-
-#define ATMEL_PMECC_IER                        0x01c   /* PMECC interrupt enable */
-#define                PMECC_IER_ENABLE                (1 << 0)
-#define ATMEL_PMECC_IDR                        0x020   /* PMECC interrupt disable */
-#define                PMECC_IER_DISABLE               (1 << 0)
-#define ATMEL_PMECC_IMR                        0x024   /* PMECC interrupt mask */
-#define                PMECC_IER_MASK                  (1 << 0)
-#define ATMEL_PMECC_ISR                        0x028   /* PMECC interrupt status */
-#define ATMEL_PMECC_ECCx               0x040   /* PMECC ECC x */
-#define ATMEL_PMECC_REMx               0x240   /* PMECC REM x */
-
-/* PMERRLOC Register Definitions */
-#define ATMEL_PMERRLOC_ELCFG           0x000   /* Error location config */
-#define                PMERRLOC_ELCFG_SECTOR_512       (0 << 0)
-#define                PMERRLOC_ELCFG_SECTOR_1024      (1 << 0)
-#define                PMERRLOC_ELCFG_NUM_ERRORS(n)    ((n) << 16)
-
-#define ATMEL_PMERRLOC_ELPRIM          0x004   /* Error location primitive */
-#define ATMEL_PMERRLOC_ELEN            0x008   /* Error location enable */
-#define ATMEL_PMERRLOC_ELDIS           0x00c   /* Error location disable */
-#define                PMERRLOC_DISABLE                (1 << 0)
-
-#define ATMEL_PMERRLOC_ELSR            0x010   /* Error location status */
-#define                PMERRLOC_ELSR_BUSY              (1 << 0)
-#define ATMEL_PMERRLOC_ELIER           0x014   /* Error location int enable */
-#define ATMEL_PMERRLOC_ELIDR           0x018   /* Error location int disable */
-#define ATMEL_PMERRLOC_ELIMR           0x01c   /* Error location int mask */
-#define ATMEL_PMERRLOC_ELISR           0x020   /* Error location int status */
-#define                PMERRLOC_ERR_NUM_MASK           (0x1f << 8)
-#define                PMERRLOC_CALC_DONE              (1 << 0)
-#define ATMEL_PMERRLOC_SIGMAx          0x028   /* Error location SIGMA x */
-
-/*
- * The ATMEL_PMERRLOC_ELx register location depends from the number of
- * bits corrected by the PMECC controller. Do not use it.
- */
-
-/* Register access macros for PMECC */
-#define pmecc_readl_relaxed(addr, reg) \
-       readl_relaxed((addr) + ATMEL_PMECC_##reg)
-
-#define pmecc_writel(addr, reg, value) \
-       writel((value), (addr) + ATMEL_PMECC_##reg)
-
-#define pmecc_readb_ecc_relaxed(addr, sector, n) \
-       readb_relaxed((addr) + ATMEL_PMECC_ECCx + ((sector) * 0x40) + (n))
-
-#define pmecc_readl_rem_relaxed(addr, sector, n) \
-       readl_relaxed((addr) + ATMEL_PMECC_REMx + ((sector) * 0x40) + ((n) * 4))
-
-#define pmerrloc_readl_relaxed(addr, reg) \
-       readl_relaxed((addr) + ATMEL_PMERRLOC_##reg)
-
-#define pmerrloc_writel(addr, reg, value) \
-       writel((value), (addr) + ATMEL_PMERRLOC_##reg)
-
-#define pmerrloc_writel_sigma_relaxed(addr, n, value) \
-       writel_relaxed((value), (addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4))
-
-#define pmerrloc_readl_sigma_relaxed(addr, n) \
-       readl_relaxed((addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4))
-
-#define pmerrloc_readl_el_relaxed(addr, n) \
-       readl_relaxed((addr) + ((n) * 4))
-
-/* Galois field dimension */
-#define PMECC_GF_DIMENSION_13                  13
-#define PMECC_GF_DIMENSION_14                  14
-
-/* Primitive Polynomial used by PMECC */
-#define PMECC_GF_13_PRIMITIVE_POLY             0x201b
-#define PMECC_GF_14_PRIMITIVE_POLY             0x4443
-
-#define PMECC_LOOKUP_TABLE_SIZE_512            0x2000
-#define PMECC_LOOKUP_TABLE_SIZE_1024           0x4000
-
-/* Time out value for reading PMECC status register */
-#define PMECC_MAX_TIMEOUT_MS                   100
-
-/* Reserved bytes in oob area */
-#define PMECC_OOB_RESERVED_BYTES               2
-
-#endif
diff --git a/drivers/mtd/nand/atmel_nand_nfc.h b/drivers/mtd/nand/atmel_nand_nfc.h
deleted file mode 100644 (file)
index 4d5d262..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Atmel Nand Flash Controller (NFC) - System peripherals regsters.
- * Based on SAMA5D3 datasheet.
- *
- * © Copyright 2013 Atmel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
-
-#ifndef ATMEL_NAND_NFC_H
-#define ATMEL_NAND_NFC_H
-
-/*
- * HSMC NFC registers
- */
-#define ATMEL_HSMC_NFC_CFG     0x00            /* NFC Configuration Register */
-#define                NFC_CFG_PAGESIZE        (7 << 0)
-#define                        NFC_CFG_PAGESIZE_512    (0 << 0)
-#define                        NFC_CFG_PAGESIZE_1024   (1 << 0)
-#define                        NFC_CFG_PAGESIZE_2048   (2 << 0)
-#define                        NFC_CFG_PAGESIZE_4096   (3 << 0)
-#define                        NFC_CFG_PAGESIZE_8192   (4 << 0)
-#define                NFC_CFG_WSPARE          (1 << 8)
-#define                NFC_CFG_RSPARE          (1 << 9)
-#define                NFC_CFG_NFC_DTOCYC      (0xf << 16)
-#define                NFC_CFG_NFC_DTOMUL      (0x7 << 20)
-#define                NFC_CFG_NFC_SPARESIZE   (0x7f << 24)
-#define                NFC_CFG_NFC_SPARESIZE_BIT_POS   24
-
-#define ATMEL_HSMC_NFC_CTRL    0x04            /* NFC Control Register */
-#define                NFC_CTRL_ENABLE         (1 << 0)
-#define                NFC_CTRL_DISABLE        (1 << 1)
-
-#define ATMEL_HSMC_NFC_SR      0x08            /* NFC Status Register */
-#define                NFC_SR_BUSY             (1 << 8)
-#define                NFC_SR_XFR_DONE         (1 << 16)
-#define                NFC_SR_CMD_DONE         (1 << 17)
-#define                NFC_SR_DTOE             (1 << 20)
-#define                NFC_SR_UNDEF            (1 << 21)
-#define                NFC_SR_AWB              (1 << 22)
-#define                NFC_SR_ASE              (1 << 23)
-#define                NFC_SR_RB_EDGE          (1 << 24)
-
-#define ATMEL_HSMC_NFC_IER     0x0c
-#define ATMEL_HSMC_NFC_IDR     0x10
-#define ATMEL_HSMC_NFC_IMR     0x14
-#define ATMEL_HSMC_NFC_CYCLE0  0x18            /* NFC Address Cycle Zero */
-#define                ATMEL_HSMC_NFC_ADDR_CYCLE0      (0xff)
-
-#define ATMEL_HSMC_NFC_BANK    0x1c            /* NFC Bank Register */
-#define                ATMEL_HSMC_NFC_BANK0            (0 << 0)
-#define                ATMEL_HSMC_NFC_BANK1            (1 << 0)
-
-#define nfc_writel(addr, reg, value) \
-       writel((value), (addr) + ATMEL_HSMC_NFC_##reg)
-
-#define nfc_readl(addr, reg) \
-       readl_relaxed((addr) + ATMEL_HSMC_NFC_##reg)
-
-/*
- * NFC Address Command definitions
- */
-#define NFCADDR_CMD_CMD1       (0xff << 2)     /* Command for Cycle 1 */
-#define NFCADDR_CMD_CMD1_BIT_POS       2
-#define NFCADDR_CMD_CMD2       (0xff << 10)    /* Command for Cycle 2 */
-#define NFCADDR_CMD_CMD2_BIT_POS       10
-#define NFCADDR_CMD_VCMD2      (0x1 << 18)     /* Valid Cycle 2 Command */
-#define NFCADDR_CMD_ACYCLE     (0x7 << 19)     /* Number of Address required */
-#define                NFCADDR_CMD_ACYCLE_NONE         (0x0 << 19)
-#define                NFCADDR_CMD_ACYCLE_1            (0x1 << 19)
-#define                NFCADDR_CMD_ACYCLE_2            (0x2 << 19)
-#define                NFCADDR_CMD_ACYCLE_3            (0x3 << 19)
-#define                NFCADDR_CMD_ACYCLE_4            (0x4 << 19)
-#define                NFCADDR_CMD_ACYCLE_5            (0x5 << 19)
-#define NFCADDR_CMD_ACYCLE_BIT_POS     19
-#define NFCADDR_CMD_CSID       (0x7 << 22)     /* Chip Select Identifier */
-#define                NFCADDR_CMD_CSID_0              (0x0 << 22)
-#define                NFCADDR_CMD_CSID_1              (0x1 << 22)
-#define                NFCADDR_CMD_CSID_2              (0x2 << 22)
-#define                NFCADDR_CMD_CSID_3              (0x3 << 22)
-#define                NFCADDR_CMD_CSID_4              (0x4 << 22)
-#define                NFCADDR_CMD_CSID_5              (0x5 << 22)
-#define                NFCADDR_CMD_CSID_6              (0x6 << 22)
-#define                NFCADDR_CMD_CSID_7              (0x7 << 22)
-#define NFCADDR_CMD_DATAEN     (0x1 << 25)     /* Data Transfer Enable */
-#define NFCADDR_CMD_DATADIS    (0x0 << 25)     /* Data Transfer Disable */
-#define NFCADDR_CMD_NFCRD      (0x0 << 26)     /* NFC Read Enable */
-#define NFCADDR_CMD_NFCWR      (0x1 << 26)     /* NFC Write Enable */
-#define NFCADDR_CMD_NFCBUSY    (0x1 << 27)     /* NFC Busy */
-
-#define nfc_cmd_addr1234_writel(cmd, addr1234, nfc_base) \
-       writel((addr1234), (cmd) + nfc_base)
-
-#define nfc_cmd_readl(bitstatus, nfc_base) \
-       readl_relaxed((bitstatus) + nfc_base)
-
-#define NFC_TIME_OUT_MS                100
-#define        NFC_SRAM_BANK1_OFFSET   0x1200
-
-#endif
index 42ebd73..7419c5c 100644 (file)
@@ -101,6 +101,9 @@ struct brcm_nand_dma_desc {
 #define BRCMNAND_MIN_BLOCKSIZE (8 * 1024)
 #define BRCMNAND_MIN_DEVSIZE   (4ULL * 1024 * 1024)
 
+#define NAND_CTRL_RDY                  (INTFC_CTLR_READY | INTFC_FLASH_READY)
+#define NAND_POLL_STATUS_TIMEOUT_MS    100
+
 /* Controller feature flags */
 enum {
        BRCMNAND_HAS_1K_SECTORS                 = BIT(0),
@@ -765,6 +768,31 @@ enum {
        CS_SELECT_AUTO_DEVICE_ID_CFG            = BIT(30),
 };
 
+static int bcmnand_ctrl_poll_status(struct brcmnand_controller *ctrl,
+                                   u32 mask, u32 expected_val,
+                                   unsigned long timeout_ms)
+{
+       unsigned long limit;
+       u32 val;
+
+       if (!timeout_ms)
+               timeout_ms = NAND_POLL_STATUS_TIMEOUT_MS;
+
+       limit = jiffies + msecs_to_jiffies(timeout_ms);
+       do {
+               val = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS);
+               if ((val & mask) == expected_val)
+                       return 0;
+
+               cpu_relax();
+       } while (time_after(limit, jiffies));
+
+       dev_warn(ctrl->dev, "timeout on status poll (expected %x got %x)\n",
+                expected_val, val & mask);
+
+       return -ETIMEDOUT;
+}
+
 static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en)
 {
        u32 val = en ? CS_SELECT_NAND_WP : 0;
@@ -1024,12 +1052,39 @@ static void brcmnand_wp(struct mtd_info *mtd, int wp)
 
        if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) {
                static int old_wp = -1;
+               int ret;
 
                if (old_wp != wp) {
                        dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off");
                        old_wp = wp;
                }
+
+               /*
+                * make sure ctrl/flash ready before and after
+                * changing state of #WP pin
+                */
+               ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY |
+                                              NAND_STATUS_READY,
+                                              NAND_CTRL_RDY |
+                                              NAND_STATUS_READY, 0);
+               if (ret)
+                       return;
+
                brcmnand_set_wp(ctrl, wp);
+               chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+               /* NAND_STATUS_WP 0x00 = protected, 0x80 = not protected */
+               ret = bcmnand_ctrl_poll_status(ctrl,
+                                              NAND_CTRL_RDY |
+                                              NAND_STATUS_READY |
+                                              NAND_STATUS_WP,
+                                              NAND_CTRL_RDY |
+                                              NAND_STATUS_READY |
+                                              (wp ? 0 : NAND_STATUS_WP), 0);
+
+               if (ret)
+                       dev_err_ratelimited(&host->pdev->dev,
+                                           "nand #WP expected %s\n",
+                                           wp ? "on" : "off");
        }
 }
 
@@ -1157,15 +1212,15 @@ static irqreturn_t brcmnand_dma_irq(int irq, void *data)
 static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd)
 {
        struct brcmnand_controller *ctrl = host->ctrl;
-       u32 intfc;
+       int ret;
 
        dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd,
                brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS));
        BUG_ON(ctrl->cmd_pending != 0);
        ctrl->cmd_pending = cmd;
 
-       intfc = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS);
-       WARN_ON(!(intfc & INTFC_CTLR_READY));
+       ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY, NAND_CTRL_RDY, 0);
+       WARN_ON(ret);
 
        mb(); /* flush previous writes */
        brcmnand_write_reg(ctrl, BRCMNAND_CMD_START,
index 226ac0b..949b940 100644 (file)
@@ -145,7 +145,7 @@ static int __init cmx270_init(void)
 
        ret = gpio_request(GPIO_NAND_CS, "NAND CS");
        if (ret) {
-               pr_warning("CM-X270: failed to request NAND CS gpio\n");
+               pr_warn("CM-X270: failed to request NAND CS gpio\n");
                return ret;
        }
 
@@ -153,7 +153,7 @@ static int __init cmx270_init(void)
 
        ret = gpio_request(GPIO_NAND_RB, "NAND R/B");
        if (ret) {
-               pr_warning("CM-X270: failed to request NAND R/B gpio\n");
+               pr_warn("CM-X270: failed to request NAND R/B gpio\n");
                goto err_gpio_request;
        }
 
index 27fa8b8..531c519 100644 (file)
@@ -581,6 +581,17 @@ static struct davinci_nand_pdata
                        "ti,davinci-nand-use-bbt"))
                        pdata->bbt_options = NAND_BBT_USE_FLASH;
 
+               /*
+                * Since kernel v4.8, this driver has been fixed to enable
+                * use of 4-bit hardware ECC with subpages and verified on
+                * TI's keystone EVMs (K2L, K2HK and K2E).
+                * However, in the interest of not breaking systems using
+                * existing UBI partitions, sub-page writes are not being
+                * (re)enabled. If you want to use subpage writes on Keystone
+                * platforms (i.e. do not have any existing UBI partitions),
+                * then use "ti,davinci-nand" as the compatible in your
+                * device-tree file.
+                */
                if (of_device_is_compatible(pdev->dev.of_node,
                                            "ti,keystone-nand")) {
                        pdata->options |= NAND_NO_SUBPAGE_WRITE;
index 73b9d4e..16634df 100644 (file)
@@ -45,16 +45,16 @@ MODULE_PARM_DESC(onfi_timing_mode,
  * We define a macro here that combines all interrupts this driver uses into
  * a single constant value, for convenience.
  */
-#define DENALI_IRQ_ALL (INTR_STATUS__DMA_CMD_COMP | \
-                       INTR_STATUS__ECC_TRANSACTION_DONE | \
-                       INTR_STATUS__ECC_ERR | \
-                       INTR_STATUS__PROGRAM_FAIL | \
-                       INTR_STATUS__LOAD_COMP | \
-                       INTR_STATUS__PROGRAM_COMP | \
-                       INTR_STATUS__TIME_OUT | \
-                       INTR_STATUS__ERASE_FAIL | \
-                       INTR_STATUS__RST_COMP | \
-                       INTR_STATUS__ERASE_COMP)
+#define DENALI_IRQ_ALL (INTR__DMA_CMD_COMP | \
+                       INTR__ECC_TRANSACTION_DONE | \
+                       INTR__ECC_ERR | \
+                       INTR__PROGRAM_FAIL | \
+                       INTR__LOAD_COMP | \
+                       INTR__PROGRAM_COMP | \
+                       INTR__TIME_OUT | \
+                       INTR__ERASE_FAIL | \
+                       INTR__RST_COMP | \
+                       INTR__ERASE_COMP)
 
 /*
  * indicates whether or not the internal value for the flash bank is
@@ -62,8 +62,6 @@ MODULE_PARM_DESC(onfi_timing_mode,
  */
 #define CHIP_SELECT_INVALID    -1
 
-#define SUPPORT_8BITECC                1
-
 /*
  * This macro divides two integers and rounds fractional values up
  * to the nearest integer value.
@@ -86,16 +84,10 @@ static inline struct denali_nand_info *mtd_to_denali(struct mtd_info *mtd)
 #define SPARE_ACCESS           0x41
 #define MAIN_ACCESS            0x42
 #define MAIN_SPARE_ACCESS      0x43
-#define PIPELINE_ACCESS                0x2000
 
 #define DENALI_READ    0
 #define DENALI_WRITE   0x100
 
-/* types of device accesses. We can issue commands and get status */
-#define COMMAND_CYCLE  0
-#define ADDR_CYCLE     1
-#define STATUS_CYCLE   2
-
 /*
  * this is a helper macro that allows us to
  * format the bank into the proper bits for the controller
@@ -164,7 +156,7 @@ static void read_status(struct denali_nand_info *denali)
 static void reset_bank(struct denali_nand_info *denali)
 {
        uint32_t irq_status;
-       uint32_t irq_mask = INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT;
+       uint32_t irq_mask = INTR__RST_COMP | INTR__TIME_OUT;
 
        clear_interrupts(denali);
 
@@ -172,7 +164,7 @@ static void reset_bank(struct denali_nand_info *denali)
 
        irq_status = wait_for_irq(denali, irq_mask);
 
-       if (irq_status & INTR_STATUS__TIME_OUT)
+       if (irq_status & INTR__TIME_OUT)
                dev_err(denali->dev, "reset bank failed.\n");
 }
 
@@ -182,22 +174,22 @@ static uint16_t denali_nand_reset(struct denali_nand_info *denali)
        int i;
 
        for (i = 0; i < denali->max_banks; i++)
-               iowrite32(INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT,
+               iowrite32(INTR__RST_COMP | INTR__TIME_OUT,
                denali->flash_reg + INTR_STATUS(i));
 
        for (i = 0; i < denali->max_banks; i++) {
                iowrite32(1 << i, denali->flash_reg + DEVICE_RESET);
                while (!(ioread32(denali->flash_reg + INTR_STATUS(i)) &
-                       (INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT)))
+                       (INTR__RST_COMP | INTR__TIME_OUT)))
                        cpu_relax();
                if (ioread32(denali->flash_reg + INTR_STATUS(i)) &
-                       INTR_STATUS__TIME_OUT)
+                       INTR__TIME_OUT)
                        dev_dbg(denali->dev,
                        "NAND Reset operation timed out on bank %d\n", i);
        }
 
        for (i = 0; i < denali->max_banks; i++)
-               iowrite32(INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT,
+               iowrite32(INTR__RST_COMP | INTR__TIME_OUT,
                          denali->flash_reg + INTR_STATUS(i));
 
        return PASS;
@@ -347,52 +339,25 @@ static void get_samsung_nand_para(struct denali_nand_info *denali,
 
 static void get_toshiba_nand_para(struct denali_nand_info *denali)
 {
-       uint32_t tmp;
-
        /*
         * Workaround to fix a controller bug which reports a wrong
         * spare area size for some kind of Toshiba NAND device
         */
        if ((ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE) == 4096) &&
-               (ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE) == 64)) {
+               (ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE) == 64))
                iowrite32(216, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
-               tmp = ioread32(denali->flash_reg + DEVICES_CONNECTED) *
-                       ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
-               iowrite32(tmp,
-                               denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE);
-#if SUPPORT_15BITECC
-               iowrite32(15, denali->flash_reg + ECC_CORRECTION);
-#elif SUPPORT_8BITECC
-               iowrite32(8, denali->flash_reg + ECC_CORRECTION);
-#endif
-       }
 }
 
 static void get_hynix_nand_para(struct denali_nand_info *denali,
                                                        uint8_t device_id)
 {
-       uint32_t main_size, spare_size;
-
        switch (device_id) {
        case 0xD5: /* Hynix H27UAG8T2A, H27UBG8U5A or H27UCG8VFA */
        case 0xD7: /* Hynix H27UDG8VEM, H27UCG8UDM or H27UCG8V5A */
                iowrite32(128, denali->flash_reg + PAGES_PER_BLOCK);
                iowrite32(4096, denali->flash_reg + DEVICE_MAIN_AREA_SIZE);
                iowrite32(224, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
-               main_size = 4096 *
-                       ioread32(denali->flash_reg + DEVICES_CONNECTED);
-               spare_size = 224 *
-                       ioread32(denali->flash_reg + DEVICES_CONNECTED);
-               iowrite32(main_size,
-                               denali->flash_reg + LOGICAL_PAGE_DATA_SIZE);
-               iowrite32(spare_size,
-                               denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE);
                iowrite32(0, denali->flash_reg + DEVICE_WIDTH);
-#if SUPPORT_15BITECC
-               iowrite32(15, denali->flash_reg + ECC_CORRECTION);
-#elif SUPPORT_8BITECC
-               iowrite32(8, denali->flash_reg + ECC_CORRECTION);
-#endif
                break;
        default:
                dev_warn(denali->dev,
@@ -454,17 +419,12 @@ static void find_valid_banks(struct denali_nand_info *denali)
 static void detect_max_banks(struct denali_nand_info *denali)
 {
        uint32_t features = ioread32(denali->flash_reg + FEATURES);
-       /*
-        * Read the revision register, so we can calculate the max_banks
-        * properly: the encoding changed from rev 5.0 to 5.1
-        */
-       u32 revision = MAKE_COMPARABLE_REVISION(
-                               ioread32(denali->flash_reg + REVISION));
 
-       if (revision < REVISION_5_1)
-               denali->max_banks = 2 << (features & FEATURES__N_BANKS);
-       else
-               denali->max_banks = 1 << (features & FEATURES__N_BANKS);
+       denali->max_banks = 1 << (features & FEATURES__N_BANKS);
+
+       /* the encoding changed from rev 5.0 to 5.1 */
+       if (denali->revision < 0x0501)
+               denali->max_banks <<= 1;
 }
 
 static uint16_t denali_nand_timing_set(struct denali_nand_info *denali)
@@ -653,7 +613,6 @@ static irqreturn_t denali_isr(int irq, void *dev_id)
        spin_unlock(&denali->irq_lock);
        return result;
 }
-#define BANK(x) ((x) << 24)
 
 static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask)
 {
@@ -718,15 +677,7 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
                                    int access_type, int op)
 {
        int status = PASS;
-       uint32_t page_count = 1;
-       uint32_t addr, cmd, irq_status, irq_mask;
-
-       if (op == DENALI_READ)
-               irq_mask = INTR_STATUS__LOAD_COMP;
-       else if (op == DENALI_WRITE)
-               irq_mask = 0;
-       else
-               BUG();
+       uint32_t addr, cmd;
 
        setup_ecc_for_xfer(denali, ecc_en, transfer_spare);
 
@@ -749,35 +700,8 @@ static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
                cmd = MODE_10 | addr;
                index_addr(denali, cmd, access_type);
 
-               /*
-                * page 33 of the NAND controller spec indicates we should not
-                * use the pipeline commands in Spare area only mode.
-                * So we don't.
-                */
-               if (access_type == SPARE_ACCESS) {
-                       cmd = MODE_01 | addr;
-                       iowrite32(cmd, denali->flash_mem);
-               } else {
-                       index_addr(denali, cmd,
-                                       PIPELINE_ACCESS | op | page_count);
-
-                       /*
-                        * wait for command to be accepted
-                        * can always use status0 bit as the
-                        * mask is identical for each bank.
-                        */
-                       irq_status = wait_for_irq(denali, irq_mask);
-
-                       if (irq_status == 0) {
-                               dev_err(denali->dev,
-                                       "cmd, page, addr on timeout (0x%x, 0x%x, 0x%x)\n",
-                                       cmd, denali->page, addr);
-                               status = FAIL;
-                       } else {
-                               cmd = MODE_01 | addr;
-                               iowrite32(cmd, denali->flash_mem);
-                       }
-               }
+               cmd = MODE_01 | addr;
+               iowrite32(cmd, denali->flash_mem);
        }
        return status;
 }
@@ -829,8 +753,7 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
 {
        struct denali_nand_info *denali = mtd_to_denali(mtd);
        uint32_t irq_status;
-       uint32_t irq_mask = INTR_STATUS__PROGRAM_COMP |
-                                               INTR_STATUS__PROGRAM_FAIL;
+       uint32_t irq_mask = INTR__PROGRAM_COMP | INTR__PROGRAM_FAIL;
        int status = 0;
 
        denali->page = page;
@@ -857,7 +780,7 @@ static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
 static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
 {
        struct denali_nand_info *denali = mtd_to_denali(mtd);
-       uint32_t irq_mask = INTR_STATUS__LOAD_COMP;
+       uint32_t irq_mask = INTR__LOAD_COMP;
        uint32_t irq_status, addr, cmd;
 
        denali->page = page;
@@ -890,98 +813,158 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
        }
 }
 
-/*
- * this function examines buffers to see if they contain data that
- * indicate that the buffer is part of an erased region of flash.
- */
-static bool is_erased(uint8_t *buf, int len)
+static int denali_check_erased_page(struct mtd_info *mtd,
+                                   struct nand_chip *chip, uint8_t *buf,
+                                   unsigned long uncor_ecc_flags,
+                                   unsigned int max_bitflips)
 {
-       int i;
+       uint8_t *ecc_code = chip->buffers->ecccode;
+       int ecc_steps = chip->ecc.steps;
+       int ecc_size = chip->ecc.size;
+       int ecc_bytes = chip->ecc.bytes;
+       int i, ret, stat;
+
+       ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+                                        chip->ecc.total);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < ecc_steps; i++) {
+               if (!(uncor_ecc_flags & BIT(i)))
+                       continue;
 
-       for (i = 0; i < len; i++)
-               if (buf[i] != 0xFF)
-                       return false;
-       return true;
+               stat = nand_check_erased_ecc_chunk(buf, ecc_size,
+                                                 ecc_code, ecc_bytes,
+                                                 NULL, 0,</