]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge tag 'nand/for-4.12' of github.com:linux-nand/linux into MTD
authorBrian Norris <computersforpeace@gmail.com>
Mon, 1 May 2017 20:36:44 +0000 (13:36 -0700)
committerBrian Norris <computersforpeace@gmail.com>
Mon, 1 May 2017 20:36:44 +0000 (13:36 -0700)
From Boris:
"""
This pull request contains:

 - 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
"""

35 files changed:
Documentation/devicetree/bindings/mtd/atmel-nand.txt
Documentation/devicetree/bindings/mtd/denali-nand.txt
MAINTAINERS
arch/cris/arch-v32/drivers/Kconfig
drivers/memory/Kconfig
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/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/sunxi_nand.c
drivers/mtd/nand/tango_nand.c
include/linux/mtd/nand.h

index 3e7ee99d3949ab07b5515e3afff350aebc2e0e71..f6bee57e453aa6dcb9114a188538e1c69860b4b2 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 b04d03a1d49951a0fe6fbecde446fb523919f811..e593bbeb2115deb92966d593c18f002e5f269e41 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 cea31977d4019aa171064d441c1d99850a8e7253..732050a0c3bee6995fbd2ed9aa4edb6d9932c76a 100644 (file)
@@ -2244,7 +2244,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>
index 2735eb7671a5ad9203251fe88726145b3a33a92a..b7cd6b9209a9e19027391931798de6bd1a0eb93e 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 ec80e35c8dfea657b63a8db511b7027f816fdaa4..fff8345f5bb05208bee38ec6bce24e67548a9a71 100644 (file)
@@ -115,7 +115,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 6d4d5672d1d8ea0945cdaebcbf06d17f5984017b..c3029528063b8578e79746c51038d643631920b2 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 19a66e404d5ba949a16a7ba358df25f033e7a51c..ade5fc4c3819a79ffd575119ed6bbf5607bd8b7f 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 42ebd73f821dd47b9346113d06375af31b68f68c..7419c5ce63f8bc46ea02e59600e68be0217de7e7 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 27fa8b87cd5fc6a779e0509ce78c010527747268..531c51991e5747b35fc68114ace9d534a1ea430e 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 73b9d4e2dca0a4c7b7f198cb6fdae0922cfb864f..16634df2e39a77ae1e34f68a7b4af0c642232b1e 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,
+                                                 chip->ecc.strength);
+               if (stat < 0) {
+                       mtd->ecc_stats.failed++;
+               } else {
+                       mtd->ecc_stats.corrected += stat;
+                       max_bitflips = max_t(unsigned int, max_bitflips, stat);
+               }
+
+               buf += ecc_size;
+               ecc_code += ecc_bytes;
+       }
+
+       return max_bitflips;
+}
+
+static int denali_hw_ecc_fixup(struct mtd_info *mtd,
+                              struct denali_nand_info *denali,
+                              unsigned long *uncor_ecc_flags)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       int bank = denali->flash_bank;
+       uint32_t ecc_cor;
+       unsigned int max_bitflips;
+
+       ecc_cor = ioread32(denali->flash_reg + ECC_COR_INFO(bank));
+       ecc_cor >>= ECC_COR_INFO__SHIFT(bank);
+
+       if (ecc_cor & ECC_COR_INFO__UNCOR_ERR) {
+               /*
+                * This flag is set when uncorrectable error occurs at least in
+                * one ECC sector.  We can not know "how many sectors", or
+                * "which sector(s)".  We need erase-page check for all sectors.
+                */
+               *uncor_ecc_flags = GENMASK(chip->ecc.steps - 1, 0);
+               return 0;
+       }
+
+       max_bitflips = ecc_cor & ECC_COR_INFO__MAX_ERRORS;
+
+       /*
+        * The register holds the maximum of per-sector corrected bitflips.
+        * This is suitable for the return value of the ->read_page() callback.
+        * Unfortunately, we can not know the total number of corrected bits in
+        * the page.  Increase the stats by max_bitflips. (compromised solution)
+        */
+       mtd->ecc_stats.corrected += max_bitflips;
+
+       return max_bitflips;
 }
+
 #define ECC_SECTOR_SIZE 512
 
 #define ECC_SECTOR(x)  (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12)
 #define ECC_BYTE(x)    (((x) & ECC_ERROR_ADDRESS__OFFSET))
 #define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK)
-#define ECC_ERROR_CORRECTABLE(x) (!((x) & ERR_CORRECTION_INFO__ERROR_TYPE))
+#define ECC_ERROR_UNCORRECTABLE(x) ((x) & ERR_CORRECTION_INFO__ERROR_TYPE)
 #define ECC_ERR_DEVICE(x)      (((x) & ERR_CORRECTION_INFO__DEVICE_NR) >> 8)
 #define ECC_LAST_ERR(x)                ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO)
 
-static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
-                      uint32_t irq_status, unsigned int *max_bitflips)
+static int denali_sw_ecc_fixup(struct mtd_info *mtd,
+                              struct denali_nand_info *denali,
+                              unsigned long *uncor_ecc_flags, uint8_t *buf)
 {
-       bool check_erased_page = false;
        unsigned int bitflips = 0;
+       unsigned int max_bitflips = 0;
+       uint32_t err_addr, err_cor_info;
+       unsigned int err_byte, err_sector, err_device;
+       uint8_t err_cor_value;
+       unsigned int prev_sector = 0;
 
-       if (irq_status & INTR_STATUS__ECC_ERR) {
-               /* read the ECC errors. we'll ignore them for now */
-               uint32_t err_address, err_correction_info, err_byte,
-                        err_sector, err_device, err_correction_value;
-               denali_set_intr_modes(denali, false);
-
-               do {
-                       err_address = ioread32(denali->flash_reg +
-                                               ECC_ERROR_ADDRESS);
-                       err_sector = ECC_SECTOR(err_address);
-                       err_byte = ECC_BYTE(err_address);
-
-                       err_correction_info = ioread32(denali->flash_reg +
-                                               ERR_CORRECTION_INFO);
-                       err_correction_value =
-                               ECC_CORRECTION_VALUE(err_correction_info);
-                       err_device = ECC_ERR_DEVICE(err_correction_info);
-
-                       if (ECC_ERROR_CORRECTABLE(err_correction_info)) {
-                               /*
-                                * If err_byte is larger than ECC_SECTOR_SIZE,
-                                * means error happened in OOB, so we ignore
-                                * it. It's no need for us to correct it
-                                * err_device is represented the NAND error
-                                * bits are happened in if there are more
-                                * than one NAND connected.
-                                */
-                               if (err_byte < ECC_SECTOR_SIZE) {
-                                       struct mtd_info *mtd =
-                                               nand_to_mtd(&denali->nand);
-                                       int offset;
-
-                                       offset = (err_sector *
-                                                       ECC_SECTOR_SIZE +
-                                                       err_byte) *
-                                                       denali->devnum +
-                                                       err_device;
-                                       /* correct the ECC error */
-                                       buf[offset] ^= err_correction_value;
-                                       mtd->ecc_stats.corrected++;
-                                       bitflips++;
-                               }
-                       } else {
-                               /*
-                                * if the error is not correctable, need to
-                                * look at the page to see if it is an erased
-                                * page. if so, then it's not a real ECC error
-                                */
-                               check_erased_page = true;
-                       }
-               } while (!ECC_LAST_ERR(err_correction_info));
-               /*
-                * Once handle all ecc errors, controller will triger
-                * a ECC_TRANSACTION_DONE interrupt, so here just wait
-                * for a while for this interrupt
-                */
-               while (!(read_interrupt_status(denali) &
-                               INTR_STATUS__ECC_TRANSACTION_DONE))
-                       cpu_relax();
-               clear_interrupts(denali);
-               denali_set_intr_modes(denali, true);
-       }
-       *max_bitflips = bitflips;
-       return check_erased_page;
+       /* read the ECC errors. we'll ignore them for now */
+       denali_set_intr_modes(denali, false);
+
+       do {
+               err_addr = ioread32(denali->flash_reg + ECC_ERROR_ADDRESS);
+               err_sector = ECC_SECTOR(err_addr);
+               err_byte = ECC_BYTE(err_addr);
+
+               err_cor_info = ioread32(denali->flash_reg + ERR_CORRECTION_INFO);
+               err_cor_value = ECC_CORRECTION_VALUE(err_cor_info);
+               err_device = ECC_ERR_DEVICE(err_cor_info);
+
+               /* reset the bitflip counter when crossing ECC sector */
+               if (err_sector != prev_sector)
+                       bitflips = 0;
+
+               if (ECC_ERROR_UNCORRECTABLE(err_cor_info)) {
+                       /*
+                        * Check later if this is a real ECC error, or
+                        * an erased sector.
+                        */
+                       *uncor_ecc_flags |= BIT(err_sector);
+               } else if (err_byte < ECC_SECTOR_SIZE) {
+                       /*
+                        * If err_byte is larger than ECC_SECTOR_SIZE, means error
+                        * happened in OOB, so we ignore it. It's no need for
+                        * us to correct it err_device is represented the NAND
+                        * error bits are happened in if there are more than
+                        * one NAND connected.
+                        */
+                       int offset;
+                       unsigned int flips_in_byte;
+
+                       offset = (err_sector * ECC_SECTOR_SIZE + err_byte) *
+                                               denali->devnum + err_device;
+
+                       /* correct the ECC error */
+                       flips_in_byte = hweight8(buf[offset] ^ err_cor_value);
+                       buf[offset] ^= err_cor_value;
+                       mtd->ecc_stats.corrected += flips_in_byte;
+                       bitflips += flips_in_byte;
+
+                       max_bitflips = max(max_bitflips, bitflips);
+               }
+
+               prev_sector = err_sector;
+       } while (!ECC_LAST_ERR(err_cor_info));
+
+       /*
+        * Once handle all ecc errors, controller will trigger a
+        * ECC_TRANSACTION_DONE interrupt, so here just wait for
+        * a while for this interrupt
+        */
+       while (!(read_interrupt_status(denali) & INTR__ECC_TRANSACTION_DONE))
+               cpu_relax();
+       clear_interrupts(denali);
+       denali_set_intr_modes(denali, true);
+
+       return max_bitflips;
 }
 
 /* programs the controller to either enable/disable DMA transfers */
@@ -991,8 +974,30 @@ static void denali_enable_dma(struct denali_nand_info *denali, bool en)
        ioread32(denali->flash_reg + DMA_ENABLE);
 }
 
-/* setups the HW to perform the data DMA */
-static void denali_setup_dma(struct denali_nand_info *denali, int op)
+static void denali_setup_dma64(struct denali_nand_info *denali, int op)
+{
+       uint32_t mode;
+       const int page_count = 1;
+       uint64_t addr = denali->buf.dma_buf;
+
+       mode = MODE_10 | BANK(denali->flash_bank) | denali->page;
+
+       /* DMA is a three step process */
+
+       /*
+        * 1. setup transfer type, interrupt when complete,
+        *    burst len = 64 bytes, the number of pages
+        */
+       index_addr(denali, mode, 0x01002000 | (64 << 16) | op | page_count);
+
+       /* 2. set memory low address */
+       index_addr(denali, mode, addr);
+
+       /* 3. set memory high address */
+       index_addr(denali, mode, addr >> 32);
+}
+
+static void denali_setup_dma32(struct denali_nand_info *denali, int op)
 {
        uint32_t mode;
        const int page_count = 1;
@@ -1015,6 +1020,14 @@ static void denali_setup_dma(struct denali_nand_info *denali, int op)
        index_addr(denali, mode | 0x14000, 0x2400);
 }
 
+static void denali_setup_dma(struct denali_nand_info *denali, int op)
+{
+       if (denali->caps & DENALI_CAP_DMA_64BIT)
+               denali_setup_dma64(denali, op);
+       else
+               denali_setup_dma32(denali, op);
+}
+
 /*
  * writes a page. user specifies type, and this function handles the
  * configuration details.
@@ -1026,8 +1039,7 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
        dma_addr_t addr = denali->buf.dma_buf;
        size_t size = mtd->writesize + mtd->oobsize;
        uint32_t irq_status;
-       uint32_t irq_mask = INTR_STATUS__DMA_CMD_COMP |
-                                               INTR_STATUS__PROGRAM_FAIL;
+       uint32_t irq_mask = INTR__DMA_CMD_COMP | INTR__PROGRAM_FAIL;
 
        /*
         * if it is a raw xfer, we want to disable ecc and send the spare area.
@@ -1118,16 +1130,15 @@ static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
 static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
                            uint8_t *buf, int oob_required, int page)
 {
-       unsigned int max_bitflips;
        struct denali_nand_info *denali = mtd_to_denali(mtd);
-
        dma_addr_t addr = denali->buf.dma_buf;
        size_t size = mtd->writesize + mtd->oobsize;
-
        uint32_t irq_status;
-       uint32_t irq_mask = INTR_STATUS__ECC_TRANSACTION_DONE |
-                           INTR_STATUS__ECC_ERR;
-       bool check_erased_page = false;
+       uint32_t irq_mask = denali->caps & DENALI_CAP_HW_ECC_FIXUP ?
+                               INTR__DMA_CMD_COMP | INTR__ECC_UNCOR_ERR :
+                               INTR__ECC_TRANSACTION_DONE | INTR__ECC_ERR;
+       unsigned long uncor_ecc_flags = 0;
+       int stat = 0;
 
        if (page != denali->page) {
                dev_err(denali->dev,
@@ -1151,21 +1162,23 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
 
        memcpy(buf, denali->buf.buf, mtd->writesize);
 
-       check_erased_page = handle_ecc(denali, buf, irq_status, &max_bitflips);
+       if (denali->caps & DENALI_CAP_HW_ECC_FIXUP)
+               stat = denali_hw_ecc_fixup(mtd, denali, &uncor_ecc_flags);
+       else if (irq_status & INTR__ECC_ERR)
+               stat = denali_sw_ecc_fixup(mtd, denali, &uncor_ecc_flags, buf);
        denali_enable_dma(denali, false);
 
-       if (check_erased_page) {
+       if (stat < 0)
+               return stat;
+
+       if (uncor_ecc_flags) {
                read_oob_data(mtd, chip->oob_poi, denali->page);
 
-               /* check ECC failures that may have occurred on erased pages */
-               if (check_erased_page) {
-                       if (!is_erased(buf, mtd->writesize))
-                               mtd->ecc_stats.failed++;
-                       if (!is_erased(buf, mtd->oobsize))
-                               mtd->ecc_stats.failed++;
-               }
+               stat = denali_check_erased_page(mtd, chip, buf,
+                                               uncor_ecc_flags, stat);
        }
-       return max_bitflips;
+
+       return stat;
 }
 
 static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
@@ -1174,7 +1187,7 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
        struct denali_nand_info *denali = mtd_to_denali(mtd);
        dma_addr_t addr = denali->buf.dma_buf;
        size_t size = mtd->writesize + mtd->oobsize;
