Merge tag 'for-linus-20141215' of git://git.infradead.org/linux-mtd
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 17 Dec 2014 17:59:26 +0000 (09:59 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 17 Dec 2014 17:59:26 +0000 (09:59 -0800)
Pull MTD updates from Brian Norris:
 "Summary:
   - Add device tree support for DoC3

   - SPI NOR:
        Refactoring, for better layering between spi-nor.c and its
        driver users (e.g., m25p80.c)

        New flash device support

        Support 6-byte ID strings

   - NAND:
        New NAND driver for Allwinner SoC's (sunxi)

        GPMI NAND: add support for raw (no ECC) access, for testing
        purposes

        Add ATO manufacturer ID

        A few odd driver fixes

   - MTD tests:
        Allow testers to compensate for OOB bitflips in oobtest

        Fix a torturetest regression

   - nandsim: Support longer ID byte strings

  And more"

* tag 'for-linus-20141215' of git://git.infradead.org/linux-mtd: (63 commits)
  mtd: tests: abort torturetest on erase errors
  mtd: physmap_of: fix potential NULL dereference
  mtd: spi-nor: allow NULL as chip name and try to auto detect it
  mtd: nand: gpmi: add raw oob access functions
  mtd: nand: gpmi: add proper raw access support
  mtd: nand: gpmi: add gpmi_copy_bits function
  mtd: spi-nor: factor out write_enable() for erase commands
  mtd: spi-nor: add support for s25fl128s
  mtd: spi-nor: remove the jedec_id/ext_id
  mtd: spi-nor: add id/id_len for flash_info{}
  mtd: nand: correct the comment of function nand_block_isreserved()
  jffs2: Drop bogus if in comment
  mtd: atmel_nand: replace memcpy32_toio/memcpy32_fromio with memcpy
  mtd: cafe_nand: drop duplicate .write_page implementation
  mtd: m25p80: Add support for serial flash Spansion S25FL132K
  MTD: m25p80: fix inconsistency in m25p_ids compared to spi_nor_ids
  mtd: spi-nor: improve wait-till-ready timeout loop
  mtd: delete unnecessary checks before two function calls
  mtd: nand: omap: Fix NAND enumeration on 3430 LDP
  mtd: nand: add ATO manufacturer info
  ...

42 files changed:
Documentation/devicetree/bindings/mtd/atmel-nand.txt
Documentation/devicetree/bindings/mtd/diskonchip.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mtd/gpio-control-nand.txt
Documentation/devicetree/bindings/mtd/sunxi-nand.txt [new file with mode: 0644]
drivers/memory/fsl_ifc.c
drivers/mtd/Kconfig
drivers/mtd/bcm47xxpart.c
drivers/mtd/chips/cfi_cmdset_0001.c
drivers/mtd/devices/docg3.c
drivers/mtd/devices/m25p80.c
drivers/mtd/devices/mtd_dataflash.c
drivers/mtd/devices/phram.c
drivers/mtd/devices/pmc551.c
drivers/mtd/inftlmount.c
drivers/mtd/maps/bfin-async-flash.c
drivers/mtd/maps/physmap_of.c
drivers/mtd/nand/Kconfig
drivers/mtd/nand/Makefile
drivers/mtd/nand/atmel_nand.c
drivers/mtd/nand/atmel_nand_ecc.h
drivers/mtd/nand/cafe_nand.c
drivers/mtd/nand/fsl_ifc_nand.c
drivers/mtd/nand/gpio.c
drivers/mtd/nand/gpmi-nand/gpmi-lib.c
drivers/mtd/nand/gpmi-nand/gpmi-nand.c
drivers/mtd/nand/gpmi-nand/gpmi-nand.h
drivers/mtd/nand/mxc_nand.c
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/nand_ids.c
drivers/mtd/nand/nandsim.c
drivers/mtd/nand/omap2.c
drivers/mtd/nand/orion_nand.c
drivers/mtd/nand/sunxi_nand.c [new file with mode: 0644]
drivers/mtd/spi-nor/fsl-quadspi.c
drivers/mtd/spi-nor/spi-nor.c
drivers/mtd/tests/oobtest.c
drivers/mtd/tests/torturetest.c
fs/jffs2/readinode.c
fs/jffs2/summary.c
include/linux/fsl_ifc.h
include/linux/mtd/nand.h
include/linux/mtd/spi-nor.h

index 6edc3b6..1fe6dde 100644 (file)
@@ -5,7 +5,9 @@ Required properties:
 - reg : should specify localbus address and size used for the chip,
        and hardware ECC controller if available.
        If the hardware ECC is PMECC, it should contain address and size for
-       PMECC, PMECC Error Location controller and ROM which has lookup tables.
+       PMECC and PMECC Error Location controller.
+       The PMECC lookup table address and size in ROM is optional. If not
+       specified, driver will build it in runtime.
 - atmel,nand-addr-offset : offset for the address latch.
 - atmel,nand-cmd-offset : offset for the command latch.
 - #address-cells, #size-cells : Must be present if the device has sub-nodes
@@ -27,7 +29,7 @@ Optional properties:
   are: 512, 1024.
 - atmel,pmecc-lookup-table-offset : includes two offsets of lookup table in ROM
   for different sector size. First one is for sector size 512, the next is for
-  sector size 1024.
+  sector size 1024. If not specified, driver will build the table in runtime.
 - nand-bus-width : 8 or 16 bus width if not present 8
 - nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
 - Nand Flash Controller(NFC) is a slave driver under Atmel nand flash
diff --git a/Documentation/devicetree/bindings/mtd/diskonchip.txt b/Documentation/devicetree/bindings/mtd/diskonchip.txt
new file mode 100644 (file)
index 0000000..3e13bfd
--- /dev/null
@@ -0,0 +1,15 @@
+M-Systems and Sandisk DiskOnChip devices
+
+M-System DiskOnChip G3
+======================
+The Sandisk (formerly M-Systems) docg3 is a nand device of 64M to 256MB.
+
+Required properties:
+ - compatible: should be "m-systems,diskonchip-g3"
+ - reg: register base and size
+
+Example:
+       docg3: flash@0 {
+               compatible = "m-systems,diskonchip-g3";
+               reg = <0x0 0x2000>;
+       };
index 36ef07d..af8915b 100644 (file)
@@ -11,8 +11,8 @@ Required properties:
   are made in native endianness.
 - #address-cells, #size-cells : Must be present if the device has sub-nodes
   representing partitions.
-- gpios : specifies the gpio pins to control the NAND device.  nwp is an
-  optional gpio and may be set to 0 if not present.
+- gpios : Specifies the GPIO pins to control the NAND device.  The order of
+  GPIO references is:  RDY, nCE, ALE, CLE, and an optional nWP.
 
 Optional properties:
 - bank-width : Width (in bytes) of the device.  If not present, the width
@@ -35,11 +35,11 @@ gpio-nand@1,0 {
        reg = <1 0x0000 0x2>;
        #address-cells = <1>;
        #size-cells = <1>;
-       gpios = <&banka 1 0     /* rdy */
-                &banka 2 0     /* nce */
-                &banka 3 0     /* ale */
-                &banka 4 0     /* cle */
-                0              /* nwp */>;
+       gpios = <&banka 1 0>,   /* RDY */
+               <&banka 2 0>,   /* nCE */
+               <&banka 3 0>,   /* ALE */
+               <&banka 4 0>,   /* CLE */
+               <0>;            /* nWP */
 
        partition@0 {
        ...
diff --git a/Documentation/devicetree/bindings/mtd/sunxi-nand.txt b/Documentation/devicetree/bindings/mtd/sunxi-nand.txt
new file mode 100644 (file)
index 0000000..0273adb
--- /dev/null
@@ -0,0 +1,45 @@
+Allwinner NAND Flash Controller (NFC)
+
+Required properties:
+- compatible : "allwinner,sun4i-a10-nand".
+- reg : shall contain registers location and length for data and reg.
+- interrupts : shall define the nand controller interrupt.
+- #address-cells: shall be set to 1. Encode the nand CS.
+- #size-cells : shall be set to 0.
+- clocks : shall reference nand controller clocks.
+- clock-names : nand controller internal clock names. Shall contain :
+    * "ahb" : AHB gating clock
+    * "mod" : nand controller clock
+
+Optional children nodes:
+Children nodes represent the available nand chips.
+
+Optional properties:
+- allwinner,rb : shall contain the native Ready/Busy ids.
+ or
+- rb-gpios : shall contain the gpios used as R/B pins.
+- nand-ecc-mode : one of the supported ECC modes ("hw", "hw_syndrome", "soft",
+  "soft_bch" or "none")
+
+see Documentation/devicetree/mtd/nand.txt for generic bindings.
+
+
+Examples:
+nfc: nand@01c03000 {
+       compatible = "allwinner,sun4i-a10-nand";
+       reg = <0x01c03000 0x1000>;
+       interrupts = <0 37 1>;
+       clocks = <&ahb_gates 13>, <&nand_clk>;
+       clock-names = "ahb", "mod";
+       #address-cells = <1>;
+       #size-cells = <0>;
+       pinctrl-names = "default";
+       pinctrl-0 = <&nand_pins_a &nand_cs0_pins_a &nand_rb0_pins_a>;
+       status = "okay";
+
+       nand@0 {
+               reg = <0>;
+               allwinner,rb = <0>;
+               nand-ecc-mode = "soft_bch";
+       };
+};
index 3d5d792..410c397 100644 (file)
@@ -61,7 +61,7 @@ int fsl_ifc_find(phys_addr_t addr_base)
        if (!fsl_ifc_ctrl_dev || !fsl_ifc_ctrl_dev->regs)
                return -ENODEV;
 
-       for (i = 0; i < ARRAY_SIZE(fsl_ifc_ctrl_dev->regs->cspr_cs); i++) {
+       for (i = 0; i < fsl_ifc_ctrl_dev->banks; i++) {
                u32 cspr = in_be32(&fsl_ifc_ctrl_dev->regs->cspr_cs[i].cspr);
                if (cspr & CSPR_V && (cspr & CSPR_BA) ==
                                convert_ifc_address(addr_base))
@@ -213,7 +213,7 @@ static irqreturn_t fsl_ifc_ctrl_irq(int irqno, void *data)
 static int fsl_ifc_ctrl_probe(struct platform_device *dev)
 {
        int ret = 0;
-
+       int version, banks;
 
        dev_info(&dev->dev, "Freescale Integrated Flash Controller\n");
 
@@ -231,6 +231,15 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
                goto err;
        }
 
+       version = ioread32be(&fsl_ifc_ctrl_dev->regs->ifc_rev) &
+                       FSL_IFC_VERSION_MASK;
+       banks = (version == FSL_IFC_VERSION_1_0_0) ? 4 : 8;
+       dev_info(&dev->dev, "IFC version %d.%d, %d banks\n",
+               version >> 24, (version >> 16) & 0xf, banks);
+
+       fsl_ifc_ctrl_dev->version = version;
+       fsl_ifc_ctrl_dev->banks = banks;
+
        /* get the Controller level irq */
        fsl_ifc_ctrl_dev->irq = irq_of_parse_and_map(dev->dev.of_node, 0);
        if (fsl_ifc_ctrl_dev->irq == NO_IRQ) {
index 94b8210..71fea89 100644 (file)
@@ -133,7 +133,7 @@ config MTD_OF_PARTS
        help
          This provides a partition parsing function which derives
          the partition map from the children of the flash node,
-         as described in Documentation/devicetree/booting-without-of.txt.
+         as described in Documentation/devicetree/bindings/mtd/partition.txt.
 
 config MTD_AR7_PARTS
        tristate "TI AR7 partitioning support"
index 8057f52..cc13ea5 100644 (file)
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 
-/* 10 parts were found on sflash on Netgear WNDR4500 */
-#define BCM47XXPART_MAX_PARTS          12
+/*
+ * NAND flash on Netgear R6250 was verified to contain 15 partitions.
+ * This will result in allocating too big array for some old devices, but the
+ * memory will be freed soon anyway (see mtd_device_parse_register).
+ */
+#define BCM47XXPART_MAX_PARTS          20
 
 /*
  * Amount of bytes we read when analyzing each block of flash memory.
@@ -168,18 +172,26 @@ static int bcm47xxpart_parse(struct mtd_info *master,
                                i++;
                        }
 
-                       bcm47xxpart_add_part(&parts[curr_part++], "linux",
-                                            offset + trx->offset[i], 0);
-                       i++;
+                       if (trx->offset[i]) {
+                               bcm47xxpart_add_part(&parts[curr_part++],
+                                                    "linux",
+                                                    offset + trx->offset[i],
+                                                    0);
+                               i++;
+                       }
 
                        /*
                         * Pure rootfs size is known and can be calculated as:
                         * trx->length - trx->offset[i]. We don't fill it as
                         * we want to have jffs2 (overlay) in the same mtd.
                         */
-                       bcm47xxpart_add_part(&parts[curr_part++], "rootfs",
-                                            offset + trx->offset[i], 0);
-                       i++;
+                       if (trx->offset[i]) {
+                               bcm47xxpart_add_part(&parts[curr_part++],
+                                                    "rootfs",
+                                                    offset + trx->offset[i],
+                                                    0);
+                               i++;
+                       }
 
                        last_trx_part = curr_part - 1;
 
index 3096f3d..286b97a 100644 (file)
@@ -2654,8 +2654,7 @@ static void cfi_intelext_destroy(struct mtd_info *mtd)
        kfree(cfi);
        for (i = 0; i < mtd->numeraseregions; i++) {
                region = &mtd->eraseregions[i];
-               if (region->lockmap)
-                       kfree(region->lockmap);
+               kfree(region->lockmap);
        }
        kfree(mtd->eraseregions);
 }
index 7234604..448ce42 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/errno.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/string.h>
 #include <linux/slab.h>
@@ -1655,22 +1656,21 @@ static int dbg_flashctrl_show(struct seq_file *s, void *p)
 {
        struct docg3 *docg3 = (struct docg3 *)s->private;
 
-       int pos = 0;
        u8 fctrl;
 
        mutex_lock(&docg3->cascade->lock);
        fctrl = doc_register_readb(docg3, DOC_FLASHCONTROL);
        mutex_unlock(&docg3->cascade->lock);
 
-       pos += seq_printf(s,
-                "FlashControl : 0x%02x (%s,CE# %s,%s,%s,flash %s)\n",
-                fctrl,
-                fctrl & DOC_CTRL_VIOLATION ? "protocol violation" : "-",
-                fctrl & DOC_CTRL_CE ? "active" : "inactive",
-                fctrl & DOC_CTRL_PROTECTION_ERROR ? "protection error" : "-",
-                fctrl & DOC_CTRL_SEQUENCE_ERROR ? "sequence error" : "-",
-                fctrl & DOC_CTRL_FLASHREADY ? "ready" : "not ready");
-       return pos;
+       seq_printf(s, "FlashControl : 0x%02x (%s,CE# %s,%s,%s,flash %s)\n",
+                  fctrl,
+                  fctrl & DOC_CTRL_VIOLATION ? "protocol violation" : "-",
+                  fctrl & DOC_CTRL_CE ? "active" : "inactive",
+                  fctrl & DOC_CTRL_PROTECTION_ERROR ? "protection error" : "-",
+                  fctrl & DOC_CTRL_SEQUENCE_ERROR ? "sequence error" : "-",
+                  fctrl & DOC_CTRL_FLASHREADY ? "ready" : "not ready");
+
+       return 0;
 }
 DEBUGFS_RO_ATTR(flashcontrol, dbg_flashctrl_show);
 
@@ -1678,58 +1678,56 @@ static int dbg_asicmode_show(struct seq_file *s, void *p)
 {
        struct docg3 *docg3 = (struct docg3 *)s->private;
 
-       int pos = 0, pctrl, mode;
+       int pctrl, mode;
 
        mutex_lock(&docg3->cascade->lock);
        pctrl = doc_register_readb(docg3, DOC_ASICMODE);
        mode = pctrl & 0x03;
        mutex_unlock(&docg3->cascade->lock);
 
-       pos += seq_printf(s,
-                        "%04x : RAM_WE=%d,RSTIN_RESET=%d,BDETCT_RESET=%d,WRITE_ENABLE=%d,POWERDOWN=%d,MODE=%d%d (",
-                        pctrl,
-                        pctrl & DOC_ASICMODE_RAM_WE ? 1 : 0,
-                        pctrl & DOC_ASICMODE_RSTIN_RESET ? 1 : 0,
-                        pctrl & DOC_ASICMODE_BDETCT_RESET ? 1 : 0,
-                        pctrl & DOC_ASICMODE_MDWREN ? 1 : 0,
-                        pctrl & DOC_ASICMODE_POWERDOWN ? 1 : 0,
-                        mode >> 1, mode & 0x1);
+       seq_printf(s,
+                  "%04x : RAM_WE=%d,RSTIN_RESET=%d,BDETCT_RESET=%d,WRITE_ENABLE=%d,POWERDOWN=%d,MODE=%d%d (",
+                  pctrl,
+                  pctrl & DOC_ASICMODE_RAM_WE ? 1 : 0,
+                  pctrl & DOC_ASICMODE_RSTIN_RESET ? 1 : 0,
+                  pctrl & DOC_ASICMODE_BDETCT_RESET ? 1 : 0,
+                  pctrl & DOC_ASICMODE_MDWREN ? 1 : 0,
+                  pctrl & DOC_ASICMODE_POWERDOWN ? 1 : 0,
+                  mode >> 1, mode & 0x1);
 
        switch (mode) {
        case DOC_ASICMODE_RESET:
-               pos += seq_puts(s, "reset");
+               seq_puts(s, "reset");
                break;
        case DOC_ASICMODE_NORMAL:
-               pos += seq_puts(s, "normal");
+               seq_puts(s, "normal");
                break;
        case DOC_ASICMODE_POWERDOWN:
-               pos += seq_puts(s, "powerdown");
+               seq_puts(s, "powerdown");
                break;
        }
-       pos += seq_puts(s, ")\n");
-       return pos;
+       seq_puts(s, ")\n");
+       return 0;
 }
 DEBUGFS_RO_ATTR(asic_mode, dbg_asicmode_show);
 
 static int dbg_device_id_show(struct seq_file *s, void *p)
 {
        struct docg3 *docg3 = (struct docg3 *)s->private;
-       int pos = 0;
        int id;
 
        mutex_lock(&docg3->cascade->lock);
        id = doc_register_readb(docg3, DOC_DEVICESELECT);
        mutex_unlock(&docg3->cascade->lock);
 
-       pos += seq_printf(s, "DeviceId = %d\n", id);
-       return pos;
+       seq_printf(s, "DeviceId = %d\n", id);
+       return 0;
 }
 DEBUGFS_RO_ATTR(device_id, dbg_device_id_show);
 
 static int dbg_protection_show(struct seq_file *s, void *p)
 {
        struct docg3 *docg3 = (struct docg3 *)s->private;
-       int pos = 0;
        int protect, dps0, dps0_low, dps0_high, dps1, dps1_low, dps1_high;
 
        mutex_lock(&docg3->cascade->lock);
@@ -1742,45 +1740,40 @@ static int dbg_protection_show(struct seq_file *s, void *p)
        dps1_high = doc_register_readw(docg3, DOC_DPS1_ADDRHIGH);
        mutex_unlock(&docg3->cascade->lock);
 
-       pos += seq_printf(s, "Protection = 0x%02x (",
-                        protect);
+       seq_printf(s, "Protection = 0x%02x (", protect);
        if (protect & DOC_PROTECT_FOUNDRY_OTP_LOCK)
-               pos += seq_puts(s, "FOUNDRY_OTP_LOCK,");
+               seq_puts(s, "FOUNDRY_OTP_LOCK,");
        if (protect & DOC_PROTECT_CUSTOMER_OTP_LOCK)
-               pos += seq_puts(s, "CUSTOMER_OTP_LOCK,");
+               seq_puts(s, "CUSTOMER_OTP_LOCK,");
        if (protect & DOC_PROTECT_LOCK_INPUT)
-               pos += seq_puts(s, "LOCK_INPUT,");
+               seq_puts(s, "LOCK_INPUT,");
        if (protect & DOC_PROTECT_STICKY_LOCK)
-               pos += seq_puts(s, "STICKY_LOCK,");
+               seq_puts(s, "STICKY_LOCK,");
        if (protect & DOC_PROTECT_PROTECTION_ENABLED)
-               pos += seq_puts(s, "PROTECTION ON,");
+               seq_puts(s, "PROTECTION ON,");
        if (protect & DOC_PROTECT_IPL_DOWNLOAD_LOCK)
-               pos += seq_puts(s, "IPL_DOWNLOAD_LOCK,");
+               seq_puts(s, "IPL_DOWNLOAD_LOCK,");
        if (protect & DOC_PROTECT_PROTECTION_ERROR)
-               pos += seq_puts(s, "PROTECT_ERR,");
+               seq_puts(s, "PROTECT_ERR,");
        else
-               pos += seq_puts(s, "NO_PROTECT_ERR");
-       pos += seq_puts(s, ")\n");
-
-       pos += seq_printf(s, "DPS0 = 0x%02x : "
-                        "Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, "
-                        "WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n",
-                        dps0, dps0_low, dps0_high,
-                        !!(dps0 & DOC_DPS_OTP_PROTECTED),
-                        !!(dps0 & DOC_DPS_READ_PROTECTED),
-                        !!(dps0 & DOC_DPS_WRITE_PROTECTED),
-                        !!(dps0 & DOC_DPS_HW_LOCK_ENABLED),
-                        !!(dps0 & DOC_DPS_KEY_OK));
-       pos += seq_printf(s, "DPS1 = 0x%02x : "
-                        "Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, "
-                        "WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n",
-                        dps1, dps1_low, dps1_high,
-                        !!(dps1 & DOC_DPS_OTP_PROTECTED),
-                        !!(dps1 & DOC_DPS_READ_PROTECTED),
-                        !!(dps1 & DOC_DPS_WRITE_PROTECTED),
-                        !!(dps1 & DOC_DPS_HW_LOCK_ENABLED),
-                        !!(dps1 & DOC_DPS_KEY_OK));
-       return pos;
+               seq_puts(s, "NO_PROTECT_ERR");
+       seq_puts(s, ")\n");
+
+       seq_printf(s, "DPS0 = 0x%02x : Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n",
+                  dps0, dps0_low, dps0_high,
+                  !!(dps0 & DOC_DPS_OTP_PROTECTED),
+                  !!(dps0 & DOC_DPS_READ_PROTECTED),
+                  !!(dps0 & DOC_DPS_WRITE_PROTECTED),
+                  !!(dps0 & DOC_DPS_HW_LOCK_ENABLED),
+                  !!(dps0 & DOC_DPS_KEY_OK));
+       seq_printf(s, "DPS1 = 0x%02x : Protected area [0x%x - 0x%x] : OTP=%d, READ=%d, WRITE=%d, HW_LOCK=%d, KEY_OK=%d\n",
+                  dps1, dps1_low, dps1_high,
+                  !!(dps1 & DOC_DPS_OTP_PROTECTED),
+                  !!(dps1 & DOC_DPS_READ_PROTECTED),
+                  !!(dps1 & DOC_DPS_WRITE_PROTECTED),
+                  !!(dps1 & DOC_DPS_HW_LOCK_ENABLED),
+                  !!(dps1 & DOC_DPS_KEY_OK));
+       return 0;
 }
 DEBUGFS_RO_ATTR(protection, dbg_protection_show);
 
@@ -2126,9 +2119,18 @@ static int __exit docg3_release(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_OF
+static struct of_device_id docg3_dt_ids[] = {
+       { .compatible = "m-systems,diskonchip-g3" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, docg3_dt_ids);
+#endif
+
 static struct platform_driver g3_driver = {
        .driver         = {
                .name   = "docg3",
+               .of_match_table = of_match_ptr(docg3_dt_ids),
        },
        .suspend        = docg3_suspend,
        .resume         = docg3_resume,
index ed827cf..85e3546 100644 (file)
@@ -128,13 +128,10 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
        struct spi_device *spi = flash->spi;
        struct spi_transfer t[2];
        struct spi_message m;
-       int dummy = nor->read_dummy;
-       int ret;
+       unsigned int dummy = nor->read_dummy;
 
-       /* Wait till previous write/erase is done. */
-       ret = nor->wait_till_ready(nor);
-       if (ret)
-               return ret;
+       /* convert the dummy cycles to the number of bytes */
+       dummy /= 8;
 
        spi_message_init(&m);
        memset(t, 0, (sizeof t));
@@ -160,21 +157,10 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
 static int m25p80_erase(struct spi_nor *nor, loff_t offset)
 {
        struct m25p *flash = nor->priv;
-       int ret;
 
        dev_dbg(nor->dev, "%dKiB at 0x%08x\n",
                flash->mtd.erasesize / 1024, (u32)offset);
 
-       /* Wait until finished previous write command. */
-       ret = nor->wait_till_ready(nor);
-       if (ret)
-               return ret;
-
-       /* Send write enable, then erase commands. */
-       ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
-       if (ret)
-               return ret;
-
        /* Set up command buffer. */
        flash->command[0] = nor->erase_opcode;
        m25p_addr2cmd(nor, offset, flash->command);
@@ -260,7 +246,6 @@ static int m25p_remove(struct spi_device *spi)
        return mtd_device_unregister(&flash->mtd);
 }
 
-
 /*
  * XXX This needs to be kept in sync with spi_nor_ids.  We can't share
  * it with spi-nor, because if this is built as a module then modpost
@@ -287,7 +272,7 @@ static const struct spi_device_id m25p_ids[] = {
        {"s25fl512s"},  {"s70fl01gs"},  {"s25sl12800"}, {"s25sl12801"},
        {"s25fl129p0"}, {"s25fl129p1"}, {"s25sl004a"},  {"s25sl008a"},
        {"s25sl016a"},  {"s25sl032a"},  {"s25sl064a"},  {"s25fl008k"},
-       {"s25fl016k"},  {"s25fl064k"},
+       {"s25fl016k"},  {"s25fl064k"},  {"s25fl132k"},
        {"sst25vf040b"},{"sst25vf080b"},{"sst25vf016b"},{"sst25vf032b"},
        {"sst25vf064c"},{"sst25wf512"}, {"sst25wf010"}, {"sst25wf020"},
        {"sst25wf040"},
@@ -300,17 +285,16 @@ static const struct spi_device_id m25p_ids[] = {
        {"m45pe10"},    {"m45pe80"},    {"m45pe16"},
        {"m25pe20"},    {"m25pe80"},    {"m25pe16"},
        {"m25px16"},    {"m25px32"},    {"m25px32-s0"}, {"m25px32-s1"},
-       {"m25px64"},
+       {"m25px64"},    {"m25px80"},
        {"w25x10"},     {"w25x20"},     {"w25x40"},     {"w25x80"},
        {"w25x16"},     {"w25x32"},     {"w25q32"},     {"w25q32dw"},
-       {"w25x64"},     {"w25q64"},     {"w25q128"},    {"w25q80"},
-       {"w25q80bl"},   {"w25q128"},    {"w25q256"},    {"cat25c11"},
+       {"w25x64"},     {"w25q64"},     {"w25q80"},     {"w25q80bl"},
+       {"w25q128"},    {"w25q256"},    {"cat25c11"},
        {"cat25c03"},   {"cat25c09"},   {"cat25c17"},   {"cat25128"},
        { },
 };
 MODULE_DEVICE_TABLE(spi, m25p_ids);
 
-
 static struct spi_driver m25p80_driver = {
        .driver = {
                .name   = "m25p80",
index dd22ce2..0099aba 100644 (file)
@@ -149,7 +149,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
        struct dataflash        *priv = mtd->priv;
        struct spi_device       *spi = priv->spi;
-       struct spi_transfer     x = { .tx_dma = 0, };
+       struct spi_transfer     x = { };
        struct spi_message      msg;
        unsigned                blocksize = priv->page_size << 3;
        uint8_t                 *command;
@@ -235,7 +235,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
                               size_t *retlen, u_char *buf)
 {
        struct dataflash        *priv = mtd->priv;
-       struct spi_transfer     x[2] = { { .tx_dma = 0, }, };
+       struct spi_transfer     x[2] = { };
        struct spi_message      msg;
        unsigned int            addr;
        uint8_t                 *command;
@@ -301,7 +301,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
 {
        struct dataflash        *priv = mtd->priv;
        struct spi_device       *spi = priv->spi;
-       struct spi_transfer     x[2] = { { .tx_dma = 0, }, };
+       struct spi_transfer     x[2] = { };
        struct spi_message      msg;
        unsigned int            pageaddr, addr, offset, writelen;
        size_t                  remaining = len;
index effd9a4..8b66e52 100644 (file)
@@ -17,7 +17,7 @@
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
-#include <asm/io.h>
+#include <linux/io.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
index f02603e..708b7e8 100644 (file)
@@ -812,8 +812,7 @@ static int __init init_pmc551(void)
        }
 
        /* Exited early, reference left over */
-       if (PCI_Device)
-               pci_dev_put(PCI_Device);
+       pci_dev_put(PCI_Device);
 
        if (!pmc551list) {
                printk(KERN_NOTICE "pmc551: not detected\n");
index 487e64f..1388c8d 100644 (file)
@@ -518,7 +518,7 @@ void INFTL_dumpVUchains(struct INFTLrecord *s)
        pr_debug("INFTL Virtual Unit Chains:\n");
        for (logical = 0; logical < s->nb_blocks; logical++) {
                block = s->VUtable[logical];
-               if (block > s->nb_blocks)
+               if (block >= s->nb_blocks)
                        continue;
                pr_debug("  LOGICAL %d --> %d ", logical, block);
                for (i = 0; i < s->nb_blocks; i++) {
index 6ea51e5..41730fe 100644 (file)
@@ -126,7 +126,6 @@ static const char * const part_probe_types[] = {
 
 static int bfin_flash_probe(struct platform_device *pdev)
 {
-       int ret;
        struct physmap_flash_data *pdata = dev_get_platdata(&pdev->dev);
        struct resource *memory = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        struct resource *flash_ambctl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
index 991d0cb..f35cd20 100644 (file)
@@ -47,14 +47,12 @@ static int of_flash_remove(struct platform_device *dev)
                return 0;
        dev_set_drvdata(&dev->dev, NULL);
 
-       if (info->cmtd != info->list[0].mtd) {
+       if (info->cmtd) {
                mtd_device_unregister(info->cmtd);
-               mtd_concat_destroy(info->cmtd);
+               if (info->cmtd != info->list[0].mtd)
+                       mtd_concat_destroy(info->cmtd);
        }
 
-       if (info->cmtd)
-               mtd_device_unregister(info->cmtd);
-
        for (i = 0; i < info->list_size; i++) {
                if (info->list[i].mtd)
                        map_destroy(info->list[i].mtd);
index dd10646..7d0150d 100644 (file)
@@ -75,10 +75,12 @@ config MTD_NAND_DENALI_SCRATCH_REG_ADDR
           boards, the scratch register is at 0xFF108018.
 
 config MTD_NAND_GPIO
-       tristate "GPIO NAND Flash driver"
+       tristate "GPIO assisted NAND Flash driver"
        depends on GPIOLIB
        help
-         This enables a GPIO based NAND flash driver.
+         This enables a NAND flash driver where control signals are
+         connected to GPIO pins, and commands and data are communicated
+         via a memory mapped interface.
 
 config MTD_NAND_AMS_DELTA
        tristate "NAND Flash device on Amstrad E3"
@@ -516,4 +518,10 @@ config MTD_NAND_XWAY
          Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
          to the External Bus Unit (EBU).
 
+config MTD_NAND_SUNXI
+       tristate "Support for NAND on Allwinner SoCs"
+       depends on ARCH_SUNXI
+       help
+         Enables support for NAND Flash chips on Allwinner SoCs.
+
 endif # MTD_NAND
index 9c847e4..bd38f21 100644 (file)
@@ -50,5 +50,6 @@ obj-$(CONFIG_MTD_NAND_JZ4740)         += jz4740_nand.o
 obj-$(CONFIG_MTD_NAND_GPMI_NAND)       += gpmi-nand/
 obj-$(CONFIG_MTD_NAND_XWAY)            += xway_nand.o
 obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)   += bcm47xxnflash/
+obj-$(CONFIG_MTD_NAND_SUNXI)           += sunxi_nand.o
 
 nand-objs := nand_base.o nand_bbt.o nand_timings.o
index 84c38f3..a345e7b 100644 (file)
@@ -92,7 +92,7 @@ static struct nand_ecclayout atmel_oobinfo_small = {
 struct atmel_nfc {
        void __iomem            *base_cmd_regs;
        void __iomem            *hsmc_regs;
-       void __iomem            *sram_bank0;
+       void                    *sram_bank0;
        dma_addr_t              sram_bank0_phys;
        bool                    use_nfc_sram;
        bool                    write_by_sram;
@@ -105,7 +105,7 @@ struct atmel_nfc {
        struct completion       comp_xfer_done;
 
        /* Point to the sram bank which include readed data via NFC */
-       void __iomem            *data_in_sram;
+       void                    *data_in_sram;
        bool                    will_write_sram;
 };
 static struct atmel_nfc        nand_nfc;
@@ -127,6 +127,7 @@ struct atmel_nand_host {
        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;
@@ -256,26 +257,6 @@ static int atmel_nand_set_enable_ready_pins(struct mtd_info *mtd)
        return res;
 }
 
-static void memcpy32_fromio(void *trg, const void __iomem  *src, size_t size)
-{
-       int i;
-       u32 *t = trg;
-       const __iomem u32 *s = src;
-
-       for (i = 0; i < (size >> 2); i++)
-               *t++ = readl_relaxed(s++);
-}
-
-static void memcpy32_toio(void __iomem *trg, const void *src, int size)
-{
-       int i;
-       u32 __iomem *t = trg;
-       const u32 *s = src;
-
-       for (i = 0; i < (size >> 2); i++)
-               writel_relaxed(*s++, t++);
-}
-
 /*
  * Minimal-overhead PIO for data access.
  */
@@ -285,7 +266,7 @@ static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
        struct atmel_nand_host *host = nand_chip->priv;
 
        if (host->nfc && host->nfc->use_nfc_sram && host->nfc->data_in_sram) {
-               memcpy32_fromio(buf, host->nfc->data_in_sram, len);
+               memcpy(buf, host->nfc->data_in_sram, len);
                host->nfc->data_in_sram += len;
        } else {
                __raw_readsb(nand_chip->IO_ADDR_R, buf, len);
@@ -298,7 +279,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
        struct atmel_nand_host *host = nand_chip->priv;
 
        if (host->nfc && host->nfc->use_nfc_sram && host->nfc->data_in_sram) {
-               memcpy32_fromio(buf, host->nfc->data_in_sram, len);
+               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);
@@ -1112,12 +1093,66 @@ static int pmecc_choose_ecc(struct atmel_nand_host *host,
        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 mtd_info *mtd = &host->mtd;
        struct nand_chip *nand_chip = &host->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);
@@ -1163,8 +1198,24 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
        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)) {
-               err_no = PTR_ERR(host->pmecc_rom_base);
-               goto err;
+               if (!host->has_no_lookup_table)
+                       /* Don't display the information again */
+                       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;
@@ -1501,8 +1552,10 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
 
        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\n");
-               return -EINVAL;
+               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");
@@ -1899,7 +1952,7 @@ static int nfc_sram_write_page(struct mtd_info *mtd, struct nand_chip *chip,
        int cfg, len;
        int status = 0;
        struct atmel_nand_host *host = chip->priv;
-       void __iomem *sram = host->nfc->sram_bank0 + nfc_get_sram_off(host);
+       void *sram = host->nfc->sram_bank0 + nfc_get_sram_off(host);
 
        /* Subpage write is not supported */
        if (offset || (data_len < mtd->writesize))
@@ -1910,14 +1963,14 @@ static int nfc_sram_write_page(struct mtd_info *mtd, struct nand_chip *chip,
        if (use_dma) {
                if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) != 0)
                        /* Fall back to use cpu copy */
-                       memcpy32_toio(sram, buf, len);
+                       memcpy(sram, buf, len);
        } else {
-               memcpy32_toio(sram, buf, len);
+               memcpy(sram, buf, len);
        }
 
        cfg = nfc_readl(host->nfc->hsmc_regs, CFG);
        if (unlikely(raw) && oob_required) {
-               memcpy32_toio(sram + len, chip->oob_poi, mtd->oobsize);
+               memcpy(sram + len, chip->oob_poi, mtd->oobsize);
                len += mtd->oobsize;
                nfc_writel(host->nfc->hsmc_regs, CFG, cfg | NFC_CFG_WSPARE);
        } else {
@@ -2260,7 +2313,8 @@ static int atmel_nand_nfc_probe(struct platform_device *pdev)
 
        nfc_sram = platform_get_resource(pdev, IORESOURCE_MEM, 2);
        if (nfc_sram) {
-               nfc->sram_bank0 = devm_ioremap_resource(&pdev->dev, 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));
index 8a1e9a6..d4035e3 100644 (file)
 #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
 
index 4e66726..9a0f45f 100644 (file)
@@ -529,50 +529,6 @@ static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
        return 0;
 }
 
-static int cafe_nand_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 status;
-
-       chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
-
-       if (unlikely(raw))
-               status = chip->ecc.write_page_raw(mtd, chip, buf, oob_required);
-       else
-               status = chip->ecc.write_page(mtd, chip, buf, oob_required);
-
-       if (status < 0)
-               return status;
-
-       /*
-        * Cached progamming disabled for now, Not sure if its worth the
-        * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
-        */
-       cached = 0;
-
-       if (!cached || !(chip->options & NAND_CACHEPRG)) {
-
-               chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
-               status = chip->waitfunc(mtd, chip);
-               /*
-                * See if operation failed and additional status checks are
-                * available
-                */
-               if ((status & NAND_STATUS_FAIL) && (chip->errstat))
-                       status = chip->errstat(mtd, chip, FL_WRITING, status,
-                                              page);
-
-               if (status & NAND_STATUS_FAIL)
-                       return -EIO;
-       } else {
-               chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
-               status = chip->waitfunc(mtd, chip);
-       }
-
-       return 0;
-}
-
 static int cafe_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
 {
        return 0;
@@ -800,7 +756,6 @@ static int cafe_nand_probe(struct pci_dev *pdev,
        cafe->nand.ecc.hwctl  = (void *)cafe_nand_bug;
        cafe->nand.ecc.calculate = (void *)cafe_nand_bug;
        cafe->nand.ecc.correct  = (void *)cafe_nand_bug;
-       cafe->nand.write_page = cafe_nand_write_page;
        cafe->nand.ecc.write_page = cafe_nand_write_page_lowlevel;
        cafe->nand.ecc.write_oob = cafe_nand_write_oob;
        cafe->nand.ecc.read_page = cafe_nand_read_page;
index b9ef7a6..4c05f4f 100644 (file)
@@ -31,7 +31,6 @@
 #include <linux/mtd/nand_ecc.h>
 #include <linux/fsl_ifc.h>
 
-#define FSL_IFC_V1_1_0 0x01010000
 #define ERR_BYTE               0xFF /* Value returned for read
                                        bytes when read failed  */
 #define IFC_TIMEOUT_MSECS      500  /* Maximum number of mSecs to wait
@@ -877,7 +876,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
        struct fsl_ifc_regs __iomem *ifc = ctrl->regs;
        struct nand_chip *chip = &priv->chip;
        struct nand_ecclayout *layout;
-       u32 csor, ver;
+       u32 csor;
 
        /* Fill in fsl_ifc_mtd structure */
        priv->mtd.priv = chip;
@@ -984,8 +983,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
                chip->ecc.mode = NAND_ECC_SOFT;
        }
 
-       ver = ioread32be(&ifc->ifc_rev);
-       if (ver == FSL_IFC_V1_1_0)
+       if (ctrl->version == FSL_IFC_VERSION_1_1_0)
                fsl_ifc_sram_init(priv);
 
        return 0;
@@ -1045,12 +1043,12 @@ static int fsl_ifc_nand_probe(struct platform_device *dev)
        }
 
        /* find which chip select it is connected to */
-       for (bank = 0; bank < FSL_IFC_BANK_COUNT; bank++) {
+       for (bank = 0; bank < fsl_ifc_ctrl_dev->banks; bank++) {
                if (match_bank(ifc, bank, res.start))
                        break;
        }
 
-       if (bank >= FSL_IFC_BANK_COUNT) {
+       if (bank >= fsl_ifc_ctrl_dev->banks) {
                dev_err(&dev->dev, "%s: address did not match any chip selects\n",
                        __func__);
                return -ENODEV;
index 9182839..73c4048 100644 (file)
@@ -8,7 +8,9 @@
  *
  * Â© 2004 Simtec Electronics
  *
- * Device driver for NAND connected via GPIO
+ * Device driver for NAND flash that uses a memory mapped interface to
+ * read/write the NAND commands and data, and GPIO pins for control signals
+ * (the DT binding refers to this as "GPIO assisted NAND flash")
  *
  * 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
index 87e658c..27f272e 100644 (file)
@@ -1353,3 +1353,156 @@ int gpmi_read_page(struct gpmi_nand_data *this,
        set_dma_type(this, DMA_FOR_READ_ECC_PAGE);
        return start_dma_with_bch_irq(this, desc);
 }
+
+/**
+ * gpmi_copy_bits - copy bits from one memory region to another
+ * @dst: destination buffer
+ * @dst_bit_off: bit offset we're starting to write at
+ * @src: source buffer
+ * @src_bit_off: bit offset we're starting to read from
+ * @nbits: number of bits to copy
+ *
+ * This functions copies bits from one memory region to another, and is used by
+ * the GPMI driver to copy ECC sections which are not guaranteed to be byte
+ * aligned.
+ *
+ * src and dst should not overlap.
+ *
+ */
+void gpmi_copy_bits(u8 *dst, size_t dst_bit_off,
+                   const u8 *src, size_t src_bit_off,
+                   size_t nbits)
+{
+       size_t i;
+       size_t nbytes;
+       u32 src_buffer = 0;
+       size_t bits_in_src_buffer = 0;
+
+       if (!nbits)
+               return;
+
+       /*
+        * Move src and dst pointers to the closest byte pointer and store bit
+        * offsets within a byte.
+        */
+       src += src_bit_off / 8;
+       src_bit_off %= 8;
+
+       dst += dst_bit_off / 8;
+       dst_bit_off %= 8;
+
+       /*
+        * Initialize the src_buffer value with bits available in the first
+        * byte of data so that we end up with a byte aligned src pointer.
+        */
+       if (src_bit_off) {
+               src_buffer = src[0] >> src_bit_off;
+               if (nbits >= (8 - src_bit_off)) {
+                       bits_in_src_buffer += 8 - src_bit_off;
+               } else {
+                       src_buffer &= GENMASK(nbits - 1, 0);
+                       bits_in_src_buffer += nbits;
+               }
+               nbits -= bits_in_src_buffer;
+               src++;
+       }
+
+       /* Calculate the number of bytes that can be copied from src to dst. */
+       nbytes = nbits / 8;
+
+       /* Try to align dst to a byte boundary. */
+       if (dst_bit_off) {
+               if (bits_in_src_buffer < (8 - dst_bit_off) && nbytes) {
+                       src_buffer |= src[0] << bits_in_src_buffer;
+                       bits_in_src_buffer += 8;
+                       src++;
+                       nbytes--;
+               }
+
+               if (bits_in_src_buffer >= (8 - dst_bit_off)) {
+                       dst[0] &= GENMASK(dst_bit_off - 1, 0);
+                       dst[0] |= src_buffer << dst_bit_off;
+                       src_buffer >>= (8 - dst_bit_off);
+                       bits_in_src_buffer -= (8 - dst_bit_off);
+                       dst_bit_off = 0;
+                       dst++;
+                       if (bits_in_src_buffer > 7) {
+                               bits_in_src_buffer -= 8;
+                               dst[0] = src_buffer;
+                               dst++;
+                               src_buffer >>= 8;
+                       }
+               }
+       }
+
+       if (!bits_in_src_buffer && !dst_bit_off) {
+               /*
+                * Both src and dst pointers are byte aligned, thus we can
+                * just use the optimized memcpy function.
+                */
+               if (nbytes)
+                       memcpy(dst, src, nbytes);
+       } else {
+               /*
+                * src buffer is not byte aligned, hence we have to copy each
+                * src byte to the src_buffer variable before extracting a byte
+                * to store in dst.
+                */
+               for (i = 0; i < nbytes; i++) {
+                       src_buffer |= src[i] << bits_in_src_buffer;
+                       dst[i] = src_buffer;
+                       src_buffer >>= 8;
+               }
+       }
+       /* Update dst and src pointers */
+       dst += nbytes;
+       src += nbytes;
+
+       /*
+        * nbits is the number of remaining bits. It should not exceed 8 as
+        * we've already copied as much bytes as possible.
+        */
+       nbits %= 8;
+
+       /*
+        * If there's no more bits to copy to the destination and src buffer
+        * was already byte aligned, then we're done.
+        */
+       if (!nbits && !bits_in_src_buffer)
+               return;
+
+       /* Copy the remaining bits to src_buffer */
+       if (nbits)
+               src_buffer |= (*src & GENMASK(nbits - 1, 0)) <<
+                             bits_in_src_buffer;
+       bits_in_src_buffer += nbits;
+
+       /*
+        * In case there were not enough bits to get a byte aligned dst buffer
+        * prepare the src_buffer variable to match the dst organization (shift
+        * src_buffer by dst_bit_off and retrieve the least significant bits
+        * from dst).
+        */
+       if (dst_bit_off)
+               src_buffer = (src_buffer << dst_bit_off) |
+                            (*dst & GENMASK(dst_bit_off - 1, 0));
+       bits_in_src_buffer += dst_bit_off;
+
+       /*
+        * Keep most significant bits from dst if we end up with an unaligned
+        * number of bits.
+        */
+       nbytes = bits_in_src_buffer / 8;
+       if (bits_in_src_buffer % 8) {
+               src_buffer |= (dst[nbytes] &
+                              GENMASK(7, bits_in_src_buffer % 8)) <<
+                             (nbytes * 8);
+               nbytes++;
+       }
+
+       /* Copy the remaining bytes to dst */
+       for (i = 0; i < nbytes; i++) {
+               dst[i] = src_buffer;
+               src_buffer >>= 8;
+       }
+}
index 959cb9b..4f3851a 100644 (file)
@@ -791,6 +791,7 @@ static void gpmi_free_dma_buffer(struct gpmi_nand_data *this)
                                        this->page_buffer_phys);
        kfree(this->cmd_buffer);
        kfree(this->data_buffer_dma);
+       kfree(this->raw_buffer);
 
        this->cmd_buffer        = NULL;
        this->data_buffer_dma   = NULL;
@@ -837,6 +838,9 @@ static int gpmi_alloc_dma_buffer(struct gpmi_nand_data *this)
        if (!this->page_buffer_virt)
                goto error_alloc;
 
+       this->raw_buffer = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+       if (!this->raw_buffer)
+               goto error_alloc;
 
        /* Slice up the page buffer. */
        this->payload_virt = this->page_buffer_virt;
@@ -1347,6 +1351,199 @@ gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page)
        return status & NAND_STATUS_FAIL ? -EIO : 0;
 }
 
+/*
+ * This function reads a NAND page without involving the ECC engine (no HW
+ * ECC correction).
+ * The tricky part in the GPMI/BCH controller is that it stores ECC bits
+ * inline (interleaved with payload DATA), and do not align data chunk on
+ * byte boundaries.
+ * We thus need to take care moving the payload data and ECC bits stored in the
+ * page into the provided buffers, which is why we're using gpmi_copy_bits.
+ *
+ * See set_geometry_by_ecc_info inline comments to have a full description
+ * of the layout used by the GPMI controller.
+ */
+static int gpmi_ecc_read_page_raw(struct mtd_info *mtd,
+                                 struct nand_chip *chip, uint8_t *buf,
+                                 int oob_required, int page)
+{
+       struct gpmi_nand_data *this = chip->priv;
+       struct bch_geometry *nfc_geo = &this->bch_geometry;
+       int eccsize = nfc_geo->ecc_chunk_size;
+       int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
+       u8 *tmp_buf = this->raw_buffer;
+       size_t src_bit_off;
+       size_t oob_bit_off;
+       size_t oob_byte_off;
+       uint8_t *oob = chip->oob_poi;
+       int step;
+
+       chip->read_buf(mtd, tmp_buf,
+                      mtd->writesize + mtd->oobsize);
+
+       /*
+        * If required, swap the bad block marker and the data stored in the
+        * metadata section, so that we don't wrongly consider a block as bad.
+        *
+        * See the layout description for a detailed explanation on why this
+        * is needed.
+        */
+       if (this->swap_block_mark) {
+               u8 swap = tmp_buf[0];
+
+               tmp_buf[0] = tmp_buf[mtd->writesize];
+               tmp_buf[mtd->writesize] = swap;
+       }
+
+       /*
+        * Copy the metadata section into the oob buffer (this section is
+        * guaranteed to be aligned on a byte boundary).
+        */
+       if (oob_required)
+               memcpy(oob, tmp_buf, nfc_geo->metadata_size);
+
+       oob_bit_off = nfc_geo->metadata_size * 8;
+       src_bit_off = oob_bit_off;
+
+       /* Extract interleaved payload data and ECC bits */
+       for (step = 0; step < nfc_geo->ecc_chunk_count; step++) {
+               if (buf)
+                       gpmi_copy_bits(buf, step * eccsize * 8,
+                                      tmp_buf, src_bit_off,
+                                      eccsize * 8);
+               src_bit_off += eccsize * 8;
+
+               /* Align last ECC block to align a byte boundary */
+               if (step == nfc_geo->ecc_chunk_count - 1 &&
+                   (oob_bit_off + eccbits) % 8)
+                       eccbits += 8 - ((oob_bit_off + eccbits) % 8);
+
+               if (oob_required)
+                       gpmi_copy_bits(oob, oob_bit_off,
+                                      tmp_buf, src_bit_off,
+                                      eccbits);
+
+               src_bit_off += eccbits;
+               oob_bit_off += eccbits;
+       }
+
+       if (oob_required) {
+               oob_byte_off = oob_bit_off / 8;
+
+               if (oob_byte_off < mtd->oobsize)
+                       memcpy(oob + oob_byte_off,
+                              tmp_buf + mtd->writesize + oob_byte_off,
+                              mtd->oobsize - oob_byte_off);
+       }
+
+       return 0;
+}
+
+/*
+ * This function writes a NAND page without involving the ECC engine (no HW
+ * ECC generation).
+ * The tricky part in the GPMI/BCH controller is that it stores ECC bits
+ * inline (interleaved with payload DATA), and do not align data chunk on
+ * byte boundaries.
+ * We thus need to take care moving the OOB area at the right place in the
+ * final page, which is why we're using gpmi_copy_bits.
+ *
+ * See set_geometry_by_ecc_info inline comments to have a full description
+ * of the layout used by the GPMI controller.
+ */
+static int gpmi_ecc_write_page_raw(struct mtd_info *mtd,
+                                  struct nand_chip *chip,
+                                  const uint8_t *buf,
+                                  int oob_required)
+{
+       struct gpmi_nand_data *this = chip->priv;
+       struct bch_geometry *nfc_geo = &this->bch_geometry;
+       int eccsize = nfc_geo->ecc_chunk_size;
+       int eccbits = nfc_geo->ecc_strength * nfc_geo->gf_len;
+       u8 *tmp_buf = this->raw_buffer;
+       uint8_t *oob = chip->oob_poi;
+       size_t dst_bit_off;
+       size_t oob_bit_off;
+       size_t oob_byte_off;
+       int step;
+
+       /*
+        * Initialize all bits to 1 in case we don't have a buffer for the
+        * payload or oob data in order to leave unspecified bits of data
+        * to their initial state.
+        */
+       if (!buf || !oob_required)
+               memset(tmp_buf, 0xff, mtd->writesize + mtd->oobsize);
+
+       /*
+        * First copy the metadata section (stored in oob buffer) at the
+        * beginning of the page, as imposed by the GPMI layout.
+        */
+       memcpy(tmp_buf, oob, nfc_geo->metadata_size);
+       oob_bit_off = nfc_geo->metadata_size * 8;
+       dst_bit_off = oob_bit_off;
+
+       /* Interleave payload data and ECC bits */
+       for (step = 0; step < nfc_geo->ecc_chunk_count; step++) {
+               if (buf)
+                       gpmi_copy_bits(tmp_buf, dst_bit_off,
+                                      buf, step * eccsize * 8, eccsize * 8);
+               dst_bit_off += eccsize * 8;
+
+               /* Align last ECC block to align a byte boundary */
+               if (step == nfc_geo->ecc_chunk_count - 1 &&
+                   (oob_bit_off + eccbits) % 8)
+                       eccbits += 8 - ((oob_bit_off + eccbits) % 8);
+
+               if (oob_required)
+                       gpmi_copy_bits(tmp_buf, dst_bit_off,
+                                      oob, oob_bit_off, eccbits);
+
+               dst_bit_off += eccbits;
+               oob_bit_off += eccbits;
+       }
+
+       oob_byte_off = oob_bit_off / 8;
+
+       if (oob_required && oob_byte_off < mtd->oobsize)
+               memcpy(tmp_buf + mtd->writesize + oob_byte_off,
+                      oob + oob_byte_off, mtd->oobsize - oob_byte_off);
+
+       /*
+        * If required, swap the bad block marker and the first byte of the
+        * metadata section, so that we don't modify the bad block marker.
+        *
+        * See the layout description for a detailed explanation on why this
+        * is needed.
+        */
+       if (this->swap_block_mark) {
+               u8 swap = tmp_buf[0];
+
+               tmp_buf[0] = tmp_buf[mtd->writesize];
+               tmp_buf[mtd->writesize] = swap;
+       }
+
+       chip->write_buf(mtd, tmp_buf, mtd->writesize + mtd->oobsize);
+
+       return 0;
+}
+
+static int gpmi_ecc_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
+                                int page)
+{
+       chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+       return gpmi_ecc_read_page_raw(mtd, chip, NULL, 1, page);
+}
+
+static int gpmi_ecc_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
+                                int page)
+{
+       chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0, page);
+
+       return gpmi_ecc_write_page_raw(mtd, chip, NULL, 1);
+}
+
 static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs)
 {
        struct nand_chip *chip = mtd->priv;
@@ -1664,6 +1861,10 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
        ecc->write_page = gpmi_ecc_write_page;
        ecc->read_oob   = gpmi_ecc_read_oob;
        ecc->write_oob  = gpmi_ecc_write_oob;
+       ecc->read_page_raw = gpmi_ecc_read_page_raw;
+       ecc->write_page_raw = gpmi_ecc_write_page_raw;
+       ecc->read_oob_raw = gpmi_ecc_read_oob_raw;
+       ecc->write_oob_raw = gpmi_ecc_write_oob_raw;
        ecc->mode       = NAND_ECC_HW;
        ecc->size       = bch_geo->ecc_chunk_size;
        ecc->strength   = bch_geo->ecc_strength;
index 32c6ba4..544062f 100644 (file)
@@ -189,6 +189,8 @@ struct gpmi_nand_data {
        void                    *auxiliary_virt;
        dma_addr_t              auxiliary_phys;
 
+       void                    *raw_buffer;
+
        /* DMA channels */
 #define DMA_CHANS              8
        struct dma_chan         *dma_chans[DMA_CHANS];
@@ -290,6 +292,10 @@ extern int gpmi_send_page(struct gpmi_nand_data *,
 extern int gpmi_read_page(struct gpmi_nand_data *,
                        dma_addr_t payload, dma_addr_t auxiliary);
 
+void gpmi_copy_bits(u8 *dst, size_t dst_bit_off,
+                   const u8 *src, size_t src_bit_off,
+                   size_t nbits);
+
 /* BCH : Status Block Completion Codes */
 #define STATUS_GOOD            0x00
 #define STATUS_ERASED          0xff
index e1d56be..a8f550f 100644 (file)
@@ -280,14 +280,10 @@ static void memcpy32_fromio(void *trg, const void __iomem  *src, size_t size)
                *t++ = __raw_readl(s++);
 }
 
-static void memcpy32_toio(void __iomem *trg, const void *src, int size)
+static inline void memcpy32_toio(void __iomem *trg, const void *src, int size)
 {
-       int i;
-       u32 __iomem *t = trg;
-       const u32 *s = src;
-
-       for (i = 0; i < (size >> 2); i++)
-               __raw_writel(*s++, t++);
+       /* __iowrite32_copy use 32bit size values so divide by 4 */
+       __iowrite32_copy(trg, src, size / 4);
 }
 
 static int check_int_v3(struct mxc_nand_host *host)
index 5b5c627..41585df 100644 (file)
@@ -485,11 +485,11 @@ static int nand_check_wp(struct mtd_info *mtd)
 }
 
 /**
- * nand_block_checkbad - [GENERIC] Check if a block is marked bad
+ * nand_block_isreserved - [GENERIC] Check if a block is marked reserved.
  * @mtd: MTD device structure
  * @ofs: offset from device start
  *
- * Check if the block is mark as reserved.
+ * Check if the block is marked as reserved.
  */
 static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
 {
@@ -720,7 +720,7 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
 
        /*
         * Program and erase have their own busy handlers status, sequential
-        * in, and deplete1 need no delay.
+        * in and status need no delay.
         */
        switch (command) {
 
@@ -3765,9 +3765,9 @@ ident_done:
                pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
                                type->name);
 
-       pr_info("%dMiB, %s, page size: %d, OOB size: %d\n",
+       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",
-               mtd->writesize, mtd->oobsize);
+               mtd->erasesize >> 10, mtd->writesize, mtd->oobsize);
        return type;
 }
 
@@ -4035,7 +4035,7 @@ int nand_scan_tail(struct mtd_info *mtd)
                 */
                if (!ecc->size && (mtd->oobsize >= 64)) {
                        ecc->size = 512;
-                       ecc->bytes = 7;
+                       ecc->bytes = DIV_ROUND_UP(13 * ecc->strength, 8);
                }
                ecc->priv = nand_bch_init(mtd, ecc->size, ecc->bytes,
                                               &ecc->layout);
index fbde891..dd620c1 100644 (file)
@@ -178,6 +178,7 @@ struct nand_manufacturers nand_manuf_ids[] = {
        {NAND_MFR_EON, "Eon"},
        {NAND_MFR_SANDISK, "SanDisk"},
        {NAND_MFR_INTEL, "Intel"},
+       {NAND_MFR_ATO, "ATO"},
        {0x0, "Unknown"}
 };
 
index 7dc1dd2..ab5bbf5 100644 (file)
 #define CONFIG_NANDSIM_MAX_PARTS  32
 #endif
 
-static uint first_id_byte  = CONFIG_NANDSIM_FIRST_ID_BYTE;
-static uint second_id_byte = CONFIG_NANDSIM_SECOND_ID_BYTE;
-static uint third_id_byte  = CONFIG_NANDSIM_THIRD_ID_BYTE;
-static uint fourth_id_byte = CONFIG_NANDSIM_FOURTH_ID_BYTE;
 static uint access_delay   = CONFIG_NANDSIM_ACCESS_DELAY;
 static uint programm_delay = CONFIG_NANDSIM_PROGRAMM_DELAY;
 static uint erase_delay    = CONFIG_NANDSIM_ERASE_DELAY;
@@ -111,11 +107,19 @@ static unsigned int overridesize = 0;
 static char *cache_file = NULL;
 static unsigned int bbt;
 static unsigned int bch;
+static u_char id_bytes[8] = {
+       [0] = CONFIG_NANDSIM_FIRST_ID_BYTE,
+       [1] = CONFIG_NANDSIM_SECOND_ID_BYTE,
+       [2] = CONFIG_NANDSIM_THIRD_ID_BYTE,
+       [3] = CONFIG_NANDSIM_FOURTH_ID_BYTE,
+       [4 ... 7] = 0xFF,
+};
 
-module_param(first_id_byte,  uint, 0400);
-module_param(second_id_byte, uint, 0400);
-module_param(third_id_byte,  uint, 0400);
-module_param(fourth_id_byte, uint, 0400);
+module_param_array(id_bytes, byte, NULL, 0400);
+module_param_named(first_id_byte, id_bytes[0], byte, 0400);
+module_param_named(second_id_byte, id_bytes[1], byte, 0400);
+module_param_named(third_id_byte, id_bytes[2], byte, 0400);
+module_param_named(fourth_id_byte, id_bytes[3], byte, 0400);
 module_param(access_delay,   uint, 0400);
 module_param(programm_delay, uint, 0400);
 module_param(erase_delay,    uint, 0400);
@@ -136,10 +140,11 @@ module_param(cache_file,     charp, 0400);
 module_param(bbt,           uint, 0400);
 module_param(bch,           uint, 0400);
 
-MODULE_PARM_DESC(first_id_byte,  "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)");
-MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)");
-MODULE_PARM_DESC(third_id_byte,  "The third byte returned by NAND Flash 'read ID' command");
-MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command");
+MODULE_PARM_DESC(id_bytes,       "The ID bytes returned by NAND Flash 'read ID' command");
+MODULE_PARM_DESC(first_id_byte,  "The first byte returned by NAND Flash 'read ID' command (manufacturer ID) (obsolete)");
+MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID) (obsolete)");
+MODULE_PARM_DESC(third_id_byte,  "The third byte returned by NAND Flash 'read ID' command (obsolete)");
+MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command (obsolete)");
 MODULE_PARM_DESC(access_delay,   "Initial page access delay (microseconds)");
 MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds");
 MODULE_PARM_DESC(erase_delay,    "Sector erase delay (milliseconds)");
@@ -304,7 +309,7 @@ struct nandsim {
        unsigned int nbparts;
 
        uint busw;              /* flash chip bus width (8 or 16) */
-       u_char ids[4];          /* chip's ID bytes */
+       u_char ids[8];          /* chip's ID bytes */
        uint32_t options;       /* chip's characteristic bits */
        uint32_t state;         /* current chip state */
        uint32_t nxstate;       /* next expected state */
@@ -2279,17 +2284,18 @@ static int __init ns_init_module(void)
         * Perform minimum nandsim structure initialization to handle
         * the initial ID read command correctly
         */
-       if (third_id_byte != 0xFF || fourth_id_byte != 0xFF)
+       if (id_bytes[6] != 0xFF || id_bytes[7] != 0xFF)
+               nand->geom.idbytes = 8;
+       else if (id_bytes[4] != 0xFF || id_bytes[5] != 0xFF)
+               nand->geom.idbytes = 6;
+       else if (id_bytes[2] != 0xFF || id_bytes[3] != 0xFF)
                nand->geom.idbytes = 4;
        else
                nand->geom.idbytes = 2;
        nand->regs.status = NS_STATUS_OK(nand);
        nand->nxstate = STATE_UNKNOWN;
        nand->options |= OPT_PAGE512; /* temporary value */
-       nand->ids[0] = first_id_byte;
-       nand->ids[1] = second_id_byte;
-       nand->ids[2] = third_id_byte;
-       nand->ids[3] = fourth_id_byte;
+       memcpy(nand->ids, id_bytes, sizeof(nand->ids));
        if (bus_width == 16) {
                nand->busw = 16;
                chip->options |= NAND_BUSWIDTH_16;
index 6d74b56..63f858e 100644 (file)
@@ -144,11 +144,13 @@ static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc,
        0xac, 0x6b, 0xff, 0x99, 0x7b};
 static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10};
 
-/* oob info generated runtime depending on ecc algorithm and layout selected */
-static struct nand_ecclayout omap_oobinfo;
+/* Shared among all NAND instances to synchronize access to the ECC Engine */
+static struct nand_hw_control omap_gpmc_controller = {
+       .lock = __SPIN_LOCK_UNLOCKED(omap_gpmc_controller.lock),
+       .wq = __WAIT_QUEUE_HEAD_INITIALIZER(omap_gpmc_controller.wq),
+};
 
 struct omap_nand_info {
-       struct nand_hw_control          controller;
        struct omap_nand_platform_data  *pdata;
        struct mtd_info                 mtd;
        struct nand_chip                nand;
@@ -168,6 +170,8 @@ struct omap_nand_info {
        u_char                          *buf;
        int                                     buf_len;
        struct gpmc_nand_regs           reg;
+       /* generated at runtime depending on ECC algorithm and layout selected */
+       struct nand_ecclayout           oobinfo;
        /* fields specific for BCHx_HW ECC scheme */
        struct device                   *elm_dev;
        struct device_node              *of_node;
@@ -1686,9 +1690,6 @@ static int omap_nand_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, info);
 
-       spin_lock_init(&info->controller.lock);
-       init_waitqueue_head(&info->controller.wq);
-
        info->pdev              = pdev;
        info->gpmc_cs           = pdata->cs;
        info->reg               = pdata->reg;
@@ -1708,7 +1709,7 @@ static int omap_nand_probe(struct platform_device *pdev)
 
        info->phys_base = res->start;
 
-       nand_chip->controller = &info->controller;
+       nand_chip->controller = &omap_gpmc_controller;
 
        nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R;
        nand_chip->cmd_ctrl  = omap_hwcontrol;
@@ -1741,13 +1742,6 @@ static int omap_nand_probe(struct platform_device *pdev)
                goto return_error;
        }
 
-       /* check for small page devices */
-       if ((mtd->oobsize < 64) && (pdata->ecc_opt != OMAP_ECC_HAM1_CODE_HW)) {
-               dev_err(&info->pdev->dev, "small page devices are not supported\n");
-               err = -EINVAL;
-               goto return_error;
-       }
-
        /* re-populate low-level callbacks based on xfer modes */
        switch (pdata->xfer_type) {
        case NAND_OMAP_PREFETCH_POLLED:
@@ -1840,7 +1834,7 @@ static int omap_nand_probe(struct platform_device *pdev)
        }
 
        /* populate MTD interface based on ECC scheme */
-       ecclayout               = &omap_oobinfo;
+       ecclayout               = &info->oobinfo;
        switch (info->ecc_opt) {
        case OMAP_ECC_HAM1_CODE_SW:
                nand_chip->ecc.mode = NAND_ECC_SOFT;
index c53e369..c3c6d30 100644 (file)
@@ -19,7 +19,7 @@
 #include <linux/mtd/partitions.h>
 #include <linux/clk.h>
 #include <linux/err.h>
-#include <asm/io.h>
+#include <linux/io.h>
 #include <asm/sizes.h>
 #include <linux/platform_data/mtd-orion_nand.h>
 
@@ -85,33 +85,24 @@ static int __init orion_nand_probe(struct platform_device *pdev)
        int ret = 0;
        u32 val = 0;
 
-       nc = kzalloc(sizeof(struct nand_chip) + sizeof(struct mtd_info), GFP_KERNEL);
-       if (!nc) {
-               ret = -ENOMEM;
-               goto no_res;
-       }
+       nc = devm_kzalloc(&pdev->dev,
+                       sizeof(struct nand_chip) + sizeof(struct mtd_info),
+                       GFP_KERNEL);
+       if (!nc)
+               return -ENOMEM;
        mtd = (struct mtd_info *)(nc + 1);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               ret = -ENODEV;
-               goto no_res;
-       }
+       io_base = devm_ioremap_resource(&pdev->dev, res);
 
-       io_base = ioremap(res->start, resource_size(res));
-       if (!io_base) {
-               dev_err(&pdev->dev, "ioremap failed\n");
-               ret = -EIO;
-               goto no_res;
-       }
+       if (IS_ERR(io_base))
+               return PTR_ERR(io_base);
 
        if (pdev->dev.of_node) {
                board = devm_kzalloc(&pdev->dev, sizeof(struct orion_nand_data),
                                        GFP_KERNEL);
-               if (!board) {
-                       ret = -ENOMEM;
-                       goto no_res;
-               }
+               if (!board)
+                       return -ENOMEM;
                if (!of_property_read_u32(pdev->dev.of_node, "cle", &val))
                        board->cle = (u8)val;
                else
@@ -185,9 +176,6 @@ no_dev:
                clk_disable_unprepare(clk);
                clk_put(clk);
        }
-       iounmap(io_base);
-no_res:
-       kfree(nc);
 
        return ret;
 }
@@ -195,15 +183,10 @@ no_res:
 static int orion_nand_remove(struct platform_device *pdev)
 {
        struct mtd_info *mtd = platform_get_drvdata(pdev);
-       struct nand_chip *nc = mtd->priv;
        struct clk *clk;
 
        nand_release(mtd);
 
-       iounmap(nc->IO_ADDR_W);
-
-       kfree(nc);
-
        clk = clk_get(&pdev->dev, NULL);
        if (!IS_ERR(clk)) {
                clk_disable_unprepare(clk);
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
new file mode 100644 (file)
index 0000000..ccaa8e2
--- /dev/null
@@ -0,0 +1,1432 @@
+/*
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon.dev@gmail.com>
+ *
+ * Derived from:
+ *     https://github.com/yuq/sunxi-nfc-mtd
+ *     Copyright (C) 2013 Qiang Yu <yuq825@gmail.com>
+ *
+ *     https://github.com/hno/Allwinner-Info
+ *     Copyright (C) 2013 Henrik Nordström <Henrik Nordström>
+ *
+ *     Copyright (C) 2013 Dmitriy B. <rzk333@gmail.com>
+ *     Copyright (C) 2013 Sergey Lapin <slapin@ossfans.org>
+ *
+ * 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/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/of_mtd.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#define NFC_REG_CTL            0x0000
+#define NFC_REG_ST             0x0004
+#define NFC_REG_INT            0x0008
+#define NFC_REG_TIMING_CTL     0x000C
+#define NFC_REG_TIMING_CFG     0x0010
+#define NFC_REG_ADDR_LOW       0x0014
+#define NFC_REG_ADDR_HIGH      0x0018
+#define NFC_REG_SECTOR_NUM     0x001C
+#define NFC_REG_CNT            0x0020
+#define NFC_REG_CMD            0x0024
+#define NFC_REG_RCMD_SET       0x0028
+#define NFC_REG_WCMD_SET       0x002C
+#define NFC_REG_IO_DATA                0x0030
+#define NFC_REG_ECC_CTL                0x0034
+#define NFC_REG_ECC_ST         0x0038
+#define NFC_REG_DEBUG          0x003C
+#define NFC_REG_ECC_CNT0       0x0040
+#define NFC_REG_ECC_CNT1       0x0044
+#define NFC_REG_ECC_CNT2       0x0048
+#define NFC_REG_ECC_CNT3       0x004c
+#define NFC_REG_USER_DATA_BASE 0x0050
+#define NFC_REG_SPARE_AREA     0x00A0
+#define NFC_RAM0_BASE          0x0400
+#define NFC_RAM1_BASE          0x0800
+
+/* define bit use in NFC_CTL */
+#define NFC_EN                 BIT(0)
+#define NFC_RESET              BIT(1)
+#define NFC_BUS_WIDYH          BIT(2)
+#define NFC_RB_SEL             BIT(3)
+#define NFC_CE_SEL             GENMASK(26, 24)
+#define NFC_CE_CTL             BIT(6)
+#define NFC_CE_CTL1            BIT(7)
+#define NFC_PAGE_SIZE          GENMASK(11, 8)
+#define NFC_SAM                        BIT(12)
+#define NFC_RAM_METHOD         BIT(14)
+#define NFC_DEBUG_CTL          BIT(31)
+
+/* define bit use in NFC_ST */
+#define NFC_RB_B2R             BIT(0)
+#define NFC_CMD_INT_FLAG       BIT(1)
+#define NFC_DMA_INT_FLAG       BIT(2)
+#define NFC_CMD_FIFO_STATUS    BIT(3)
+#define NFC_STA                        BIT(4)
+#define NFC_NATCH_INT_FLAG     BIT(5)
+#define NFC_RB_STATE0          BIT(8)
+#define NFC_RB_STATE1          BIT(9)
+#define NFC_RB_STATE2          BIT(10)
+#define NFC_RB_STATE3          BIT(11)
+
+/* define bit use in NFC_INT */
+#define NFC_B2R_INT_ENABLE     BIT(0)
+#define NFC_CMD_INT_ENABLE     BIT(1)
+#define NFC_DMA_INT_ENABLE     BIT(2)
+#define NFC_INT_MASK           (NFC_B2R_INT_ENABLE | \
+                                NFC_CMD_INT_ENABLE | \
+                                NFC_DMA_INT_ENABLE)
+
+/* define bit use in NFC_CMD */
+#define NFC_CMD_LOW_BYTE       GENMASK(7, 0)
+#define NFC_CMD_HIGH_BYTE      GENMASK(15, 8)
+#define NFC_ADR_NUM            GENMASK(18, 16)
+#define NFC_SEND_ADR           BIT(19)
+#define NFC_ACCESS_DIR         BIT(20)
+#define NFC_DATA_TRANS         BIT(21)
+#define NFC_SEND_CMD1          BIT(22)
+#define NFC_WAIT_FLAG          BIT(23)
+#define NFC_SEND_CMD2          BIT(24)
+#define NFC_SEQ                        BIT(25)
+#define NFC_DATA_SWAP_METHOD   BIT(26)
+#define NFC_ROW_AUTO_INC       BIT(27)
+#define NFC_SEND_CMD3          BIT(28)
+#define NFC_SEND_CMD4          BIT(29)
+#define NFC_CMD_TYPE           GENMASK(31, 30)
+
+/* define bit use in NFC_RCMD_SET */
+#define NFC_READ_CMD           GENMASK(7, 0)
+#define NFC_RANDOM_READ_CMD0   GENMASK(15, 8)
+#define NFC_RANDOM_READ_CMD1   GENMASK(23, 16)
+
+/* define bit use in NFC_WCMD_SET */
+#define NFC_PROGRAM_CMD                GENMASK(7, 0)
+#define NFC_RANDOM_WRITE_CMD   GENMASK(15, 8)
+#define NFC_READ_CMD0          GENMASK(23, 16)
+#define NFC_READ_CMD1          GENMASK(31, 24)
+
+/* define bit use in NFC_ECC_CTL */
+#define NFC_ECC_EN             BIT(0)
+#define NFC_ECC_PIPELINE       BIT(3)
+#define NFC_ECC_EXCEPTION      BIT(4)
+#define NFC_ECC_BLOCK_SIZE     BIT(5)
+#define NFC_RANDOM_EN          BIT(9)
+#define NFC_RANDOM_DIRECTION   BIT(10)
+#define NFC_ECC_MODE_SHIFT     12
+#define NFC_ECC_MODE           GENMASK(15, 12)
+#define NFC_RANDOM_SEED                GENMASK(30, 16)
+
+#define NFC_DEFAULT_TIMEOUT_MS 1000
+
+#define NFC_SRAM_SIZE          1024
+
+#define NFC_MAX_CS             7
+
+/*
+ * Ready/Busy detection type: describes the Ready/Busy detection modes
+ *
+ * @RB_NONE:   no external detection available, rely on STATUS command
+ *             and software timeouts
+ * @RB_NATIVE: use sunxi NAND controller Ready/Busy support. The Ready/Busy
+ *             pin of the NAND flash chip must be connected to one of the
+ *             native NAND R/B pins (those which can be muxed to the NAND
+ *             Controller)
+ * @RB_GPIO:   use a simple GPIO to handle Ready/Busy status. The Ready/Busy
+ *             pin of the NAND flash chip must be connected to a GPIO capable
+ *             pin.
+ */
+enum sunxi_nand_rb_type {
+       RB_NONE,
+       RB_NATIVE,
+       RB_GPIO,
+};
+
+/*
+ * Ready/Busy structure: stores information related to Ready/Busy detection
+ *
+ * @type:      the Ready/Busy detection mode
+ * @info:      information related to the R/B detection mode. Either a gpio
+ *             id or a native R/B id (those supported by the NAND controller).
+ */
+struct sunxi_nand_rb {
+       enum sunxi_nand_rb_type type;
+       union {
+               int gpio;
+               int nativeid;
+       } info;
+};
+
+/*
+ * Chip Select structure: stores information related to NAND Chip Select
+ *
+ * @cs:                the NAND CS id used to communicate with a NAND Chip
+ * @rb:                the Ready/Busy description
+ */
+struct sunxi_nand_chip_sel {
+       u8 cs;
+       struct sunxi_nand_rb rb;
+};
+
+/*
+ * sunxi HW ECC infos: stores information related to HW ECC support
+ *
+ * @mode:      the sunxi ECC mode field deduced from ECC requirements
+ * @layout:    the OOB layout depending on the ECC requirements and the
+ *             selected ECC mode
+ */
+struct sunxi_nand_hw_ecc {
+       int mode;
+       struct nand_ecclayout layout;
+};
+
+/*
+ * NAND chip structure: stores NAND chip device related information
+ *
+ * @node:              used to store NAND chips into a list
+ * @nand:              base NAND chip structure
+ * @mtd:               base MTD structure
+ * @clk_rate:          clk_rate required for this NAND chip
+ * @selected:          current active CS
+ * @nsels:             number of CS lines required by the NAND chip
+ * @sels:              array of CS lines descriptions
+ */
+struct sunxi_nand_chip {
+       struct list_head node;
+       struct nand_chip nand;
+       struct mtd_info mtd;
+       unsigned long clk_rate;
+       int selected;
+       int nsels;
+       struct sunxi_nand_chip_sel sels[0];
+};
+
+static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand)
+{
+       return container_of(nand, struct sunxi_nand_chip, nand);
+}
+
+/*
+ * NAND Controller structure: stores sunxi NAND controller information
+ *
+ * @controller:                base controller structure
+ * @dev:               parent device (used to print error messages)
+ * @regs:              NAND controller registers
+ * @ahb_clk:           NAND Controller AHB clock
+ * @mod_clk:           NAND Controller mod clock
+ * @assigned_cs:       bitmask describing already assigned CS lines
+ * @clk_rate:          NAND controller current clock rate
+ * @chips:             a list containing all the NAND chips attached to
+ *                     this NAND controller
+ * @complete:          a completion object used to wait for NAND
+ *                     controller events
+ */
+struct sunxi_nfc {
+       struct nand_hw_control controller;
+       struct device *dev;
+       void __iomem *regs;
+       struct clk *ahb_clk;
+       struct clk *mod_clk;
+       unsigned long assigned_cs;
+       unsigned long clk_rate;
+       struct list_head chips;
+       struct completion complete;
+};
+
+static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl)
+{
+       return container_of(ctrl, struct sunxi_nfc, controller);
+}
+
+static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id)
+{
+       struct sunxi_nfc *nfc = dev_id;
+       u32 st = readl(nfc->regs + NFC_REG_ST);
+       u32 ien = readl(nfc->regs + NFC_REG_INT);
+
+       if (!(ien & st))
+               return IRQ_NONE;
+
+       if ((ien & st) == ien)
+               complete(&nfc->complete);
+
+       writel(st & NFC_INT_MASK, nfc->regs + NFC_REG_ST);
+       writel(~st & ien & NFC_INT_MASK, nfc->regs + NFC_REG_INT);
+
+       return IRQ_HANDLED;
+}
+
+static int sunxi_nfc_wait_int(struct sunxi_nfc *nfc, u32 flags,
+                             unsigned int timeout_ms)
+{
+       init_completion(&nfc->complete);
+
+       writel(flags, nfc->regs + NFC_REG_INT);
+
+       if (!timeout_ms)
+               timeout_ms = NFC_DEFAULT_TIMEOUT_MS;
+
+       if (!wait_for_completion_timeout(&nfc->complete,
+                                        msecs_to_jiffies(timeout_ms))) {
+               dev_err(nfc->dev, "wait interrupt timedout\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static int sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc)
+{
+       unsigned long timeout = jiffies +
+                               msecs_to_jiffies(NFC_DEFAULT_TIMEOUT_MS);
+
+       do {
+               if (!(readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
+                       return 0;
+       } while (time_before(jiffies, timeout));
+
+       dev_err(nfc->dev, "wait for empty cmd FIFO timedout\n");
+       return -ETIMEDOUT;
+}
+
+static int sunxi_nfc_rst(struct sunxi_nfc *nfc)
+{
+       unsigned long timeout = jiffies +
+                               msecs_to_jiffies(NFC_DEFAULT_TIMEOUT_MS);
+
+       writel(0, nfc->regs + NFC_REG_ECC_CTL);
+       writel(NFC_RESET, nfc->regs + NFC_REG_CTL);
+
+       do {
+               if (!(readl(nfc->regs + NFC_REG_CTL) & NFC_RESET))
+                       return 0;
+       } while (time_before(jiffies, timeout));
+
+       dev_err(nfc->dev, "wait for NAND controller reset timedout\n");
+       return -ETIMEDOUT;
+}
+
+static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
+{
+       struct nand_chip *nand = mtd->priv;
+       struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+       struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+       struct sunxi_nand_rb *rb;
+       unsigned long timeo = (sunxi_nand->nand.state == FL_ERASING ? 400 : 20);
+       int ret;
+
+       if (sunxi_nand->selected < 0)
+               return 0;
+
+       rb = &sunxi_nand->sels[sunxi_nand->selected].rb;
+
+       switch (rb->type) {
+       case RB_NATIVE:
+               ret = !!(readl(nfc->regs + NFC_REG_ST) &
+                        (NFC_RB_STATE0 << rb->info.nativeid));
+               if (ret)
+                       break;
+
+               sunxi_nfc_wait_int(nfc, NFC_RB_B2R, timeo);
+               ret = !!(readl(nfc->regs + NFC_REG_ST) &
+                        (NFC_RB_STATE0 << rb->info.nativeid));
+               break;
+       case RB_GPIO:
+               ret = gpio_get_value(rb->info.gpio);
+               break;
+       case RB_NONE:
+       default:
+               ret = 0;
+               dev_err(nfc->dev, "cannot check R/B NAND status!\n");
+               break;
+       }
+
+       return ret;
+}
+
+static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip)
+{
+       struct nand_chip *nand = mtd->priv;
+       struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+       struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+       struct sunxi_nand_chip_sel *sel;
+       u32 ctl;
+
+       if (chip > 0 && chip >= sunxi_nand->nsels)
+               return;
+
+       if (chip == sunxi_nand->selected)
+               return;
+
+       ctl = readl(nfc->regs + NFC_REG_CTL) &
+             ~(NFC_CE_SEL | NFC_RB_SEL | NFC_EN);
+
+       if (chip >= 0) {
+               sel = &sunxi_nand->sels[chip];
+
+               ctl |= (sel->cs << 24) | NFC_EN |
+                      (((nand->page_shift - 10) & 0xf) << 8);
+               if (sel->rb.type == RB_NONE) {
+                       nand->dev_ready = NULL;
+               } else {
+                       nand->dev_ready = sunxi_nfc_dev_ready;
+                       if (sel->rb.type == RB_NATIVE)
+                               ctl |= (sel->rb.info.nativeid << 3);
+               }
+
+               writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA);
+
+               if (nfc->clk_rate != sunxi_nand->clk_rate) {
+                       clk_set_rate(nfc->mod_clk, sunxi_nand->clk_rate);
+                       nfc->clk_rate = sunxi_nand->clk_rate;
+               }
+       }
+
+       writel(ctl, nfc->regs + NFC_REG_CTL);
+
+       sunxi_nand->selected = chip;
+}
+
+static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+       struct nand_chip *nand = mtd->priv;
+       struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+       struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+       int ret;
+       int cnt;
+       int offs = 0;
+       u32 tmp;
+
+       while (len > offs) {
+               cnt = min(len - offs, NFC_SRAM_SIZE);
+
+               ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+               if (ret)
+                       break;
+
+               writel(cnt, nfc->regs + NFC_REG_CNT);
+               tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
+               writel(tmp, nfc->regs + NFC_REG_CMD);
+
+               ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+               if (ret)
+                       break;
+
+               if (buf)
+                       memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE,
+                                     cnt);
+               offs += cnt;
+       }
+}
+
+static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+                               int len)
+{
+       struct nand_chip *nand = mtd->priv;
+       struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+       struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+       int ret;
+       int cnt;
+       int offs = 0;
+       u32 tmp;
+
+       while (len > offs) {
+               cnt = min(len - offs, NFC_SRAM_SIZE);
+
+               ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+               if (ret)
+                       break;
+
+               writel(cnt, nfc->regs + NFC_REG_CNT);
+               memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt);
+               tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
+                     NFC_ACCESS_DIR;
+               writel(tmp, nfc->regs + NFC_REG_CMD);
+
+               ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+               if (ret)
+                       break;
+
+               offs += cnt;
+       }
+}
+
+static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd)
+{
+       uint8_t ret;
+
+       sunxi_nfc_read_buf(mtd, &ret, 1);
+
+       return ret;
+}
+
+static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
+                              unsigned int ctrl)
+{
+       struct nand_chip *nand = mtd->priv;
+       struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+       struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+       int ret;
+       u32 tmp;
+
+       ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+       if (ret)
+               return;
+
+       if (ctrl & NAND_CTRL_CHANGE) {
+               tmp = readl(nfc->regs + NFC_REG_CTL);
+               if (ctrl & NAND_NCE)
+                       tmp |= NFC_CE_CTL;
+               else
+                       tmp &= ~NFC_CE_CTL;
+               writel(tmp, nfc->regs + NFC_REG_CTL);
+       }
+
+       if (dat == NAND_CMD_NONE)
+               return;
+
+       if (ctrl & NAND_CLE) {
+               writel(NFC_SEND_CMD1 | dat, nfc->regs + NFC_REG_CMD);
+       } else {
+               writel(dat, nfc->regs + NFC_REG_ADDR_LOW);
+               writel(NFC_SEND_ADR, nfc->regs + NFC_REG_CMD);
+       }
+
+       sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+}
+
+static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,
+                                     struct nand_chip *chip, uint8_t *buf,
+                                     int oob_required, int page)
+{
+       struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
+       struct nand_ecc_ctrl *ecc = &chip->ecc;
+       struct nand_ecclayout *layout = ecc->layout;
+       struct sunxi_nand_hw_ecc *data = ecc->priv;
+       unsigned int max_bitflips = 0;
+       int offset;
+       int ret;
+       u32 tmp;
+       int i;
+       int cnt;
+
+       tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+       tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE);
+       tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) |
+              NFC_ECC_EXCEPTION;
+
+       writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+
+       for (i = 0; i < ecc->steps; i++) {
+               if (i)
+                       chip->cmdfunc(mtd, NAND_CMD_RNDOUT, i * ecc->size, -1);
+
+               offset = mtd->writesize + layout->eccpos[i * ecc->bytes] - 4;
+
+               chip->read_buf(mtd, NULL, ecc->size);
+
+               chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
+
+               ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+               if (ret)
+                       return ret;
+
+               tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30);
+               writel(tmp, nfc->regs + NFC_REG_CMD);
+
+               ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+               if (ret)
+                       return ret;
+
+               memcpy_fromio(buf + (i * ecc->size),
+                             nfc->regs + NFC_RAM0_BASE, ecc->size);
+
+               if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) {
+                       mtd->ecc_stats.failed++;
+               } else {
+                       tmp = readl(nfc->regs + NFC_REG_ECC_CNT0) & 0xff;
+                       mtd->ecc_stats.corrected += tmp;
+                       max_bitflips = max_t(unsigned int, max_bitflips, tmp);
+               }
+
+               if (oob_required) {
+                       chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
+
+                       ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+                       if (ret)
+                               return ret;
+
+                       offset -= mtd->writesize;
+                       chip->read_buf(mtd, chip->oob_poi + offset,
+                                     ecc->bytes + 4);
+               }
+       }
+
+       if (oob_required) {
+               cnt = ecc->layout->oobfree[ecc->steps].length;
+               if (cnt > 0) {
+                       offset = mtd->writesize +
+                                ecc->layout->oobfree[ecc->steps].offset;
+                       chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
+                       offset -= mtd->writesize;
+                       chip->read_buf(mtd, chip->oob_poi + offset, cnt);
+               }
+       }
+
+       tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+       tmp &= ~NFC_ECC_EN;
+
+       writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+
+       return max_bitflips;
+}
+
+static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,
+                                      struct nand_chip *chip,
+                                      const uint8_t *buf, int oob_required)
+{
+       struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
+       struct nand_ecc_ctrl *ecc = &chip->ecc;
+       struct nand_ecclayout *layout = ecc->layout;
+       struct sunxi_nand_hw_ecc *data = ecc->priv;
+       int offset;
+       int ret;
+       u32 tmp;
+       int i;
+       int cnt;
+
+       tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+       tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE);
+       tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) |
+              NFC_ECC_EXCEPTION;
+
+       writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+
+       for (i = 0; i < ecc->steps; i++) {
+               if (i)
+                       chip->cmdfunc(mtd, NAND_CMD_RNDIN, i * ecc->size, -1);
+
+               chip->write_buf(mtd, buf + (i * ecc->size), ecc->size);
+
+               offset = layout->eccpos[i * ecc->bytes] - 4 + mtd->writesize;
+
+               /* Fill OOB data in */
+               if (oob_required) {
+                       tmp = 0xffffffff;
+                       memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
+                                   4);
+               } else {
+                       memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE,
+                                   chip->oob_poi + offset - mtd->writesize,
+                                   4);
+               }
+
+               chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
+
+               ret = sunxi_nfc_wait_cmd_fifo_empty(nfc);
+               if (ret)
+                       return ret;
+
+               tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR |
+                     (1 << 30);
+               writel(tmp, nfc->regs + NFC_REG_CMD);
+               ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+               if (ret)
+                       return ret;
+       }
+
+       if (oob_required) {
+               cnt = ecc->layout->oobfree[i].length;
+               if (cnt > 0) {
+                       offset = mtd->writesize +
+                                ecc->layout->oobfree[i].offset;
+                       chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
+                       offset -= mtd->writesize;
+                       chip->write_buf(mtd, chip->oob_poi + offset, cnt);
+               }
+       }
+
+       tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+       tmp &= ~NFC_ECC_EN;
+
+       writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+
+       return 0;
+}
+
+static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,
+                                              struct nand_chip *chip,
+                                              uint8_t *buf, int oob_required,
+                                              int page)
+{
+       struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
+       struct nand_ecc_ctrl *ecc = &chip->ecc;
+       struct sunxi_nand_hw_ecc *data = ecc->priv;
+       unsigned int max_bitflips = 0;
+       uint8_t *oob = chip->oob_poi;
+       int offset = 0;
+       int ret;
+       int cnt;
+       u32 tmp;
+       int i;
+
+       tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+       tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE);
+       tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) |
+              NFC_ECC_EXCEPTION;
+
+       writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+
+       for (i = 0; i < ecc->steps; i++) {
+               chip->read_buf(mtd, NULL, ecc->size);
+
+               tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30);
+               writel(tmp, nfc->regs + NFC_REG_CMD);
+
+               ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+               if (ret)
+                       return ret;
+
+               memcpy_fromio(buf, nfc->regs + NFC_RAM0_BASE, ecc->size);
+               buf += ecc->size;
+               offset += ecc->size;
+
+               if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) {
+                       mtd->ecc_stats.failed++;
+               } else {
+                       tmp = readl(nfc->regs + NFC_REG_ECC_CNT0) & 0xff;
+                       mtd->ecc_stats.corrected += tmp;
+                       max_bitflips = max_t(unsigned int, max_bitflips, tmp);
+               }
+
+               if (oob_required) {
+                       chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
+                       chip->read_buf(mtd, oob, ecc->bytes + ecc->prepad);
+                       oob += ecc->bytes + ecc->prepad;
+               }
+
+               offset += ecc->bytes + ecc->prepad;
+       }
+
+       if (oob_required) {
+               cnt = mtd->oobsize - (oob - chip->oob_poi);
+               if (cnt > 0) {
+                       chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
+                       chip->read_buf(mtd, oob, cnt);
+               }
+       }
+
+       writel(readl(nfc->regs + NFC_REG_ECC_CTL) & ~NFC_ECC_EN,
+              nfc->regs + NFC_REG_ECC_CTL);
+
+       return max_bitflips;
+}
+
+static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,
+                                               struct nand_chip *chip,
+                                               const uint8_t *buf,
+                                               int oob_required)
+{
+       struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller);
+       struct nand_ecc_ctrl *ecc = &chip->ecc;
+       struct sunxi_nand_hw_ecc *data = ecc->priv;
+       uint8_t *oob = chip->oob_poi;
+       int offset = 0;
+       int ret;
+       int cnt;
+       u32 tmp;
+       int i;
+
+       tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+       tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE);
+       tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT) |
+              NFC_ECC_EXCEPTION;
+
+       writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+
+       for (i = 0; i < ecc->steps; i++) {
+               chip->write_buf(mtd, buf + (i * ecc->size), ecc->size);
+               offset += ecc->size;
+
+               /* Fill OOB data in */
+               if (oob_required) {
+                       tmp = 0xffffffff;
+                       memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, &tmp,
+                                   4);
+               } else {
+                       memcpy_toio(nfc->regs + NFC_REG_USER_DATA_BASE, oob,
+                                   4);
+               }
+
+               tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ACCESS_DIR |
+                     (1 << 30);
+               writel(tmp, nfc->regs + NFC_REG_CMD);
+
+               ret = sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+               if (ret)
+                       return ret;
+
+               offset += ecc->bytes + ecc->prepad;
+               oob += ecc->bytes + ecc->prepad;
+       }
+
+       if (oob_required) {
+               cnt = mtd->oobsize - (oob - chip->oob_poi);
+               if (cnt > 0) {
+                       chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
+                       chip->write_buf(mtd, oob, cnt);
+               }
+       }
+
+       tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+       tmp &= ~NFC_ECC_EN;
+
+       writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+
+       return 0;
+}
+
+static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
+                                      const struct nand_sdr_timings *timings)
+{
+       u32 min_clk_period = 0;
+
+       /* T1 <=> tCLS */
+       if (timings->tCLS_min > min_clk_period)
+               min_clk_period = timings->tCLS_min;
+
+       /* T2 <=> tCLH */
+       if (timings->tCLH_min > min_clk_period)
+               min_clk_period = timings->tCLH_min;
+
+       /* T3 <=> tCS */
+       if (timings->tCS_min > min_clk_period)
+               min_clk_period = timings->tCS_min;
+
+       /* T4 <=> tCH */
+       if (timings->tCH_min > min_clk_period)
+               min_clk_period = timings->tCH_min;
+
+       /* T5 <=> tWP */
+       if (timings->tWP_min > min_clk_period)
+               min_clk_period = timings->tWP_min;
+
+       /* T6 <=> tWH */
+       if (timings->tWH_min > min_clk_period)
+               min_clk_period = timings->tWH_min;
+
+       /* T7 <=> tALS */
+       if (timings->tALS_min > min_clk_period)
+               min_clk_period = timings->tALS_min;
+
+       /* T8 <=> tDS */
+       if (timings->tDS_min > min_clk_period)
+               min_clk_period = timings->tDS_min;
+
+       /* T9 <=> tDH */
+       if (timings->tDH_min > min_clk_period)
+               min_clk_period = timings->tDH_min;
+
+       /* T10 <=> tRR */
+       if (timings->tRR_min > (min_clk_period * 3))
+               min_clk_period = DIV_ROUND_UP(timings->tRR_min, 3);
+
+       /* T11 <=> tALH */
+       if (timings->tALH_min > min_clk_period)
+               min_clk_period = timings->tALH_min;
+
+       /* T12 <=> tRP */
+       if (timings->tRP_min > min_clk_period)
+               min_clk_period = timings->tRP_min;
+
+       /* T13 <=> tREH */
+       if (timings->tREH_min > min_clk_period)
+               min_clk_period = timings->tREH_min;
+
+       /* T14 <=> tRC */
+       if (timings->tRC_min > (min_clk_period * 2))
+               min_clk_period = DIV_ROUND_UP(timings->tRC_min, 2);
+
+       /* T15 <=> tWC */
+       if (timings->tWC_min > (min_clk_period * 2))
+               min_clk_period = DIV_ROUND_UP(timings->tWC_min, 2);
+
+
+       /* Convert min_clk_period from picoseconds to nanoseconds */
+       min_clk_period = DIV_ROUND_UP(min_clk_period, 1000);
+
+       /*
+        * Convert min_clk_period into a clk frequency, then get the
+        * appropriate rate for the NAND controller IP given this formula
+        * (specified in the datasheet):
+        * nand clk_rate = 2 * min_clk_rate
+        */
+       chip->clk_rate = (2 * NSEC_PER_SEC) / min_clk_period;
+
+       /* TODO: configure T16-T19 */
+
+       return 0;
+}
+
+static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip,
+                                       struct device_node *np)
+{
+       const struct nand_sdr_timings *timings;
+       int ret;
+       int mode;
+
+       mode = onfi_get_async_timing_mode(&chip->nand);
+       if (mode == ONFI_TIMING_MODE_UNKNOWN) {
+               mode = chip->nand.onfi_timing_mode_default;
+       } else {
+               uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {};
+
+               mode = fls(mode) - 1;
+               if (mode < 0)
+                       mode = 0;
+
+               feature[0] = mode;
+               ret = chip->nand.onfi_set_features(&chip->mtd, &chip->nand,
+                                               ONFI_FEATURE_ADDR_TIMING_MODE,
+                                               feature);
+               if (ret)
+                       return ret;
+       }
+
+       timings = onfi_async_timing_mode_to_sdr_timings(mode);
+       if (IS_ERR(timings))
+               return PTR_ERR(timings);
+
+       return sunxi_nand_chip_set_timings(chip, timings);
+}
+
+static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd,
+                                             struct nand_ecc_ctrl *ecc,
+                                             struct device_node *np)
+{
+       static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
+       struct nand_chip *nand = mtd->priv;
+       struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand);
+       struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+       struct sunxi_nand_hw_ecc *data;
+       struct nand_ecclayout *layout;
+       int nsectors;
+       int ret;
+       int i;
+
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       /* Add ECC info retrieval from DT */
+       for (i = 0; i < ARRAY_SIZE(strengths); i++) {
+               if (ecc->strength <= strengths[i])
+                       break;
+       }
+
+       if (i >= ARRAY_SIZE(strengths)) {
+               dev_err(nfc->dev, "unsupported strength\n");
+               ret = -ENOTSUPP;
+               goto err;
+       }
+
+       data->mode = i;
+
+       /* HW ECC always request ECC bytes for 1024 bytes blocks */
+       ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * 1024), 8);
+
+       /* HW ECC always work with even numbers of ECC bytes */
+       ecc->bytes = ALIGN(ecc->bytes, 2);
+
+       layout = &data->layout;
+       nsectors = mtd->writesize / ecc->size;
+
+       if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) {
+               ret = -EINVAL;
+               goto err;
+       }
+
+       layout->eccbytes = (ecc->bytes * nsectors);
+
+       ecc->layout = layout;
+       ecc->priv = data;
+
+       return 0;
+
+err:
+       kfree(data);
+
+       return ret;
+}
+
+static void sunxi_nand_hw_common_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc)
+{
+       kfree(ecc->priv);
+}
+
+static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd,
+                                      struct nand_ecc_ctrl *ecc,
+                                      struct device_node *np)
+{
+       struct nand_ecclayout *layout;
+       int nsectors;
+       int i, j;
+       int ret;
+
+       ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np);
+       if (ret)
+               return ret;
+
+       ecc->read_page = sunxi_nfc_hw_ecc_read_page;
+       ecc->write_page = sunxi_nfc_hw_ecc_write_page;
+       layout = ecc->layout;
+       nsectors = mtd->writesize / ecc->size;
+
+       for (i = 0; i < nsectors; i++) {
+               if (i) {
+                       layout->oobfree[i].offset =
+                               layout->oobfree[i - 1].offset +
+                               layout->oobfree[i - 1].length +
+                               ecc->bytes;
+                       layout->oobfree[i].length = 4;
+               } else {
+                       /*
+                        * The first 2 bytes are used for BB markers, hence we
+                        * only have 2 bytes available in the first user data
+                        * section.
+                        */
+                       layout->oobfree[i].length = 2;
+                       layout->oobfree[i].offset = 2;
+               }
+
+               for (j = 0; j < ecc->bytes; j++)
+                       layout->eccpos[(ecc->bytes * i) + j] =
+                                       layout->oobfree[i].offset +
+                                       layout->oobfree[i].length + j;
+       }
+
+       if (mtd->oobsize > (ecc->bytes + 4) * nsectors) {
+               layout->oobfree[nsectors].offset =
+                               layout->oobfree[nsectors - 1].offset +
+                               layout->oobfree[nsectors - 1].length +
+                               ecc->bytes;
+               layout->oobfree[nsectors].length = mtd->oobsize -
+                               ((ecc->bytes + 4) * nsectors);
+       }
+
+       return 0;
+}
+
+static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd,
+                                               struct nand_ecc_ctrl *ecc,
+                                               struct device_node *np)
+{
+       struct nand_ecclayout *layout;
+       int nsectors;
+       int i;
+       int ret;
+
+       ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc, np);
+       if (ret)
+               return ret;
+
+       ecc->prepad = 4;
+       ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page;
+       ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page;
+
+       layout = ecc->layout;
+       nsectors = mtd->writesize / ecc->size;
+
+       for (i = 0; i < (ecc->bytes * nsectors); i++)
+               layout->eccpos[i] = i;
+
+       layout->oobfree[0].length = mtd->oobsize - i;
+       layout->oobfree[0].offset = i;
+
+       return 0;
+}
+
+static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc)
+{
+       switch (ecc->mode) {
+       case NAND_ECC_HW:
+       case NAND_ECC_HW_SYNDROME:
+               sunxi_nand_hw_common_ecc_ctrl_cleanup(ecc);
+               break;
+       case NAND_ECC_NONE:
+               kfree(ecc->layout);
+       default:
+               break;
+       }
+}
+
+static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
+                              struct device_node *np)
+{
+       struct nand_chip *nand = mtd->priv;
+       int strength;
+       int blk_size;
+       int ret;
+
+       blk_size = of_get_nand_ecc_step_size(np);
+       strength = of_get_nand_ecc_strength(np);
+       if (blk_size > 0 && strength > 0) {
+               ecc->size = blk_size;
+               ecc->strength = strength;
+       } else {
+               ecc->size = nand->ecc_step_ds;
+               ecc->strength = nand->ecc_strength_ds;
+       }
+
+       if (!ecc->size || !ecc->strength)
+               return -EINVAL;
+
+       ecc->mode = NAND_ECC_HW;
+
+       ret = of_get_nand_ecc_mode(np);
+       if (ret >= 0)
+               ecc->mode = ret;
+
+       switch (ecc->mode) {
+       case NAND_ECC_SOFT_BCH:
+               ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * ecc->size),
+                                         8);
+               break;
+       case NAND_ECC_HW:
+               ret = sunxi_nand_hw_ecc_ctrl_init(mtd, ecc, np);
+               if (ret)
+                       return ret;
+               break;
+       case NAND_ECC_HW_SYNDROME:
+               ret = sunxi_nand_hw_syndrome_ecc_ctrl_init(mtd, ecc, np);
+               if (ret)
+                       return ret;
+               break;
+       case NAND_ECC_NONE:
+               ecc->layout = kzalloc(sizeof(*ecc->layout), GFP_KERNEL);
+               if (!ecc->layout)
+                       return -ENOMEM;
+               ecc->layout->oobfree[0].length = mtd->oobsize;
+       case NAND_ECC_SOFT:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
+                               struct device_node *np)
+{
+       const struct nand_sdr_timings *timings;
+       struct sunxi_nand_chip *chip;
+       struct mtd_part_parser_data ppdata;
+       struct mtd_info *mtd;
+       struct nand_chip *nand;
+       int nsels;
+       int ret;
+       int i;
+       u32 tmp;
+
+       if (!of_get_property(np, "reg", &nsels))
+               return -EINVAL;
+
+       nsels /= sizeof(u32);
+       if (!nsels) {
+               dev_err(dev, "invalid reg property size\n");
+               return -EINVAL;
+       }
+
+       chip = devm_kzalloc(dev,
+                           sizeof(*chip) +
+                           (nsels * sizeof(struct sunxi_nand_chip_sel)),
+                           GFP_KERNEL);
+       if (!chip) {
+               dev_err(dev, "could not allocate chip\n");
+               return -ENOMEM;
+       }
+
+       chip->nsels = nsels;
+       chip->selected = -1;
+
+       for (i = 0; i < nsels; i++) {
+               ret = of_property_read_u32_index(np, "reg", i, &tmp);
+               if (ret) {
+                       dev_err(dev, "could not retrieve reg property: %d\n",
+                               ret);
+                       return ret;
+               }
+
+               if (tmp > NFC_MAX_CS) {
+                       dev_err(dev,
+                               "invalid reg value: %u (max CS = 7)\n",
+                               tmp);
+                       return -EINVAL;
+               }
+
+               if (test_and_set_bit(tmp, &nfc->assigned_cs)) {
+                       dev_err(dev, "CS %d already assigned\n", tmp);
+                       return -EINVAL;
+               }
+
+               chip->sels[i].cs = tmp;
+
+               if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) &&
+                   tmp < 2) {
+                       chip->sels[i].rb.type = RB_NATIVE;
+                       chip->sels[i].rb.info.nativeid = tmp;
+               } else {
+                       ret = of_get_named_gpio(np, "rb-gpios", i);
+                       if (ret >= 0) {
+                               tmp = ret;
+                               chip->sels[i].rb.type = RB_GPIO;
+                               chip->sels[i].rb.info.gpio = tmp;
+                               ret = devm_gpio_request(dev, tmp, "nand-rb");
+                               if (ret)
+                                       return ret;
+
+                               ret = gpio_direction_input(tmp);
+                               if (ret)
+                                       return ret;
+                       } else {
+                               chip->sels[i].rb.type = RB_NONE;
+                       }
+               }
+       }
+
+       timings = onfi_async_timing_mode_to_sdr_timings(0);
+       if (IS_ERR(timings)) {
+               ret = PTR_ERR(timings);
+               dev_err(dev,
+                       "could not retrieve timings for ONFI mode 0: %d\n",
+                       ret);
+               return ret;
+       }
+
+       ret = sunxi_nand_chip_set_timings(chip, timings);
+       if (ret) {
+               dev_err(dev, "could not configure chip timings: %d\n", ret);
+               return ret;
+       }
+
+       nand = &chip->nand;
+       /* Default tR value specified in the ONFI spec (chapter 4.15.1) */
+       nand->chip_delay = 200;
+       nand->controller = &nfc->controller;
+       nand->select_chip = sunxi_nfc_select_chip;
+       nand->cmd_ctrl = sunxi_nfc_cmd_ctrl;
+       nand->read_buf = sunxi_nfc_read_buf;
+       nand->write_buf = sunxi_nfc_write_buf;
+       nand->read_byte = sunxi_nfc_read_byte;
+
+       if (of_get_nand_on_flash_bbt(np))
+               nand->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
+
+       mtd = &chip->mtd;
+       mtd->dev.parent = dev;
+       mtd->priv = nand;
+       mtd->owner = THIS_MODULE;
+
+       ret = nand_scan_ident(mtd, nsels, NULL);
+       if (ret)
+               return ret;
+
+       ret = sunxi_nand_chip_init_timings(chip, np);
+       if (ret) {
+               dev_err(dev, "could not configure chip timings: %d\n", ret);
+               return ret;
+       }
+
+       ret = sunxi_nand_ecc_init(mtd, &nand->ecc, np);
+       if (ret) {
+               dev_err(dev, "ECC init failed: %d\n", ret);
+               return ret;
+       }
+
+       ret = nand_scan_tail(mtd);
+       if (ret) {
+               dev_err(dev, "nand_scan_tail failed: %d\n", ret);
+               return ret;
+       }
+
+       ppdata.of_node = np;
+       ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+       if (ret) {
+               dev_err(dev, "failed to register mtd device: %d\n", ret);
+               nand_release(mtd);
+               return ret;
+       }
+
+       list_add_tail(&chip->node, &nfc->chips);
+
+       return 0;
+}
+
+static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc)
+{
+       struct device_node *np = dev->of_node;
+       struct device_node *nand_np;
+       int nchips = of_get_child_count(np);
+       int ret;
+
+       if (nchips > 8) {
+               dev_err(dev, "too many NAND chips: %d (max = 8)\n", nchips);
+               return -EINVAL;
+       }
+
+       for_each_child_of_node(np, nand_np) {
+               ret = sunxi_nand_chip_init(dev, nfc, nand_np);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc)
+{
+       struct sunxi_nand_chip *chip;
+
+       while (!list_empty(&nfc->chips)) {
+               chip = list_first_entry(&nfc->chips, struct sunxi_nand_chip,
+                                       node);
+               nand_release(&chip->mtd);
+               sunxi_nand_ecc_cleanup(&chip->nand.ecc);
+       }
+}
+
+static int sunxi_nfc_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct resource *r;
+       struct sunxi_nfc *nfc;
+       int irq;
+       int ret;
+
+       nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
+       if (!nfc)
+               return -ENOMEM;
+
+       nfc->dev = dev;
+       spin_lock_init(&nfc->controller.lock);
+       init_waitqueue_head(&nfc->controller.wq);
+       INIT_LIST_HEAD(&nfc->chips);
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       nfc->regs = devm_ioremap_resource(dev, r);
+       if (IS_ERR(nfc->regs))
+               return PTR_ERR(nfc->regs);
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(dev, "failed to retrieve irq\n");
+               return irq;
+       }
+
+       nfc->ahb_clk = devm_clk_get(dev, "ahb");
+       if (IS_ERR(nfc->ahb_clk)) {
+               dev_err(dev, "failed to retrieve ahb clk\n");
+               return PTR_ERR(nfc->ahb_clk);
+       }
+
+       ret = clk_prepare_enable(nfc->ahb_clk);
+       if (ret)
+               return ret;
+
+       nfc->mod_clk = devm_clk_get(dev, "mod");
+       if (IS_ERR(nfc->mod_clk)) {
+               dev_err(dev, "failed to retrieve mod clk\n");
+               ret = PTR_ERR(nfc->mod_clk);
+               goto out_ahb_clk_unprepare;
+       }
+
+       ret = clk_prepare_enable(nfc->mod_clk);
+       if (ret)
+               goto out_ahb_clk_unprepare;
+
+       ret = sunxi_nfc_rst(nfc);
+       if (ret)
+               goto out_mod_clk_unprepare;
+
+       writel(0, nfc->regs + NFC_REG_INT);
+       ret = devm_request_irq(dev, irq, sunxi_nfc_interrupt,
+                              0, "sunxi-nand", nfc);
+       if (ret)
+               goto out_mod_clk_unprepare;
+
+       platform_set_drvdata(pdev, nfc);
+
+       /*
+        * TODO: replace these magic values with proper flags as soon as we
+        * know what they are encoding.
+        */
+       writel(0x100, nfc->regs + NFC_REG_TIMING_CTL);
+       writel(0x7ff, nfc->regs + NFC_REG_TIMING_CFG);
+
+       ret = sunxi_nand_chips_init(dev, nfc);
+       if (ret) {
+               dev_err(dev, "failed to init nand chips\n");
+               goto out_mod_clk_unprepare;
+       }
+
+       return 0;
+
+out_mod_clk_unprepare:
+       clk_disable_unprepare(nfc->mod_clk);
+out_ahb_clk_unprepare:
+       clk_disable_unprepare(nfc->ahb_clk);
+
+       return ret;
+}
+
+static int sunxi_nfc_remove(struct platform_device *pdev)
+{
+       struct sunxi_nfc *nfc = platform_get_drvdata(pdev);
+
+       sunxi_nand_chips_cleanup(nfc);
+
+       return 0;
+}
+
+static const struct of_device_id sunxi_nfc_ids[] = {
+       { .compatible = "allwinner,sun4i-a10-nand" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sunxi_nfc_ids);
+
+static struct platform_driver sunxi_nfc_driver = {
+       .driver = {
+               .name = "sunxi_nand",
+               .of_match_table = sunxi_nfc_ids,
+       },
+       .probe = sunxi_nfc_probe,
+       .remove = sunxi_nfc_remove,
+};
+module_platform_driver(sunxi_nfc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Boris BREZILLON");
+MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver");
+MODULE_ALIAS("platform:sunxi_nand");
index 2fb07ec..39763b9 100644 (file)
@@ -719,16 +719,10 @@ static int fsl_qspi_read(struct spi_nor *nor, loff_t from,
 {
        struct fsl_qspi *q = nor->priv;
        u8 cmd = nor->read_opcode;
-       int ret;
 
        dev_dbg(q->dev, "cmd [%x],read from (0x%p, 0x%.8x, 0x%.8x),len:%d\n",
                cmd, q->ahb_base, q->chip_base_addr, (unsigned int)from, len);
 
-       /* Wait until the previous command is finished. */
-       ret = nor->wait_till_ready(nor);
-       if (ret)
-               return ret;
-
        /* Read out the data directly from the AHB buffer.*/
        memcpy(buf, q->ahb_base + q->chip_base_addr + from, len);
 
@@ -744,16 +738,6 @@ static int fsl_qspi_erase(struct spi_nor *nor, loff_t offs)
        dev_dbg(nor->dev, "%dKiB at 0x%08x:0x%08x\n",
                nor->mtd->erasesize / 1024, q->chip_base_addr, (u32)offs);
 
-       /* Wait until finished previous write command. */
-       ret = nor->wait_till_ready(nor);
-       if (ret)
-               return ret;
-
-       /* Send write enable, then erase commands. */
-       ret = nor->write_reg(nor, SPINOR_OP_WREN, NULL, 0, 0);
-       if (ret)
-               return ret;
-
        ret = fsl_qspi_runcmd(q, nor->erase_opcode, offs, 0);
        if (ret)
                return ret;
@@ -849,9 +833,8 @@ static int fsl_qspi_probe(struct platform_device *pdev)
 
        ret = clk_prepare_enable(q->clk);
        if (ret) {
-               clk_disable_unprepare(q->clk_en);
                dev_err(dev, "can not enable the qspi clock\n");
-               goto map_failed;
+               goto clk_failed;
        }
 
        /* find the irq */
@@ -905,7 +888,8 @@ static int fsl_qspi_probe(struct platform_device *pdev)
                nor->prepare = fsl_qspi_prep;
                nor->unprepare = fsl_qspi_unprep;
 
-               if (of_modalias_node(np, modalias, sizeof(modalias)) < 0)
+               ret = of_modalias_node(np, modalias, sizeof(modalias));
+               if (ret < 0)
                        goto map_failed;
 
                ret = of_property_read_u32(np, "spi-max-frequency",
@@ -964,6 +948,7 @@ last_init_failed:
 
 irq_failed:
        clk_disable_unprepare(q->clk);
+clk_failed:
        clk_disable_unprepare(q->clk_en);
 map_failed:
        dev_err(dev, "Freescale QuadSPI probe failed\n");
index c51ee52..0f8ec3c 100644 (file)
 /* Define max times to check status register before we give up. */
 #define        MAX_READY_WAIT_JIFFIES  (40 * HZ) /* M25P16 specs 40s max chip erase */
 
-#define JEDEC_MFR(_jedec_id)   ((_jedec_id) >> 16)
+#define SPI_NOR_MAX_ID_LEN     6
+
+struct flash_info {
+       /*
+        * This array stores the ID bytes.
+        * The first three bytes are the JEDIC ID.
+        * JEDEC ID zero means "no ID" (mostly older chips).
+        */
+       u8              id[SPI_NOR_MAX_ID_LEN];
+       u8              id_len;
+
+       /* The size listed here is what works with SPINOR_OP_SE, which isn't
+        * necessarily called a "sector" by the vendor.
+        */
+       unsigned        sector_size;
+       u16             n_sectors;
+
+       u16             page_size;
+       u16             addr_width;
+
+       u16             flags;
+#define        SECT_4K                 0x01    /* SPINOR_OP_BE_4K works uniformly */
+#define        SPI_NOR_NO_ERASE        0x02    /* No erase command needed */
+#define        SST_WRITE               0x04    /* use SST byte programming */
+#define        SPI_NOR_NO_FR           0x08    /* Can't do fastread */
+#define        SECT_4K_PMC             0x10    /* SPINOR_OP_BE_4K_PMC works uniformly */
+#define        SPI_NOR_DUAL_READ       0x20    /* Flash supports Dual Read */
+#define        SPI_NOR_QUAD_READ       0x40    /* Flash supports Quad Read */
+#define        USE_FSR                 0x80    /* use flag status register */
+};
+
+#define JEDEC_MFR(info)        ((info)->id[0])
 
 static const struct spi_device_id *spi_nor_match_id(const char *name);
 
@@ -98,7 +129,7 @@ static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
        case SPI_NOR_FAST:
        case SPI_NOR_DUAL:
        case SPI_NOR_QUAD:
-               return 1;
+               return 8;
        case SPI_NOR_NORMAL:
                return 0;
        }
@@ -138,13 +169,14 @@ static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
 }
 
 /* Enable/disable 4-byte addressing mode. */
-static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable)
+static inline int set_4byte(struct spi_nor *nor, struct flash_info *info,
+                           int enable)
 {
        int status;
        bool need_wren = false;
        u8 cmd;
 
-       switch (JEDEC_MFR(jedec_id)) {
+       switch (JEDEC_MFR(info)) {
        case CFI_MFR_ST: /* Micron, actually */
                /* Some Micron need WREN command; all will accept it */
                need_wren = true;
@@ -165,62 +197,65 @@ static inline int set_4byte(struct spi_nor *nor, u32 jedec_id, int enable)
                return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1, 0);
        }
 }
-
-static int spi_nor_wait_till_ready(struct spi_nor *nor)
+static inline int spi_nor_sr_ready(struct spi_nor *nor)
 {
-       unsigned long deadline;
-       int sr;
-
-       deadline = jiffies + MAX_READY_WAIT_JIFFIES;
-
-       do {
-               cond_resched();
+       int sr = read_sr(nor);
+       if (sr < 0)
+               return sr;
+       else
+               return !(sr & SR_WIP);
+}
 
-               sr = read_sr(nor);
-               if (sr < 0)
-                       break;
-               else if (!(sr & SR_WIP))
-                       return 0;
-       } while (!time_after_eq(jiffies, deadline));
+static inline int spi_nor_fsr_ready(struct spi_nor *nor)
+{
+       int fsr = read_fsr(nor);
+       if (fsr < 0)
+               return fsr;
+       else
+               return fsr & FSR_READY;
+}
 
-       return -ETIMEDOUT;
+static int spi_nor_ready(struct spi_nor *nor)
+{
+       int sr, fsr;
+       sr = spi_nor_sr_ready(nor);
+       if (sr < 0)
+               return sr;
+       fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1;
+       if (fsr < 0)
+               return fsr;
+       return sr && fsr;
 }
 
-static int spi_nor_wait_till_fsr_ready(struct spi_nor *nor)
+/*
+ * Service routine to read status register until ready, or timeout occurs.
+ * Returns non-zero if error.
+ */
+static int spi_nor_wait_till_ready(struct spi_nor *nor)
 {
        unsigned long deadline;
-       int sr;
-       int fsr;
+       int timeout = 0, ret;
 
        deadline = jiffies + MAX_READY_WAIT_JIFFIES;
 
-       do {
+       while (!timeout) {
+               if (time_after_eq(jiffies, deadline))
+                       timeout = 1;
+
+               ret = spi_nor_ready(nor);
+               if (ret < 0)
+                       return ret;
+               if (ret)
+                       return 0;
+
                cond_resched();
+       }
 
-               sr = read_sr(nor);
-               if (sr < 0) {
-                       break;
-               } else if (!(sr & SR_WIP)) {
-                       fsr = read_fsr(nor);
-                       if (fsr < 0)
-                               break;
-                       if (fsr & FSR_READY)
-                               return 0;
-               }
-       } while (!time_after_eq(jiffies, deadline));
+       dev_err(nor->dev, "flash operation timed out\n");
 
        return -ETIMEDOUT;
 }
 
-/*
- * Service routine to read status register until ready, or timeout occurs.
- * Returns non-zero if error.
- */
-static int wait_till_ready(struct spi_nor *nor)
-{
-       return nor->wait_till_ready(nor);
-}
-
 /*
  * Erase the whole flash memory
  *
@@ -228,18 +263,8 @@ static int wait_till_ready(struct spi_nor *nor)
  */
 static int erase_chip(struct spi_nor *nor)
 {
-       int ret;
-
        dev_dbg(nor->dev, " %lldKiB\n", (long long)(nor->mtd->size >> 10));
 
-       /* Wait until finished previous write command. */
-       ret = wait_till_ready(nor);
-       if (ret)
-               return ret;
-
-       /* Send write enable, then erase commands. */
-       write_enable(nor);
-
        return nor->write_reg(nor, SPINOR_OP_CHIP_ERASE, NULL, 0, 0);
 }
 
@@ -294,11 +319,17 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 
        /* whole-chip erase? */
        if (len == mtd->size) {
+               write_enable(nor);
+
                if (erase_chip(nor)) {
                        ret = -EIO;
                        goto erase_err;
                }
 
+               ret = spi_nor_wait_till_ready(nor);
+               if (ret)
+                       goto erase_err;
+
        /* REVISIT in some cases we could speed up erasing large regions
         * by using SPINOR_OP_SE instead of SPINOR_OP_BE_4K.  We may have set up
         * to use "small sector erase", but that's not always optimal.
@@ -307,6 +338,8 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
        /* "sector"-at-a-time erase */
        } else {
                while (len) {
+                       write_enable(nor);
+
                        if (nor->erase(nor, addr)) {
                                ret = -EIO;
                                goto erase_err;
@@ -314,9 +347,15 @@ static int spi_nor_erase(struct mtd_info *mtd, struct erase_info *instr)
 
                        addr += mtd->erasesize;
                        len -= mtd->erasesize;
+
+                       ret = spi_nor_wait_till_ready(nor);
+                       if (ret)
+                               goto erase_err;
                }
        }
 
+       write_disable(nor);
+
        spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
 
        instr->state = MTD_ERASE_DONE;
@@ -341,11 +380,6 @@ static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
        if (ret)
                return ret;
 
-       /* Wait until finished previous command */
-       ret = wait_till_ready(nor);
-       if (ret)
-               goto err;
-
        status_old = read_sr(nor);
 
        if (offset < mtd->size - (mtd->size / 2))
@@ -388,11 +422,6 @@ static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
        if (ret)
                return ret;
 
-       /* Wait until finished previous command */
-       ret = wait_till_ready(nor);
-       if (ret)
-               goto err;
-
        status_old = read_sr(nor);
 
        if (offset+len > mtd->size - (mtd->size / 64))
@@ -424,38 +453,34 @@ err:
        return ret;
 }
 
-struct flash_info {
-       /* JEDEC id zero means "no ID" (most older chips); otherwise it has
-        * a high byte of zero plus three data bytes: the manufacturer id,
-        * then a two byte device id.
-        */
-       u32             jedec_id;
-       u16             ext_id;
-
-       /* The size listed here is what works with SPINOR_OP_SE, which isn't
-        * necessarily called a "sector" by the vendor.
-        */
-       unsigned        sector_size;
-       u16             n_sectors;
-
-       u16             page_size;
-       u16             addr_width;
-
-       u16             flags;
-#define        SECT_4K                 0x01    /* SPINOR_OP_BE_4K works uniformly */
-#define        SPI_NOR_NO_ERASE        0x02    /* No erase command needed */
-#define        SST_WRITE               0x04    /* use SST byte programming */
-#define        SPI_NOR_NO_FR           0x08    /* Can't do fastread */
-#define        SECT_4K_PMC             0x10    /* SPINOR_OP_BE_4K_PMC works uniformly */
-#define        SPI_NOR_DUAL_READ       0x20    /* Flash supports Dual Read */
-#define        SPI_NOR_QUAD_READ       0x40    /* Flash supports Quad Read */
-#define        USE_FSR                 0x80    /* use flag status register */
-};
-
+/* Used when the "_ext_id" is two bytes at most */
 #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)     \
        ((kernel_ulong_t)&(struct flash_info) {                         \
-               .jedec_id = (_jedec_id),                                \
-               .ext_id = (_ext_id),                                    \
+               .id = {                                                 \
+                       ((_jedec_id) >> 16) & 0xff,                     \
+                       ((_jedec_id) >> 8) & 0xff,                      \
+                       (_jedec_id) & 0xff,                             \
+                       ((_ext_id) >> 8) & 0xff,                        \
+                       (_ext_id) & 0xff,                               \
+                       },                                              \
+               .id_len = (!(_jedec_id) ? 0 : (3 + ((_ext_id) ? 2 : 0))),       \
+               .sector_size = (_sector_size),                          \
+               .n_sectors = (_n_sectors),                              \
+               .page_size = 256,                                       \
+               .flags = (_flags),                                      \
+       })
+
+#define INFO6(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)    \
+       ((kernel_ulong_t)&(struct flash_info) {                         \
+               .id = {                                                 \
+                       ((_jedec_id) >> 16) & 0xff,                     \
+                       ((_jedec_id) >> 8) & 0xff,                      \
+                       (_jedec_id) & 0xff,                             \
+                       ((_ext_id) >> 16) & 0xff,                       \
+                       ((_ext_id) >> 8) & 0xff,                        \
+                       (_ext_id) & 0xff,                               \
+                       },                                              \
+               .id_len = 6,                                            \
                .sector_size = (_sector_size),                          \
                .n_sectors = (_n_sectors),                              \
                .page_size = 256,                                       \
@@ -507,6 +532,9 @@ static const struct spi_device_id spi_nor_ids[] = {
        { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
        { "mr25h10",  CAT25_INFO(128 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
 
+       /* Fujitsu */
+       { "mb85rs1mt", INFO(0x047f27, 0, 128 * 1024, 1, SPI_NOR_NO_ERASE) },
+
        /* GigaDevice */
        { "gd25q32", INFO(0xc84016, 0, 64 * 1024,  64, SECT_4K) },
        { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) },
@@ -532,6 +560,7 @@ static const struct spi_device_id spi_nor_ids[] = {
        { "mx66l1g55g",  INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
 
        /* Micron */
+       { "n25q032",     INFO(0x20ba16, 0, 64 * 1024,   64, 0) },
        { "n25q064",     INFO(0x20ba17, 0, 64 * 1024,  128, 0) },
        { "n25q128a11",  INFO(0x20bb18, 0, 64 * 1024,  256, 0) },
        { "n25q128a13",  INFO(0x20ba18, 0, 64 * 1024,  256, 0) },
@@ -556,6 +585,7 @@ static const struct spi_device_id spi_nor_ids[] = {
        { "s70fl01gs",  INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
        { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024,  64, 0) },
        { "s25sl12801", INFO(0x012018, 0x0301,  64 * 1024, 256, 0) },
+       { "s25fl128s",  INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_QUAD_READ) },
        { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024,  64, 0) },
        { "s25fl129p1", INFO(0x012018, 0x4d01,  64 * 1024, 256, 0) },
        { "s25sl004a",  INFO(0x010212,      0,  64 * 1024,   8, 0) },
@@ -566,6 +596,7 @@ static const struct spi_device_id spi_nor_ids[] = {
        { "s25fl008k",  INFO(0xef4014,      0,  64 * 1024,  16, SECT_4K) },
        { "s25fl016k",  INFO(0xef4015,      0,  64 * 1024,  32, SECT_4K) },
        { "s25fl064k",  INFO(0xef4017,      0,  64 * 1024, 128, SECT_4K) },
+       { "s25fl132k",  INFO(0x014016,      0,  64 * 1024,  64, 0) },
 
        /* SST -- large erase sizes are "overlays", "sectors" are 4K */
        { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) },
@@ -577,6 +608,7 @@ static const struct spi_device_id spi_nor_ids[] = {
        { "sst25wf010",  INFO(0xbf2502, 0, 64 * 1024,  2, SECT_4K | SST_WRITE) },
        { "sst25wf020",  INFO(0xbf2503, 0, 64 * 1024,  4, SECT_4K | SST_WRITE) },
        { "sst25wf040",  INFO(0xbf2504, 0, 64 * 1024,  8, SECT_4K | SST_WRITE) },
+       { "sst25wf080",  INFO(0xbf2505, 0, 64 * 1024, 16, SECT_4K | SST_WRITE) },
 
        /* ST Microelectronics -- newer production may have feature updates */
        { "m25p05",  INFO(0x202010,  0,  32 * 1024,   2, 0) },
@@ -588,7 +620,6 @@ static const struct spi_device_id spi_nor_ids[] = {
        { "m25p32",  INFO(0x202016,  0,  64 * 1024,  64, 0) },
        { "m25p64",  INFO(0x202017,  0,  64 * 1024, 128, 0) },
        { "m25p128", INFO(0x202018,  0, 256 * 1024,  64, 0) },
-       { "n25q032", INFO(0x20ba16,  0,  64 * 1024,  64, 0) },
 
        { "m25p05-nonjedec",  INFO(0, 0,  32 * 1024,   2, 0) },
        { "m25p10-nonjedec",  INFO(0, 0,  32 * 1024,   4, 0) },
@@ -643,32 +674,24 @@ static const struct spi_device_id spi_nor_ids[] = {
 static const struct spi_device_id *spi_nor_read_id(struct spi_nor *nor)
 {
        int                     tmp;
-       u8                      id[5];
-       u32                     jedec;
-       u16                     ext_jedec;
+       u8                      id[SPI_NOR_MAX_ID_LEN];
        struct flash_info       *info;
 
-       tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, 5);
+       tmp = nor->read_reg(nor, SPINOR_OP_RDID, id, SPI_NOR_MAX_ID_LEN);
        if (tmp < 0) {
                dev_dbg(nor->dev, " error %d reading JEDEC ID\n", tmp);
                return ERR_PTR(tmp);
        }
-       jedec = id[0];
-       jedec = jedec << 8;
-       jedec |= id[1];
-       jedec = jedec << 8;
-       jedec |= id[2];
-
-       ext_jedec = id[3] << 8 | id[4];
 
        for (tmp = 0; tmp < ARRAY_SIZE(spi_nor_ids) - 1; tmp++) {
                info = (void *)spi_nor_ids[tmp].driver_data;
-               if (info->jedec_id == jedec) {
-                       if (info->ext_id == 0 || info->ext_id == ext_jedec)
+               if (info->id_len) {
+                       if (!memcmp(info->id, id, info->id_len))
                                return &spi_nor_ids[tmp];
                }
        }
-       dev_err(nor->dev, "unrecognized JEDEC id %06x\n", jedec);
+       dev_err(nor->dev, "unrecognized JEDEC id bytes: %02x, %2x, %2x\n",
+               id[0], id[1], id[2]);
        return ERR_PTR(-ENODEV);
 }
 
@@ -703,11 +726,6 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
        if (ret)
                return ret;
 
-       /* Wait until finished previous write command. */
-       ret = wait_till_ready(nor);
-       if (ret)
-               goto time_out;
-
        write_enable(nor);
 
        nor->sst_write_second = false;
@@ -719,7 +737,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
 
                /* write one byte. */
                nor->write(nor, to, 1, retlen, buf);
-               ret = wait_till_ready(nor);
+               ret = spi_nor_wait_till_ready(nor);
                if (ret)
                        goto time_out;
        }
@@ -731,7 +749,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
 
                /* write two bytes. */
                nor->write(nor, to, 2, retlen, buf + actual);
-               ret = wait_till_ready(nor);
+               ret = spi_nor_wait_till_ready(nor);
                if (ret)
                        goto time_out;
                to += 2;
@@ -740,7 +758,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
        nor->sst_write_second = false;
 
        write_disable(nor);
-       ret = wait_till_ready(nor);
+       ret = spi_nor_wait_till_ready(nor);
        if (ret)
                goto time_out;
 
@@ -751,7 +769,7 @@ static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
                nor->program_opcode = SPINOR_OP_BP;
                nor->write(nor, to, 1, retlen, buf + actual);
 
-               ret = wait_till_ready(nor);
+               ret = spi_nor_wait_till_ready(nor);
                if (ret)
                        goto time_out;
                write_disable(nor);
@@ -779,11 +797,6 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
        if (ret)
                return ret;
 
-       /* Wait until finished previous write command. */
-       ret = wait_till_ready(nor);
-       if (ret)
-               goto write_err;
-
        write_enable(nor);
 
        page_offset = to & (nor->page_size - 1);
@@ -802,16 +815,20 @@ static int spi_nor_write(struct mtd_info *mtd, loff_t to, size_t len,
                        if (page_size > nor->page_size)
                                page_size = nor->page_size;
 
-                       wait_till_ready(nor);
+                       ret = spi_nor_wait_till_ready(nor);
+                       if (ret)
+                               goto write_err;
+
                        write_enable(nor);
 
                        nor->write(nor, to + i, page_size, retlen, buf + i);
                }
        }
 
+       ret = spi_nor_wait_till_ready(nor);
 write_err:
        spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
-       return 0;
+       return ret;
 }
 
 static int macronix_quad_enable(struct spi_nor *nor)
@@ -824,7 +841,7 @@ static int macronix_quad_enable(struct spi_nor *nor)
        nor->cmd_buf[0] = val | SR_QUAD_EN_MX;
        nor->write_reg(nor, SPINOR_OP_WRSR, nor->cmd_buf, 1, 0);
 
-       if (wait_till_ready(nor))
+       if (spi_nor_wait_till_ready(nor))
                return 1;
 
        ret = read_sr(nor);
@@ -874,11 +891,11 @@ static int spansion_quad_enable(struct spi_nor *nor)
        return 0;
 }
 
-static int set_quad_mode(struct spi_nor *nor, u32 jedec_id)
+static int set_quad_mode(struct spi_nor *nor, struct flash_info *info)
 {
        int status;
 
-       switch (JEDEC_MFR(jedec_id)) {
+       switch (JEDEC_MFR(info)) {
        case CFI_MFR_MACRONIX:
                status = macronix_quad_enable(nor);
                if (status) {
@@ -904,11 +921,6 @@ static int spi_nor_check(struct spi_nor *nor)
                return -EINVAL;
        }
 
-       if (!nor->read_id)
-               nor->read_id = spi_nor_read_id;
-       if (!nor->wait_till_ready)
-               nor->wait_till_ready = spi_nor_wait_till_ready;
-
        return 0;
 }
 
@@ -926,16 +938,24 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
        if (ret)
                return ret;
 
-       id = spi_nor_match_id(name);
-       if (!id)
+       /* Try to auto-detect if chip name wasn't specified */
+       if (!name)
+               id = spi_nor_read_id(nor);
+       else
+               id = spi_nor_match_id(name);
+       if (IS_ERR_OR_NULL(id))
                return -ENOENT;
 
        info = (void *)id->driver_data;
 
-       if (info->jedec_id) {
+       /*
+        * If caller has specified name of flash model that can normally be
+        * detected using JEDEC, let's verify it.
+        */
+       if (name && info->id_len) {
                const struct spi_device_id *jid;
 
-               jid = nor->read_id(nor);
+               jid = spi_nor_read_id(nor);
                if (IS_ERR(jid)) {
                        return PTR_ERR(jid);
                } else if (jid != id) {
@@ -960,9 +980,9 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
         * up with the software protection bits set
         */
 
-       if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ATMEL ||
-           JEDEC_MFR(info->jedec_id) == CFI_MFR_INTEL ||
-           JEDEC_MFR(info->jedec_id) == CFI_MFR_SST) {
+       if (JEDEC_MFR(info) == CFI_MFR_ATMEL ||
+           JEDEC_MFR(info) == CFI_MFR_INTEL ||
+           JEDEC_MFR(info) == CFI_MFR_SST) {
                write_enable(nor);
                write_sr(nor, 0);
        }
@@ -977,7 +997,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
        mtd->_read = spi_nor_read;
 
        /* nor protection support for STmicro chips */
-       if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) {
+       if (JEDEC_MFR(info) == CFI_MFR_ST) {
                mtd->_lock = spi_nor_lock;
                mtd->_unlock = spi_nor_unlock;
        }
@@ -988,9 +1008,8 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
        else
                mtd->_write = spi_nor_write;
 
-       if ((info->flags & USE_FSR) &&
-           nor->wait_till_ready == spi_nor_wait_till_ready)
-               nor->wait_till_ready = spi_nor_wait_till_fsr_ready;
+       if (info->flags & USE_FSR)
+               nor->flags |= SNOR_F_USE_FSR;
 
 #ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
        /* prefer "small sector" erase if possible */
@@ -1031,7 +1050,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
 
        /* Quad/Dual-read mode takes precedence over fast/normal */
        if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
-               ret = set_quad_mode(nor, info->jedec_id);
+               ret = set_quad_mode(nor, info);
                if (ret) {
                        dev_err(dev, "quad mode not supported\n");
                        return ret;
@@ -1067,7 +1086,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
        else if (mtd->size > 0x1000000) {
                /* enable 4-byte addressing if the device exceeds 16MiB */
                nor->addr_width = 4;
-               if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) {
+               if (JEDEC_MFR(info) == CFI_MFR_AMD) {
                        /* Dedicated 4-byte command set */
                        switch (nor->flash_read) {
                        case SPI_NOR_QUAD:
@@ -1088,7 +1107,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
                        nor->erase_opcode = SPINOR_OP_SE_4B;
                        mtd->erasesize = info->sector_size;
                } else
-                       set_4byte(nor, info->jedec_id, 1);
+                       set_4byte(nor, info, 1);
        } else {
                nor->addr_width = 3;
        }
index dc4f960..5e06118 100644 (file)
 #include "mtd_test.h"
 
 static int dev = -EINVAL;
+static int bitflip_limit;
 module_param(dev, int, S_IRUGO);
 MODULE_PARM_DESC(dev, "MTD device number to use");
+module_param(bitflip_limit, int, S_IRUGO);
+MODULE_PARM_DESC(bitflip_limit, "Max. allowed bitflips per page");
 
 static struct mtd_info *mtd;
 static unsigned char *readbuf;
@@ -115,12 +118,36 @@ static int write_whole_device(void)
        return 0;
 }
 
+/*
+ * Display the address, offset and data bytes at comparison failure.
+ * Return number of bitflips encountered.
+ */
+static size_t memcmpshow(loff_t addr, const void *cs, const void *ct, size_t count)
+{
+       const unsigned char *su1, *su2;
+       int res;
+       size_t i = 0;
+       size_t bitflips = 0;
+
+       for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--, i++) {
+               res = *su1 ^ *su2;
+               if (res) {
+                       pr_info("error @addr[0x%lx:0x%zx] 0x%x -> 0x%x diff 0x%x\n",
+                               (unsigned long)addr, i, *su1, *su2, res);
+                       bitflips += hweight8(res);
+               }
+       }
+
+       return bitflips;
+}
+
 static int verify_eraseblock(int ebnum)
 {
        int i;
        struct mtd_oob_ops ops;
        int err = 0;
        loff_t addr = (loff_t)ebnum * mtd->erasesize;
+       size_t bitflips;
 
        prandom_bytes_state(&rnd_state, writebuf, use_len_max * pgcnt);
        for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
@@ -139,8 +166,11 @@ static int verify_eraseblock(int ebnum)
                        errcnt += 1;
                        return err ? err : -1;
                }
-               if (memcmp(readbuf, writebuf + (use_len_max * i) + use_offset,
-                          use_len)) {
+
+               bitflips = memcmpshow(addr, readbuf,
+                                     writebuf + (use_len_max * i) + use_offset,
+                                     use_len);
+               if (bitflips > bitflip_limit) {
                        pr_err("error: verify failed at %#llx\n",
                               (long long)addr);
                        errcnt += 1;
@@ -148,7 +178,10 @@ static int verify_eraseblock(int ebnum)
                                pr_err("error: too many errors\n");
                                return -1;
                        }
+               } else if (bitflips) {
+                       pr_info("ignoring error as within bitflip_limit\n");
                }
+
                if (use_offset != 0 || use_len < mtd->ecclayout->oobavail) {
                        int k;
 
@@ -167,9 +200,10 @@ static int verify_eraseblock(int ebnum)
                                errcnt += 1;
                                return err ? err : -1;
                        }
-                       if (memcmp(readbuf + use_offset,
-                                  writebuf + (use_len_max * i) + use_offset,
-                                  use_len)) {
+                       bitflips = memcmpshow(addr, readbuf + use_offset,
+                                             writebuf + (use_len_max * i) + use_offset,
+                                             use_len);
+                       if (bitflips > bitflip_limit) {
                                pr_err("error: verify failed at %#llx\n",
                                                (long long)addr);
                                errcnt += 1;
@@ -177,7 +211,10 @@ static int verify_eraseblock(int ebnum)
                                        pr_err("error: too many errors\n");
                                        return -1;
                                }
+                       } else if (bitflips) {
+                               pr_info("ignoring error as within bitflip_limit\n");
                        }
+
                        for (k = 0; k < use_offset; ++k)
                                if (readbuf[k] != 0xff) {
                                        pr_err("error: verify 0xff "
@@ -216,6 +253,9 @@ static int verify_eraseblock_in_one_go(int ebnum)
        int err = 0;
        loff_t addr = (loff_t)ebnum * mtd->erasesize;
        size_t len = mtd->ecclayout->oobavail * pgcnt;
+       size_t oobavail = mtd->ecclayout->oobavail;
+       size_t bitflips;
+       int i;
 
        prandom_bytes_state(&rnd_state, writebuf, len);
        ops.mode      = MTD_OPS_AUTO_OOB;
@@ -226,6 +266,8 @@ static int verify_eraseblock_in_one_go(int ebnum)
        ops.ooboffs   = 0;
        ops.datbuf    = NULL;
        ops.oobbuf    = readbuf;
+
+       /* read entire block's OOB at one go */
        err = mtd_read_oob(mtd, addr, &ops);
        if (err || ops.oobretlen != len) {
                pr_err("error: readoob failed at %#llx\n",
@@ -233,13 +275,21 @@ static int verify_eraseblock_in_one_go(int ebnum)
                errcnt += 1;
                return err ? err : -1;
        }
-       if (memcmp(readbuf, writebuf, len)) {
-               pr_err("error: verify failed at %#llx\n",
-                      (long long)addr);
-               errcnt += 1;
-               if (errcnt > 1000) {
-                       pr_err("error: too many errors\n");
-                       return -1;
+
+       /* verify one page OOB at a time for bitflip per page limit check */
+       for (i = 0; i < pgcnt; ++i, addr += mtd->writesize) {
+               bitflips = memcmpshow(addr, readbuf + (i * oobavail),
+                                     writebuf + (i * oobavail), oobavail);
+               if (bitflips > bitflip_limit) {
+                       pr_err("error: verify failed at %#llx\n",
+                              (long long)addr);
+                       errcnt += 1;
+                       if (errcnt > 1000) {
+                               pr_err("error: too many errors\n");
+                               return -1;
+                       }
+               } else if (bitflips) {
+                       pr_info("ignoring error as within bitflip_limit\n");
                }
        }
 
@@ -610,7 +660,8 @@ static int __init mtd_oobtest_init(void)
                err = mtd_read_oob(mtd, addr, &ops);
                if (err)
                        goto out;
-               if (memcmp(readbuf, writebuf, mtd->ecclayout->oobavail * 2)) {
+               if (memcmpshow(addr, readbuf, writebuf,
+                              mtd->ecclayout->oobavail * 2)) {
                        pr_err("error: verify failed at %#llx\n",
                               (long long)addr);
                        errcnt += 1;
index eeab969..b55bc52 100644 (file)
@@ -264,7 +264,9 @@ static int __init tort_init(void)
                int i;
                void *patt;
 
-               mtdtest_erase_good_eraseblocks(mtd, bad_ebs, eb, ebcnt);
+               err = mtdtest_erase_good_eraseblocks(mtd, bad_ebs, eb, ebcnt);
+               if (err)
+                       goto out;
 
                /* Check if the eraseblocks contain only 0xFF bytes */
                if (check) {
index 386303d..dddbde4 100644 (file)
@@ -224,7 +224,7 @@ static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
 
        dbg_readinode("insert fragment %#04x-%#04x, ver %u at %08x\n", tn->fn->ofs, fn_end, tn->version, ref_offset(tn->fn->raw));
 
-       /* If a node has zero dsize, we only have to keep if it if it might be the
+       /* If a node has zero dsize, we only have to keep it if it might be the
           node with highest version -- i.e. the one which will end up as f->metadata.
           Note that such nodes won't be REF_UNCHECKED since there are no data to
           check anyway. */
index c522d09..bc53854 100644 (file)
@@ -844,6 +844,7 @@ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock
 /* Write out summary information - called from jffs2_do_reserve_space */
 
 int jffs2_sum_write_sumnode(struct jffs2_sb_info *c)
+       __must_hold(&c->erase_completion_block)
 {
        int datasize, infosize, padsize;
        struct jffs2_eraseblock *jeb;
index 84d60cb..bf0321e 100644 (file)
 #include <linux/of_platform.h>
 #include <linux/interrupt.h>
 
-#define FSL_IFC_BANK_COUNT 4
+/*
+ * The actual number of banks implemented depends on the IFC version
+ *    - IFC version 1.0 implements 4 banks.
+ *    - IFC version 1.1 onward implements 8 banks.
+ */
+#define FSL_IFC_BANK_COUNT 8
+
+#define FSL_IFC_VERSION_MASK   0x0F0F0000
+#define FSL_IFC_VERSION_1_0_0  0x01000000
+#define FSL_IFC_VERSION_1_1_0  0x01010000
 
 /*
  * CSPR - Chip Select Property Register
@@ -776,23 +785,23 @@ struct fsl_ifc_regs {
                __be32 cspr;
                u32 res2;
        } cspr_cs[FSL_IFC_BANK_COUNT];
-       u32 res3[0x19];
+       u32 res3[0xd];
        struct {
                __be32 amask;
                u32 res4[0x2];
        } amask_cs[FSL_IFC_BANK_COUNT];
-       u32 res5[0x18];
+       u32 res5[0xc];
        struct {
                __be32 csor;
                __be32 csor_ext;
                u32 res6;
        } csor_cs[FSL_IFC_BANK_COUNT];
-       u32 res7[0x18];
+       u32 res7[0xc];
        struct {
                __be32 ftim[4];
                u32 res8[0x8];
        } ftim_cs[FSL_IFC_BANK_COUNT];
-       u32 res9[0x60];
+       u32 res9[0x30];
        __be32 rb_stat;
        u32 res10[0x2];
        __be32 ifc_gcr;
@@ -827,6 +836,8 @@ struct fsl_ifc_ctrl {
        int                             nand_irq;
        spinlock_t                      lock;
        void                            *nand;
+       int                             version;
+       int                             banks;
 
        u32 nand_stat;
        wait_queue_head_t nand_wait;
index e4d451e..3d4ea7e 100644 (file)
@@ -455,8 +455,21 @@ struct nand_hw_control {
  *             be provided if an hardware ECC is available
  * @calculate: function for ECC calculation or readback from ECC hardware
  * @correct:   function for ECC correction, matching to ECC generator (sw/hw)
- * @read_page_raw:     function to read a raw page without ECC
- * @write_page_raw:    function to write a raw page without ECC
+ * @read_page_raw:     function to read a raw page without ECC. This function
+ *                     should hide the specific layout used by the ECC
+ *                     controller and always return contiguous in-band and
+ *                     out-of-band data even if they're not stored
+ *                     contiguously on the NAND chip (e.g.
+ *                     NAND_ECC_HW_SYNDROME interleaves in-band and
+ *                     out-of-band data).
+ * @write_page_raw:    function to write a raw page without ECC. This function
+ *                     should hide the specific layout used by the ECC
+ *                     controller and consider the passed data as contiguous
+ *                     in-band and out-of-band data. ECC controller is
+ *                     responsible for doing the appropriate transformations
+ *                     to adapt to its specific layout (e.g.
+ *                     NAND_ECC_HW_SYNDROME interleaves in-band and
+ *                     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
@@ -723,6 +736,7 @@ struct nand_chip {
 #define NAND_MFR_EON           0x92
 #define NAND_MFR_SANDISK       0x45
 #define NAND_MFR_INTEL         0x89
+#define NAND_MFR_ATO           0x9b
 
 /* The maximum expected count of bytes in the NAND ID sequence */
 #define NAND_MAX_ID_LEN 8
index 046a0a2..63aeccf 100644 (file)
@@ -116,6 +116,10 @@ enum spi_nor_ops {
        SPI_NOR_OPS_UNLOCK,
 };
 
+enum spi_nor_option_flags {
+       SNOR_F_USE_FSR          = BIT(0),
+};
+
 /**
  * struct spi_nor - Structure for defining a the SPI NOR layer
  * @mtd:               point to a mtd_info structure
@@ -129,6 +133,7 @@ enum spi_nor_ops {
  * @program_opcode:    the program opcode
  * @flash_read:                the mode of the read
  * @sst_write_second:  used by the SST write operation
+ * @flags:             flag options for the current SPI-NOR (SNOR_F_*)
  * @cfg:               used by the read_xfer/write_xfer
  * @cmd_buf:           used by the write_reg
  * @prepare:           [OPTIONAL] do some preparations for the
@@ -139,9 +144,6 @@ enum spi_nor_ops {
  * @write_xfer:                [OPTIONAL] the writefundamental primitive
  * @read_reg:          [DRIVER-SPECIFIC] read out the register
  * @write_reg:         [DRIVER-SPECIFIC] write data to the register
- * @read_id:           [REPLACEABLE] read out the ID data, and find
- *                     the proper spi_device_id
- * @wait_till_ready:   [REPLACEABLE] wait till the NOR becomes ready
  * @read:              [DRIVER-SPECIFIC] read data from the SPI NOR
  * @write:             [DRIVER-SPECIFIC] write data to the SPI NOR
  * @erase:             [DRIVER-SPECIFIC] erase a sector of the SPI NOR
@@ -160,6 +162,7 @@ struct spi_nor {
        u8                      program_opcode;
        enum read_mode          flash_read;
        bool                    sst_write_second;
+       u32                     flags;
        struct spi_nor_xfer_cfg cfg;
        u8                      cmd_buf[SPI_NOR_MAX_CMD_SIZE];
 
@@ -172,8 +175,6 @@ struct spi_nor {
        int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
        int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len,
                        int write_enable);
-       const struct spi_device_id *(*read_id)(struct spi_nor *nor);
-       int (*wait_till_ready)(struct spi_nor *nor);
 
        int (*read)(struct spi_nor *nor, loff_t from,
                        size_t len, size_t *retlen, u_char *read_buf);