-       uint32_t irq_mask = INTR_STATUS__DMA_CMD_COMP;
+       uint32_t irq_mask = INTR__DMA_CMD_COMP;
 
        if (page != denali->page) {
                dev_err(denali->dev,
@@ -1247,10 +1260,9 @@ static int denali_erase(struct mtd_info *mtd, int page)
        index_addr(denali, cmd, 0x1);
 
        /* wait for erase to complete or failure to occur */
-       irq_status = wait_for_irq(denali, INTR_STATUS__ERASE_COMP |
-                                       INTR_STATUS__ERASE_FAIL);
+       irq_status = wait_for_irq(denali, INTR__ERASE_COMP | INTR__ERASE_FAIL);
 
-       return irq_status & INTR_STATUS__ERASE_FAIL ? NAND_STATUS_FAIL : PASS;
+       return irq_status & INTR__ERASE_FAIL ? NAND_STATUS_FAIL : PASS;
 }
 
 static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
@@ -1302,6 +1314,14 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
 /* Initialization code to bring the device up to a known good state */
 static void denali_hw_init(struct denali_nand_info *denali)
 {
+       /*
+        * The REVISION register may not be reliable.  Platforms are allowed to
+        * override it.
+        */
+       if (!denali->revision)
+               denali->revision =
+                               swab16(ioread32(denali->flash_reg + REVISION));
+
        /*
         * tell driver how many bit controller will skip before
         * writing ECC code in OOB, this register may be already
@@ -1413,9 +1433,61 @@ static void denali_drv_init(struct denali_nand_info *denali)
        denali->irq_status = 0;
 }
 
+static int denali_multidev_fixup(struct denali_nand_info *denali)
+{
+       struct nand_chip *chip = &denali->nand;
+       struct mtd_info *mtd = nand_to_mtd(chip);
+
+       /*
+        * Support for multi device:
+        * When the IP configuration is x16 capable and two x8 chips are
+        * connected in parallel, DEVICES_CONNECTED should be set to 2.
+        * In this case, the core framework knows nothing about this fact,
+        * so we should tell it the _logical_ pagesize and anything necessary.
+        */
+       denali->devnum = ioread32(denali->flash_reg + DEVICES_CONNECTED);
+
+       /*
+        * On some SoCs, DEVICES_CONNECTED is not auto-detected.
+        * For those, DEVICES_CONNECTED is left to 0.  Set 1 if it is the case.
+        */
+       if (denali->devnum == 0) {
+               denali->devnum = 1;
+               iowrite32(1, denali->flash_reg + DEVICES_CONNECTED);
+       }
+
+       if (denali->devnum == 1)
+               return 0;
+
+       if (denali->devnum != 2) {
+               dev_err(denali->dev, "unsupported number of devices %d\n",
+                       denali->devnum);
+               return -EINVAL;
+       }
+
+       /* 2 chips in parallel */
+       mtd->size <<= 1;
+       mtd->erasesize <<= 1;
+       mtd->writesize <<= 1;
+       mtd->oobsize <<= 1;
+       chip->chipsize <<= 1;
+       chip->page_shift += 1;
+       chip->phys_erase_shift += 1;
+       chip->bbt_erase_shift += 1;
+       chip->chip_shift += 1;
+       chip->pagemask <<= 1;
+       chip->ecc.size <<= 1;
+       chip->ecc.bytes <<= 1;
+       chip->ecc.strength <<= 1;
+       denali->bbtskipbytes <<= 1;
+
+       return 0;
+}
+
 int denali_init(struct denali_nand_info *denali)
 {
-       struct mtd_info *mtd = nand_to_mtd(&denali->nand);
+       struct nand_chip *chip = &denali->nand;
+       struct mtd_info *mtd = nand_to_mtd(chip);
        int ret;
 
        if (denali->platform == INTEL_CE4100) {
@@ -1449,13 +1521,16 @@ int denali_init(struct denali_nand_info *denali)
 
        /* now that our ISR is registered, we can enable interrupts */
        denali_set_intr_modes(denali, true);
-       mtd->name = "denali-nand";
+       nand_set_flash_node(chip, denali->dev->of_node);
+       /* Fallback to the default name if DT did not give "label" property */
+       if (!mtd->name)
+               mtd->name = "denali-nand";
 
        /* register the driver with the NAND core subsystem */
-       denali->nand.select_chip = denali_select_chip;
-       denali->nand.cmdfunc = denali_cmdfunc;
-       denali->nand.read_byte = denali_read_byte;
-       denali->nand.waitfunc = denali_waitfunc;
+       chip->select_chip = denali_select_chip;
+       chip->cmdfunc = denali_cmdfunc;
+       chip->read_byte = denali_read_byte;
+       chip->waitfunc = denali_waitfunc;
 
        /*
         * scan for NAND devices attached to the controller
@@ -1476,8 +1551,9 @@ int denali_init(struct denali_nand_info *denali)
                goto failed_req_irq;
        }
 
-       /* Is 32-bit DMA supported? */
-       ret = dma_set_mask(denali->dev, DMA_BIT_MASK(32));
+       ret = dma_set_mask(denali->dev,
+                          DMA_BIT_MASK(denali->caps & DENALI_CAP_DMA_64BIT ?
+                                       64 : 32));
        if (ret) {
                dev_err(denali->dev, "No usable DMA configuration\n");
                goto failed_req_irq;
@@ -1492,25 +1568,6 @@ int denali_init(struct denali_nand_info *denali)
                goto failed_req_irq;
        }
 
-       /*
-        * support for multi nand
-        * MTD known nothing about multi nand, so we should tell it
-        * the real pagesize and anything necessery
-        */
-       denali->devnum = ioread32(denali->flash_reg + DEVICES_CONNECTED);
-       denali->nand.chipsize <<= denali->devnum - 1;
-       denali->nand.page_shift += denali->devnum - 1;
-       denali->nand.pagemask = (denali->nand.chipsize >>
-                                               denali->nand.page_shift) - 1;
-       denali->nand.bbt_erase_shift += denali->devnum - 1;
-       denali->nand.phys_erase_shift = denali->nand.bbt_erase_shift;
-       denali->nand.chip_shift += denali->devnum - 1;
-       mtd->writesize <<= denali->devnum - 1;
-       mtd->oobsize <<= denali->devnum - 1;
-       mtd->erasesize <<= denali->devnum - 1;
-       mtd->size = denali->nand.numchips * denali->nand.chipsize;
-       denali->bbtskipbytes *= denali->devnum;
-
        /*
         * second stage of the NAND scan
         * this stage requires information regarding ECC and
@@ -1518,29 +1575,29 @@ int denali_init(struct denali_nand_info *denali)
         */
 
        /* Bad block management */
-       denali->nand.bbt_td = &bbt_main_descr;
-       denali->nand.bbt_md = &bbt_mirror_descr;
+       chip->bbt_td = &bbt_main_descr;
+       chip->bbt_md = &bbt_mirror_descr;
 
        /* skip the scan for now until we have OOB read and write support */
-       denali->nand.bbt_options |= NAND_BBT_USE_FLASH;
-       denali->nand.options |= NAND_SKIP_BBTSCAN;
-       denali->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
+       chip->bbt_options |= NAND_BBT_USE_FLASH;
+       chip->options |= NAND_SKIP_BBTSCAN;
+       chip->ecc.mode = NAND_ECC_HW_SYNDROME;
 
        /* no subpage writes on denali */
-       denali->nand.options |= NAND_NO_SUBPAGE_WRITE;
+       chip->options |= NAND_NO_SUBPAGE_WRITE;
 
        /*
         * Denali Controller only support 15bit and 8bit ECC in MRST,
         * so just let controller do 15bit ECC for MLC and 8bit ECC for
         * SLC if possible.
         * */
-       if (!nand_is_slc(&denali->nand) &&
+       if (!nand_is_slc(chip) &&
                        (mtd->oobsize > (denali->bbtskipbytes +
                        ECC_15BITS * (mtd->writesize /
                        ECC_SECTOR_SIZE)))) {
                /* if MLC OOB size is large enough, use 15bit ECC*/
-               denali->nand.ecc.strength = 15;
-               denali->nand.ecc.bytes = ECC_15BITS;
+               chip->ecc.strength = 15;
+               chip->ecc.bytes = ECC_15BITS;
                iowrite32(15, denali->flash_reg + ECC_CORRECTION);
        } else if (mtd->oobsize < (denali->bbtskipbytes +
                        ECC_8BITS * (mtd->writesize /
@@ -1548,24 +1605,26 @@ int denali_init(struct denali_nand_info *denali)
                pr_err("Your NAND chip OOB is not large enough to contain 8bit ECC correction codes");
                goto failed_req_irq;
        } else {
-               denali->nand.ecc.strength = 8;
-               denali->nand.ecc.bytes = ECC_8BITS;
+               chip->ecc.strength = 8;
+               chip->ecc.bytes = ECC_8BITS;
                iowrite32(8, denali->flash_reg + ECC_CORRECTION);
        }
 
        mtd_set_ooblayout(mtd, &denali_ooblayout_ops);
-       denali->nand.ecc.bytes *= denali->devnum;
-       denali->nand.ecc.strength *= denali->devnum;
 
        /* override the default read operations */
-       denali->nand.ecc.size = ECC_SECTOR_SIZE * denali->devnum;
-       denali->nand.ecc.read_page = denali_read_page;
-       denali->nand.ecc.read_page_raw = denali_read_page_raw;
-       denali->nand.ecc.write_page = denali_write_page;
-       denali->nand.ecc.write_page_raw = denali_write_page_raw;
-       denali->nand.ecc.read_oob = denali_read_oob;
-       denali->nand.ecc.write_oob = denali_write_oob;
-       denali->nand.erase = denali_erase;
+       chip->ecc.size = ECC_SECTOR_SIZE;
+       chip->ecc.read_page = denali_read_page;
+       chip->ecc.read_page_raw = denali_read_page_raw;
+       chip->ecc.write_page = denali_write_page;
+       chip->ecc.write_page_raw = denali_write_page_raw;
+       chip->ecc.read_oob = denali_read_oob;
+       chip->ecc.write_oob = denali_write_oob;
+       chip->erase = denali_erase;
+
+       ret = denali_multidev_fixup(denali);
+       if (ret)
+               goto failed_req_irq;
 
        ret = nand_scan_tail(mtd);
        if (ret)
index ea22191e85157c1603c798fef6147a4934c9f50a..ec004850652a7a67df8be4c984d70db6faf86954 100644 (file)
@@ -20,6 +20,7 @@
 #ifndef __DENALI_H__
 #define __DENALI_H__
 
+#include <linux/bitops.h>
 #include <linux/mtd/nand.h>
 
 #define DEVICE_RESET                           0x0
 
 #define REVISION                               0x370
 #define     REVISION__VALUE                            0xffff
-#define MAKE_COMPARABLE_REVISION(x)            swab16((x) & REVISION__VALUE)
-#define REVISION_5_1                           0x00000501
 
 #define ONFI_DEVICE_FEATURES                   0x380
 #define     ONFI_DEVICE_FEATURES__VALUE                        0x003f
 
 #define INTR_STATUS(__bank)    (0x410 + ((__bank) * 0x50))
 #define INTR_EN(__bank)                (0x420 + ((__bank) * 0x50))
-
-#define     INTR_STATUS__ECC_TRANSACTION_DONE          0x0001
-#define     INTR_STATUS__ECC_ERR                       0x0002
-#define     INTR_STATUS__DMA_CMD_COMP                  0x0004
-#define     INTR_STATUS__TIME_OUT                      0x0008
-#define     INTR_STATUS__PROGRAM_FAIL                  0x0010
-#define     INTR_STATUS__ERASE_FAIL                    0x0020
-#define     INTR_STATUS__LOAD_COMP                     0x0040
-#define     INTR_STATUS__PROGRAM_COMP                  0x0080
-#define     INTR_STATUS__ERASE_COMP                    0x0100
-#define     INTR_STATUS__PIPE_CPYBCK_CMD_COMP          0x0200
-#define     INTR_STATUS__LOCKED_BLK                    0x0400
-#define     INTR_STATUS__UNSUP_CMD                     0x0800
-#define     INTR_STATUS__INT_ACT                       0x1000
-#define     INTR_STATUS__RST_COMP                      0x2000
-#define     INTR_STATUS__PIPE_CMD_ERR                  0x4000
-#define     INTR_STATUS__PAGE_XFER_INC                 0x8000
-
-#define     INTR_EN__ECC_TRANSACTION_DONE              0x0001
-#define     INTR_EN__ECC_ERR                           0x0002
-#define     INTR_EN__DMA_CMD_COMP                      0x0004
-#define     INTR_EN__TIME_OUT                          0x0008
-#define     INTR_EN__PROGRAM_FAIL                      0x0010
-#define     INTR_EN__ERASE_FAIL                                0x0020
-#define     INTR_EN__LOAD_COMP                         0x0040
-#define     INTR_EN__PROGRAM_COMP                      0x0080
-#define     INTR_EN__ERASE_COMP                                0x0100
-#define     INTR_EN__PIPE_CPYBCK_CMD_COMP              0x0200
-#define     INTR_EN__LOCKED_BLK                                0x0400
-#define     INTR_EN__UNSUP_CMD                         0x0800
-#define     INTR_EN__INT_ACT                           0x1000
-#define     INTR_EN__RST_COMP                          0x2000
-#define     INTR_EN__PIPE_CMD_ERR                      0x4000
-#define     INTR_EN__PAGE_XFER_INC                     0x8000
+/* bit[1:0] is used differently depending on IP version */
+#define     INTR__ECC_UNCOR_ERR                                0x0001  /* new IP */
+#define     INTR__ECC_TRANSACTION_DONE                 0x0001  /* old IP */
+#define     INTR__ECC_ERR                              0x0002  /* old IP */
+#define     INTR__DMA_CMD_COMP                         0x0004
+#define     INTR__TIME_OUT                             0x0008
+#define     INTR__PROGRAM_FAIL                         0x0010
+#define     INTR__ERASE_FAIL                           0x0020
+#define     INTR__LOAD_COMP                            0x0040
+#define     INTR__PROGRAM_COMP                         0x0080
+#define     INTR__ERASE_COMP                           0x0100
+#define     INTR__PIPE_CPYBCK_CMD_COMP                 0x0200
+#define     INTR__LOCKED_BLK                           0x0400
+#define     INTR__UNSUP_CMD                            0x0800
+#define     INTR__INT_ACT                              0x1000
+#define     INTR__RST_COMP                             0x2000
+#define     INTR__PIPE_CMD_ERR                         0x4000
+#define     INTR__PAGE_XFER_INC                                0x8000
 
 #define PAGE_CNT(__bank)       (0x430 + ((__bank) * 0x50))
 #define ERR_PAGE_ADDR(__bank)  (0x440 + ((__bank) * 0x50))
 #define ERR_BLOCK_ADDR(__bank) (0x450 + ((__bank) * 0x50))
 
-#define DATA_INTR                              0x550
-#define     DATA_INTR__WRITE_SPACE_AV                  0x0001
-#define     DATA_INTR__READ_DATA_AV                    0x0002
-
-#define DATA_INTR_EN                           0x560
-#define     DATA_INTR_EN__WRITE_SPACE_AV               0x0001
-#define     DATA_INTR_EN__READ_DATA_AV                 0x0002
-
-#define GPREG_0                                        0x570
-#define     GPREG_0__VALUE                             0xffff
-
-#define GPREG_1                                        0x580
-#define     GPREG_1__VALUE                             0xffff
-
-#define GPREG_2                                        0x590
-#define     GPREG_2__VALUE                             0xffff
-
-#define GPREG_3                                        0x5a0
-#define     GPREG_3__VALUE                             0xffff
-
 #define ECC_THRESHOLD                          0x600
 #define     ECC_THRESHOLD__VALUE                       0x03ff
 
 #define     ERR_CORRECTION_INFO__ERROR_TYPE            0x4000
 #define     ERR_CORRECTION_INFO__LAST_ERR_INFO         0x8000
 
+#define ECC_COR_INFO(bank)                     (0x650 + (bank) / 2 * 0x10)
+#define     ECC_COR_INFO__SHIFT(bank)                  ((bank) % 2 * 8)
+#define     ECC_COR_INFO__MAX_ERRORS                   0x007f
+#define     ECC_COR_INFO__UNCOR_ERR                    0x0080
+
 #define DMA_ENABLE                             0x700
 #define     DMA_ENABLE__FLAG                           0x0001
 
 #define     IGNORE_ECC_DONE__FLAG                      0x0001
 
 #define DMA_INTR                               0x720
+#define DMA_INTR_EN                            0x730
 #define     DMA_INTR__TARGET_ERROR                     0x0001
 #define     DMA_INTR__DESC_COMP_CHANNEL0               0x0002
 #define     DMA_INTR__DESC_COMP_CHANNEL1               0x0004
 #define     DMA_INTR__DESC_COMP_CHANNEL2               0x0008
 #define     DMA_INTR__DESC_COMP_CHANNEL3               0x0010
-#define     DMA_INTR__MEMCOPY_DESC_COMP                0x0020
-
-#define DMA_INTR_EN                            0x730
-#define     DMA_INTR_EN__TARGET_ERROR                  0x0001
-#define     DMA_INTR_EN__DESC_COMP_CHANNEL0            0x0002
-#define     DMA_INTR_EN__DESC_COMP_CHANNEL1            0x0004
-#define     DMA_INTR_EN__DESC_COMP_CHANNEL2            0x0008
-#define     DMA_INTR_EN__DESC_COMP_CHANNEL3            0x0010
-#define     DMA_INTR_EN__MEMCOPY_DESC_COMP             0x0020
+#define     DMA_INTR__MEMCOPY_DESC_COMP                        0x0020
 
 #define TARGET_ERR_ADDR_LO                     0x740
 #define     TARGET_ERR_ADDR_LO__VALUE                  0xffff
 #define     CHNL_ACTIVE__CHANNEL2                      0x0004
 #define     CHNL_ACTIVE__CHANNEL3                      0x0008
 
-#define ACTIVE_SRC_ID                          0x800
-#define     ACTIVE_SRC_ID__VALUE                       0x00ff
-
-#define PTN_INTR                                       0x810
-#define     PTN_INTR__CONFIG_ERROR                     0x0001
-#define     PTN_INTR__ACCESS_ERROR_BANK0               0x0002
-#define     PTN_INTR__ACCESS_ERROR_BANK1               0x0004
-#define     PTN_INTR__ACCESS_ERROR_BANK2               0x0008
-#define     PTN_INTR__ACCESS_ERROR_BANK3               0x0010
-#define     PTN_INTR__REG_ACCESS_ERROR                 0x0020
-
-#define PTN_INTR_EN                            0x820
-#define     PTN_INTR_EN__CONFIG_ERROR                  0x0001
-#define     PTN_INTR_EN__ACCESS_ERROR_BANK0            0x0002
-#define     PTN_INTR_EN__ACCESS_ERROR_BANK1            0x0004
-#define     PTN_INTR_EN__ACCESS_ERROR_BANK2            0x0008
-#define     PTN_INTR_EN__ACCESS_ERROR_BANK3            0x0010
-#define     PTN_INTR_EN__REG_ACCESS_ERROR              0x0020
-
-#define PERM_SRC_ID(__bank)    (0x830 + ((__bank) * 0x40))
-#define     PERM_SRC_ID__SRCID                         0x00ff
-#define     PERM_SRC_ID__DIRECT_ACCESS_ACTIVE          0x0800
-#define     PERM_SRC_ID__WRITE_ACTIVE                  0x2000
-#define     PERM_SRC_ID__READ_ACTIVE                   0x4000
-#define     PERM_SRC_ID__PARTITION_VALID               0x8000
-
-#define MIN_BLK_ADDR(__bank)   (0x840 + ((__bank) * 0x40))
-#define     MIN_BLK_ADDR__VALUE                                0xffff
-
-#define MAX_BLK_ADDR(__bank)   (0x850 + ((__bank) * 0x40))
-#define     MAX_BLK_ADDR__VALUE                                0xffff
-
-#define MIN_MAX_BANK(__bank)   (0x860 + ((__bank) * 0x40))
-#define     MIN_MAX_BANK__MIN_VALUE                    0x0003
-#define     MIN_MAX_BANK__MAX_VALUE                    0x000c
-
-
-/* ffsdefs.h */
-#define CLEAR 0                 /*use this to clear a field instead of "fail"*/
-#define SET   1                 /*use this to set a field instead of "pass"*/
 #define FAIL 1                  /*failed flag*/
 #define PASS 0                  /*success flag*/
-#define ERR -1                  /*error flag*/
-
-/* lld.h */
-#define GOOD_BLOCK 0
-#define DEFECTIVE_BLOCK 1
-#define READ_ERROR 2
 
 #define CLK_X  5
 #define CLK_MULTI 4
 
-/* KBV - Updated to LNW scratch register address */
-#define SCRATCH_REG_ADDR    CONFIG_MTD_NAND_DENALI_SCRATCH_REG_ADDR
-#define SCRATCH_REG_SIZE    64
-
-#define GLOB_HWCTL_DEFAULT_BLKS    2048
-
-#define SUPPORT_15BITECC        1
-#define SUPPORT_8BITECC         1
-
-#define CUSTOM_CONF_PARAMS      0
-
 #define ONFI_BLOOM_TIME         1
 #define MODE5_WORKAROUND        0
 
 #define MODE_10    0x08000000
 #define MODE_11    0x0C000000
 
-
-#define DATA_TRANSFER_MODE              0
-#define PROTECTION_PER_BLOCK            1
-#define LOAD_WAIT_COUNT                 2
-#define PROGRAM_WAIT_COUNT              3
-#define ERASE_WAIT_COUNT                4
-#define INT_MONITOR_CYCLE_COUNT         5
-#define READ_BUSY_PIN_ENABLED           6
-#define MULTIPLANE_OPERATION_SUPPORT    7
-#define PRE_FETCH_MODE                  8
-#define CE_DONT_CARE_SUPPORT            9
-#define COPYBACK_SUPPORT                10
-#define CACHE_WRITE_SUPPORT             11
-#define CACHE_READ_SUPPORT              12
-#define NUM_PAGES_IN_BLOCK              13
-#define ECC_ENABLE_SELECT               14
-#define WRITE_ENABLE_2_READ_ENABLE      15
-#define ADDRESS_2_DATA                  16
-#define READ_ENABLE_2_WRITE_ENABLE      17
-#define TWO_ROW_ADDRESS_CYCLES          18
-#define MULTIPLANE_ADDRESS_RESTRICT     19
-#define ACC_CLOCKS                      20
-#define READ_WRITE_ENABLE_LOW_COUNT     21
-#define READ_WRITE_ENABLE_HIGH_COUNT    22
-
 #define ECC_SECTOR_SIZE     512
 
 struct nand_buf {
@@ -449,23 +328,26 @@ struct denali_nand_info {
        struct nand_buf buf;
        struct device *dev;
        int total_used_banks;
-       uint32_t block;  /* stored for future use */
-       uint16_t page;
-       void __iomem *flash_reg;  /* Mapped io reg base address */
-       void __iomem *flash_mem;  /* Mapped io reg base address */
+       int page;
+       void __iomem *flash_reg;        /* Register Interface */
+       void __iomem *flash_mem;        /* Host Data/Command Interface */
 
        /* elements used by ISR */
        struct completion complete;
        spinlock_t irq_lock;
        uint32_t irq_status;
-       int irq_debug_array[32];
        int irq;
 
-       uint32_t devnum;        /* represent how many nands connected */
-       uint32_t bbtskipbytes;
-       uint32_t max_banks;
+       int devnum;     /* represent how many nands connected */
+       int bbtskipbytes;
+       int max_banks;
+       unsigned int revision;
+       unsigned int caps;
 };
 
+#define DENALI_CAP_HW_ECC_FIXUP                        BIT(0)
+#define DENALI_CAP_DMA_64BIT                   BIT(1)
+
 extern int denali_init(struct denali_nand_info *denali);
 extern void denali_remove(struct denali_nand_info *denali);
 
index 5607fcd3b8ed5f765219ca5bfb084290d18a962b..df9ef36cc2ce3323da883e722152bf0b5a1d2f8b 100644 (file)
@@ -29,64 +29,66 @@ struct denali_dt {
        struct clk              *clk;
 };
 
-static const struct of_device_id denali_nand_dt_ids[] = {
-               { .compatible = "denali,denali-nand-dt" },
-               { /* sentinel */ }
-       };
+struct denali_dt_data {
+       unsigned int revision;
+       unsigned int caps;
+};
 
-MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
+static const struct denali_dt_data denali_socfpga_data = {
+       .caps = DENALI_CAP_HW_ECC_FIXUP,
+};
 
-static u64 denali_dma_mask;
+static const struct of_device_id denali_nand_dt_ids[] = {
+       {
+               .compatible = "altr,socfpga-denali-nand",
+               .data = &denali_socfpga_data,
+       },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
 
-static int denali_dt_probe(struct platform_device *ofdev)
+static int denali_dt_probe(struct platform_device *pdev)
 {
        struct resource *denali_reg, *nand_data;
        struct denali_dt *dt;
+       const struct denali_dt_data *data;
        struct denali_nand_info *denali;
        int ret;
-       const struct of_device_id *of_id;
 
-       of_id = of_match_device(denali_nand_dt_ids, &ofdev->dev);
-       if (of_id) {
-               ofdev->id_entry = of_id->data;
-       } else {
-               pr_err("Failed to find the right device id.\n");
-               return -ENOMEM;
-       }
-
-       dt = devm_kzalloc(&ofdev->dev, sizeof(*dt), GFP_KERNEL);
+       dt = devm_kzalloc(&pdev->dev, sizeof(*dt), GFP_KERNEL);
        if (!dt)
                return -ENOMEM;
        denali = &dt->denali;
 
+       data = of_device_get_match_data(&pdev->dev);
+       if (data) {
+               denali->revision = data->revision;
+               denali->caps = data->caps;
+       }
+
        denali->platform = DT;
-       denali->dev = &ofdev->dev;
-       denali->irq = platform_get_irq(ofdev, 0);
+       denali->dev = &pdev->dev;
+       denali->irq = platform_get_irq(pdev, 0);
        if (denali->irq < 0) {
-               dev_err(&ofdev->dev, "no irq defined\n");
+               dev_err(&pdev->dev, "no irq defined\n");
                return denali->irq;
        }
 
-       denali_reg = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "denali_reg");
-       denali->flash_reg = devm_ioremap_resource(&ofdev->dev, denali_reg);
+       denali_reg = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                 "denali_reg");
+       denali->flash_reg = devm_ioremap_resource(&pdev->dev, denali_reg);
        if (IS_ERR(denali->flash_reg))
                return PTR_ERR(denali->flash_reg);
 
-       nand_data = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "nand_data");
-       denali->flash_mem = devm_ioremap_resource(&ofdev->dev, nand_data);
+       nand_data = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                "nand_data");
+       denali->flash_mem = devm_ioremap_resource(&pdev->dev, nand_data);
        if (IS_ERR(denali->flash_mem))
                return PTR_ERR(denali->flash_mem);
 
-       if (!of_property_read_u32(ofdev->dev.of_node,
-               "dma-mask", (u32 *)&denali_dma_mask)) {
-               denali->dev->dma_mask = &denali_dma_mask;
-       } else {
-               denali->dev->dma_mask = NULL;
-       }
-
-       dt->clk = devm_clk_get(&ofdev->dev, NULL);
+       dt->clk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(dt->clk)) {
-               dev_err(&ofdev->dev, "no clk available\n");
+               dev_err(&pdev->dev, "no clk available\n");
                return PTR_ERR(dt->clk);
        }
        clk_prepare_enable(dt->clk);
@@ -95,7 +97,7 @@ static int denali_dt_probe(struct platform_device *ofdev)
        if (ret)
                goto out_disable_clk;
 
-       platform_set_drvdata(ofdev, dt);
+       platform_set_drvdata(pdev, dt);
        return 0;
 
 out_disable_clk:
@@ -104,9 +106,9 @@ out_disable_clk:
        return ret;
 }
 
-static int denali_dt_remove(struct platform_device *ofdev)
+static int denali_dt_remove(struct platform_device *pdev)
 {
-       struct denali_dt *dt = platform_get_drvdata(ofdev);
+       struct denali_dt *dt = platform_get_drvdata(pdev);
 
        denali_remove(&dt->denali);
        clk_disable_unprepare(dt->clk);
index bda1e4667138ab3cc392f961f7db6da377829b03..cea50d2f218c1d33c09005f3a5084372fa7a18e1 100644 (file)
 #include <linux/amba/bus.h>
 #include <mtd/mtd-abi.h>
 
-#define FSMC_NAND_BW8          1
-#define FSMC_NAND_BW16         2
-
-#define FSMC_MAX_NOR_BANKS     4
-#define FSMC_MAX_NAND_BANKS    4
-
-#define FSMC_FLASH_WIDTH8      1
-#define FSMC_FLASH_WIDTH16     2
-
 /* fsmc controller registers for NOR flash */
 #define CTRL                   0x0
        /* ctrl register definitions */
@@ -133,33 +124,48 @@ enum access_mode {
 };
 
 /**
- * fsmc_nand_platform_data - platform specific NAND controller config
- * @nand_timings: timing setup for the physical NAND interface
- * @partitions: partition table for the platform, use a default fallback
- * if this is NULL
- * @nr_partitions: the number of partitions in the previous entry
- * @options: different options for the driver
- * @width: bus width
- * @bank: default bank
- * @select_bank: callback to select a certain bank, this is
- * platform-specific. If the controller only supports one bank
- * this may be set to NULL
+ * struct fsmc_nand_data - structure for FSMC NAND device state
+ *
+ * @pid:               Part ID on the AMBA PrimeCell format
+ * @mtd:               MTD info for a NAND flash.
+ * @nand:              Chip related info for a NAND flash.
+ * @partitions:                Partition info for a NAND Flash.
+ * @nr_partitions:     Total number of partition of a NAND flash.
+ *
+ * @bank:              Bank number for probed device.
+ * @clk:               Clock structure for FSMC.
+ *
+ * @read_dma_chan:     DMA channel for read access
+ * @write_dma_chan:    DMA channel for write access to NAND
+ * @dma_access_complete: Completion structure
+ *
+ * @data_pa:           NAND Physical port for Data.
+ * @data_va:           NAND port for Data.
+ * @cmd_va:            NAND port for Command.
+ * @addr_va:           NAND port for Address.
+ * @regs_va:           FSMC regs base address.
  */
-struct fsmc_nand_platform_data {
-       struct fsmc_nand_timings *nand_timings;
-       struct mtd_partition    *partitions;
-       unsigned int            nr_partitions;
-       unsigned int            options;
-       unsigned int            width;
-       unsigned int            bank;
+struct fsmc_nand_data {
+       u32                     pid;
+       struct nand_chip        nand;
 
+       unsigned int            bank;
+       struct device           *dev;
        enum access_mode        mode;
+       struct clk              *clk;
 
-       void                    (*select_bank)(uint32_t bank, uint32_t busw);
+       /* DMA related objects */
+       struct dma_chan         *read_dma_chan;
+       struct dma_chan         *write_dma_chan;
+       struct completion       dma_access_complete;
 
-       /* priv structures for dma accesses */
-       void                    *read_dma_priv;
-       void                    *write_dma_priv;
+       struct fsmc_nand_timings *dev_timings;
+
+       dma_addr_t              data_pa;
+       void __iomem            *data_va;
+       void __iomem            *cmd_va;
+       void __iomem            *addr_va;
+       void __iomem            *regs_va;
 };
 
 static int fsmc_ecc1_ooblayout_ecc(struct mtd_info *mtd, int section,
@@ -246,86 +252,11 @@ static const struct mtd_ooblayout_ops fsmc_ecc4_ooblayout_ops = {
        .free = fsmc_ecc4_ooblayout_free,
 };
 
-/**
- * struct fsmc_nand_data - structure for FSMC NAND device state
- *
- * @pid:               Part ID on the AMBA PrimeCell format
- * @mtd:               MTD info for a NAND flash.
- * @nand:              Chip related info for a NAND flash.
- * @partitions:                Partition info for a NAND Flash.
- * @nr_partitions:     Total number of partition of a NAND flash.
- *
- * @bank:              Bank number for probed device.
- * @clk:               Clock structure for FSMC.
- *
- * @read_dma_chan:     DMA channel for read access
- * @write_dma_chan:    DMA channel for write access to NAND
- * @dma_access_complete: Completion structure
- *
- * @data_pa:           NAND Physical port for Data.
- * @data_va:           NAND port for Data.
- * @cmd_va:            NAND port for Command.
- * @addr_va:           NAND port for Address.
- * @regs_va:           FSMC regs base address.
- */
-struct fsmc_nand_data {
-       u32                     pid;
-       struct nand_chip        nand;
-       struct mtd_partition    *partitions;
-       unsigned int            nr_partitions;
-
-       unsigned int            bank;
-       struct device           *dev;
-       enum access_mode        mode;
-       struct clk              *clk;
-
-       /* DMA related objects */
-       struct dma_chan         *read_dma_chan;
-       struct dma_chan         *write_dma_chan;
-       struct completion       dma_access_complete;
-
-       struct fsmc_nand_timings *dev_timings;
-
-       dma_addr_t              data_pa;
-       void __iomem            *data_va;
-       void __iomem            *cmd_va;
-       void __iomem            *addr_va;
-       void __iomem            *regs_va;
-
-       void                    (*select_chip)(uint32_t bank, uint32_t busw);
-};
-
 static inline struct fsmc_nand_data *mtd_to_fsmc(struct mtd_info *mtd)
 {
        return container_of(mtd_to_nand(mtd), struct fsmc_nand_data, nand);
 }
 
-/* Assert CS signal based on chipnr */
-static void fsmc_select_chip(struct mtd_info *mtd, int chipnr)
-{
-       struct nand_chip *chip = mtd_to_nand(mtd);
-       struct fsmc_nand_data *host;
-
-       host = mtd_to_fsmc(mtd);
-
-       switch (chipnr) {
-       case -1:
-               chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
-               break;
-       case 0:
-       case 1:
-       case 2:
-       case 3:
-               if (host->select_chip)
-                       host->select_chip(chipnr,
-                                       chip->options & NAND_BUSWIDTH_16);
-               break;
-
-       default:
-               dev_err(host->dev, "unsupported chip-select %d\n", chipnr);
-       }
-}
-
 /*
  * fsmc_cmd_ctrl - For facilitaing Hardware access
  * This routine allows hardware specific access to control-lines(ALE,CLE)
@@ -838,44 +769,46 @@ static bool filter(struct dma_chan *chan, void *slave)
 }
 
 static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
-                                    struct device_node *np)
+                                    struct fsmc_nand_data *host,
+                                    struct nand_chip *nand)
 {
-       struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
+       struct device_node *np = pdev->dev.of_node;
        u32 val;
        int ret;
 
-       /* Set default NAND width to 8 bits */
-       pdata->width = 8;
+       nand->options = 0;
+
        if (!of_property_read_u32(np, "bank-width", &val)) {
                if (val == 2) {
-                       pdata->width = 16;
+                       nand->options |= NAND_BUSWIDTH_16;
                } else if (val != 1) {
                        dev_err(&pdev->dev, "invalid bank-width %u\n", val);
                        return -EINVAL;
                }
        }
+
        if (of_get_property(np, "nand-skip-bbtscan", NULL))
-               pdata->options = NAND_SKIP_BBTSCAN;
+               nand->options |= NAND_SKIP_BBTSCAN;
 
-       pdata->nand_timings = devm_kzalloc(&pdev->dev,
-                               sizeof(*pdata->nand_timings), GFP_KERNEL);
-       if (!pdata->nand_timings)
+       host->dev_timings = devm_kzalloc(&pdev->dev,
+                               sizeof(*host->dev_timings), GFP_KERNEL);
+       if (!host->dev_timings)
                return -ENOMEM;
-       ret = of_property_read_u8_array(np, "timings", (u8 *)pdata->nand_timings,
-                                               sizeof(*pdata->nand_timings));
+       ret = of_property_read_u8_array(np, "timings", (u8 *)host->dev_timings,
+                                               sizeof(*host->dev_timings));
        if (ret) {
                dev_info(&pdev->dev, "No timings in dts specified, using default timings!\n");
-               pdata->nand_timings = NULL;
+               host->dev_timings = NULL;
        }
 
        /* Set default NAND bank to 0 */
-       pdata->bank = 0;
+       host->bank = 0;
        if (!of_property_read_u32(np, "bank", &val)) {
                if (val > 3) {
                        dev_err(&pdev->dev, "invalid bank %u\n", val);
                        return -EINVAL;
                }
-               pdata->bank = val;
+               host->bank = val;
        }
        return 0;
 }
@@ -886,8 +819,6 @@ static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
  */
 static int __init fsmc_nand_probe(struct platform_device *pdev)
 {
-       struct fsmc_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
-       struct device_node __maybe_unused *np = pdev->dev.of_node;
        struct fsmc_nand_data *host;
        struct mtd_info *mtd;
        struct nand_chip *nand;
@@ -897,22 +828,17 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
        u32 pid;
        int i;
 
-       pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
-       if (!pdata)
-               return -ENOMEM;
-
-       pdev->dev.platform_data = pdata;
-       ret = fsmc_nand_probe_config_dt(pdev, np);
-       if (ret) {
-               dev_err(&pdev->dev, "no platform data\n");
-               return -ENODEV;
-       }
-
        /* Allocate memory for the device structure (and zero it) */
        host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
        if (!host)
                return -ENOMEM;
 
+       nand = &host->nand;
+
+       ret = fsmc_nand_probe_config_dt(pdev, host, nand);
+       if (ret)
+               return ret;
+
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
        host->data_va = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(host->data_va))
@@ -935,7 +861,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
        if (IS_ERR(host->regs_va))
                return PTR_ERR(host->regs_va);
 
-       host->clk = clk_get(&pdev->dev, NULL);
+       host->clk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(host->clk)) {
                dev_err(&pdev->dev, "failed to fetch block clock\n");
                return PTR_ERR(host->clk);
@@ -943,7 +869,7 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
 
        ret = clk_prepare_enable(host->clk);
        if (ret)
-               goto err_clk_prepare_enable;
+               return ret;
 
        /*
         * This device ID is actually a common AMBA ID as used on the
@@ -957,22 +883,15 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
                 AMBA_PART_BITS(pid), AMBA_MANF_BITS(pid),
                 AMBA_REV_BITS(pid), AMBA_CONFIG_BITS(pid));
 
-       host->bank = pdata->bank;
-       host->select_chip = pdata->select_bank;
-       host->partitions = pdata->partitions;
-       host->nr_partitions = pdata->nr_partitions;
        host->dev = &pdev->dev;
-       host->dev_timings = pdata->nand_timings;
-       host->mode = pdata->mode;
 
        if (host->mode == USE_DMA_ACCESS)
                init_completion(&host->dma_access_complete);
 
        /* Link all private pointers */
        mtd = nand_to_mtd(&host->nand);
-       nand = &host->nand;
        nand_set_controller_data(nand, host);
-       nand_set_flash_node(nand, np);
+       nand_set_flash_node(nand, pdev->dev.of_node);
 
        mtd->dev.parent = &pdev->dev;
        nand->IO_ADDR_R = host->data_va;
@@ -987,26 +906,18 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
        nand->ecc.mode = NAND_ECC_HW;
        nand->ecc.hwctl = fsmc_enable_hwecc;
        nand->ecc.size = 512;
-       nand->options = pdata->options;
-       nand->select_chip = fsmc_select_chip;
        nand->badblockbits = 7;
-       nand_set_flash_node(nand, np);
-
-       if (pdata->width == FSMC_NAND_BW16)
-               nand->options |= NAND_BUSWIDTH_16;
 
        switch (host->mode) {
        case USE_DMA_ACCESS:
                dma_cap_zero(mask);
                dma_cap_set(DMA_MEMCPY, mask);
-               host->read_dma_chan = dma_request_channel(mask, filter,
-                               pdata->read_dma_priv);
+               host->read_dma_chan = dma_request_channel(mask, filter, NULL);
                if (!host->read_dma_chan) {
                        dev_err(&pdev->dev, "Unable to get read dma channel\n");
                        goto err_req_read_chnl;
                }
-               host->write_dma_chan = dma_request_channel(mask, filter,
-                               pdata->write_dma_priv);
+               host->write_dma_chan = dma_request_channel(mask, filter, NULL);
                if (!host->write_dma_chan) {
                        dev_err(&pdev->dev, "Unable to get write dma channel\n");
                        goto err_req_write_chnl;
@@ -1107,18 +1018,8 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
        if (ret)
                goto err_probe;
 
-       /*
-        * The partition information can is accessed by (in the same precedence)
-        *
-        * command line through Bootloader,
-        * platform data,
-        * default partition information present in driver.
-        */
-       /*
-        * Check for partition info passed
-        */
        mtd->name = "nand";
-       ret = mtd_device_register(mtd, host->partitions, host->nr_partitions);
+       ret = mtd_device_register(mtd, NULL, 0);
        if (ret)
                goto err_probe;
 
@@ -1135,8 +1036,6 @@ err_req_write_chnl:
                dma_release_channel(host->read_dma_chan);
 err_req_read_chnl:
        clk_disable_unprepare(host->clk);
-err_clk_prepare_enable:
-       clk_put(host->clk);
        return ret;
 }
 
@@ -1155,7 +1054,6 @@ static int fsmc_nand_remove(struct platform_device *pdev)
                        dma_release_channel(host->read_dma_chan);
                }
                clk_disable_unprepare(host->clk);
-               clk_put(host->clk);
        }
 
        return 0;
@@ -1185,20 +1083,18 @@ static int fsmc_nand_resume(struct device *dev)
 
 static SIMPLE_DEV_PM_OPS(fsmc_nand_pm_ops, fsmc_nand_suspend, fsmc_nand_resume);
 
-#ifdef CONFIG_OF
 static const struct of_device_id fsmc_nand_id_table[] = {
        { .compatible = "st,spear600-fsmc-nand" },
        { .compatible = "stericsson,fsmc-nand" },
        {}
 };
 MODULE_DEVICE_TABLE(of, fsmc_nand_id_table);
-#endif
 
 static struct platform_driver fsmc_nand_driver = {
        .remove = fsmc_nand_remove,
        .driver = {
                .name = "fsmc-nand",
-               .of_match_table = of_match_ptr(fsmc_nand_id_table),
+               .of_match_table = fsmc_nand_id_table,
                .pm = &fsmc_nand_pm_ops,
        },
 };
index 0d24857469ab396ac10c165076fd380639aa4463..85294f150f4ff36effe127722003f6e69340bbba 100644 (file)
@@ -78,7 +78,9 @@ static void gpio_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
        gpio_nand_dosync(gpiomtd);
 
        if (ctrl & NAND_CTRL_CHANGE) {
-               gpio_set_value(gpiomtd->plat.gpio_nce, !(ctrl & NAND_NCE));
+               if (gpio_is_valid(gpiomtd->plat.gpio_nce))
+                       gpio_set_value(gpiomtd->plat.gpio_nce,
+                                      !(ctrl & NAND_NCE));
                gpio_set_value(gpiomtd->plat.gpio_cle, !!(ctrl & NAND_CLE));
                gpio_set_value(gpiomtd->plat.gpio_ale, !!(ctrl & NAND_ALE));
                gpio_nand_dosync(gpiomtd);
@@ -201,7 +203,8 @@ static int gpio_nand_remove(struct platform_device *pdev)
 
        if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
                gpio_set_value(gpiomtd->plat.gpio_nwp, 0);
-       gpio_set_value(gpiomtd->plat.gpio_nce, 1);
+       if (gpio_is_valid(gpiomtd->plat.gpio_nce))
+               gpio_set_value(gpiomtd->plat.gpio_nce, 1);
 
        return 0;
 }
@@ -239,10 +242,13 @@ static int gpio_nand_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
-       ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nce, "NAND NCE");
-       if (ret)
-               return ret;
-       gpio_direction_output(gpiomtd->plat.gpio_nce, 1);
+       if (gpio_is_valid(gpiomtd->plat.gpio_nce)) {
+               ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nce,
+                                       "NAND NCE");
+               if (ret)
+                       return ret;
+               gpio_direction_output(gpiomtd->plat.gpio_nce, 1);
+       }
 
        if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) {
                ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nwp,
diff --git a/drivers/mtd/nand/nand_amd.c b/drivers/mtd/nand/nand_amd.c
new file mode 100644 (file)
index 0000000..170403a
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 Free Electrons
+ * Copyright (C) 2017 NextThing Co
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mtd/nand.h>
+
+static void amd_nand_decode_id(struct nand_chip *chip)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+
+       nand_decode_ext_id(chip);
+
+       /*
+        * Check for Spansion/AMD ID + repeating 5th, 6th byte since
+        * some Spansion chips have erasesize that conflicts with size
+        * listed in nand_ids table.
+        * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
+        */
+       if (chip->id.data[4] != 0x00 && chip->id.data[5] == 0x00 &&
+           chip->id.data[6] == 0x00 && chip->id.data[7] == 0x00 &&
+           mtd->writesize == 512) {
+               mtd->erasesize = 128 * 1024;
+               mtd->erasesize <<= ((chip->id.data[3] & 0x03) << 1);
+       }
+}
+
+static int amd_nand_init(struct nand_chip *chip)
+{
+       if (nand_is_slc(chip))
+               chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
+
+       return 0;
+}
+
+const struct nand_manufacturer_ops amd_nand_manuf_ops = {
+       .detect = amd_nand_decode_id,
+       .init = amd_nand_init,
+};
index b0524f8accb6206f50e5b30d422b81ec311a40f3..ed49a1d634b04487998c0296ecdaf681e9d4da3f 100644 (file)
@@ -354,40 +354,32 @@ static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
  */
 static int nand_block_bad(struct mtd_info *mtd, loff_t ofs)
 {
-       int page, res = 0, i = 0;
+       int page, page_end, res;
        struct nand_chip *chip = mtd_to_nand(mtd);
-       u16 bad;
+       u8 bad;
 
        if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
                ofs += mtd->erasesize - mtd->writesize;
 
        page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+       page_end = page + (chip->bbt_options & NAND_BBT_SCAN2NDPAGE ? 2 : 1);
 
-       do {
-               if (chip->options & NAND_BUSWIDTH_16) {
-                       chip->cmdfunc(mtd, NAND_CMD_READOOB,
-                                       chip->badblockpos & 0xFE, page);
-                       bad = cpu_to_le16(chip->read_word(mtd));
-                       if (chip->badblockpos & 0x1)
-                               bad >>= 8;
-                       else
-                               bad &= 0xFF;
-               } else {
-                       chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos,
-                                       page);
-                       bad = chip->read_byte(mtd);
-               }
+       for (; page < page_end; page++) {
+               res = chip->ecc.read_oob(mtd, chip, page);
+               if (res)
+                       return res;
+
+               bad = chip->oob_poi[chip->badblockpos];
 
                if (likely(chip->badblockbits == 8))
                        res = bad != 0xFF;
                else
                        res = hweight8(bad) < chip->badblockbits;
-               ofs += mtd->writesize;
-               page = (int)(ofs >> chip->page_shift) & chip->pagemask;
-               i++;
-       } while (!res && i < 2 && (chip->bbt_options & NAND_BBT_SCAN2NDPAGE));
+               if (res)
+                       return res;
+       }
 
-       return res;
+       return 0;
 }
 
 /**
@@ -676,6 +668,8 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
        case NAND_CMD_ERASE2:
        case NAND_CMD_SEQIN:
        case NAND_CMD_STATUS:
+       case NAND_CMD_READID:
+       case NAND_CMD_SET_FEATURES:
                return;
 
        case NAND_CMD_RESET:
@@ -794,6 +788,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
        case NAND_CMD_ERASE2:
        case NAND_CMD_SEQIN:
        case NAND_CMD_STATUS:
+       case NAND_CMD_READID:
+       case NAND_CMD_SET_FEATURES:
                return;
 
        case NAND_CMD_RNDIN:
@@ -1958,7 +1954,9 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
                if (!aligned)
                        use_bufpoi = 1;
                else if (chip->options & NAND_USE_BOUNCE_BUFFER)
-                       use_bufpoi = !virt_addr_valid(buf);
+                       use_bufpoi = !virt_addr_valid(buf) ||
+                                    !IS_ALIGNED((unsigned long)buf,
+                                                chip->buf_align);
                else
                        use_bufpoi = 0;
 
@@ -1997,8 +1995,6 @@ read_retry:
                                break;
                        }
 
-                       max_bitflips = max_t(unsigned int, max_bitflips, ret);
-
                        /* Transfer not aligned data */
                        if (use_bufpoi) {
                                if (!NAND_HAS_SUBPAGE_READ(chip) && !oob &&
@@ -2049,6 +2045,7 @@ read_retry:
                        }
 
                        buf += bytes;
+                       max_bitflips = max_t(unsigned int, max_bitflips, ret);
                } else {
                        memcpy(buf, chip->buffers->databuf + col, bytes);
                        buf += bytes;
@@ -2637,7 +2634,7 @@ static int nand_write_page_syndrome(struct mtd_info *mtd,
 }
 
 /**
- * nand_write_page - [REPLACEABLE] write one page
+ * nand_write_page - write one page
  * @mtd: MTD device structure
  * @chip: NAND chip descriptor
  * @offset: address offset within the page
@@ -2815,7 +2812,9 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
                if (part_pagewr)
                        use_bufpoi = 1;
                else if (chip->options & NAND_USE_BOUNCE_BUFFER)
-                       use_bufpoi = !virt_addr_valid(buf);
+                       use_bufpoi = !virt_addr_valid(buf) ||
+                                    !IS_ALIGNED((unsigned long)buf,
+                                                chip->buf_align);
                else
                        use_bufpoi = 0;
 
@@ -2840,9 +2839,10 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
                        /* We still need to erase leftover OOB data */
                        memset(chip->oob_poi, 0xff, mtd->oobsize);
                }
-               ret = chip->write_page(mtd, chip, column, bytes, wbuf,
-                                       oob_required, page, cached,
-                                       (ops->mode == MTD_OPS_RAW));
+
+               ret = nand_write_page(mtd, chip, column, bytes, wbuf,
+                                     oob_required, page, cached,
+                                     (ops->mode == MTD_OPS_RAW));
                if (ret)
                        break;
 
@@ -3385,8 +3385,10 @@ static void nand_shutdown(struct mtd_info *mtd)
 }
 
 /* Set default functions */
-static void nand_set_defaults(struct nand_chip *chip, int busw)
+static void nand_set_defaults(struct nand_chip *chip)
 {
+       unsigned int busw = chip->options & NAND_BUSWIDTH_16;
+
        /* check for proper chip_delay setup, set 20us if not */
        if (!chip->chip_delay)
                chip->chip_delay = 20;
@@ -3431,6 +3433,8 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
                nand_hw_control_init(chip->controller);
        }
 
+       if (!chip->buf_align)
+               chip->buf_align = 1;
 }
 
 /* Sanitize ONFI strings so we can safely print them */
@@ -3464,9 +3468,10 @@ static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
 }
 
 /* Parse the Extended Parameter Page. */
-static int nand_flash_detect_ext_param_page(struct mtd_info *mtd,
-               struct nand_chip *chip, struct nand_onfi_params *p)
+static int nand_flash_detect_ext_param_page(struct nand_chip *chip,
+                                           struct nand_onfi_params *p)
 {
+       struct mtd_info *mtd = nand_to_mtd(chip);
        struct onfi_ext_param_page *ep;
        struct onfi_ext_section *s;
        struct onfi_ext_ecc_info *ecc;
@@ -3534,36 +3539,12 @@ ext_out:
        return ret;
 }
 
-static int nand_setup_read_retry_micron(struct mtd_info *mtd, int retry_mode)
-{
-       struct nand_chip *chip = mtd_to_nand(mtd);
-       uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode};
-
-       return chip->onfi_set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY,
-                       feature);
-}
-
-/*
- * Configure chip properties from Micron vendor-specific ONFI table
- */
-static void nand_onfi_detect_micron(struct nand_chip *chip,
-               struct nand_onfi_params *p)
-{
-       struct nand_onfi_vendor_micron *micron = (void *)p->vendor;
-
-       if (le16_to_cpu(p->vendor_revision) < 1)
-               return;
-
-       chip->read_retries = micron->read_retry_options;
-       chip->setup_read_retry = nand_setup_read_retry_micron;
-}
-
 /*
  * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise.
  */
-static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
-                                       int *busw)
+static int nand_flash_detect_onfi(struct nand_chip *chip)
 {
+       struct mtd_info *mtd = nand_to_mtd(chip);
        struct nand_onfi_params *p = &chip->onfi_params;
        int i, j;
        int val;
@@ -3633,9 +3614,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
        chip->blocks_per_die = le32_to_cpu(p->blocks_per_lun);
 
        if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS)
-               *busw = NAND_BUSWIDTH_16;
-       else
-               *busw = 0;
+               chip->options |= NAND_BUSWIDTH_16;
 
        if (p->ecc_bits != 0xff) {
                chip->ecc_strength_ds = p->ecc_bits;
@@ -3653,24 +3632,21 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
                        chip->cmdfunc = nand_command_lp;
 
                /* The Extended Parameter Page is supported since ONFI 2.1. */
-               if (nand_flash_detect_ext_param_page(mtd, chip, p))
+               if (nand_flash_detect_ext_param_page(chip, p))
                        pr_warn("Failed to detect ONFI extended param page\n");
        } else {
                pr_warn("Could not retrieve ONFI ECC requirements\n");
        }
 
-       if (p->jedec_id == NAND_MFR_MICRON)
-               nand_onfi_detect_micron(chip, p);
-
        return 1;
 }
 
 /*
  * Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise.
  */
-static int nand_flash_detect_jedec(struct mtd_info *mtd, struct nand_chip *chip,
-                                       int *busw)
+static int nand_flash_detect_jedec(struct nand_chip *chip)
 {
+       struct mtd_info *mtd = nand_to_mtd(chip);
        struct nand_jedec_params *p = &chip->jedec_params;
        struct jedec_ecc_info *ecc;
        int val;
@@ -3729,9 +3705,7 @@ static int nand_flash_detect_jedec(struct mtd_info *mtd, struct nand_chip *chip,
        chip->bits_per_cell = p->bits_per_cell;
 
        if (jedec_feature(chip) & JEDEC_FEATURE_16_BIT_BUS)
-               *busw = NAND_BUSWIDTH_16;
-       else
-               *busw = 0;
+               chip->options |= NAND_BUSWIDTH_16;
 
        /* ECC info */
        ecc = &p->ecc_info[0];
@@ -3820,165 +3794,46 @@ static int nand_get_bits_per_cell(u8 cellinfo)
  * chip. The rest of the parameters must be decoded according to generic or
  * manufacturer-specific "extended ID" decoding patterns.
  */
-static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip,
-                               u8 id_data[8], int *busw)
+void nand_decode_ext_id(struct nand_chip *chip)
 {
-       int extid, id_len;
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       int extid;
+       u8 *id_data = chip->id.data;
        /* The 3rd id byte holds MLC / multichip data */
        chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]);
        /* The 4th id byte is the important one */
        extid = id_data[3];
 
-       id_len = nand_id_len(id_data, 8);
-
-       /*
-        * Field definitions are in the following datasheets:
-        * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32)
-        * New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44)
-        * Hynix MLC   (6 byte ID): Hynix H27UBG8T2B (p.22)
-        *
-        * Check for ID length, non-zero 6th byte, cell type, and Hynix/Samsung
-        * ID to decide what to do.
-        */
-       if (id_len == 6 && id_data[0] == NAND_MFR_SAMSUNG &&
-                       !nand_is_slc(chip) && id_data[5] != 0x00) {
-               /* Calc pagesize */
-               mtd->writesize = 2048 << (extid & 0x03);
-               extid >>= 2;
-               /* Calc oobsize */
-               switch (((extid >> 2) & 0x04) | (extid & 0x03)) {
-               case 1:
-                       mtd->oobsize = 128;
-                       break;
-               case 2:
-                       mtd->oobsize = 218;
-                       break;
-               case 3:
-                       mtd->oobsize = 400;
-                       break;
-               case 4:
-                       mtd->oobsize = 436;
-                       break;
-               case 5:
-                       mtd->oobsize = 512;
-                       break;
-               case 6:
-                       mtd->oobsize = 640;
-                       break;
-               case 7:
-               default: /* Other cases are "reserved" (unknown) */
-                       mtd->oobsize = 1024;
-                       break;
-               }
-               extid >>= 2;
-               /* Calc blocksize */
-               mtd->erasesize = (128 * 1024) <<
-                       (((extid >> 1) & 0x04) | (extid & 0x03));
-               *busw = 0;
-       } else if (id_len == 6 && id_data[0] == NAND_MFR_HYNIX &&
-                       !nand_is_slc(chip)) {
-               unsigned int tmp;
-
-               /* Calc pagesize */
-               mtd->writesize = 2048 << (extid & 0x03);
-               extid >>= 2;
-               /* Calc oobsize */
-               switch (((extid >> 2) & 0x04) | (extid & 0x03)) {
-               case 0:
-                       mtd->oobsize = 128;
-                       break;
-               case 1:
-                       mtd->oobsize = 224;
-                       break;
-               case 2:
-                       mtd->oobsize = 448;
-                       break;
-               case 3:
-                       mtd->oobsize = 64;
-                       break;
-               case 4:
-                       mtd->oobsize = 32;
-                       break;
-               case 5:
-                       mtd->oobsize = 16;
-                       break;
-               default:
-                       mtd->oobsize = 640;
-                       break;
-               }
-               extid >>= 2;
-               /* Calc blocksize */
-               tmp = ((extid >> 1) & 0x04) | (extid & 0x03);
-               if (tmp < 0x03)
-                       mtd->erasesize = (128 * 1024) << tmp;
-               else if (tmp == 0x03)
-                       mtd->erasesize = 768 * 1024;
-               else
-                       mtd->erasesize = (64 * 1024) << tmp;
-               *busw = 0;
-       } else {
-               /* Calc pagesize */
-               mtd->writesize = 1024 << (extid & 0x03);
-               extid >>= 2;
-               /* Calc oobsize */
-               mtd->oobsize = (8 << (extid & 0x01)) *
-                       (mtd->writesize >> 9);
-               extid >>= 2;
-               /* Calc blocksize. Blocksize is multiples of 64KiB */
-               mtd->erasesize = (64 * 1024) << (extid & 0x03);
-               extid >>= 2;
-               /* Get buswidth information */
-               *busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
-
-               /*
-                * Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per
-                * 512B page. For Toshiba SLC, we decode the 5th/6th byte as
-                * follows:
-                * - ID byte 6, bits[2:0]: 100b -> 43nm, 101b -> 32nm,
-                *                         110b -> 24nm
-                * - ID byte 5, bit[7]:    1 -> BENAND, 0 -> raw SLC
-                */
-               if (id_len >= 6 && id_data[0] == NAND_MFR_TOSHIBA &&
-                               nand_is_slc(chip) &&
-                               (id_data[5] & 0x7) == 0x6 /* 24nm */ &&
-                               !(id_data[4] & 0x80) /* !BENAND */) {
-                       mtd->oobsize = 32 * mtd->writesize >> 9;
-               }
-
-       }
+       /* Calc pagesize */
+       mtd->writesize = 1024 << (extid & 0x03);
+       extid >>= 2;
+       /* Calc oobsize */
+       mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
+       extid >>= 2;
+       /* Calc blocksize. Blocksize is multiples of 64KiB */
+       mtd->erasesize = (64 * 1024) << (extid & 0x03);
+       extid >>= 2;
+       /* Get buswidth information */
+       if (extid & 0x1)
+               chip->options |= NAND_BUSWIDTH_16;
 }
+EXPORT_SYMBOL_GPL(nand_decode_ext_id);
 
 /*
  * Old devices have chip data hardcoded in the device ID table. nand_decode_id
  * decodes a matching ID table entry and assigns the MTD size parameters for
  * the chip.
  */
-static void nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip,
-                               struct nand_flash_dev *type, u8 id_data[8],
-                               int *busw)
+static void nand_decode_id(struct nand_chip *chip, struct nand_flash_dev *type)
 {
-       int maf_id = id_data[0];
+       struct mtd_info *mtd = nand_to_mtd(chip);
 
        mtd->erasesize = type->erasesize;
        mtd->writesize = type->pagesize;
        mtd->oobsize = mtd->writesize / 32;
-       *busw = type->options & NAND_BUSWIDTH_16;
 
        /* All legacy ID NAND are small-page, SLC */
        chip->bits_per_cell = 1;
-
-       /*
-        * Check for Spansion/AMD ID + repeating 5th, 6th byte since
-        * some Spansion chips have erasesize that conflicts with size
-        * listed in nand_ids table.
-        * Data sheet (5 byte ID): Spansion S30ML-P ORNAND (p.39)
-        */
-       if (maf_id == NAND_MFR_AMD && id_data[4] != 0x00 && id_data[5] == 0x00
-                       && id_data[6] == 0x00 && id_data[7] == 0x00
-                       && mtd->writesize == 512) {
-               mtd->erasesize = 128 * 1024;
-               mtd->erasesize <<= ((id_data[3] & 0x03) << 1);
-       }
 }
 
 /*
@@ -3986,36 +3841,15 @@ static void nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip,
  * heuristic patterns using various detected parameters (e.g., manufacturer,
  * page size, cell-type information).
  */
-static void nand_decode_bbm_options(struct mtd_info *mtd,
-                                   struct nand_chip *chip, u8 id_data[8])
+static void nand_decode_bbm_options(struct nand_chip *chip)
 {
-       int maf_id = id_data[0];
+       struct mtd_info *mtd = nand_to_mtd(chip);
 
        /* Set the bad block position */
        if (mtd->writesize > 512 || (chip->options & NAND_BUSWIDTH_16))
                chip->badblockpos = NAND_LARGE_BADBLOCK_POS;
        else
                chip->badblockpos = NAND_SMALL_BADBLOCK_POS;
-
-       /*
-        * Bad block marker is stored in the last page of each block on Samsung
-        * and Hynix MLC devices; stored in first two pages of each block on
-        * Micron devices with 2KiB pages and on SLC Samsung, Hynix, Toshiba,
-        * AMD/Spansion, and Macronix.  All others scan only the first page.
-        */
-       if (!nand_is_slc(chip) &&
-                       (maf_id == NAND_MFR_SAMSUNG ||
-                        maf_id == NAND_MFR_HYNIX))
-               chip->bbt_options |= NAND_BBT_SCANLASTPAGE;
-       else if ((nand_is_slc(chip) &&
-                               (maf_id == NAND_MFR_SAMSUNG ||
-                                maf_id == NAND_MFR_HYNIX ||
-                                maf_id == NAND_MFR_TOSHIBA ||
-                                maf_id == NAND_MFR_AMD ||
-                                maf_id == NAND_MFR_MACRONIX)) ||
-                       (mtd->writesize == 2048 &&
-                        maf_id == NAND_MFR_MICRON))
-               chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
 }
 
 static inline bool is_full_id_nand(struct nand_flash_dev *type)
@@ -4023,9 +3857,12 @@ static inline bool is_full_id_nand(struct nand_flash_dev *type)
        return type->id_len;
 }
 
-static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
-                  struct nand_flash_dev *type, u8 *id_data, int *busw)
+static bool find_full_id_nand(struct nand_chip *chip,
+                             struct nand_flash_dev *type)
 {
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       u8 *id_data = chip->id.data;
+
        if (!strncmp(type->id, id_data, type->id_len)) {
                mtd->writesize = type->pagesize;
                mtd->erasesize = type->erasesize;
@@ -4039,8 +3876,6 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
                chip->onfi_timing_mode_default =
                                        type->onfi_timing_mode_default;
 
-               *busw = type->options & NAND_BUSWIDTH_16;
-
                if (!mtd->name)
                        mtd->name = type->name;
 
@@ -4049,16 +3884,64 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
        return false;
 }
 
+/*
+ * Manufacturer detection. Only used when the NAND is not ONFI or JEDEC
+ * compliant and does not have a full-id or legacy-id entry in the nand_ids
+ * table.
+ */
+static void nand_manufacturer_detect(struct nand_chip *chip)
+{
+       /*
+        * Try manufacturer detection if available and use
+        * nand_decode_ext_id() otherwise.
+        */
+       if (chip->manufacturer.desc && chip->manufacturer.desc->ops &&
+           chip->manufacturer.desc->ops->detect)
+               chip->manufacturer.desc->ops->detect(chip);
+       else
+               nand_decode_ext_id(chip);
+}
+
+/*
+ * Manufacturer initialization. This function is called for all NANDs including
+ * ONFI and JEDEC compliant ones.
+ * Manufacturer drivers should put all their specific initialization code in
+ * their ->init() hook.
+ */
+static int nand_manufacturer_init(struct nand_chip *chip)
+{
+       if (!chip->manufacturer.desc || !chip->manufacturer.desc->ops ||
+           !chip->manufacturer.desc->ops->init)
+               return 0;
+
+       return chip->manufacturer.desc->ops->init(chip);
+}
+
+/*
+ * Manufacturer cleanup. This function is called for all NANDs including
+ * ONFI and JEDEC compliant ones.
+ * Manufacturer drivers should put all their specific cleanup code in their
+ * ->cleanup() hook.
+ */
+static void nand_manufacturer_cleanup(struct nand_chip *chip)
+{
+       /* Release manufacturer private data */
+       if (chip->manufacturer.desc && chip->manufacturer.desc->ops &&
+           chip->manufacturer.desc->ops->cleanup)
+               chip->manufacturer.desc->ops->cleanup(chip);
+}
+
 /*
  * Get the flash and manufacturer id and lookup if the type is supported.
  */
-static int nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip,
-                              int *maf_id, int *dev_id,
-                              struct nand_flash_dev *type)
+static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
 {
+       const struct nand_manufacturer *manufacturer;
+       struct mtd_info *mtd = nand_to_mtd(chip);
        int busw;
-       int i, maf_idx;
-       u8 id_data[8];
+       int i, ret;
+       u8 *id_data = chip->id.data;
+       u8 maf_id, dev_id;
 
        /*
         * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
@@ -4073,8 +3956,8 @@ static int nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip,
        chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
 
        /* Read manufacturer and device IDs */
-       *maf_id = chip->read_byte(mtd);
-       *dev_id = chip->read_byte(mtd);
+       maf_id = chip->read_byte(mtd);
+       dev_id = chip->read_byte(mtd);
 
        /*
         * Try again to make sure, as some systems the bus-hold or other
@@ -4089,20 +3972,41 @@ static int nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip,
        for (i = 0; i < 8; i++)
                id_data[i] = chip->read_byte(mtd);
 
-       if (id_data[0] != *maf_id || id_data[1] != *dev_id) {
+       if (id_data[0] != maf_id || id_data[1] != dev_id) {
                pr_info("second ID read did not match %02x,%02x against %02x,%02x\n",
-                       *maf_id, *dev_id, id_data[0], id_data[1]);
+                       maf_id, dev_id, id_data[0], id_data[1]);
                return -ENODEV;
        }
 
+       chip->id.len = nand_id_len(id_data, 8);
+
+       /* Try to identify manufacturer */
+       manufacturer = nand_get_manufacturer(maf_id);
+       chip->manufacturer.desc = manufacturer;
+
        if (!type)
                type = nand_flash_ids;
 
+       /*
+        * Save the NAND_BUSWIDTH_16 flag before letting auto-detection logic
+        * override it.
+        * This is required to make sure initial NAND bus width set by the
+        * NAND controller driver is coherent with the real NAND bus width
+        * (extracted by auto-detection code).
+        */
+       busw = chip->options & NAND_BUSWIDTH_16;
+
+       /*
+        * The flag is only set (never cleared), reset it to its default value
+        * before starting auto-detection.
+        */
+       chip->options &= ~NAND_BUSWIDTH_16;
+
        for (; type->name != NULL; type++) {
                if (is_full_id_nand(type)) {
-                       if (find_full_id_nand(mtd, chip, type, id_data, &busw))
+                       if (find_full_id_nand(chip, type))
                                goto ident_done;
-               } else if (*dev_id == type->dev_id) {
+               } else if (dev_id == type->dev_id) {
                        break;
                }
        }
@@ -4110,11 +4014,11 @@ static int nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip,
        chip->onfi_version = 0;
        if (!type->name || !type->pagesize) {
                /* Check if the chip is ONFI compliant */
-               if (nand_flash_detect_onfi(mtd, chip, &busw))
+               if (nand_flash_detect_onfi(chip))
                        goto ident_done;
 
                /* Check if the chip is JEDEC compliant */
-               if (nand_flash_detect_jedec(mtd, chip, &busw))
+               if (nand_flash_detect_jedec(chip))
                        goto ident_done;
        }
 
@@ -4126,48 +4030,34 @@ static int nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip,
 
        chip->chipsize = (uint64_t)type->chipsize << 20;
 
-       if (!type->pagesize) {
-               /* Decode parameters from extended ID */
-               nand_decode_ext_id(mtd, chip, id_data, &busw);
-       } else {
-               nand_decode_id(mtd, chip, type, id_data, &busw);
-       }
+       if (!type->pagesize)
+               nand_manufacturer_detect(chip);
+       else
+               nand_decode_id(chip, type);
+
        /* Get chip options */
        chip->options |= type->options;
 
-       /*
-        * Check if chip is not a Samsung device. Do not clear the
-        * options for chips which do not have an extended id.
-        */
-       if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
-               chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
 ident_done:
 
-       /* Try to identify manufacturer */
-       for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
-               if (nand_manuf_ids[maf_idx].id == *maf_id)
-                       break;
-       }
-
        if (chip->options & NAND_BUSWIDTH_AUTO) {
-               WARN_ON(chip->options & NAND_BUSWIDTH_16);
-               chip->options |= busw;
-               nand_set_defaults(chip, busw);
+               WARN_ON(busw & NAND_BUSWIDTH_16);
+               nand_set_defaults(chip);
        } else if (busw != (chip->options & NAND_BUSWIDTH_16)) {
                /*
                 * Check, if buswidth is correct. Hardware drivers should set
                 * chip correct!
                 */
                pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
-                       *maf_id, *dev_id);
-               pr_info("%s %s\n", nand_manuf_ids[maf_idx].name, mtd->name);
-               pr_warn("bus width %d instead %d bit\n",
-                          (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
-                          busw ? 16 : 8);
+                       maf_id, dev_id);
+               pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
+                       mtd->name);
+               pr_warn("bus width %d instead of %d bits\n", busw ? 16 : 8,
+                       (chip->options & NAND_BUSWIDTH_16) ? 16 : 8);
                return -EINVAL;
        }
 
-       nand_decode_bbm_options(mtd, chip, id_data);
+       nand_decode_bbm_options(chip);
 
        /* Calculate the address shift from the page size */
        chip->page_shift = ffs(mtd->writesize) - 1;
@@ -4190,18 +4080,22 @@ ident_done:
        if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
                chip->cmdfunc = nand_command_lp;
 
+       ret = nand_manufacturer_init(chip);
+       if (ret)
+               return ret;
+
        pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
-               *maf_id, *dev_id);
+               maf_id, dev_id);
 
        if (chip->onfi_version)
-               pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
-                               chip->onfi_params.model);
+               pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
+                       chip->onfi_params.model);
        else if (chip->jedec_version)
-               pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
-                               chip->jedec_params.model);
+               pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
+                       chip->jedec_params.model);
        else
-               pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
-                               type->name);
+               pr_info("%s %s\n", nand_manufacturer_name(manufacturer),
+                       type->name);
 
        pr_info("%d MiB, %s, erase size: %d KiB, page size: %d, OOB size: %d\n",
                (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
@@ -4333,12 +4227,6 @@ static int nand_dt_init(struct nand_chip *chip)
        ecc_strength = of_get_nand_ecc_strength(dn);
        ecc_step = of_get_nand_ecc_step_size(dn);
 
-       if ((ecc_step >= 0 && !(ecc_strength >= 0)) ||
-           (!(ecc_step >= 0) && ecc_strength >= 0)) {
-               pr_err("must set both strength and step size in DT\n");
-               return -EINVAL;
-       }
-
        if (ecc_mode >= 0)
                chip->ecc.mode = ecc_mode;
 
@@ -4391,10 +4279,10 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
                return -EINVAL;
        }
        /* Set the default functions */
-       nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);
+       nand_set_defaults(chip);
 
        /* Read the flash type */
-       ret = nand_get_flash_type(mtd, chip, &nand_maf_id, &nand_dev_id, table);
+       ret = nand_detect(chip, table);
        if (ret) {
                if (!(chip->options & NAND_SCAN_SILENT_NODEV))
                        pr_warn("No NAND device found\n");
@@ -4419,6 +4307,9 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,
        if (ret)
                return ret;
 
+       nand_maf_id = chip->id.data[0];
+       nand_dev_id = chip->id.data[1];
+
        chip->select_chip(mtd, -1);
 
        /* Check for a chip array */
@@ -4610,7 +4501,7 @@ int nand_scan_tail(struct mtd_info *mtd)
 {
        struct nand_chip *chip = mtd_to_nand(mtd);
        struct nand_ecc_ctrl *ecc = &chip->ecc;
-       struct nand_buffers *nbuf;
+       struct nand_buffers *nbuf = NULL;
        int ret;
 
        /* New bad blocks should be marked in OOB, flash-based BBT, or both */
@@ -4624,13 +4515,28 @@ int nand_scan_tail(struct mtd_info *mtd)
        }
 
        if (!(chip->options & NAND_OWN_BUFFERS)) {
-               nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize
-                               + mtd->oobsize * 3, GFP_KERNEL);
+               nbuf = kzalloc(sizeof(*nbuf), GFP_KERNEL);
                if (!nbuf)
                        return -ENOMEM;
-               nbuf->ecccalc = (uint8_t *)(nbuf + 1);
-               nbuf->ecccode = nbuf->ecccalc + mtd->oobsize;
-               nbuf->databuf = nbuf->ecccode + mtd->oobsize;
+
+               nbuf->ecccalc = kmalloc(mtd->oobsize, GFP_KERNEL);
+               if (!nbuf->ecccalc) {
+                       ret = -ENOMEM;
+                       goto err_free;
+               }
+
+               nbuf->ecccode = kmalloc(mtd->oobsize, GFP_KERNEL);
+               if (!nbuf->ecccode) {
+                       ret = -ENOMEM;
+                       goto err_free;
+               }
+
+               nbuf->databuf = kmalloc(mtd->writesize + mtd->oobsize,
+                                       GFP_KERNEL);
+               if (!nbuf->databuf) {
+                       ret = -ENOMEM;
+                       goto err_free;
+               }
 
                chip->buffers = nbuf;
        } else {
@@ -4663,9 +4569,6 @@ int nand_scan_tail(struct mtd_info *mtd)
                }
        }
 
-       if (!chip->write_page)
-               chip->write_page = nand_write_page;
-
        /*
         * Check ECC mode, default to software if 3byte/512byte hardware ECC is
         * selected and we have 256 byte pagesize fallback to software ECC
@@ -4873,8 +4776,12 @@ int nand_scan_tail(struct mtd_info *mtd)
        /* Build bad block table */
        return chip->scan_bbt(mtd);
 err_free:
-       if (!(chip->options & NAND_OWN_BUFFERS))
-               kfree(chip->buffers);
+       if (nbuf) {
+               kfree(nbuf->databuf);
+               kfree(nbuf->ecccode);
+               kfree(nbuf->ecccalc);
+               kfree(nbuf);
+       }
        return ret;
 }
 EXPORT_SYMBOL(nand_scan_tail);
@@ -4925,13 +4832,20 @@ void nand_cleanup(struct nand_chip *chip)
 
        /* Free bad block table memory */
        kfree(chip->bbt);
-       if (!(chip->options & NAND_OWN_BUFFERS))
+       if (!(chip->options & NAND_OWN_BUFFERS) && chip->buffers) {
+               kfree(chip->buffers->databuf);
+               kfree(chip->buffers->ecccode);
+               kfree(chip->buffers->ecccalc);
                kfree(chip->buffers);
+       }
 
        /* Free bad block descriptor memory */
        if (chip->badblock_pattern && chip->badblock_pattern->options
                        & NAND_BBT_DYNAMICSTRUCT)
                kfree(chip->badblock_pattern);
+
+       /* Free manufacturer priv data. */
+       nand_manufacturer_cleanup(chip);
 }
 EXPORT_SYMBOL_GPL(nand_cleanup);
 
diff --git a/drivers/mtd/nand/nand_hynix.c b/drivers/mtd/nand/nand_hynix.c
new file mode 100644 (file)
index 0000000..b12dc73
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ * Copyright (C) 2017 Free Electrons
+ * Copyright (C) 2017 NextThing Co
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mtd/nand.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+
+#define NAND_HYNIX_CMD_SET_PARAMS      0x36
+#define NAND_HYNIX_CMD_APPLY_PARAMS    0x16
+
+#define NAND_HYNIX_1XNM_RR_REPEAT      8
+
+/**
+ * struct hynix_read_retry - read-retry data
+ * @nregs: number of register to set when applying a new read-retry mode
+ * @regs: register offsets (NAND chip dependent)
+ * @values: array of values to set in registers. The array size is equal to
+ *         (nregs * nmodes)
+ */
+struct hynix_read_retry {
+       int nregs;
+       const u8 *regs;
+       u8 values[0];
+};
+
+/**
+ * struct hynix_nand - private Hynix NAND struct
+ * @nand_technology: manufacturing process expressed in picometer
+ * @read_retry: read-retry information
+ */
+struct hynix_nand {
+       const struct hynix_read_retry *read_retry;
+};
+
+/**
+ * struct hynix_read_retry_otp - structure describing how the read-retry OTP
+ *                              area
+ * @nregs: number of hynix private registers to set before reading the reading
+ *        the OTP area
+ * @regs: registers that should be configured
+ * @values: values that should be set in regs
+ * @page: the address to pass to the READ_PAGE command. Depends on the NAND
+ *       chip
+ * @size: size of the read-retry OTP section
+ */
+struct hynix_read_retry_otp {
+       int nregs;
+       const u8 *regs;
+       const u8 *values;
+       int page;
+       int size;
+};
+
+static bool hynix_nand_has_valid_jedecid(struct nand_chip *chip)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       u8 jedecid[6] = { };
+       int i = 0;
+
+       chip->cmdfunc(mtd, NAND_CMD_READID, 0x40, -1);
+       for (i = 0; i < 5; i++)
+               jedecid[i] = chip->read_byte(mtd);
+
+       return !strcmp("JEDEC", jedecid);
+}
+
+static int hynix_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct hynix_nand *hynix = nand_get_manufacturer_data(chip);
+       const u8 *values;
+       int status;
+       int i;
+
+       values = hynix->read_retry->values +
+                (retry_mode * hynix->read_retry->nregs);
+
+       /* Enter 'Set Hynix Parameters' mode */
+       chip->cmdfunc(mtd, NAND_HYNIX_CMD_SET_PARAMS, -1, -1);
+
+       /*
+        * Configure the NAND in the requested read-retry mode.
+        * This is done by setting pre-defined values in internal NAND
+        * registers.
+        *
+        * The set of registers is NAND specific, and the values are either
+        * predefined or extracted from an OTP area on the NAND (values are
+        * probably tweaked at production in this case).
+        */
+       for (i = 0; i < hynix->read_retry->nregs; i++) {
+               int column = hynix->read_retry->regs[i];
+
+               column |= column << 8;
+               chip->cmdfunc(mtd, NAND_CMD_NONE, column, -1);
+               chip->write_byte(mtd, values[i]);
+       }
+
+       /* Apply the new settings. */
+       chip->cmdfunc(mtd, NAND_HYNIX_CMD_APPLY_PARAMS, -1, -1);
+
+       status = chip->waitfunc(mtd, chip);
+       if (status & NAND_STATUS_FAIL)
+               return -EIO;
+
+       return 0;
+}
+
+/**
+ * hynix_get_majority - get the value that is occurring the most in a given
+ *                     set of values
+ * @in: the array of values to test
+ * @repeat: the size of the in array
+ * @out: pointer used to store the output value
+ *
+ * This function implements the 'majority check' logic that is supposed to
+ * overcome the unreliability of MLC NANDs when reading the OTP area storing
+ * the read-retry parameters.
+ *
+ * It's based on a pretty simple assumption: if we repeat the same value
+ * several times and then take the one that is occurring the most, we should
+ * find the correct value.
+ * Let's hope this dummy algorithm prevents us from losing the read-retry
+ * parameters.
+ */
+static int hynix_get_majority(const u8 *in, int repeat, u8 *out)
+{
+       int i, j, half = repeat / 2;
+
+       /*
+        * We only test the first half of the in array because we must ensure
+        * that the value is at least occurring repeat / 2 times.
+        *
+        * This loop is suboptimal since we may count the occurrences of the
+        * same value several time, but we are doing that on small sets, which
+        * makes it acceptable.
+        */
+       for (i = 0; i < half; i++) {
+               int cnt = 0;
+               u8 val = in[i];
+
+               /* Count all values that are matching the one at index i. */
+               for (j = i + 1; j < repeat; j++) {
+                       if (in[j] == val)
+                               cnt++;
+               }
+
+               /* We found a value occurring more than repeat / 2. */
+               if (cnt > half) {
+                       *out = val;
+                       return 0;
+               }
+       }
+
+       return -EIO;
+}
+
+static int hynix_read_rr_otp(struct nand_chip *chip,
+                            const struct hynix_read_retry_otp *info,
+                            void *buf)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       int i;
+
+       chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+       chip->cmdfunc(mtd, NAND_HYNIX_CMD_SET_PARAMS, -1, -1);
+
+       for (i = 0; i < info->nregs; i++) {
+               int column = info->regs[i];
+
+               column |= column << 8;
+               chip->cmdfunc(mtd, NAND_CMD_NONE, column, -1);
+               chip->write_byte(mtd, info->values[i]);
+       }
+
+       chip->cmdfunc(mtd, NAND_HYNIX_CMD_APPLY_PARAMS, -1, -1);
+
+       /* Sequence to enter OTP mode? */
+       chip->cmdfunc(mtd, 0x17, -1, -1);
+       chip->cmdfunc(mtd, 0x04, -1, -1);
+       chip->cmdfunc(mtd, 0x19, -1, -1);
+
+       /* Now read the page */
+       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, info->page);
+       chip->read_buf(mtd, buf, info->size);
+
+       /* Put everything back to normal */
+       chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+       chip->cmdfunc(mtd, NAND_HYNIX_CMD_SET_PARAMS, 0x38, -1);
+       chip->write_byte(mtd, 0x0);
+       chip->cmdfunc(mtd, NAND_HYNIX_CMD_APPLY_PARAMS, -1, -1);
+       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, -1);
+
+       return 0;
+}
+
+#define NAND_HYNIX_1XNM_RR_COUNT_OFFS                          0
+#define NAND_HYNIX_1XNM_RR_REG_COUNT_OFFS                      8
+#define NAND_HYNIX_1XNM_RR_SET_OFFS(x, setsize, inv)           \
+       (16 + ((((x) * 2) + ((inv) ? 1 : 0)) * (setsize)))
+
+static int hynix_mlc_1xnm_rr_value(const u8 *buf, int nmodes, int nregs,
+                                  int mode, int reg, bool inv, u8 *val)
+{
+       u8 tmp[NAND_HYNIX_1XNM_RR_REPEAT];
+       int val_offs = (mode * nregs) + reg;
+       int set_size = nmodes * nregs;
+       int i, ret;
+
+       for (i = 0; i < NAND_HYNIX_1XNM_RR_REPEAT; i++) {
+               int set_offs = NAND_HYNIX_1XNM_RR_SET_OFFS(i, set_size, inv);
+
+               tmp[i] = buf[val_offs + set_offs];
+       }
+
+       ret = hynix_get_majority(tmp, NAND_HYNIX_1XNM_RR_REPEAT, val);
+       if (ret)
+               return ret;
+
+       if (inv)
+               *val = ~*val;
+
+       return 0;
+}
+
+static u8 hynix_1xnm_mlc_read_retry_regs[] = {
+       0xcc, 0xbf, 0xaa, 0xab, 0xcd, 0xad, 0xae, 0xaf
+};
+
+static int hynix_mlc_1xnm_rr_init(struct nand_chip *chip,
+                                 const struct hynix_read_retry_otp *info)
+{
+       struct hynix_nand *hynix = nand_get_manufacturer_data(chip);
+       struct hynix_read_retry *rr = NULL;
+       int ret, i, j;
+       u8 nregs, nmodes;
+       u8 *buf;
+
+       buf = kmalloc(info->size, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       ret = hynix_read_rr_otp(chip, info, buf);
+       if (ret)
+               goto out;
+
+       ret = hynix_get_majority(buf, NAND_HYNIX_1XNM_RR_REPEAT,
+                                &nmodes);
+       if (ret)
+               goto out;
+
+       ret = hynix_get_majority(buf + NAND_HYNIX_1XNM_RR_REPEAT,
+                                NAND_HYNIX_1XNM_RR_REPEAT,
+                                &nregs);
+       if (ret)
+               goto out;
+
+       rr = kzalloc(sizeof(*rr) + (nregs * nmodes), GFP_KERNEL);
+       if (!rr) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       for (i = 0; i < nmodes; i++) {
+               for (j = 0; j < nregs; j++) {
+                       u8 *val = rr->values + (i * nregs);
+
+                       ret = hynix_mlc_1xnm_rr_value(buf, nmodes, nregs, i, j,
+                                                     false, val);
+                       if (!ret)
+                               continue;
+
+                       ret = hynix_mlc_1xnm_rr_value(buf, nmodes, nregs, i, j,
+                                                     true, val);
+                       if (ret)
+                               goto out;
+               }
+       }
+
+       rr->nregs = nregs;
+       rr->regs = hynix_1xnm_mlc_read_retry_regs;
+       hynix->read_retry = rr;
+       chip->setup_read_retry = hynix_nand_setup_read_retry;
+       chip->read_retries = nmodes;
+
+out:
+       kfree(buf);
+
+       if (ret)
+               kfree(rr);
+
+       return ret;
+}
+
+static const u8 hynix_mlc_1xnm_rr_otp_regs[] = { 0x38 };
+static const u8 hynix_mlc_1xnm_rr_otp_values[] = { 0x52 };
+
+static const struct hynix_read_retry_otp hynix_mlc_1xnm_rr_otps[] = {
+       {
+               .nregs = ARRAY_SIZE(hynix_mlc_1xnm_rr_otp_regs),
+               .regs = hynix_mlc_1xnm_rr_otp_regs,
+               .values = hynix_mlc_1xnm_rr_otp_values,
+               .page = 0x21f,
+               .size = 784
+       },
+       {
+               .nregs = ARRAY_SIZE(hynix_mlc_1xnm_rr_otp_regs),
+               .regs = hynix_mlc_1xnm_rr_otp_regs,
+               .values = hynix_mlc_1xnm_rr_otp_values,
+               .page = 0x200,
+               .size = 528,
+       },
+};
+
+static int hynix_nand_rr_init(struct nand_chip *chip)
+{
+       int i, ret = 0;
+       bool valid_jedecid;
+
+       valid_jedecid = hynix_nand_has_valid_jedecid(chip);
+
+       /*
+        * We only support read-retry for 1xnm NANDs, and those NANDs all
+        * expose a valid JEDEC ID.
+        */
+       if (valid_jedecid) {
+               u8 nand_tech = chip->id.data[5] >> 4;
+
+               /* 1xnm technology */
+               if (nand_tech == 4) {
+                       for (i = 0; i < ARRAY_SIZE(hynix_mlc_1xnm_rr_otps);
+                            i++) {
+                               /*
+                                * FIXME: Hynix recommend to copy the
+                                * read-retry OTP area into a normal page.
+                                */
+                               ret = hynix_mlc_1xnm_rr_init(chip,
+                                               hynix_mlc_1xnm_rr_otps);
+                               if (!ret)
+                                       break;
+                       }
+               }
+       }
+
+       if (ret)
+               pr_warn("failed to initialize read-retry infrastructure");
+
+       return 0;
+}
+
+static void hynix_nand_extract_oobsize(struct nand_chip *chip,
+                                      bool valid_jedecid)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       u8 oobsize;
+
+       oobsize = ((chip->id.data[3] >> 2) & 0x3) |
+                 ((chip->id.data[3] >> 4) & 0x4);
+
+       if (valid_jedecid) {
+               switch (oobsize) {
+               case 0:
+                       mtd->oobsize = 2048;
+                       break;
+               case 1:
+                       mtd->oobsize = 1664;
+                       break;
+               case 2:
+                       mtd->oobsize = 1024;
+                       break;
+               case 3:
+                       mtd->oobsize = 640;
+                       break;
+               default:
+                       /*
+                        * We should never reach this case, but if that
+                        * happens, this probably means Hynix decided to use
+                        * a different extended ID format, and we should find
+                        * a way to support it.
+                        */
+                       WARN(1, "Invalid OOB size");
+                       break;
+               }
+       } else {
+               switch (oobsize) {
+               case 0:
+                       mtd->oobsize = 128;
+                       break;
+               case 1:
+                       mtd->oobsize = 224;
+                       break;
+               case 2:
+                       mtd->oobsize = 448;
+                       break;
+               case 3:
+                       mtd->oobsize = 64;
+                       break;
+               case 4:
+                       mtd->oobsize = 32;
+                       break;
+               case 5:
+                       mtd->oobsize = 16;
+                       break;
+               case 6:
+                       mtd->oobsize = 640;
+                       break;
+               default:
+                       /*
+                        * We should never reach this case, but if that
+                        * happens, this probably means Hynix decided to use
+                        * a different extended ID format, and we should find
+                        * a way to support it.
+                        */
+                       WARN(1, "Invalid OOB size");
+                       break;
+               }
+       }
+}
+
+static void hynix_nand_extract_ecc_requirements(struct nand_chip *chip,
+                                               bool valid_jedecid)
+{
+       u8 ecc_level = (chip->id.data[4] >> 4) & 0x7;
+
+       if (valid_jedecid) {
+               /* Reference: H27UCG8T2E datasheet */
+               chip->ecc_step_ds = 1024;
+
+               switch (ecc_level) {
+               case 0:
+                       chip->ecc_step_ds = 0;
+                       chip->ecc_strength_ds = 0;
+                       break;
+               case 1:
+                       chip->ecc_strength_ds = 4;
+                       break;
+               case 2:
+                       chip->ecc_strength_ds = 24;
+                       break;
+               case 3:
+                       chip->ecc_strength_ds = 32;
+                       break;
+               case 4:
+                       chip->ecc_strength_ds = 40;
+                       break;
+               case 5:
+                       chip->ecc_strength_ds = 50;
+                       break;
+               case 6:
+                       chip->ecc_strength_ds = 60;
+                       break;
+               default:
+                       /*
+                        * We should never reach this case, but if that
+                        * happens, this probably means Hynix decided to use
+                        * a different extended ID format, and we should find
+                        * a way to support it.
+                        */
+                       WARN(1, "Invalid ECC requirements");
+               }
+       } else {
+               /*
+                * The ECC requirements field meaning depends on the
+                * NAND technology.
+                */
+               u8 nand_tech = chip->id.data[5] & 0x3;
+
+               if (nand_tech < 3) {
+                       /* > 26nm, reference: H27UBG8T2A datasheet */
+                       if (ecc_level < 5) {
+                               chip->ecc_step_ds = 512;
+                               chip->ecc_strength_ds = 1 << ecc_level;
+                       } else if (ecc_level < 7) {
+                               if (ecc_level == 5)
+                                       chip->ecc_step_ds = 2048;
+                               else
+                                       chip->ecc_step_ds = 1024;
+                               chip->ecc_strength_ds = 24;
+                       } else {
+                               /*
+                                * We should never reach this case, but if that
+                                * happens, this probably means Hynix decided
+                                * to use a different extended ID format, and
+                                * we should find a way to support it.
+                                */
+                               WARN(1, "Invalid ECC requirements");
+                       }
+               } else {
+                       /* <= 26nm, reference: H27UBG8T2B datasheet */
+                       if (!ecc_level) {
+                               chip->ecc_step_ds = 0;
+                               chip->ecc_strength_ds = 0;
+                       } else if (ecc_level < 5) {
+                               chip->ecc_step_ds = 512;
+                               chip->ecc_strength_ds = 1 << (ecc_level - 1);
+                       } else {
+                               chip->ecc_step_ds = 1024;
+                               chip->ecc_strength_ds = 24 +
+                                                       (8 * (ecc_level - 5));
+                       }
+               }
+       }
+}
+
+static void hynix_nand_extract_scrambling_requirements(struct nand_chip *chip,
+                                                      bool valid_jedecid)
+{
+       u8 nand_tech;
+
+       /* We need scrambling on all TLC NANDs*/
+       if (chip->bits_per_cell > 2)
+               chip->options |= NAND_NEED_SCRAMBLING;
+
+       /* And on MLC NANDs with sub-3xnm process */
+       if (valid_jedecid) {
+               nand_tech = chip->id.data[5] >> 4;
+
+               /* < 3xnm */
+               if (nand_tech > 0)
+                       chip->options |= NAND_NEED_SCRAMBLING;
+       } else {
+               nand_tech = chip->id.data[5] & 0x3;
+
+               /* < 32nm */
+               if (nand_tech > 2)
+                       chip->options |= NAND_NEED_SCRAMBLING;
+       }
+}
+
+static void hynix_nand_decode_id(struct nand_chip *chip)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       bool valid_jedecid;
+       u8 tmp;
+
+       /*
+        * Exclude all SLC NANDs from this advanced detection scheme.
+        * According to the ranges defined in several datasheets, it might
+        * appear that even SLC NANDs could fall in this extended ID scheme.
+        * If that the case rework the test to let SLC NANDs go through the
+        * detection process.
+        */
+       if (chip->id.len < 6 || nand_is_slc(chip)) {
+               nand_decode_ext_id(chip);
+               return;
+       }
+
+       /* Extract pagesize */
+       mtd->writesize = 2048 << (chip->id.data[3] & 0x03);
+
+       tmp = (chip->id.data[3] >> 4) & 0x3;
+       /*
+        * When bit7 is set that means we start counting at 1MiB, otherwise
+        * we start counting at 128KiB and shift this value the content of
+        * ID[3][4:5].
+        * The only exception is when ID[3][4:5] == 3 and ID[3][7] == 0, in
+        * this case the erasesize is set to 768KiB.
+        */
+       if (chip->id.data[3] & 0x80)
+               mtd->erasesize = SZ_1M << tmp;
+       else if (tmp == 3)
+               mtd->erasesize = SZ_512K + SZ_256K;
+       else
+               mtd->erasesize = SZ_128K << tmp;
+
+       /*
+        * Modern Toggle DDR NANDs have a valid JEDECID even though they are
+        * not exposing a valid JEDEC parameter table.
+        * These NANDs use a different NAND ID scheme.
+        */
+       valid_jedecid = hynix_nand_has_valid_jedecid(chip);
+
+       hynix_nand_extract_oobsize(chip, valid_jedecid);
+       hynix_nand_extract_ecc_requirements(chip, valid_jedecid);
+       hynix_nand_extract_scrambling_requirements(chip, valid_jedecid);
+}
+
+static void hynix_nand_cleanup(struct nand_chip *chip)
+{
+       struct hynix_nand *hynix = nand_get_manufacturer_data(chip);
+
+       if (!hynix)
+               return;
+
+       kfree(hynix->read_retry);
+       kfree(hynix);
+       nand_set_manufacturer_data(chip, NULL);
+}
+
+static int hynix_nand_init(struct nand_chip *chip)
+{
+       struct hynix_nand *hynix;
+       int ret;
+
+       if (!nand_is_slc(chip))
+               chip->bbt_options |= NAND_BBT_SCANLASTPAGE;
+       else
+               chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
+
+       hynix = kzalloc(sizeof(*hynix), GFP_KERNEL);
+       if (!hynix)
+               return -ENOMEM;
+
+       nand_set_manufacturer_data(chip, hynix);
+
+       ret = hynix_nand_rr_init(chip);
+       if (ret)
+               hynix_nand_cleanup(chip);
+
+       return ret;
+}
+
+const struct nand_manufacturer_ops hynix_nand_manuf_ops = {
+       .detect = hynix_nand_decode_id,
+       .init = hynix_nand_init,
+       .cleanup = hynix_nand_cleanup,
+};
index 4a2f75b0c200a1135e64941bf24b753c356775a0..9d5ca0e540b5bc5c9e3be9a6a3b476624e63be14 100644 (file)
@@ -10,7 +10,7 @@
 #include <linux/mtd/nand.h>
 #include <linux/sizes.h>
 
-#define LP_OPTIONS NAND_SAMSUNG_LP_OPTIONS
+#define LP_OPTIONS 0
 #define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16)
 
 #define SP_OPTIONS NAND_NEED_READRDY
@@ -169,29 +169,40 @@ struct nand_flash_dev nand_flash_ids[] = {
 };
 
 /* Manufacturer IDs */
-struct nand_manufacturers nand_manuf_ids[] = {
-       {NAND_MFR_TOSHIBA, "Toshiba"},
+static const struct nand_manufacturer nand_manufacturers[] = {
+       {NAND_MFR_TOSHIBA, "Toshiba", &toshiba_nand_manuf_ops},
        {NAND_MFR_ESMT, "ESMT"},
-       {NAND_MFR_SAMSUNG, "Samsung"},
+       {NAND_MFR_SAMSUNG, "Samsung", &samsung_nand_manuf_ops},
        {NAND_MFR_FUJITSU, "Fujitsu"},
        {NAND_MFR_NATIONAL, "National"},
        {NAND_MFR_RENESAS, "Renesas"},
        {NAND_MFR_STMICRO, "ST Micro"},
-       {NAND_MFR_HYNIX, "Hynix"},
-       {NAND_MFR_MICRON, "Micron"},
-       {NAND_MFR_AMD, "AMD/Spansion"},
-       {NAND_MFR_MACRONIX, "Macronix"},
+       {NAND_MFR_HYNIX, "Hynix", &hynix_nand_manuf_ops},
+       {NAND_MFR_MICRON, "Micron", &micron_nand_manuf_ops},
+       {NAND_MFR_AMD, "AMD/Spansion", &amd_nand_manuf_ops},
+       {NAND_MFR_MACRONIX, "Macronix", &macronix_nand_manuf_ops},
        {NAND_MFR_EON, "Eon"},
        {NAND_MFR_SANDISK, "SanDisk"},
        {NAND_MFR_INTEL, "Intel"},
        {NAND_MFR_ATO, "ATO"},
        {NAND_MFR_WINBOND, "Winbond"},
-       {0x0, "Unknown"}
 };
 
-EXPORT_SYMBOL(nand_manuf_ids);
-EXPORT_SYMBOL(nand_flash_ids);
+/**
+ * nand_get_manufacturer - Get manufacturer information from the manufacturer
+ *                        ID
+ * @id: manufacturer ID
+ *
+ * Returns a pointer a nand_manufacturer object if the manufacturer is defined
+ * in the NAND manufacturers database, NULL otherwise.
+ */
+const struct nand_manufacturer *nand_get_manufacturer(u8 id)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(nand_manufacturers); i++)
+               if (nand_manufacturers[i].id == id)
+                       return &nand_manufacturers[i];
 
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
-MODULE_DESCRIPTION("Nand device & manufacturer IDs");
+       return NULL;
+}
diff --git a/drivers/mtd/nand/nand_macronix.c b/drivers/mtd/nand/nand_macronix.c
new file mode 100644 (file)
index 0000000..84855c3
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 Free Electrons
+ * Copyright (C) 2017 NextThing Co
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mtd/nand.h>
+
+static int macronix_nand_init(struct nand_chip *chip)
+{
+       if (nand_is_slc(chip))
+               chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
+
+       return 0;
+}
+
+const struct nand_manufacturer_ops macronix_nand_manuf_ops = {
+       .init = macronix_nand_init,
+};
diff --git a/drivers/mtd/nand/nand_micron.c b/drivers/mtd/nand/nand_micron.c
new file mode 100644 (file)
index 0000000..8770110
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 Free Electrons
+ * Copyright (C) 2017 NextThing Co
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mtd/nand.h>
+
+struct nand_onfi_vendor_micron {
+       u8 two_plane_read;
+       u8 read_cache;
+       u8 read_unique_id;
+       u8 dq_imped;
+       u8 dq_imped_num_settings;
+       u8 dq_imped_feat_addr;
+       u8 rb_pulldown_strength;
+       u8 rb_pulldown_strength_feat_addr;
+       u8 rb_pulldown_strength_num_settings;
+       u8 otp_mode;
+       u8 otp_page_start;
+       u8 otp_data_prot_addr;
+       u8 otp_num_pages;
+       u8 otp_feat_addr;
+       u8 read_retry_options;
+       u8 reserved[72];
+       u8 param_revision;
+} __packed;
+
+static int micron_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
+{
+       struct nand_chip *chip = mtd_to_nand(mtd);
+       u8 feature[ONFI_SUBFEATURE_PARAM_LEN] = {retry_mode};
+
+       return chip->onfi_set_features(mtd, chip, ONFI_FEATURE_ADDR_READ_RETRY,
+                                      feature);
+}
+
+/*
+ * Configure chip properties from Micron vendor-specific ONFI table
+ */
+static int micron_nand_onfi_init(struct nand_chip *chip)
+{
+       struct nand_onfi_params *p = &chip->onfi_params;
+       struct nand_onfi_vendor_micron *micron = (void *)p->vendor;
+
+       if (!chip->onfi_version)
+               return 0;
+
+       if (le16_to_cpu(p->vendor_revision) < 1)
+               return 0;
+
+       chip->read_retries = micron->read_retry_options;
+       chip->setup_read_retry = micron_nand_setup_read_retry;
+
+       return 0;
+}
+
+static int micron_nand_init(struct nand_chip *chip)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       int ret;
+
+       ret = micron_nand_onfi_init(chip);
+       if (ret)
+               return ret;
+
+       if (mtd->writesize == 2048)
+               chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
+
+       return 0;
+}
+
+const struct nand_manufacturer_ops micron_nand_manuf_ops = {
+       .init = micron_nand_init,
+};
diff --git a/drivers/mtd/nand/nand_samsung.c b/drivers/mtd/nand/nand_samsung.c
new file mode 100644 (file)
index 0000000..9cfc403
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 Free Electrons
+ * Copyright (C) 2017 NextThing Co
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mtd/nand.h>
+
+static void samsung_nand_decode_id(struct nand_chip *chip)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+
+       /* New Samsung (6 byte ID): Samsung K9GAG08U0F (p.44) */
+       if (chip->id.len == 6 && !nand_is_slc(chip) &&
+           chip->id.data[5] != 0x00) {
+               u8 extid = chip->id.data[3];
+
+               /* Get pagesize */
+               mtd->writesize = 2048 << (extid & 0x03);
+
+               extid >>= 2;
+
+               /* Get oobsize */
+               switch (((extid >> 2) & 0x4) | (extid & 0x3)) {
+               case 1:
+                       mtd->oobsize = 128;
+                       break;
+               case 2:
+                       mtd->oobsize = 218;
+                       break;
+               case 3:
+                       mtd->oobsize = 400;
+                       break;
+               case 4:
+                       mtd->oobsize = 436;
+                       break;
+               case 5:
+                       mtd->oobsize = 512;
+                       break;
+               case 6:
+                       mtd->oobsize = 640;
+                       break;
+               default:
+                       /*
+                        * We should never reach this case, but if that
+                        * happens, this probably means Samsung decided to use
+                        * a different extended ID format, and we should find
+                        * a way to support it.
+                        */
+                       WARN(1, "Invalid OOB size value");
+                       break;
+               }
+
+               /* Get blocksize */
+               extid >>= 2;
+               mtd->erasesize = (128 * 1024) <<
+                                (((extid >> 1) & 0x04) | (extid & 0x03));
+
+               /* Extract ECC requirements from 5th id byte*/
+               extid = (chip->id.data[4] >> 4) & 0x07;
+               if (extid < 5) {
+                       chip->ecc_step_ds = 512;
+                       chip->ecc_strength_ds = 1 << extid;
+               } else {
+                       chip->ecc_step_ds = 1024;
+                       switch (extid) {
+                       case 5:
+                               chip->ecc_strength_ds = 24;
+                               break;
+                       case 6:
+                               chip->ecc_strength_ds = 40;
+                               break;
+                       case 7:
+                               chip->ecc_strength_ds = 60;
+                               break;
+                       }
+               }
+       } else {
+               nand_decode_ext_id(chip);
+       }
+}
+
+static int samsung_nand_init(struct nand_chip *chip)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+
+       if (mtd->writesize > 512)
+               chip->options |= NAND_SAMSUNG_LP_OPTIONS;
+
+       if (!nand_is_slc(chip))
+               chip->bbt_options |= NAND_BBT_SCANLASTPAGE;
+       else
+               chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
+
+       return 0;
+}
+
+const struct nand_manufacturer_ops samsung_nand_manuf_ops = {
+       .detect = samsung_nand_decode_id,
+       .init = samsung_nand_init,
+};
diff --git a/drivers/mtd/nand/nand_toshiba.c b/drivers/mtd/nand/nand_toshiba.c
new file mode 100644 (file)
index 0000000..fa787ba
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 Free Electrons
+ * Copyright (C) 2017 NextThing Co
+ *
+ * Author: Boris Brezillon <boris.brezillon@free-electrons.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mtd/nand.h>
+
+static void toshiba_nand_decode_id(struct nand_chip *chip)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+
+       nand_decode_ext_id(chip);
+
+       /*
+        * Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per
+        * 512B page. For Toshiba SLC, we decode the 5th/6th byte as
+        * follows:
+        * - ID byte 6, bits[2:0]: 100b -> 43nm, 101b -> 32nm,
+        *                         110b -> 24nm
+        * - ID byte 5, bit[7]:    1 -> BENAND, 0 -> raw SLC
+        */
+       if (chip->id.len >= 6 && nand_is_slc(chip) &&
+           (chip->id.data[5] & 0x7) == 0x6 /* 24nm */ &&
+           !(chip->id.data[4] & 0x80) /* !BENAND */)
+               mtd->oobsize = 32 * mtd->writesize >> 9;
+}
+
+static int toshiba_nand_init(struct nand_chip *chip)
+{
+       if (nand_is_slc(chip))
+               chip->bbt_options |= NAND_BBT_SCAN2NDPAGE;
+
+       return 0;
+}
+
+const struct nand_manufacturer_ops toshiba_nand_manuf_ops = {
+       .detect = toshiba_nand_decode_id,
+       .init = toshiba_nand_init,
+};
index c84742671a5f9782a23bec53b0182de0e8871289..cef818f535ed603ce4c6eebc29277642320dc2a1 100644 (file)
@@ -901,7 +901,7 @@ static int parse_weakpages(void)
                zero_ok = (*w == '0' ? 1 : 0);
                page_no = simple_strtoul(w, &w, 0);
                if (!zero_ok && !page_no) {
-                       NS_ERR("invalid weakpagess.\n");
+                       NS_ERR("invalid weakpages.\n");
                        return -EINVAL;
                }
                max_writes = 3;
index 2a52101120d466dc81d27eb8ac2c9fd10b19fb5e..084934a9f19cc4df255b8b7b0a86d3e0d28ab98c 100644 (file)
@@ -1856,6 +1856,15 @@ static int omap_nand_probe(struct platform_device *pdev)
        nand_chip->ecc.priv     = NULL;
        nand_set_flash_node(nand_chip, dev->of_node);
 
+       if (!mtd->name) {
+               mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+                                          "omap2-nand.%d", info->gpmc_cs);
+               if (!mtd->name) {
+                       dev_err(&pdev->dev, "Failed to set MTD name\n");
+                       return -ENOMEM;
+               }
+       }
+
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(nand_chip->IO_ADDR_R))
index 4a91c5d000be790bc6200a50a9d00f80f4de9bc4..f8e463a97b9ee479028dd9c282ff74436d885389 100644 (file)
 #include <asm/sizes.h>
 #include <linux/platform_data/mtd-orion_nand.h>
 
+struct orion_nand_info {
+       struct nand_chip chip;
+       struct clk *clk;
+};
+
 static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
 {
        struct nand_chip *nc = mtd_to_nand(mtd);
@@ -75,20 +80,21 @@ static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 
 static int __init orion_nand_probe(struct platform_device *pdev)
 {
+       struct orion_nand_info *info;
        struct mtd_info *mtd;
        struct nand_chip *nc;
        struct orion_nand_data *board;
        struct resource *res;
-       struct clk *clk;
        void __iomem *io_base;
        int ret = 0;
        u32 val = 0;
 
-       nc = devm_kzalloc(&pdev->dev,
-                       sizeof(struct nand_chip),
+       info = devm_kzalloc(&pdev->dev,
+                       sizeof(struct orion_nand_info),
                        GFP_KERNEL);
-       if (!nc)
+       if (!info)
                return -ENOMEM;
+       nc = &info->chip;
        mtd = nand_to_mtd(nc);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -145,16 +151,23 @@ static int __init orion_nand_probe(struct platform_device *pdev)
        if (board->dev_ready)
                nc->dev_ready = board->dev_ready;
 
-       platform_set_drvdata(pdev, mtd);
+       platform_set_drvdata(pdev, info);
 
        /* Not all platforms can gate the clock, so it is not
           an error if the clock does not exists. */
-       clk = clk_get(&pdev->dev, NULL);
-       if (!IS_ERR(clk)) {
-               clk_prepare_enable(clk);
-               clk_put(clk);
+       info->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(info->clk)) {
+               ret = PTR_ERR(info->clk);
+               if (ret == -ENOENT) {
+                       info->clk = NULL;
+               } else {
+                       dev_err(&pdev->dev, "failed to get clock!\n");
+                       return ret;
+               }
        }
 
+       clk_prepare_enable(info->clk);
+
        ret = nand_scan(mtd, 1);
        if (ret)
                goto no_dev;
@@ -169,26 +182,19 @@ static int __init orion_nand_probe(struct platform_device *pdev)
        return 0;
 
 no_dev:
-       if (!IS_ERR(clk)) {
-               clk_disable_unprepare(clk);
-               clk_put(clk);
-       }
-
+       clk_disable_unprepare(info->clk);
        return ret;
 }
 
 static int orion_nand_remove(struct platform_device *pdev)
 {
-       struct mtd_info *mtd = platform_get_drvdata(pdev);
-       struct clk *clk;
+       struct orion_nand_info *info = platform_get_drvdata(pdev);
+       struct nand_chip *chip = &info->chip;
+       struct mtd_info *mtd = nand_to_mtd(chip);
 
        nand_release(mtd);
 
-       clk = clk_get(&pdev->dev, NULL);
-       if (!IS_ERR(clk)) {
-               clk_disable_unprepare(clk);
-               clk_put(clk);
-       }
+       clk_disable_unprepare(info->clk);
 
        return 0;
 }
index 0eeeb8b889ea8af83a12126ded319e84f0389618..118a26fff36856dd3f47fa523494ab05909924e4 100644 (file)
@@ -2212,17 +2212,17 @@ static int sunxi_nfc_probe(struct platform_device *pdev)
                goto out_ahb_clk_unprepare;
 
        nfc->reset = devm_reset_control_get_optional(dev, "ahb");
-       if (!IS_ERR(nfc->reset)) {
-               ret = reset_control_deassert(nfc->reset);
-               if (ret) {
-                       dev_err(dev, "reset err %d\n", ret);
-                       goto out_mod_clk_unprepare;
-               }
-       } else if (PTR_ERR(nfc->reset) != -ENOENT) {
+       if (IS_ERR(nfc->reset)) {
                ret = PTR_ERR(nfc->reset);
                goto out_mod_clk_unprepare;
        }
 
+       ret = reset_control_deassert(nfc->reset);
+       if (ret) {
+               dev_err(dev, "reset err %d\n", ret);
+               goto out_mod_clk_unprepare;
+       }
+
        ret = sunxi_nfc_rst(nfc);
        if (ret)
                goto out_ahb_reset_reassert;
@@ -2262,8 +2262,7 @@ out_release_dmac:
        if (nfc->dmac)
                dma_release_channel(nfc->dmac);
 out_ahb_reset_reassert:
-       if (!IS_ERR(nfc->reset))
-               reset_control_assert(nfc->reset);
+       reset_control_assert(nfc->reset);
 out_mod_clk_unprepare:
        clk_disable_unprepare(nfc->mod_clk);
 out_ahb_clk_unprepare:
@@ -2278,8 +2277,7 @@ static int sunxi_nfc_remove(struct platform_device *pdev)
 
        sunxi_nand_chips_cleanup(nfc);
 
-       if (!IS_ERR(nfc->reset))
-               reset_control_assert(nfc->reset);
+       reset_control_assert(nfc->reset);
 
        if (nfc->dmac)
                dma_release_channel(nfc->dmac);
index 4a5e948c62df1b7d29ff9fe78b50e7d0858b01d5..05b6e106520331ddd48f619f798d21c4cfeb2059 100644 (file)
@@ -223,12 +223,13 @@ static void tango_dma_callback(void *arg)
        complete(arg);
 }
 
-static int do_dma(struct tango_nfc *nfc, int dir, int cmd, const void *buf,
-                 int len, int page)
+static int do_dma(struct tango_nfc *nfc, enum dma_data_direction dir, int cmd,
+                 const void *buf, int len, int page)
 {
        void __iomem *addr = nfc->reg_base + NFC_STATUS;
        struct dma_chan *chan = nfc->chan;
        struct dma_async_tx_descriptor *desc;
+       enum dma_transfer_direction tdir;
        struct scatterlist sg;
        struct completion tx_done;
        int err = -EIO;
@@ -238,7 +239,8 @@ static int do_dma(struct tango_nfc *nfc, int dir, int cmd, const void *buf,
        if (dma_map_sg(chan->device->dev, &sg, 1, dir) != 1)
                return -EIO;
 
-       desc = dmaengine_prep_slave_sg(chan, &sg, 1, dir, DMA_PREP_INTERRUPT);
+       tdir = dir == DMA_TO_DEVICE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
+       desc = dmaengine_prep_slave_sg(chan, &sg, 1, tdir, DMA_PREP_INTERRUPT);
        if (!desc)
                goto dma_unmap;
 
index 9591e0fbe5bd76a1c47e2deb4292469e0a2cf8e6..8f67b15816836a1127fddae062c1ad76774e4534 100644 (file)
@@ -366,26 +366,6 @@ struct onfi_ext_param_page {
         */
 } __packed;
 
-struct nand_onfi_vendor_micron {
-       u8 two_plane_read;
-       u8 read_cache;
-       u8 read_unique_id;
-       u8 dq_imped;
-       u8 dq_imped_num_settings;
-       u8 dq_imped_feat_addr;
-       u8 rb_pulldown_strength;
-       u8 rb_pulldown_strength_feat_addr;
-       u8 rb_pulldown_strength_num_settings;
-       u8 otp_mode;
-       u8 otp_page_start;
-       u8 otp_data_prot_addr;
-       u8 otp_num_pages;
-       u8 otp_feat_addr;
-       u8 read_retry_options;
-       u8 reserved[72];
-       u8 param_revision;
-} __packed;
-
 struct jedec_ecc_info {
        u8 ecc_bits;
        u8 codeword_size;
@@ -464,6 +444,17 @@ struct nand_jedec_params {
        __le16 crc;
 } __packed;
 
+/**
+ * struct nand_id - NAND id structure
+ * @data: buffer containing the id bytes. Currently 8 bytes large, but can
+ *       be extended if required.
+ * @len: ID length.
+ */
+struct nand_id {
+       u8 data[8];
+       int len;
+};
+
 /**
  * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
  * @lock:               protection lock
@@ -525,7 +516,7 @@ static inline void nand_hw_control_init(struct nand_hw_control *nfc)
  *                     out-of-band data).
  * @read_page: function to read a page according to the ECC generator
  *             requirements; returns maximum number of bitflips corrected in
- *             any single ECC step, 0 if bitflips uncorrectable, -EIO hw error
+ *             any single ECC step, -EIO hw error
  * @read_subpage:      function to read parts of the page covered by ECC;
  *                     returns same as read_page()
  * @write_subpage:     function to write parts of the page covered by ECC.
@@ -720,6 +711,20 @@ nand_get_sdr_timings(const struct nand_data_interface *conf)
        return &conf->timings.sdr;
 }
 
+/**
+ * struct nand_manufacturer_ops - NAND Manufacturer operations
+ * @detect: detect the NAND memory organization and capabilities
+ * @init: initialize all vendor specific fields (like the ->read_retry()
+ *       implementation) if any.
+ * @cleanup: the ->init() function may have allocated resources, ->cleanup()
+ *          is here to let vendor specific code release those resources.
+ */
+struct nand_manufacturer_ops {
+       void (*detect)(struct nand_chip *chip);
+       int (*init)(struct nand_chip *chip);
+       void (*cleanup)(struct nand_chip *chip);
+};
+
 /**
  * struct nand_chip - NAND Private Flash Chip Data
  * @mtd:               MTD device registered to the MTD framework
@@ -750,6 +755,7 @@ nand_get_sdr_timings(const struct nand_data_interface *conf)
  *                     setting the read-retry mode. Mostly needed for MLC NAND.
  * @ecc:               [BOARDSPECIFIC] ECC control structure
  * @buffers:           buffer structure for read/write
+ * @buf_align:         minimum buffer alignment required by a platform
  * @hwcontrol:         platform-specific hardware control structure
  * @erase:             [REPLACEABLE] erase function
  * @scan_bbt:          [REPLACEABLE] function to scan bad block table
@@ -793,6 +799,7 @@ nand_get_sdr_timings(const struct nand_data_interface *conf)
  * @pagebuf_bitflips:  [INTERN] holds the bitflip count for the page which is
  *                     currently in data_buf.
  * @subpagesize:       [INTERN] holds the subpagesize
+ * @id:                        [INTERN] holds NAND ID
  * @onfi_version:      [INTERN] holds the chip ONFI version (BCD encoded),
  *                     non 0 if ONFI supported.
  * @jedec_version:     [INTERN] holds the chip JEDEC version (BCD encoded),
@@ -822,7 +829,7 @@ nand_get_sdr_timings(const struct nand_data_interface *conf)
  * @errstat:           [OPTIONAL] hardware specific function to perform
  *                     additional error status checks (determine if errors are
  *                     correctable).
- * @write_page:                [REPLACEABLE] High-level page write function
+ * @manufacturer:      [INTERN] Contains manufacturer information
  */
 
 struct nand_chip {
@@ -847,9 +854,6 @@ struct nand_chip {
        int (*scan_bbt)(struct mtd_info *mtd);
        int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,
                        int status, int page);
-       int (*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 (*onfi_set_features)(struct mtd_info *mtd, struct nand_chip *chip,
                        int feature_addr, uint8_t *subfeature_para);
        int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
@@ -881,6 +885,7 @@ struct nand_chip {
        int badblockpos;
        int badblockbits;
 
+       struct nand_id id;
        int onfi_version;
        int jedec_version;
        union {
@@ -901,6 +906,7 @@ struct nand_chip {
 
        struct nand_ecc_ctrl ecc;
        struct nand_buffers *buffers;
+       unsigned long buf_align;
        struct nand_hw_control hwcontrol;
 
        uint8_t *bbt;
@@ -910,6 +916,11 @@ struct nand_chip {
        struct nand_bbt_descr *badblock_pattern;
 
        void *priv;
+
+       struct {
+               const struct nand_manufacturer *desc;
+               void *priv;
+       } manufacturer;
 };
 
 extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops;
@@ -946,6 +957,17 @@ static inline void nand_set_controller_data(struct nand_chip *chip, void *priv)
        chip->priv = priv;
 }
 
+static inline void nand_set_manufacturer_data(struct nand_chip *chip,
+                                             void *priv)
+{
+       chip->manufacturer.priv = priv;
+}
+
+static inline void *nand_get_manufacturer_data(struct nand_chip *chip)
+{
+       return chip->manufacturer.priv;
+}
+
 /*
  * NAND Flash Manufacturer ID Codes
  */
@@ -1049,17 +1071,33 @@ struct nand_flash_dev {
 };
 
 /**
- * struct nand_manufacturers - NAND Flash Manufacturer ID Structure
+ * struct nand_manufacturer - NAND Flash Manufacturer structure
  * @name:      Manufacturer name
  * @id:                manufacturer ID code of device.
+ * @ops:       manufacturer operations
 */
-struct nand_manufacturers {
+struct nand_manufacturer {
        int id;
        char *name;
+       const struct nand_manufacturer_ops *ops;
 };
 
+const struct nand_manufacturer *nand_get_manufacturer(u8 id);
+
+static inline const char *
+nand_manufacturer_name(const struct nand_manufacturer *manufacturer)
+{
+       return manufacturer ? manufacturer->name : "Unknown";
+}
+
 extern struct nand_flash_dev nand_flash_ids[];
-extern struct nand_manufacturers nand_manuf_ids[];
+
+extern const struct nand_manufacturer_ops toshiba_nand_manuf_ops;
+extern const struct nand_manufacturer_ops samsung_nand_manuf_ops;
+extern const struct nand_manufacturer_ops hynix_nand_manuf_ops;
+extern const struct nand_manufacturer_ops micron_nand_manuf_ops;
+extern const struct nand_manufacturer_ops amd_nand_manuf_ops;
+extern const struct nand_manufacturer_ops macronix_nand_manuf_ops;
 
 int nand_default_bbt(struct mtd_info *mtd);
 int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs);
@@ -1226,4 +1264,6 @@ int nand_reset(struct nand_chip *chip, int chipnr);
 /* Free resources held by the NAND device */
 void nand_cleanup(struct nand_chip *chip);
 
+/* Default extended ID decoding function */
+void nand_decode_ext_id(struct nand_chip *chip);
 #endif /* __LINUX_MTD_NAND_H */