Merge tag 'for-linus-20140405' of git://git.infradead.org/linux-mtd
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 7 Apr 2014 17:17:30 +0000 (10:17 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 7 Apr 2014 17:17:30 +0000 (10:17 -0700)
Pull MTD updates from Brian Norris:
 - A few SPI NOR ID definitions
 - Kill the NAND "max pagesize" restriction
 - Fix some x16 bus-width NAND support
 - Add NAND JEDEC parameter page support
 - DT bindings for NAND ECC
 - GPMI NAND updates (subpage reads)
 - More OMAP NAND refactoring
 - New STMicro SPI NOR driver (now in 40 patches!)
 - A few other random bugfixes

* tag 'for-linus-20140405' of git://git.infradead.org/linux-mtd: (120 commits)
  Fix index regression in nand_read_subpage
  mtd: diskonchip: mem resource name is not optional
  mtd: nand: fix mention to CONFIG_MTD_NAND_ECC_BCH
  mtd: nand: fix GET/SET_FEATURES address on 16-bit devices
  mtd: omap2: Use devm_ioremap_resource()
  mtd: denali_dt: Use devm_ioremap_resource()
  mtd: devices: elm: update DRIVER_NAME as "omap-elm"
  mtd: devices: elm: configure parallel channels based on ecc_steps
  mtd: devices: elm: clean elm_load_syndrome
  mtd: devices: elm: check for hardware engine's design constraints
  mtd: st_spi_fsm: Succinctly reorganise .remove()
  mtd: st_spi_fsm: Allow loop to run at least once before giving up CPU
  mtd: st_spi_fsm: Correct vendor name spelling issue - missing "M"
  mtd: st_spi_fsm: Avoid duplicating MTD core code
  mtd: st_spi_fsm: Remove useless consts from function arguments
  mtd: st_spi_fsm: Convert ST SPI FSM (NOR) Flash driver to new DT partitions
  mtd: st_spi_fsm: Move runtime configurable msg sequences into device's struct
  mtd: st_spi_fsm: Supply the W25Qxxx chip specific configuration call-back
  mtd: st_spi_fsm: Supply the S25FLxxx chip specific configuration call-back
  mtd: st_spi_fsm: Supply the MX25xxx chip specific configuration call-back
  ...

83 files changed:
Documentation/devicetree/bindings/mtd/nand.txt
Documentation/devicetree/bindings/mtd/st-fsm.txt [new file with mode: 0644]
drivers/mtd/Kconfig
drivers/mtd/bcm47xxpart.c
drivers/mtd/chips/cfi_cmdset_0001.c
drivers/mtd/chips/cfi_cmdset_0002.c
drivers/mtd/chips/cfi_cmdset_0020.c
drivers/mtd/chips/cfi_probe.c
drivers/mtd/chips/cfi_util.c
drivers/mtd/chips/gen_probe.c
drivers/mtd/devices/Kconfig
drivers/mtd/devices/Makefile
drivers/mtd/devices/block2mtd.c
drivers/mtd/devices/elm.c
drivers/mtd/devices/m25p80.c
drivers/mtd/devices/mtd_dataflash.c
drivers/mtd/devices/phram.c
drivers/mtd/devices/pmc551.c
drivers/mtd/devices/serial_flash_cmds.h [new file with mode: 0644]
drivers/mtd/devices/spear_smi.c
drivers/mtd/devices/sst25l.c
drivers/mtd/devices/st_spi_fsm.c [new file with mode: 0644]
drivers/mtd/inftlmount.c
drivers/mtd/lpddr/lpddr_cmds.c
drivers/mtd/lpddr/qinfo_probe.c
drivers/mtd/maps/Kconfig
drivers/mtd/maps/bfin-async-flash.c
drivers/mtd/maps/gpio-addr-flash.c
drivers/mtd/maps/intel_vr_nor.c
drivers/mtd/maps/ixp4xx.c
drivers/mtd/maps/lantiq-flash.c
drivers/mtd/maps/latch-addr-flash.c
drivers/mtd/maps/pci.c
drivers/mtd/maps/physmap_of.c
drivers/mtd/maps/plat-ram.c
drivers/mtd/maps/pxa2xx-flash.c
drivers/mtd/maps/rbtx4939-flash.c
drivers/mtd/maps/scb2_flash.c
drivers/mtd/maps/sun_uflash.c
drivers/mtd/mtd_blkdevs.c
drivers/mtd/mtdchar.c
drivers/mtd/mtdcore.c
drivers/mtd/mtdpart.c
drivers/mtd/nand/Kconfig
drivers/mtd/nand/ams-delta.c
drivers/mtd/nand/atmel_nand.c
drivers/mtd/nand/au1550nd.c
drivers/mtd/nand/bf5xx_nand.c
drivers/mtd/nand/cafe_nand.c
drivers/mtd/nand/davinci_nand.c
drivers/mtd/nand/denali_dt.c
drivers/mtd/nand/diskonchip.c
drivers/mtd/nand/fsl_elbc_nand.c
drivers/mtd/nand/fsl_ifc_nand.c
drivers/mtd/nand/gpio.c
drivers/mtd/nand/gpmi-nand/gpmi-nand.c
drivers/mtd/nand/mpc5121_nfc.c
drivers/mtd/nand/mxc_nand.c
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/nand_ids.c
drivers/mtd/nand/nuc900_nand.c
drivers/mtd/nand/omap2.c
drivers/mtd/nand/pasemi_nand.c
drivers/mtd/nand/pxa3xx_nand.c
drivers/mtd/nand/s3c2410.c
drivers/mtd/onenand/generic.c
drivers/mtd/onenand/omap2.c
drivers/mtd/onenand/onenand_base.c
drivers/mtd/onenand/samsung.c
drivers/mtd/rfd_ftl.c
drivers/mtd/sm_ftl.c
drivers/mtd/tests/mtd_test.c
drivers/mtd/ubi/ubi.h
drivers/of/of_mtd.c
fs/jffs2/compr_rtime.c
fs/jffs2/fs.c
fs/jffs2/nodelist.h
fs/jffs2/nodemgmt.c
include/linux/mtd/mtd.h
include/linux/mtd/nand.h
include/linux/of_mtd.h
include/linux/platform_data/elm.h
include/linux/platform_data/mtd-nand-s3c2410.h

index 03855c8..b53f92e 100644 (file)
@@ -5,3 +5,17 @@
   "soft_bch".
 - 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-ecc-strength: integer representing the number of bits to correct
+                    per ECC step.
+
+- nand-ecc-step-size: integer representing the number of data bytes
+                     that are covered by a single ECC step.
+
+The ECC strength and ECC step size properties define the correction capability
+of a controller. Together, they say a controller can correct "{strength} bit
+errors per {size} bytes".
+
+The interpretation of these parameters is implementation-defined, so not all
+implementations must support all possible combinations. However, implementations
+are encouraged to further specify the value(s) they support.
diff --git a/Documentation/devicetree/bindings/mtd/st-fsm.txt b/Documentation/devicetree/bindings/mtd/st-fsm.txt
new file mode 100644 (file)
index 0000000..c248939
--- /dev/null
@@ -0,0 +1,26 @@
+* ST-Microelectronics SPI FSM Serial (NOR) Flash Controller
+
+Required properties:
+  - compatible : Should be "st,spi-fsm"
+  - reg        : Contains register's location and length.
+  - reg-names  : Should contain the reg names "spi-fsm"
+  - interrupts : The interrupt number
+  - pinctrl-0  : Standard Pinctrl phandle (see: pinctrl/pinctrl-bindings.txt)
+
+Optional properties:
+  - st,syscfg          : Phandle to boot-device system configuration registers
+  - st,boot-device-reg : Address of the aforementioned boot-device register(s)
+  - st,boot-device-spi : Expected boot-device value if booted via this device
+
+Example:
+       spifsm: spifsm@fe902000{
+               compatible         = "st,spi-fsm";
+               reg                =  <0xfe902000 0x1000>;
+               reg-names          = "spi-fsm";
+               pinctrl-0          = <&pinctrl_fsm>;
+               st,syscfg          = <&syscfg_rear>;
+               st,boot-device-reg = <0x958>;
+               st,boot-device-spi = <0x1a>;
+               status = "okay";
+       };
+
index 5ebcda3..5d49a21 100644 (file)
@@ -150,7 +150,7 @@ config MTD_BCM63XX_PARTS
 
 config MTD_BCM47XX_PARTS
        tristate "BCM47XX partitioning support"
-       depends on BCM47XX
+       depends on BCM47XX || ARCH_BCM_5301X
        help
          This provides partitions parser for devices based on BCM47xx
          boards.
index de1eb92..adfa74c 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/slab.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
-#include <bcm47xx_nvram.h>
 
 /* 10 parts were found on sflash on Netgear WNDR4500 */
 #define BCM47XXPART_MAX_PARTS          12
@@ -30,6 +29,7 @@
 #define BOARD_DATA_MAGIC2              0xBD0D0BBD
 #define CFE_MAGIC                      0x43464531      /* 1EFC */
 #define FACTORY_MAGIC                  0x59544346      /* FCTY */
+#define NVRAM_HEADER                   0x48534C46      /* FLSH */
 #define POT_MAGIC1                     0x54544f50      /* POTT */
 #define POT_MAGIC2                     0x504f          /* OP */
 #define ML_MAGIC1                      0x39685a42
@@ -91,7 +91,7 @@ static int bcm47xxpart_parse(struct mtd_info *master,
                if (offset >= 0x2000000)
                        break;
 
-               if (curr_part > BCM47XXPART_MAX_PARTS) {
+               if (curr_part >= BCM47XXPART_MAX_PARTS) {
                        pr_warn("Reached maximum number of partitions, scanning stopped!\n");
                        break;
                }
@@ -147,6 +147,11 @@ static int bcm47xxpart_parse(struct mtd_info *master,
 
                /* TRX */
                if (buf[0x000 / 4] == TRX_MAGIC) {
+                       if (BCM47XXPART_MAX_PARTS - curr_part < 4) {
+                               pr_warn("Not enough partitions left to register trx, scanning stopped!\n");
+                               break;
+                       }
+
                        trx = (struct trx_header *)buf;
 
                        trx_part = curr_part;
@@ -212,7 +217,7 @@ static int bcm47xxpart_parse(struct mtd_info *master,
 
        /* Look for NVRAM at the end of the last block. */
        for (i = 0; i < ARRAY_SIZE(possible_nvram_sizes); i++) {
-               if (curr_part > BCM47XXPART_MAX_PARTS) {
+               if (curr_part >= BCM47XXPART_MAX_PARTS) {
                        pr_warn("Reached maximum number of partitions, scanning stopped!\n");
                        break;
                }
index 7751443..e4ec355 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
-#include <linux/init.h>
 #include <asm/io.h>
 #include <asm/byteorder.h>
 
@@ -69,10 +68,10 @@ static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, s
 static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
 static int cfi_intelext_write_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
 static int cfi_intelext_lock_user_prot_reg (struct mtd_info *, loff_t, size_t);
-static int cfi_intelext_get_fact_prot_info (struct mtd_info *,
-                                           struct otp_info *, size_t);
-static int cfi_intelext_get_user_prot_info (struct mtd_info *,
-                                           struct otp_info *, size_t);
+static int cfi_intelext_get_fact_prot_info(struct mtd_info *, size_t,
+                                          size_t *, struct otp_info *);
+static int cfi_intelext_get_user_prot_info(struct mtd_info *, size_t,
+                                          size_t *, struct otp_info *);
 #endif
 static int cfi_intelext_suspend (struct mtd_info *);
 static void cfi_intelext_resume (struct mtd_info *);
@@ -435,10 +434,8 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
        int i;
 
        mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
-       if (!mtd) {
-               printk(KERN_ERR "Failed to allocate memory for MTD device\n");
+       if (!mtd)
                return NULL;
-       }
        mtd->priv = map;
        mtd->type = MTD_NORFLASH;
 
@@ -564,10 +561,8 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
        mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
        mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info)
                        * mtd->numeraseregions, GFP_KERNEL);
-       if (!mtd->eraseregions) {
-               printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n");
+       if (!mtd->eraseregions)
                goto setup_err;
-       }
 
        for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
                unsigned long ernum, ersize;
@@ -2399,24 +2394,19 @@ static int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd,
                                     NULL, do_otp_lock, 1);
 }
 
-static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd,
-                                          struct otp_info *buf, size_t len)
-{
-       size_t retlen;
-       int ret;
+static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd, size_t len,
+                                          size_t *retlen, struct otp_info *buf)
 
-       ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 0);
-       return ret ? : retlen;
+{
+       return cfi_intelext_otp_walk(mtd, 0, len, retlen, (u_char *)buf,
+                                    NULL, 0);
 }
 
-static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd,
-                                          struct otp_info *buf, size_t len)
+static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd, size_t len,
+                                          size_t *retlen, struct otp_info *buf)
 {
-       size_t retlen;
-       int ret;
-
-       ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 1);
-       return ret ? : retlen;
+       return cfi_intelext_otp_walk(mtd, 0, len, retlen, (u_char *)buf,
+                                    NULL, 1);
 }
 
 #endif
index 89b9d68..e21fde9 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
-#include <linux/init.h>
 #include <asm/io.h>
 #include <asm/byteorder.h>
 
@@ -507,10 +506,8 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
        int i;
 
        mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
-       if (!mtd) {
-               printk(KERN_WARNING "Failed to allocate memory for MTD device\n");
+       if (!mtd)
                return NULL;
-       }
        mtd->priv = map;
        mtd->type = MTD_NORFLASH;
 
@@ -661,10 +658,8 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
        mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
        mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info)
                                    * mtd->numeraseregions, GFP_KERNEL);
-       if (!mtd->eraseregions) {
-               printk(KERN_WARNING "Failed to allocate memory for MTD erase region info\n");
+       if (!mtd->eraseregions)
                goto setup_err;
-       }
 
        for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
                unsigned long ernum, ersize;
index 096993f..6293855 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
-#include <linux/init.h>
 #include <asm/io.h>
 #include <asm/byteorder.h>
 
@@ -176,7 +175,6 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
        //printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips);
 
        if (!mtd) {
-               printk(KERN_ERR "Failed to allocate memory for MTD device\n");
                kfree(cfi->cmdset_priv);
                return NULL;
        }
@@ -189,7 +187,6 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
        mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info)
                        * mtd->numeraseregions, GFP_KERNEL);
        if (!mtd->eraseregions) {
-               printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n");
                kfree(cfi->cmdset_priv);
                kfree(mtd);
                return NULL;
index d255352..e8d0164 100644 (file)
@@ -168,10 +168,8 @@ static int __xipram cfi_chip_setup(struct map_info *map,
                return 0;
 
        cfi->cfiq = kmalloc(sizeof(struct cfi_ident) + num_erase_regions * 4, GFP_KERNEL);
-       if (!cfi->cfiq) {
-               printk(KERN_WARNING "%s: kmalloc failed for CFI ident structure\n", map->name);
+       if (!cfi->cfiq)
                return 0;
-       }
 
        memset(cfi->cfiq,0,sizeof(struct cfi_ident));
 
index f992418..08049f6 100644 (file)
@@ -116,10 +116,8 @@ __xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* n
        printk(KERN_INFO "%s Extended Query Table at 0x%4.4X\n", name, adr);
 
        extp = kmalloc(size, GFP_KERNEL);
-       if (!extp) {
-               printk(KERN_ERR "Failed to allocate memory\n");
+       if (!extp)
                goto out;
-       }
 
 #ifdef CONFIG_MTD_XIP
        local_irq_disable();
index ffb36ba..b57ceea 100644 (file)
@@ -114,7 +114,6 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi
        mapsize = sizeof(long) * DIV_ROUND_UP(max_chips, BITS_PER_LONG);
        chip_map = kzalloc(mapsize, GFP_KERNEL);
        if (!chip_map) {
-               printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name);
                kfree(cfi.cfiq);
                return NULL;
        }
@@ -139,7 +138,6 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi
        retcfi = kmalloc(sizeof(struct cfi_private) + cfi.numchips * sizeof(struct flchip), GFP_KERNEL);
 
        if (!retcfi) {
-               printk(KERN_WARNING "%s: kmalloc failed for CFI private structure\n", map->name);
                kfree(cfi.cfiq);
                kfree(chip_map);
                return NULL;
index 0128138..1210bc2 100644 (file)
@@ -210,6 +210,14 @@ config MTD_DOCG3
          M-Systems and now Sandisk. The support is very experimental,
          and doesn't give access to any write operations.
 
+config MTD_ST_SPI_FSM
+       tristate "ST Microelectronics SPI FSM Serial Flash Controller"
+       depends on ARM || SH
+       help
+         This provides an MTD device driver for the ST Microelectronics
+         SPI Fast Sequence Mode (FSM) Serial Flash Controller and support
+         for a subset of connected Serial Flash devices.
+
 if MTD_DOCG3
 config BCH_CONST_M
        default 14
index d83bd73..c68868f 100644 (file)
@@ -16,6 +16,7 @@ obj-$(CONFIG_MTD_NAND_OMAP_BCH)       += elm.o
 obj-$(CONFIG_MTD_SPEAR_SMI)    += spear_smi.o
 obj-$(CONFIG_MTD_SST25L)       += sst25l.o
 obj-$(CONFIG_MTD_BCM47XXSFLASH)        += bcm47xxsflash.o
+obj-$(CONFIG_MTD_ST_SPI_FSM)    += st_spi_fsm.o
 
 
 CFLAGS_docg3.o                 += -I$(src)
index d9fd87a..66f0405 100644 (file)
@@ -209,7 +209,6 @@ static void block2mtd_free_device(struct block2mtd_dev *dev)
 }
 
 
-/* FIXME: ensure that mtd->size % erase_size == 0 */
 static struct block2mtd_dev *add_device(char *devname, int erase_size)
 {
        const fmode_t mode = FMODE_READ | FMODE_WRITE | FMODE_EXCL;
@@ -240,13 +239,18 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
 
        if (IS_ERR(bdev)) {
                pr_err("error: cannot open device %s\n", devname);
-               goto devinit_err;
+               goto err_free_block2mtd;
        }
        dev->blkdev = bdev;
 
        if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
                pr_err("attempting to use an MTD device as a block device\n");
-               goto devinit_err;
+               goto err_free_block2mtd;
+       }
+
+       if ((long)dev->blkdev->bd_inode->i_size % erase_size) {
+               pr_err("erasesize must be a divisor of device size\n");
+               goto err_free_block2mtd;
        }
 
        mutex_init(&dev->write_mutex);
@@ -255,7 +259,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
        /* make the name contain the block device in */
        name = kasprintf(GFP_KERNEL, "block2mtd: %s", devname);
        if (!name)
-               goto devinit_err;
+               goto err_destroy_mutex;
 
        dev->mtd.name = name;
 
@@ -274,7 +278,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
 
        if (mtd_device_register(&dev->mtd, NULL, 0)) {
                /* Device didn't get added, so free the entry */
-               goto devinit_err;
+               goto err_destroy_mutex;
        }
        list_add(&dev->list, &blkmtd_device_list);
        pr_info("mtd%d: [%s] erase_size = %dKiB [%d]\n",
@@ -283,7 +287,9 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
                dev->mtd.erasesize >> 10, dev->mtd.erasesize);
        return dev;
 
-devinit_err:
+err_destroy_mutex:
+       mutex_destroy(&dev->write_mutex);
+err_free_block2mtd:
        block2mtd_free_device(dev);
        return NULL;
 }
@@ -448,6 +454,7 @@ static void block2mtd_exit(void)
                struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list);
                block2mtd_sync(&dev->mtd);
                mtd_device_unregister(&dev->mtd);
+               mutex_destroy(&dev->write_mutex);
                pr_info("mtd%d: [%s] removed\n",
                        dev->mtd.index,
                        dev->mtd.name + strlen("block2mtd: "));
index d1dd6a3..1fd4a0f 100644 (file)
@@ -15,6 +15,8 @@
  *
  */
 
+#define DRIVER_NAME    "omap-elm"
+
 #include <linux/platform_device.h>
 #include <linux/module.h>
 #include <linux/interrupt.h>
@@ -84,6 +86,8 @@ struct elm_info {
        struct list_head list;
        enum bch_ecc bch_type;
        struct elm_registers elm_regs;
+       int ecc_steps;
+       int ecc_syndrome_size;
 };
 
 static LIST_HEAD(elm_devices);
@@ -103,7 +107,8 @@ static u32 elm_read_reg(struct elm_info *info, int offset)
  * @dev:       ELM device
  * @bch_type:  Type of BCH ecc
  */
-int elm_config(struct device *dev, enum bch_ecc bch_type)
+int elm_config(struct device *dev, enum bch_ecc bch_type,
+       int ecc_steps, int ecc_step_size, int ecc_syndrome_size)
 {
        u32 reg_val;
        struct elm_info *info = dev_get_drvdata(dev);
@@ -112,10 +117,22 @@ int elm_config(struct device *dev, enum bch_ecc bch_type)
                dev_err(dev, "Unable to configure elm - device not probed?\n");
                return -ENODEV;
        }
+       /* ELM cannot detect ECC errors for chunks > 1KB */
+       if (ecc_step_size > ((ELM_ECC_SIZE + 1) / 2)) {
+               dev_err(dev, "unsupported config ecc-size=%d\n", ecc_step_size);
+               return -EINVAL;
+       }
+       /* ELM support 8 error syndrome process */
+       if (ecc_steps > ERROR_VECTOR_MAX) {
+               dev_err(dev, "unsupported config ecc-step=%d\n", ecc_steps);
+               return -EINVAL;
+       }
 
        reg_val = (bch_type & ECC_BCH_LEVEL_MASK) | (ELM_ECC_SIZE << 16);
        elm_write_reg(info, ELM_LOCATION_CONFIG, reg_val);
-       info->bch_type = bch_type;
+       info->bch_type          = bch_type;
+       info->ecc_steps         = ecc_steps;
+       info->ecc_syndrome_size = ecc_syndrome_size;
 
        return 0;
 }
@@ -157,17 +174,15 @@ static void elm_load_syndrome(struct elm_info *info,
        int i, offset;
        u32 val;
 
-       for (i = 0; i < ERROR_VECTOR_MAX; i++) {
+       for (i = 0; i < info->ecc_steps; i++) {
 
                /* Check error reported */
                if (err_vec[i].error_reported) {
                        elm_configure_page_mode(info, i, true);
                        offset = ELM_SYNDROME_FRAGMENT_0 +
                                SYNDROME_FRAGMENT_REG_SIZE * i;
-
-                       /* BCH8 */
-                       if (info->bch_type) {
-
+                       switch (info->bch_type) {
+                       case BCH8_ECC:
                                /* syndrome fragment 0 = ecc[9-12B] */
                                val = cpu_to_be32(*(u32 *) &ecc[9]);
                                elm_write_reg(info, offset, val);
@@ -186,7 +201,8 @@ static void elm_load_syndrome(struct elm_info *info,
                                offset += 4;
                                val = ecc[0];
                                elm_write_reg(info, offset, val);
-                       } else {
+                               break;
+                       case BCH4_ECC:
                                /* syndrome fragment 0 = ecc[20-52b] bits */
                                val = (cpu_to_be32(*(u32 *) &ecc[3]) >> 4) |
                                        ((ecc[2] & 0xf) << 28);
@@ -196,11 +212,14 @@ static void elm_load_syndrome(struct elm_info *info,
                                offset += 4;
                                val = cpu_to_be32(*(u32 *) &ecc[0]) >> 12;
                                elm_write_reg(info, offset, val);
+                               break;
+                       default:
+                               pr_err("invalid config bch_type\n");
                        }
                }
 
                /* Update ecc pointer with ecc byte size */
-               ecc += info->bch_type ? BCH8_SIZE : BCH4_SIZE;
+               ecc += info->ecc_syndrome_size;
        }
 }
 
@@ -223,7 +242,7 @@ static void elm_start_processing(struct elm_info *info,
         * Set syndrome vector valid, so that ELM module
         * will process it for vectors error is reported
         */
-       for (i = 0; i < ERROR_VECTOR_MAX; i++) {
+       for (i = 0; i < info->ecc_steps; i++) {
                if (err_vec[i].error_reported) {
                        offset = ELM_SYNDROME_FRAGMENT_6 +
                                SYNDROME_FRAGMENT_REG_SIZE * i;
@@ -252,7 +271,7 @@ static void elm_error_correction(struct elm_info *info,
        int offset;
        u32 reg_val;
 
-       for (i = 0; i < ERROR_VECTOR_MAX; i++) {
+       for (i = 0; i < info->ecc_steps; i++) {
 
                /* Check error reported */
                if (err_vec[i].error_reported) {
@@ -354,10 +373,8 @@ static int elm_probe(struct platform_device *pdev)
        struct elm_info *info;
 
        info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
-       if (!info) {
-               dev_err(&pdev->dev, "failed to allocate memory\n");
+       if (!info)
                return -ENOMEM;
-       }
 
        info->dev = &pdev->dev;
 
@@ -380,7 +397,7 @@ static int elm_probe(struct platform_device *pdev)
        }
 
        pm_runtime_enable(&pdev->dev);
-       if (pm_runtime_get_sync(&pdev->dev)) {
+       if (pm_runtime_get_sync(&pdev->dev) < 0) {
                ret = -EINVAL;
                pm_runtime_disable(&pdev->dev);
                dev_err(&pdev->dev, "can't enable clock\n");
@@ -505,7 +522,7 @@ MODULE_DEVICE_TABLE(of, elm_of_match);
 
 static struct platform_driver elm_driver = {
        .driver = {
-               .name   = "elm",
+               .name   = DRIVER_NAME,
                .owner  = THIS_MODULE,
                .of_match_table = of_match_ptr(elm_of_match),
                .pm     = &elm_pm_ops,
index ad19139..524dab3 100644 (file)
@@ -15,7 +15,6 @@
  *
  */
 
-#include <linux/init.h>
 #include <linux/err.h>
 #include <linux/errno.h>
 #include <linux/module.h>
@@ -41,7 +40,8 @@
 #define        OPCODE_WRSR             0x01    /* Write status register 1 byte */
 #define        OPCODE_NORM_READ        0x03    /* Read data bytes (low frequency) */
 #define        OPCODE_FAST_READ        0x0b    /* Read data bytes (high frequency) */
-#define        OPCODE_QUAD_READ        0x6b    /* Read data bytes */
+#define        OPCODE_DUAL_READ        0x3b    /* Read data bytes (Dual SPI) */
+#define        OPCODE_QUAD_READ        0x6b    /* Read data bytes (Quad SPI) */
 #define        OPCODE_PP               0x02    /* Page program (up to 256 bytes) */
 #define        OPCODE_BE_4K            0x20    /* Erase 4KiB block */
 #define        OPCODE_BE_4K_PMC        0xd7    /* Erase 4KiB block on PMC chips */
@@ -54,7 +54,8 @@
 /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
 #define        OPCODE_NORM_READ_4B     0x13    /* Read data bytes (low frequency) */
 #define        OPCODE_FAST_READ_4B     0x0c    /* Read data bytes (high frequency) */
-#define        OPCODE_QUAD_READ_4B     0x6c    /* Read data bytes */
+#define        OPCODE_DUAL_READ_4B     0x3c    /* Read data bytes (Dual SPI) */
+#define        OPCODE_QUAD_READ_4B     0x6c    /* Read data bytes (Quad SPI) */
 #define        OPCODE_PP_4B            0x12    /* Page program (up to 256 bytes) */
 #define        OPCODE_SE_4B            0xdc    /* Sector erase (usually 64KiB) */
 
@@ -95,6 +96,7 @@
 enum read_type {
        M25P80_NORMAL = 0,
        M25P80_FAST,
+       M25P80_DUAL,
        M25P80_QUAD,
 };
 
@@ -479,6 +481,7 @@ static inline int m25p80_dummy_cycles_read(struct m25p *flash)
 {
        switch (flash->flash_read) {
        case M25P80_FAST:
+       case M25P80_DUAL:
        case M25P80_QUAD:
                return 1;
        case M25P80_NORMAL:
@@ -492,6 +495,8 @@ static inline int m25p80_dummy_cycles_read(struct m25p *flash)
 static inline unsigned int m25p80_rx_nbits(const struct m25p *flash)
 {
        switch (flash->flash_read) {
+       case M25P80_DUAL:
+               return 2;
        case M25P80_QUAD:
                return 4;
        default:
@@ -855,7 +860,8 @@ struct flash_info {
 #define        SST_WRITE       0x04            /* use SST byte programming */
 #define        M25P_NO_FR      0x08            /* Can't do fastread */
 #define        SECT_4K_PMC     0x10            /* OPCODE_BE_4K_PMC works uniformly */
-#define        M25P80_QUAD_READ        0x20    /* Flash supports Quad Read */
+#define        M25P80_DUAL_READ        0x20    /* Flash supports Dual Read */
+#define        M25P80_QUAD_READ        0x40    /* Flash supports Quad Read */
 };
 
 #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags)     \
@@ -934,6 +940,7 @@ static const struct spi_device_id m25p_ids[] = {
        { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
        { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
        { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, M25P80_QUAD_READ) },
+       { "mx66l1g55g",  INFO(0xc2261b, 0, 64 * 1024, 2048, M25P80_QUAD_READ) },
 
        /* Micron */
        { "n25q064",     INFO(0x20ba17, 0, 64 * 1024,  128, 0) },
@@ -953,8 +960,8 @@ static const struct spi_device_id m25p_ids[] = {
        { "s25sl032p",  INFO(0x010215, 0x4d00,  64 * 1024,  64, 0) },
        { "s25sl064p",  INFO(0x010216, 0x4d00,  64 * 1024, 128, 0) },
        { "s25fl256s0", INFO(0x010219, 0x4d00, 256 * 1024, 128, 0) },
-       { "s25fl256s1", INFO(0x010219, 0x4d01,  64 * 1024, 512, M25P80_QUAD_READ) },
-       { "s25fl512s",  INFO(0x010220, 0x4d00, 256 * 1024, 256, M25P80_QUAD_READ) },
+       { "s25fl256s1", INFO(0x010219, 0x4d01,  64 * 1024, 512, M25P80_DUAL_READ | M25P80_QUAD_READ) },
+       { "s25fl512s",  INFO(0x010220, 0x4d00, 256 * 1024, 256, M25P80_DUAL_READ | M25P80_QUAD_READ) },
        { "s70fl01gs",  INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
        { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024,  64, 0) },
        { "s25sl12801", INFO(0x012018, 0x0301,  64 * 1024, 256, 0) },
@@ -965,6 +972,7 @@ static const struct spi_device_id m25p_ids[] = {
        { "s25sl016a",  INFO(0x010214,      0,  64 * 1024,  32, 0) },
        { "s25sl032a",  INFO(0x010215,      0,  64 * 1024,  64, 0) },
        { "s25sl064a",  INFO(0x010216,      0,  64 * 1024, 128, 0) },
+       { "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) },
 
@@ -1072,9 +1080,8 @@ static const struct spi_device_id *jedec_probe(struct spi_device *spi)
        for (tmp = 0; tmp < ARRAY_SIZE(m25p_ids) - 1; tmp++) {
                info = (void *)m25p_ids[tmp].driver_data;
                if (info->jedec_id == jedec) {
-                       if (info->ext_id != 0 && info->ext_id != ext_jedec)
-                               continue;
-                       return &m25p_ids[tmp];
+                       if (info->ext_id == 0 || info->ext_id == ext_jedec)
+                               return &m25p_ids[tmp];
                }
        }
        dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec);
@@ -1226,7 +1233,7 @@ static int m25p_probe(struct spi_device *spi)
        if (info->flags & M25P_NO_FR)
                flash->flash_read = M25P80_NORMAL;
 
-       /* Quad-read mode takes precedence over fast/normal */
+       /* Quad/Dual-read mode takes precedence over fast/normal */
        if (spi->mode & SPI_RX_QUAD && info->flags & M25P80_QUAD_READ) {
                ret = set_quad_mode(flash, info->jedec_id);
                if (ret) {
@@ -1234,6 +1241,8 @@ static int m25p_probe(struct spi_device *spi)
                        return ret;
                }
                flash->flash_read = M25P80_QUAD;
+       } else if (spi->mode & SPI_RX_DUAL && info->flags & M25P80_DUAL_READ) {
+               flash->flash_read = M25P80_DUAL;
        }
 
        /* Default commands */
@@ -1241,6 +1250,9 @@ static int m25p_probe(struct spi_device *spi)
        case M25P80_QUAD:
                flash->read_opcode = OPCODE_QUAD_READ;
                break;
+       case M25P80_DUAL:
+               flash->read_opcode = OPCODE_DUAL_READ;
+               break;
        case M25P80_FAST:
                flash->read_opcode = OPCODE_FAST_READ;
                break;
@@ -1265,6 +1277,9 @@ static int m25p_probe(struct spi_device *spi)
                        case M25P80_QUAD:
                                flash->read_opcode = OPCODE_QUAD_READ_4B;
                                break;
+                       case M25P80_DUAL:
+                               flash->read_opcode = OPCODE_DUAL_READ_4B;
+                               break;
                        case M25P80_FAST:
                                flash->read_opcode = OPCODE_FAST_READ_4B;
                                break;
index 624069d..dd22ce2 100644 (file)
@@ -10,7 +10,6 @@
  * 2 of the License, or (at your option) any later version.
 */
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/device.h>
@@ -440,8 +439,8 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
 
 #ifdef CONFIG_MTD_DATAFLASH_OTP
 
-static int dataflash_get_otp_info(struct mtd_info *mtd,
-               struct otp_info *info, size_t len)
+static int dataflash_get_otp_info(struct mtd_info *mtd, size_t len,
+                                 size_t *retlen, struct otp_info *info)
 {
        /* Report both blocks as identical:  bytes 0..64, locked.
         * Unless the user block changed from all-ones, we can't
@@ -450,7 +449,8 @@ static int dataflash_get_otp_info(struct mtd_info *mtd,
        info->start = 0;
        info->length = 64;
        info->locked = 1;
-       return sizeof(*info);
+       *retlen = sizeof(*info);
+       return 0;
 }
 
 static ssize_t otp_read(struct spi_device *spi, unsigned base,
@@ -542,14 +542,18 @@ static int dataflash_write_user_otp(struct mtd_info *mtd,
        struct dataflash        *priv = mtd->priv;
        int                     status;
 
-       if (len > 64)
-               return -EINVAL;
+       if (from >= 64) {
+               /*
+                * Attempting to write beyond the end of OTP memory,
+                * no data can be written.
+                */
+               *retlen = 0;
+               return 0;
+       }
 
-       /* Strictly speaking, we *could* truncate the write ... but
-        * let's not do that for the only write that's ever possible.
-        */
+       /* Truncate the write to fit into OTP memory. */
        if ((from + len) > 64)
-               return -EINVAL;
+               len = 64 - from;
 
        /* OUT: OP_WRITE_SECURITY, 3 zeroes, 64 data-or-zero bytes
         * IN:  ignore all
index e1f2aeb..2cceebf 100644 (file)
@@ -205,6 +205,8 @@ static inline void kill_final_newline(char *str)
        return 1;               \
 } while (0)
 
+#ifndef MODULE
+static int phram_init_called;
 /*
  * This shall contain the module parameter if any. It is of the form:
  * - phram=<device>,<address>,<size> for module case
@@ -213,9 +215,10 @@ static inline void kill_final_newline(char *str)
  * size.
  * Example: phram.phram=rootfs,0xa0000000,512Mi
  */
-static __initdata char phram_paramline[64 + 20 + 20];
+static char phram_paramline[64 + 20 + 20];
+#endif
 
-static int __init phram_setup(const char *val)
+static int phram_setup(const char *val)
 {
        char buf[64 + 20 + 20], *str = buf;
        char *token[3];
@@ -264,17 +267,36 @@ static int __init phram_setup(const char *val)
        return ret;
 }
 
-static int __init phram_param_call(const char *val, struct kernel_param *kp)
+static int phram_param_call(const char *val, struct kernel_param *kp)
 {
+#ifdef MODULE
+       return phram_setup(val);
+#else
        /*
-        * This function is always called before 'init_phram()', whether
-        * built-in or module.
+        * If more parameters are later passed in via
+        * /sys/module/phram/parameters/phram
+        * and init_phram() has already been called,
+        * we can parse the argument now.
         */
+
+       if (phram_init_called)
+               return phram_setup(val);
+
+       /*
+        * During early boot stage, we only save the parameters
+        * here. We must parse them later: if the param passed
+        * from kernel boot command line, phram_param_call() is
+        * called so early that it is not possible to resolve
+        * the device (even kmalloc() fails). Defer that work to
+        * phram_setup().
+        */
+
        if (strlen(val) >= sizeof(phram_paramline))
                return -ENOSPC;
        strcpy(phram_paramline, val);
 
        return 0;
+#endif
 }
 
 module_param_call(phram, phram_param_call, NULL, NULL, 000);
@@ -283,10 +305,15 @@ MODULE_PARM_DESC(phram, "Memory region to map. \"phram=<name>,<start>,<length>\"
 
 static int __init init_phram(void)
 {
+       int ret = 0;
+
+#ifndef MODULE
        if (phram_paramline[0])
-               return phram_setup(phram_paramline);
+               ret = phram_setup(phram_paramline);
+       phram_init_called = 1;
+#endif
 
-       return 0;
+       return ret;
 }
 
 static void __exit cleanup_phram(void)
index 0c51b98..f02603e 100644 (file)
@@ -725,16 +725,11 @@ static int __init init_pmc551(void)
                }
 
                mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
-               if (!mtd) {
-                       printk(KERN_NOTICE "pmc551: Cannot allocate new MTD "
-                               "device.\n");
+               if (!mtd)
                        break;
-               }
 
                priv = kzalloc(sizeof(struct mypriv), GFP_KERNEL);
                if (!priv) {
-                       printk(KERN_NOTICE "pmc551: Cannot allocate new MTD "
-                               "device.\n");
                        kfree(mtd);
                        break;
                }
diff --git a/drivers/mtd/devices/serial_flash_cmds.h b/drivers/mtd/devices/serial_flash_cmds.h
new file mode 100644 (file)
index 0000000..4f0c2c7
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Generic/SFDP Flash Commands and Device Capabilities
+ *
+ * Copyright (C) 2013 Lee Jones <lee.jones@lianro.org>
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef _MTD_SERIAL_FLASH_CMDS_H
+#define _MTD_SERIAL_FLASH_CMDS_H
+
+/* Generic Flash Commands/OPCODEs */
+#define FLASH_CMD_WREN         0x06
+#define FLASH_CMD_WRDI         0x04
+#define FLASH_CMD_RDID         0x9f
+#define FLASH_CMD_RDSR         0x05
+#define FLASH_CMD_RDSR2                0x35
+#define FLASH_CMD_WRSR         0x01
+#define FLASH_CMD_SE_4K                0x20
+#define FLASH_CMD_SE_32K       0x52
+#define FLASH_CMD_SE           0xd8
+#define FLASH_CMD_CHIPERASE    0xc7
+#define FLASH_CMD_WRVCR                0x81
+#define FLASH_CMD_RDVCR                0x85
+
+/* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
+#define FLASH_CMD_READ         0x03    /* READ */
+#define FLASH_CMD_READ_FAST    0x0b    /* FAST READ */
+#define FLASH_CMD_READ_1_1_2   0x3b    /* DUAL OUTPUT READ */
+#define FLASH_CMD_READ_1_2_2   0xbb    /* DUAL I/O READ */
+#define FLASH_CMD_READ_1_1_4   0x6b    /* QUAD OUTPUT READ */
+#define FLASH_CMD_READ_1_4_4   0xeb    /* QUAD I/O READ */
+
+#define FLASH_CMD_WRITE                0x02    /* PAGE PROGRAM */
+#define FLASH_CMD_WRITE_1_1_2  0xa2    /* DUAL INPUT PROGRAM */
+#define FLASH_CMD_WRITE_1_2_2  0xd2    /* DUAL INPUT EXT PROGRAM */
+#define FLASH_CMD_WRITE_1_1_4  0x32    /* QUAD INPUT PROGRAM */
+#define FLASH_CMD_WRITE_1_4_4  0x12    /* QUAD INPUT EXT PROGRAM */
+
+#define FLASH_CMD_EN4B_ADDR    0xb7    /* Enter 4-byte address mode */
+#define FLASH_CMD_EX4B_ADDR    0xe9    /* Exit 4-byte address mode */
+
+/* READ commands with 32-bit addressing */
+#define FLASH_CMD_READ4                0x13
+#define FLASH_CMD_READ4_FAST   0x0c
+#define FLASH_CMD_READ4_1_1_2  0x3c
+#define FLASH_CMD_READ4_1_2_2  0xbc
+#define FLASH_CMD_READ4_1_1_4  0x6c
+#define FLASH_CMD_READ4_1_4_4  0xec
+
+/* Configuration flags */
+#define FLASH_FLAG_SINGLE      0x000000ff
+#define FLASH_FLAG_READ_WRITE  0x00000001
+#define FLASH_FLAG_READ_FAST   0x00000002
+#define FLASH_FLAG_SE_4K       0x00000004
+#define FLASH_FLAG_SE_32K      0x00000008
+#define FLASH_FLAG_CE          0x00000010
+#define FLASH_FLAG_32BIT_ADDR  0x00000020
+#define FLASH_FLAG_RESET       0x00000040
+#define FLASH_FLAG_DYB_LOCKING 0x00000080
+
+#define FLASH_FLAG_DUAL                0x0000ff00
+#define FLASH_FLAG_READ_1_1_2  0x00000100
+#define FLASH_FLAG_READ_1_2_2  0x00000200
+#define FLASH_FLAG_READ_2_2_2  0x00000400
+#define FLASH_FLAG_WRITE_1_1_2 0x00001000
+#define FLASH_FLAG_WRITE_1_2_2 0x00002000
+#define FLASH_FLAG_WRITE_2_2_2 0x00004000
+
+#define FLASH_FLAG_QUAD                0x00ff0000
+#define FLASH_FLAG_READ_1_1_4  0x00010000
+#define FLASH_FLAG_READ_1_4_4  0x00020000
+#define FLASH_FLAG_READ_4_4_4  0x00040000
+#define FLASH_FLAG_WRITE_1_1_4 0x00100000
+#define FLASH_FLAG_WRITE_1_4_4 0x00200000
+#define FLASH_FLAG_WRITE_4_4_4 0x00400000
+
+#endif /* _MTD_SERIAL_FLASH_CMDS_H */
index 4238214..363da96 100644 (file)
@@ -913,7 +913,6 @@ static int spear_smi_probe(struct platform_device *pdev)
        if (np) {
                pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
                if (!pdata) {
-                       pr_err("%s: ERROR: no memory", __func__);
                        ret = -ENOMEM;
                        goto err;
                }
@@ -943,7 +942,6 @@ static int spear_smi_probe(struct platform_device *pdev)
        dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_ATOMIC);
        if (!dev) {
                ret = -ENOMEM;
-               dev_err(&pdev->dev, "mem alloc fail\n");
                goto err;
        }
 
index 687bf27..c63ecbc 100644 (file)
@@ -15,7 +15,6 @@
  *
  */
 
-#include <linux/init.h>
 #include <linux/module.h>
 #include <linux/device.h>
 #include <linux/mutex.h>
diff --git a/drivers/mtd/devices/st_spi_fsm.c b/drivers/mtd/devices/st_spi_fsm.c
new file mode 100644 (file)
index 0000000..1957d7c
--- /dev/null
@@ -0,0 +1,2108 @@
+/*
+ * st_spi_fsm.c        - ST Fast Sequence Mode (FSM) Serial Flash Controller
+ *
+ * Author: Angus Clark <angus.clark@st.com>
+ *
+ * Copyright (C) 2010-2014 STMicroelectronics Limited
+ *
+ * JEDEC probe based on drivers/mtd/devices/m25p80.c
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include "serial_flash_cmds.h"
+
+/*
+ * FSM SPI Controller Registers
+ */
+#define SPI_CLOCKDIV                   0x0010
+#define SPI_MODESELECT                 0x0018
+#define SPI_CONFIGDATA                 0x0020
+#define SPI_STA_MODE_CHANGE            0x0028
+#define SPI_FAST_SEQ_TRANSFER_SIZE     0x0100
+#define SPI_FAST_SEQ_ADD1              0x0104
+#define SPI_FAST_SEQ_ADD2              0x0108
+#define SPI_FAST_SEQ_ADD_CFG           0x010c
+#define SPI_FAST_SEQ_OPC1              0x0110
+#define SPI_FAST_SEQ_OPC2              0x0114
+#define SPI_FAST_SEQ_OPC3              0x0118
+#define SPI_FAST_SEQ_OPC4              0x011c
+#define SPI_FAST_SEQ_OPC5              0x0120
+#define SPI_MODE_BITS                  0x0124
+#define SPI_DUMMY_BITS                 0x0128
+#define SPI_FAST_SEQ_FLASH_STA_DATA    0x012c
+#define SPI_FAST_SEQ_1                 0x0130
+#define SPI_FAST_SEQ_2                 0x0134
+#define SPI_FAST_SEQ_3                 0x0138
+#define SPI_FAST_SEQ_4                 0x013c
+#define SPI_FAST_SEQ_CFG               0x0140
+#define SPI_FAST_SEQ_STA               0x0144
+#define SPI_QUAD_BOOT_SEQ_INIT_1       0x0148
+#define SPI_QUAD_BOOT_SEQ_INIT_2       0x014c
+#define SPI_QUAD_BOOT_READ_SEQ_1       0x0150
+#define SPI_QUAD_BOOT_READ_SEQ_2       0x0154
+#define SPI_PROGRAM_ERASE_TIME         0x0158
+#define SPI_MULT_PAGE_REPEAT_SEQ_1     0x015c
+#define SPI_MULT_PAGE_REPEAT_SEQ_2     0x0160
+#define SPI_STATUS_WR_TIME_REG         0x0164
+#define SPI_FAST_SEQ_DATA_REG          0x0300
+
+/*
+ * Register: SPI_MODESELECT
+ */
+#define SPI_MODESELECT_CONTIG          0x01
+#define SPI_MODESELECT_FASTREAD                0x02
+#define SPI_MODESELECT_DUALIO          0x04
+#define SPI_MODESELECT_FSM             0x08
+#define SPI_MODESELECT_QUADBOOT                0x10
+
+/*
+ * Register: SPI_CONFIGDATA
+ */
+#define SPI_CFG_DEVICE_ST              0x1
+#define SPI_CFG_DEVICE_ATMEL           0x4
+#define SPI_CFG_MIN_CS_HIGH(x)         (((x) & 0xfff) << 4)
+#define SPI_CFG_CS_SETUPHOLD(x)                (((x) & 0xff) << 16)
+#define SPI_CFG_DATA_HOLD(x)           (((x) & 0xff) << 24)
+
+#define SPI_CFG_DEFAULT_MIN_CS_HIGH    SPI_CFG_MIN_CS_HIGH(0x0AA)
+#define SPI_CFG_DEFAULT_CS_SETUPHOLD   SPI_CFG_CS_SETUPHOLD(0xA0)
+#define SPI_CFG_DEFAULT_DATA_HOLD      SPI_CFG_DATA_HOLD(0x00)
+
+/*
+ * Register: SPI_FAST_SEQ_TRANSFER_SIZE
+ */
+#define TRANSFER_SIZE(x)               ((x) * 8)
+
+/*
+ * Register: SPI_FAST_SEQ_ADD_CFG
+ */
+#define ADR_CFG_CYCLES_ADD1(x)         ((x) << 0)
+#define ADR_CFG_PADS_1_ADD1            (0x0 << 6)
+#define ADR_CFG_PADS_2_ADD1            (0x1 << 6)
+#define ADR_CFG_PADS_4_ADD1            (0x3 << 6)
+#define ADR_CFG_CSDEASSERT_ADD1                (1   << 8)
+#define ADR_CFG_CYCLES_ADD2(x)         ((x) << (0+16))
+#define ADR_CFG_PADS_1_ADD2            (0x0 << (6+16))
+#define ADR_CFG_PADS_2_ADD2            (0x1 << (6+16))
+#define ADR_CFG_PADS_4_ADD2            (0x3 << (6+16))
+#define ADR_CFG_CSDEASSERT_ADD2                (1   << (8+16))
+
+/*
+ * Register: SPI_FAST_SEQ_n
+ */
+#define SEQ_OPC_OPCODE(x)              ((x) << 0)
+#define SEQ_OPC_CYCLES(x)              ((x) << 8)
+#define SEQ_OPC_PADS_1                 (0x0 << 14)
+#define SEQ_OPC_PADS_2                 (0x1 << 14)
+#define SEQ_OPC_PADS_4                 (0x3 << 14)
+#define SEQ_OPC_CSDEASSERT             (1   << 16)
+
+/*
+ * Register: SPI_FAST_SEQ_CFG
+ */
+#define SEQ_CFG_STARTSEQ               (1 << 0)
+#define SEQ_CFG_SWRESET                        (1 << 5)
+#define SEQ_CFG_CSDEASSERT             (1 << 6)
+#define SEQ_CFG_READNOTWRITE           (1 << 7)
+#define SEQ_CFG_ERASE                  (1 << 8)
+#define SEQ_CFG_PADS_1                 (0x0 << 16)
+#define SEQ_CFG_PADS_2                 (0x1 << 16)
+#define SEQ_CFG_PADS_4                 (0x3 << 16)
+
+/*
+ * Register: SPI_MODE_BITS
+ */
+#define MODE_DATA(x)                   (x & 0xff)
+#define MODE_CYCLES(x)                 ((x & 0x3f) << 16)
+#define MODE_PADS_1                    (0x0 << 22)
+#define MODE_PADS_2                    (0x1 << 22)
+#define MODE_PADS_4                    (0x3 << 22)
+#define DUMMY_CSDEASSERT               (1   << 24)
+
+/*
+ * Register: SPI_DUMMY_BITS
+ */
+#define DUMMY_CYCLES(x)                        ((x & 0x3f) << 16)
+#define DUMMY_PADS_1                   (0x0 << 22)
+#define DUMMY_PADS_2                   (0x1 << 22)
+#define DUMMY_PADS_4                   (0x3 << 22)
+#define DUMMY_CSDEASSERT               (1   << 24)
+
+/*
+ * Register: SPI_FAST_SEQ_FLASH_STA_DATA
+ */
+#define STA_DATA_BYTE1(x)              ((x & 0xff) << 0)
+#define STA_DATA_BYTE2(x)              ((x & 0xff) << 8)
+#define STA_PADS_1                     (0x0 << 16)
+#define STA_PADS_2                     (0x1 << 16)
+#define STA_PADS_4                     (0x3 << 16)
+#define STA_CSDEASSERT                 (0x1 << 20)
+#define STA_RDNOTWR                    (0x1 << 21)
+
+/*
+ * FSM SPI Instruction Opcodes
+ */
+#define STFSM_OPC_CMD                  0x1
+#define STFSM_OPC_ADD                  0x2
+#define STFSM_OPC_STA                  0x3
+#define STFSM_OPC_MODE                 0x4
+#define STFSM_OPC_DUMMY                0x5
+#define STFSM_OPC_DATA                 0x6
+#define STFSM_OPC_WAIT                 0x7
+#define STFSM_OPC_JUMP                 0x8
+#define STFSM_OPC_GOTO                 0x9
+#define STFSM_OPC_STOP                 0xF
+
+/*
+ * FSM SPI Instructions (== opcode + operand).
+ */
+#define STFSM_INSTR(cmd, op)           ((cmd) | ((op) << 4))
+
+#define STFSM_INST_CMD1                        STFSM_INSTR(STFSM_OPC_CMD,      1)
+#define STFSM_INST_CMD2                        STFSM_INSTR(STFSM_OPC_CMD,      2)
+#define STFSM_INST_CMD3                        STFSM_INSTR(STFSM_OPC_CMD,      3)
+#define STFSM_INST_CMD4                        STFSM_INSTR(STFSM_OPC_CMD,      4)
+#define STFSM_INST_CMD5                        STFSM_INSTR(STFSM_OPC_CMD,      5)
+#define STFSM_INST_ADD1                        STFSM_INSTR(STFSM_OPC_ADD,      1)
+#define STFSM_INST_ADD2                        STFSM_INSTR(STFSM_OPC_ADD,      2)
+
+#define STFSM_INST_DATA_WRITE          STFSM_INSTR(STFSM_OPC_DATA,     1)
+#define STFSM_INST_DATA_READ           STFSM_INSTR(STFSM_OPC_DATA,     2)
+
+#define STFSM_INST_STA_RD1             STFSM_INSTR(STFSM_OPC_STA,      0x1)
+#define STFSM_INST_STA_WR1             STFSM_INSTR(STFSM_OPC_STA,      0x1)
+#define STFSM_INST_STA_RD2             STFSM_INSTR(STFSM_OPC_STA,      0x2)
+#define STFSM_INST_STA_WR1_2           STFSM_INSTR(STFSM_OPC_STA,      0x3)
+
+#define STFSM_INST_MODE                        STFSM_INSTR(STFSM_OPC_MODE,     0)
+#define STFSM_INST_DUMMY               STFSM_INSTR(STFSM_OPC_DUMMY,    0)
+#define STFSM_INST_WAIT                        STFSM_INSTR(STFSM_OPC_WAIT,     0)
+#define STFSM_INST_STOP                        STFSM_INSTR(STFSM_OPC_STOP,     0)
+
+#define STFSM_DEFAULT_EMI_FREQ 100000000UL                        /* 100 MHz */
+#define STFSM_DEFAULT_WR_TIME  (STFSM_DEFAULT_EMI_FREQ * (15/1000)) /* 15ms */
+
+#define STFSM_FLASH_SAFE_FREQ  10000000UL                         /* 10 MHz */
+
+#define STFSM_MAX_WAIT_SEQ_MS  1000     /* FSM execution time */
+
+/* Flash Commands */
+#define FLASH_CMD_WREN         0x06
+#define FLASH_CMD_WRDI         0x04
+#define FLASH_CMD_RDID         0x9f
+#define FLASH_CMD_RDSR         0x05
+#define FLASH_CMD_RDSR2                0x35
+#define FLASH_CMD_WRSR         0x01
+#define FLASH_CMD_SE_4K                0x20
+#define FLASH_CMD_SE_32K       0x52
+#define FLASH_CMD_SE           0xd8
+#define FLASH_CMD_CHIPERASE    0xc7
+#define FLASH_CMD_WRVCR                0x81
+#define FLASH_CMD_RDVCR                0x85
+
+#define FLASH_CMD_READ         0x03    /* READ */
+#define FLASH_CMD_READ_FAST    0x0b    /* FAST READ */
+#define FLASH_CMD_READ_1_1_2   0x3b    /* DUAL OUTPUT READ */
+#define FLASH_CMD_READ_1_2_2   0xbb    /* DUAL I/O READ */
+#define FLASH_CMD_READ_1_1_4   0x6b    /* QUAD OUTPUT READ */
+#define FLASH_CMD_READ_1_4_4   0xeb    /* QUAD I/O READ */
+
+#define FLASH_CMD_WRITE                0x02    /* PAGE PROGRAM */
+#define FLASH_CMD_WRITE_1_1_2  0xa2    /* DUAL INPUT PROGRAM */
+#define FLASH_CMD_WRITE_1_2_2  0xd2    /* DUAL INPUT EXT PROGRAM */
+#define FLASH_CMD_WRITE_1_1_4  0x32    /* QUAD INPUT PROGRAM */
+#define FLASH_CMD_WRITE_1_4_4  0x12    /* QUAD INPUT EXT PROGRAM */
+
+#define FLASH_CMD_EN4B_ADDR    0xb7    /* Enter 4-byte address mode */
+#define FLASH_CMD_EX4B_ADDR    0xe9    /* Exit 4-byte address mode */
+
+/* READ commands with 32-bit addressing (N25Q256 and S25FLxxxS) */
+#define FLASH_CMD_READ4                0x13
+#define FLASH_CMD_READ4_FAST   0x0c
+#define FLASH_CMD_READ4_1_1_2  0x3c
+#define FLASH_CMD_READ4_1_2_2  0xbc
+#define FLASH_CMD_READ4_1_1_4  0x6c
+#define FLASH_CMD_READ4_1_4_4  0xec
+
+/* S25FLxxxS commands */
+#define S25FL_CMD_WRITE4_1_1_4 0x34
+#define S25FL_CMD_SE4          0xdc
+#define S25FL_CMD_CLSR         0x30
+#define S25FL_CMD_DYBWR                0xe1
+#define S25FL_CMD_DYBRD                0xe0
+#define S25FL_CMD_WRITE4       0x12    /* Note, opcode clashes with
+                                       * 'FLASH_CMD_WRITE_1_4_4'
+                                       * as found on N25Qxxx devices! */
+
+/* Status register */
+#define FLASH_STATUS_BUSY      0x01
+#define FLASH_STATUS_WEL       0x02
+#define FLASH_STATUS_BP0       0x04
+#define FLASH_STATUS_BP1       0x08
+#define FLASH_STATUS_BP2       0x10
+#define FLASH_STATUS_SRWP0     0x80
+#define FLASH_STATUS_TIMEOUT   0xff
+/* S25FL Error Flags */
+#define S25FL_STATUS_E_ERR     0x20
+#define S25FL_STATUS_P_ERR     0x40
+
+#define FLASH_PAGESIZE         256                     /* In Bytes    */
+#define FLASH_PAGESIZE_32      (FLASH_PAGESIZE / 4)    /* In uint32_t */
+#define FLASH_MAX_BUSY_WAIT    (300 * HZ)      /* Maximum 'CHIPERASE' time */
+
+/*
+ * Flags to tweak operation of default read/write/erase routines
+ */
+#define CFG_READ_TOGGLE_32BIT_ADDR     0x00000001
+#define CFG_WRITE_TOGGLE_32BIT_ADDR    0x00000002
+#define CFG_WRITE_EX_32BIT_ADDR_DELAY  0x00000004
+#define CFG_ERASESEC_TOGGLE_32BIT_ADDR 0x00000008
+#define CFG_S25FL_CHECK_ERROR_FLAGS    0x00000010
+
+struct stfsm_seq {
+       uint32_t data_size;
+       uint32_t addr1;
+       uint32_t addr2;
+       uint32_t addr_cfg;
+       uint32_t seq_opc[5];
+       uint32_t mode;
+       uint32_t dummy;
+       uint32_t status;
+       uint8_t  seq[16];
+       uint32_t seq_cfg;
+} __packed __aligned(4);
+
+struct stfsm {
+       struct device           *dev;
+       void __iomem            *base;
+       struct resource         *region;
+       struct mtd_info         mtd;
+       struct mutex            lock;
+       struct flash_info       *info;
+
+       uint32_t                configuration;
+       uint32_t                fifo_dir_delay;
+       bool                    booted_from_spi;
+       bool                    reset_signal;
+       bool                    reset_por;
+
+       struct stfsm_seq stfsm_seq_read;
+       struct stfsm_seq stfsm_seq_write;
+       struct stfsm_seq stfsm_seq_en_32bit_addr;
+};
+
+/* Parameters to configure a READ or WRITE FSM sequence */
+struct seq_rw_config {
+       uint32_t        flags;          /* flags to support config */
+       uint8_t         cmd;            /* FLASH command */
+       int             write;          /* Write Sequence */
+       uint8_t         addr_pads;      /* No. of addr pads (MODE & DUMMY) */
+       uint8_t         data_pads;      /* No. of data pads */
+       uint8_t         mode_data;      /* MODE data */
+       uint8_t         mode_cycles;    /* No. of MODE cycles */
+       uint8_t         dummy_cycles;   /* No. of DUMMY cycles */
+};
+
+/* SPI Flash Device Table */
+struct flash_info {
+       char            *name;
+       /*
+        * 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 FLASH_CMD_SE, which isn't
+        * necessarily called a "sector" by the vendor.
+        */
+       unsigned        sector_size;
+       u16             n_sectors;
+       u32             flags;
+       /*
+        * Note, where FAST_READ is supported, freq_max specifies the
+        * FAST_READ frequency, not the READ frequency.
+        */
+       u32             max_freq;
+       int             (*config)(struct stfsm *);
+};
+
+static int stfsm_n25q_config(struct stfsm *fsm);
+static int stfsm_mx25_config(struct stfsm *fsm);
+static int stfsm_s25fl_config(struct stfsm *fsm);
+static int stfsm_w25q_config(struct stfsm *fsm);
+
+static struct flash_info flash_types[] = {
+       /*
+        * ST Microelectronics/Numonyx --
+        * (newer production versions may have feature updates
+        * (eg faster operating frequency)
+        */
+#define M25P_FLAG (FLASH_FLAG_READ_WRITE | FLASH_FLAG_READ_FAST)
+       { "m25p40",  0x202013, 0,  64 * 1024,   8, M25P_FLAG, 25, NULL },
+       { "m25p80",  0x202014, 0,  64 * 1024,  16, M25P_FLAG, 25, NULL },
+       { "m25p16",  0x202015, 0,  64 * 1024,  32, M25P_FLAG, 25, NULL },
+       { "m25p32",  0x202016, 0,  64 * 1024,  64, M25P_FLAG, 50, NULL },
+       { "m25p64",  0x202017, 0,  64 * 1024, 128, M25P_FLAG, 50, NULL },
+       { "m25p128", 0x202018, 0, 256 * 1024,  64, M25P_FLAG, 50, NULL },
+
+#define M25PX_FLAG (FLASH_FLAG_READ_WRITE      |       \
+                   FLASH_FLAG_READ_FAST        |       \
+                   FLASH_FLAG_READ_1_1_2       |       \
+                   FLASH_FLAG_WRITE_1_1_2)
+       { "m25px32", 0x207116, 0,  64 * 1024,  64, M25PX_FLAG, 75, NULL },
+       { "m25px64", 0x207117, 0,  64 * 1024, 128, M25PX_FLAG, 75, NULL },
+
+#define MX25_FLAG (FLASH_FLAG_READ_WRITE       |       \
+                  FLASH_FLAG_READ_FAST         |       \
+                  FLASH_FLAG_READ_1_1_2        |       \
+                  FLASH_FLAG_READ_1_2_2        |       \
+                  FLASH_FLAG_READ_1_1_4        |       \
+                  FLASH_FLAG_READ_1_4_4        |       \
+                  FLASH_FLAG_SE_4K             |       \
+                  FLASH_FLAG_SE_32K)
+       { "mx25l25635e", 0xc22019, 0, 64*1024, 512,
+         (MX25_FLAG | FLASH_FLAG_32BIT_ADDR | FLASH_FLAG_RESET), 70,
+         stfsm_mx25_config },
+
+#define N25Q_FLAG (FLASH_FLAG_READ_WRITE       |       \
+                  FLASH_FLAG_READ_FAST         |       \
+                  FLASH_FLAG_READ_1_1_2        |       \
+                  FLASH_FLAG_READ_1_2_2        |       \
+                  FLASH_FLAG_READ_1_1_4        |       \
+                  FLASH_FLAG_READ_1_4_4        |       \
+                  FLASH_FLAG_WRITE_1_1_2       |       \
+                  FLASH_FLAG_WRITE_1_2_2       |       \
+                  FLASH_FLAG_WRITE_1_1_4       |       \
+                  FLASH_FLAG_WRITE_1_4_4)
+       { "n25q128", 0x20ba18, 0, 64 * 1024,  256, N25Q_FLAG, 108,
+         stfsm_n25q_config },
+       { "n25q256", 0x20ba19, 0, 64 * 1024,  512,
+         N25Q_FLAG | FLASH_FLAG_32BIT_ADDR, 108, stfsm_n25q_config },
+
+       /*
+        * Spansion S25FLxxxP
+        *     - 256KiB and 64KiB sector variants (identified by ext. JEDEC)
+        */
+#define S25FLXXXP_FLAG (FLASH_FLAG_READ_WRITE  |       \
+                       FLASH_FLAG_READ_1_1_2   |       \
+                       FLASH_FLAG_READ_1_2_2   |       \
+                       FLASH_FLAG_READ_1_1_4   |       \
+                       FLASH_FLAG_READ_1_4_4   |       \
+                       FLASH_FLAG_WRITE_1_1_4  |       \
+                       FLASH_FLAG_READ_FAST)
+       { "s25fl129p0", 0x012018, 0x4d00, 256 * 1024,  64, S25FLXXXP_FLAG, 80,
+         stfsm_s25fl_config },
+       { "s25fl129p1", 0x012018, 0x4d01,  64 * 1024, 256, S25FLXXXP_FLAG, 80,
+         stfsm_s25fl_config },
+
+       /*
+        * Spansion S25FLxxxS
+        *     - 256KiB and 64KiB sector variants (identified by ext. JEDEC)
+        *     - RESET# signal supported by die but not bristled out on all
+        *       package types.  The package type is a function of board design,
+        *       so this information is captured in the board's flags.
+        *     - Supports 'DYB' sector protection. Depending on variant, sectors
+        *       may default to locked state on power-on.
+        */
+#define S25FLXXXS_FLAG (S25FLXXXP_FLAG         |       \
+                       FLASH_FLAG_RESET        |       \
+                       FLASH_FLAG_DYB_LOCKING)
+       { "s25fl128s0", 0x012018, 0x0300,  256 * 1024, 64, S25FLXXXS_FLAG, 80,
+         stfsm_s25fl_config },
+       { "s25fl128s1", 0x012018, 0x0301,  64 * 1024, 256, S25FLXXXS_FLAG, 80,
+         stfsm_s25fl_config },
+       { "s25fl256s0", 0x010219, 0x4d00, 256 * 1024, 128,
+         S25FLXXXS_FLAG | FLASH_FLAG_32BIT_ADDR, 80, stfsm_s25fl_config },
+       { "s25fl256s1", 0x010219, 0x4d01,  64 * 1024, 512,
+         S25FLXXXS_FLAG | FLASH_FLAG_32BIT_ADDR, 80, stfsm_s25fl_config },
+
+       /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
+#define W25X_FLAG (FLASH_FLAG_READ_WRITE       |       \
+                  FLASH_FLAG_READ_FAST         |       \
+                  FLASH_FLAG_READ_1_1_2        |       \
+                  FLASH_FLAG_WRITE_1_1_2)
+       { "w25x40",  0xef3013, 0,  64 * 1024,   8, W25X_FLAG, 75, NULL },
+       { "w25x80",  0xef3014, 0,  64 * 1024,  16, W25X_FLAG, 75, NULL },
+       { "w25x16",  0xef3015, 0,  64 * 1024,  32, W25X_FLAG, 75, NULL },
+       { "w25x32",  0xef3016, 0,  64 * 1024,  64, W25X_FLAG, 75, NULL },
+       { "w25x64",  0xef3017, 0,  64 * 1024, 128, W25X_FLAG, 75, NULL },
+
+       /* Winbond -- w25q "blocks" are 64K, "sectors" are 4KiB */
+#define W25Q_FLAG (FLASH_FLAG_READ_WRITE       |       \
+                  FLASH_FLAG_READ_FAST         |       \
+                  FLASH_FLAG_READ_1_1_2        |       \
+                  FLASH_FLAG_READ_1_2_2        |       \
+                  FLASH_FLAG_READ_1_1_4        |       \
+                  FLASH_FLAG_READ_1_4_4        |       \
+                  FLASH_FLAG_WRITE_1_1_4)
+       { "w25q80",  0xef4014, 0,  64 * 1024,  16, W25Q_FLAG, 80,
+         stfsm_w25q_config },
+       { "w25q16",  0xef4015, 0,  64 * 1024,  32, W25Q_FLAG, 80,
+         stfsm_w25q_config },
+       { "w25q32",  0xef4016, 0,  64 * 1024,  64, W25Q_FLAG, 80,
+         stfsm_w25q_config },
+       { "w25q64",  0xef4017, 0,  64 * 1024, 128, W25Q_FLAG, 80,
+         stfsm_w25q_config },
+
+       /* Sentinel */
+       { NULL, 0x000000, 0, 0, 0, 0, 0, NULL },
+};
+
+/*
+ * FSM message sequence configurations:
+ *
+ * All configs are presented in order of preference
+ */
+
+/* Default READ configurations, in order of preference */
+static struct seq_rw_config default_read_configs[] = {
+       {FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ_1_4_4,   0, 4, 4, 0x00, 2, 4},
+       {FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ_1_1_4,   0, 1, 4, 0x00, 4, 0},
+       {FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ_1_2_2,   0, 2, 2, 0x00, 4, 0},
+       {FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ_1_1_2,   0, 1, 2, 0x00, 0, 8},
+       {FLASH_FLAG_READ_FAST,  FLASH_CMD_READ_FAST,    0, 1, 1, 0x00, 0, 8},
+       {FLASH_FLAG_READ_WRITE, FLASH_CMD_READ,         0, 1, 1, 0x00, 0, 0},
+       {0x00,                  0,                      0, 0, 0, 0x00, 0, 0},
+};
+
+/* Default WRITE configurations */
+static struct seq_rw_config default_write_configs[] = {
+       {FLASH_FLAG_WRITE_1_4_4, FLASH_CMD_WRITE_1_4_4, 1, 4, 4, 0x00, 0, 0},
+       {FLASH_FLAG_WRITE_1_1_4, FLASH_CMD_WRITE_1_1_4, 1, 1, 4, 0x00, 0, 0},
+       {FLASH_FLAG_WRITE_1_2_2, FLASH_CMD_WRITE_1_2_2, 1, 2, 2, 0x00, 0, 0},
+       {FLASH_FLAG_WRITE_1_1_2, FLASH_CMD_WRITE_1_1_2, 1, 1, 2, 0x00, 0, 0},
+       {FLASH_FLAG_READ_WRITE,  FLASH_CMD_WRITE,       1, 1, 1, 0x00, 0, 0},
+       {0x00,                   0,                     0, 0, 0, 0x00, 0, 0},
+};
+
+/*
+ * [N25Qxxx] Configuration
+ */
+#define N25Q_VCR_DUMMY_CYCLES(x)       (((x) & 0xf) << 4)
+#define N25Q_VCR_XIP_DISABLED          ((uint8_t)0x1 << 3)
+#define N25Q_VCR_WRAP_CONT             0x3
+
+/* N25Q 3-byte Address READ configurations
+ *     - 'FAST' variants configured for 8 dummy cycles.
+ *
+ * Note, the number of dummy cycles used for 'FAST' READ operations is
+ * configurable and would normally be tuned according to the READ command and
+ * operating frequency.  However, this applies universally to all 'FAST' READ
+ * commands, including those used by the SPIBoot controller, and remains in
+ * force until the device is power-cycled.  Since the SPIBoot controller is
+ * hard-wired to use 8 dummy cycles, we must configure the device to also use 8
+ * cycles.
+ */
+static struct seq_rw_config n25q_read3_configs[] = {
+       {FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ_1_4_4,   0, 4, 4, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ_1_1_4,   0, 1, 4, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ_1_2_2,   0, 2, 2, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ_1_1_2,   0, 1, 2, 0x00, 0, 8},
+       {FLASH_FLAG_READ_FAST,  FLASH_CMD_READ_FAST,    0, 1, 1, 0x00, 0, 8},
+       {FLASH_FLAG_READ_WRITE, FLASH_CMD_READ,         0, 1, 1, 0x00, 0, 0},
+       {0x00,                  0,                      0, 0, 0, 0x00, 0, 0},
+};
+
+/* N25Q 4-byte Address READ configurations
+ *     - use special 4-byte address READ commands (reduces overheads, and
+ *        reduces risk of hitting watchdog reset issues).
+ *     - 'FAST' variants configured for 8 dummy cycles (see note above.)
+ */
+static struct seq_rw_config n25q_read4_configs[] = {
+       {FLASH_FLAG_READ_1_4_4, FLASH_CMD_READ4_1_4_4,  0, 4, 4, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_1_4, FLASH_CMD_READ4_1_1_4,  0, 1, 4, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_2_2, FLASH_CMD_READ4_1_2_2,  0, 2, 2, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_1_2, FLASH_CMD_READ4_1_1_2,  0, 1, 2, 0x00, 0, 8},
+       {FLASH_FLAG_READ_FAST,  FLASH_CMD_READ4_FAST,   0, 1, 1, 0x00, 0, 8},
+       {FLASH_FLAG_READ_WRITE, FLASH_CMD_READ4,        0, 1, 1, 0x00, 0, 0},
+       {0x00,                  0,                      0, 0, 0, 0x00, 0, 0},
+};
+
+/*
+ * [MX25xxx] Configuration
+ */
+#define MX25_STATUS_QE                 (0x1 << 6)
+
+static int stfsm_mx25_en_32bit_addr_seq(struct stfsm_seq *seq)
+{
+       seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
+                          SEQ_OPC_CYCLES(8) |
+                          SEQ_OPC_OPCODE(FLASH_CMD_EN4B_ADDR) |
+                          SEQ_OPC_CSDEASSERT);
+
+       seq->seq[0] = STFSM_INST_CMD1;
+       seq->seq[1] = STFSM_INST_WAIT;
+       seq->seq[2] = STFSM_INST_STOP;
+
+       seq->seq_cfg = (SEQ_CFG_PADS_1 |
+                       SEQ_CFG_ERASE |
+                       SEQ_CFG_READNOTWRITE |
+                       SEQ_CFG_CSDEASSERT |
+                       SEQ_CFG_STARTSEQ);
+
+       return 0;
+}
+
+/*
+ * [S25FLxxx] Configuration
+ */
+#define STFSM_S25FL_CONFIG_QE          (0x1 << 1)
+
+/*
+ * S25FLxxxS devices provide three ways of supporting 32-bit addressing: Bank
+ * Register, Extended Address Modes, and a 32-bit address command set.  The
+ * 32-bit address command set is used here, since it avoids any problems with
+ * entering a state that is incompatible with the SPIBoot Controller.
+ */
+static struct seq_rw_config stfsm_s25fl_read4_configs[] = {
+       {FLASH_FLAG_READ_1_4_4,  FLASH_CMD_READ4_1_4_4,  0, 4, 4, 0x00, 2, 4},
+       {FLASH_FLAG_READ_1_1_4,  FLASH_CMD_READ4_1_1_4,  0, 1, 4, 0x00, 0, 8},
+       {FLASH_FLAG_READ_1_2_2,  FLASH_CMD_READ4_1_2_2,  0, 2, 2, 0x00, 4, 0},
+       {FLASH_FLAG_READ_1_1_2,  FLASH_CMD_READ4_1_1_2,  0, 1, 2, 0x00, 0, 8},
+       {FLASH_FLAG_READ_FAST,   FLASH_CMD_READ4_FAST,   0, 1, 1, 0x00, 0, 8},
+       {FLASH_FLAG_READ_WRITE,  FLASH_CMD_READ4,        0, 1, 1, 0x00, 0, 0},
+       {0x00,                   0,                      0, 0, 0, 0x00, 0, 0},
+};
+
+static struct seq_rw_config stfsm_s25fl_write4_configs[] = {
+       {FLASH_FLAG_WRITE_1_1_4, S25FL_CMD_WRITE4_1_1_4, 1, 1, 4, 0x00, 0, 0},
+       {FLASH_FLAG_READ_WRITE,  S25FL_CMD_WRITE4,       1, 1, 1, 0x00, 0, 0},
+       {0x00,                   0,                      0, 0, 0, 0x00, 0, 0},
+};
+
+/*
+ * [W25Qxxx] Configuration
+ */
+#define W25Q_STATUS_QE                 (0x1 << 9)
+
+static struct stfsm_seq stfsm_seq_read_jedec = {
+       .data_size = TRANSFER_SIZE(8),
+       .seq_opc[0] = (SEQ_OPC_PADS_1 |
+                      SEQ_OPC_CYCLES(8) |
+                      SEQ_OPC_OPCODE(FLASH_CMD_RDID)),
+       .seq = {
+               STFSM_INST_CMD1,
+               STFSM_INST_DATA_READ,
+               STFSM_INST_STOP,
+       },
+       .seq_cfg = (SEQ_CFG_PADS_1 |
+                   SEQ_CFG_READNOTWRITE |
+                   SEQ_CFG_CSDEASSERT |
+                   SEQ_CFG_STARTSEQ),
+};
+
+static struct stfsm_seq stfsm_seq_read_status_fifo = {
+       .data_size = TRANSFER_SIZE(4),
+       .seq_opc[0] = (SEQ_OPC_PADS_1 |
+                      SEQ_OPC_CYCLES(8) |
+                      SEQ_OPC_OPCODE(FLASH_CMD_RDSR)),
+       .seq = {
+               STFSM_INST_CMD1,
+               STFSM_INST_DATA_READ,
+               STFSM_INST_STOP,
+       },
+       .seq_cfg = (SEQ_CFG_PADS_1 |
+                   SEQ_CFG_READNOTWRITE |
+                   SEQ_CFG_CSDEASSERT |
+                   SEQ_CFG_STARTSEQ),
+};
+
+static struct stfsm_seq stfsm_seq_erase_sector = {
+       /* 'addr_cfg' configured during initialisation */
+       .seq_opc = {
+               (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+                SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
+
+               (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+                SEQ_OPC_OPCODE(FLASH_CMD_SE)),
+       },
+       .seq = {
+               STFSM_INST_CMD1,
+               STFSM_INST_CMD2,
+               STFSM_INST_ADD1,
+               STFSM_INST_ADD2,
+               STFSM_INST_STOP,
+       },
+       .seq_cfg = (SEQ_CFG_PADS_1 |
+                   SEQ_CFG_READNOTWRITE |
+                   SEQ_CFG_CSDEASSERT |
+                   SEQ_CFG_STARTSEQ),
+};
+
+static struct stfsm_seq stfsm_seq_erase_chip = {
+       .seq_opc = {
+               (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+                SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
+
+               (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+                SEQ_OPC_OPCODE(FLASH_CMD_CHIPERASE) | SEQ_OPC_CSDEASSERT),
+       },
+       .seq = {
+               STFSM_INST_CMD1,
+               STFSM_INST_CMD2,
+               STFSM_INST_WAIT,
+               STFSM_INST_STOP,
+       },
+       .seq_cfg = (SEQ_CFG_PADS_1 |
+                   SEQ_CFG_ERASE |
+                   SEQ_CFG_READNOTWRITE |
+                   SEQ_CFG_CSDEASSERT |
+                   SEQ_CFG_STARTSEQ),
+};
+
+static struct stfsm_seq stfsm_seq_write_status = {
+       .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+                      SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
+       .seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+                      SEQ_OPC_OPCODE(FLASH_CMD_WRSR)),
+       .seq = {
+               STFSM_INST_CMD1,
+               STFSM_INST_CMD2,
+               STFSM_INST_STA_WR1,
+               STFSM_INST_STOP,
+       },
+       .seq_cfg = (SEQ_CFG_PADS_1 |
+                   SEQ_CFG_READNOTWRITE |
+                   SEQ_CFG_CSDEASSERT |
+                   SEQ_CFG_STARTSEQ),
+};
+
+static struct stfsm_seq stfsm_seq_wrvcr = {
+       .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+                      SEQ_OPC_OPCODE(FLASH_CMD_WREN) | SEQ_OPC_CSDEASSERT),
+       .seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+                      SEQ_OPC_OPCODE(FLASH_CMD_WRVCR)),
+       .seq = {
+               STFSM_INST_CMD1,
+               STFSM_INST_CMD2,
+               STFSM_INST_STA_WR1,
+               STFSM_INST_STOP,
+       },
+       .seq_cfg = (SEQ_CFG_PADS_1 |
+                   SEQ_CFG_READNOTWRITE |
+                   SEQ_CFG_CSDEASSERT |
+                   SEQ_CFG_STARTSEQ),
+};
+
+static int stfsm_n25q_en_32bit_addr_seq(struct stfsm_seq *seq)
+{
+       seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+                          SEQ_OPC_OPCODE(FLASH_CMD_EN4B_ADDR));
+       seq->seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+                          SEQ_OPC_OPCODE(FLASH_CMD_WREN) |
+                          SEQ_OPC_CSDEASSERT);
+
+       seq->seq[0] = STFSM_INST_CMD2;
+       seq->seq[1] = STFSM_INST_CMD1;
+       seq->seq[2] = STFSM_INST_WAIT;
+       seq->seq[3] = STFSM_INST_STOP;
+
+       seq->seq_cfg = (SEQ_CFG_PADS_1 |
+                       SEQ_CFG_ERASE |
+                       SEQ_CFG_READNOTWRITE |
+                       SEQ_CFG_CSDEASSERT |
+                       SEQ_CFG_STARTSEQ);
+
+       return 0;
+}
+
+static inline int stfsm_is_idle(struct stfsm *fsm)
+{
+       return readl(fsm->base + SPI_FAST_SEQ_STA) & 0x10;
+}
+
+static inline uint32_t stfsm_fifo_available(struct stfsm *fsm)
+{
+       return (readl(fsm->base + SPI_FAST_SEQ_STA) >> 5) & 0x7f;
+}
+
+static void stfsm_clear_fifo(struct stfsm *fsm)
+{
+       uint32_t avail;
+
+       for (;;) {
+               avail = stfsm_fifo_available(fsm);
+               if (!avail)
+                       break;
+
+               while (avail) {
+                       readl(fsm->base + SPI_FAST_SEQ_DATA_REG);
+                       avail--;
+               }
+       }
+}
+
+static inline void stfsm_load_seq(struct stfsm *fsm,
+                                 const struct stfsm_seq *seq)
+{
+       void __iomem *dst = fsm->base + SPI_FAST_SEQ_TRANSFER_SIZE;
+       const uint32_t *src = (const uint32_t *)seq;
+       int words = sizeof(*seq) / sizeof(*src);
+
+       BUG_ON(!stfsm_is_idle(fsm));
+
+       while (words--) {
+               writel(*src, dst);
+               src++;
+               dst += 4;
+       }
+}
+
+static void stfsm_wait_seq(struct stfsm *fsm)
+{
+       unsigned long deadline;
+       int timeout = 0;
+
+       deadline = jiffies + msecs_to_jiffies(STFSM_MAX_WAIT_SEQ_MS);
+
+       while (!timeout) {
+               if (time_after_eq(jiffies, deadline))
+                       timeout = 1;
+
+               if (stfsm_is_idle(fsm))
+                       return;
+
+               cond_resched();
+       }
+
+       dev_err(fsm->dev, "timeout on sequence completion\n");
+}
+
+static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, uint32_t size)
+{
+       uint32_t remaining = size >> 2;
+       uint32_t avail;
+       uint32_t words;
+
+       dev_dbg(fsm->dev, "Reading %d bytes from FIFO\n", size);
+
+       BUG_ON((((uint32_t)buf) & 0x3) || (size & 0x3));
+
+       while (remaining) {
+               for (;;) {
+                       avail = stfsm_fifo_available(fsm);
+                       if (avail)
+                               break;
+                       udelay(1);
+               }
+               words = min(avail, remaining);
+               remaining -= words;
+
+               readsl(fsm->base + SPI_FAST_SEQ_DATA_REG, buf, words);
+               buf += words;
+       }
+}
+
+static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf,
+                           uint32_t size)
+{
+       uint32_t words = size >> 2;
+
+       dev_dbg(fsm->dev, "writing %d bytes to FIFO\n", size);
+
+       BUG_ON((((uint32_t)buf) & 0x3) || (size & 0x3));
+
+       writesl(fsm->base + SPI_FAST_SEQ_DATA_REG, buf, words);
+
+       return size;
+}
+
+static int stfsm_enter_32bit_addr(struct stfsm *fsm, int enter)
+{
+       struct stfsm_seq *seq = &fsm->stfsm_seq_en_32bit_addr;
+       uint32_t cmd = enter ? FLASH_CMD_EN4B_ADDR : FLASH_CMD_EX4B_ADDR;
+
+       seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
+                          SEQ_OPC_CYCLES(8) |
+                          SEQ_OPC_OPCODE(cmd) |
+                          SEQ_OPC_CSDEASSERT);
+
+       stfsm_load_seq(fsm, seq);
+
+       stfsm_wait_seq(fsm);
+
+       return 0;
+}
+
+static uint8_t stfsm_wait_busy(struct stfsm *fsm)
+{
+       struct stfsm_seq *seq = &stfsm_seq_read_status_fifo;
+       unsigned long deadline;
+       uint32_t status;
+       int timeout = 0;
+
+       /* Use RDRS1 */
+       seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
+                          SEQ_OPC_CYCLES(8) |
+                          SEQ_OPC_OPCODE(FLASH_CMD_RDSR));
+
+       /* Load read_status sequence */
+       stfsm_load_seq(fsm, seq);
+
+       /*
+        * Repeat until busy bit is deasserted, or timeout, or error (S25FLxxxS)
+        */
+       deadline = jiffies + FLASH_MAX_BUSY_WAIT;
+       while (!timeout) {
+               if (time_after_eq(jiffies, deadline))
+                       timeout = 1;
+
+               stfsm_wait_seq(fsm);
+
+               stfsm_read_fifo(fsm, &status, 4);
+
+               if ((status & FLASH_STATUS_BUSY) == 0)
+                       return 0;
+
+               if ((fsm->configuration & CFG_S25FL_CHECK_ERROR_FLAGS) &&
+                   ((status & S25FL_STATUS_P_ERR) ||
+                    (status & S25FL_STATUS_E_ERR)))
+                       return (uint8_t)(status & 0xff);
+
+               if (!timeout)
+                       /* Restart */
+                       writel(seq->seq_cfg, fsm->base + SPI_FAST_SEQ_CFG);
+
+               cond_resched();
+       }
+
+       dev_err(fsm->dev, "timeout on wait_busy\n");
+
+       return FLASH_STATUS_TIMEOUT;
+}
+
+static int stfsm_read_status(struct stfsm *fsm, uint8_t cmd,
+                          uint8_t *status)
+{
+       struct stfsm_seq *seq = &stfsm_seq_read_status_fifo;
+       uint32_t tmp;
+
+       dev_dbg(fsm->dev, "reading STA[%s]\n",
+               (cmd == FLASH_CMD_RDSR) ? "1" : "2");
+
+       seq->seq_opc[0] = (SEQ_OPC_PADS_1 |
+                          SEQ_OPC_CYCLES(8) |
+                          SEQ_OPC_OPCODE(cmd)),
+
+       stfsm_load_seq(fsm, seq);
+
+       stfsm_read_fifo(fsm, &tmp, 4);
+
+       *status = (uint8_t)(tmp >> 24);
+
+       stfsm_wait_seq(fsm);
+
+       return 0;
+}
+
+static int stfsm_write_status(struct stfsm *fsm, uint16_t status,
+                              int sta_bytes)
+{
+       struct stfsm_seq *seq = &stfsm_seq_write_status;
+
+       dev_dbg(fsm->dev, "writing STA[%s] 0x%04x\n",
+               (sta_bytes == 1) ? "1" : "1+2", status);
+
+       seq->status = (uint32_t)status | STA_PADS_1 | STA_CSDEASSERT;
+       seq->seq[2] = (sta_bytes == 1) ?
+               STFSM_INST_STA_WR1 : STFSM_INST_STA_WR1_2;
+
+       stfsm_load_seq(fsm, seq);
+
+       stfsm_wait_seq(fsm);
+
+       return 0;
+};
+
+static int stfsm_wrvcr(struct stfsm *fsm, uint8_t data)
+{
+       struct stfsm_seq *seq = &stfsm_seq_wrvcr;
+
+       dev_dbg(fsm->dev, "writing VCR 0x%02x\n", data);
+
+       seq->status = (STA_DATA_BYTE1(data) | STA_PADS_1 | STA_CSDEASSERT);
+
+       stfsm_load_seq(fsm, seq);
+
+       stfsm_wait_seq(fsm);
+
+       return 0;
+}
+
+/*
+ * SoC reset on 'boot-from-spi' systems
+ *
+ * Certain modes of operation cause the Flash device to enter a particular state
+ * for a period of time (e.g. 'Erase Sector', 'Quad Enable', and 'Enter 32-bit
+ * Addr' commands).  On boot-from-spi systems, it is important to consider what
+ * happens if a warm reset occurs during this period.  The SPIBoot controller
+ * assumes that Flash device is in its default reset state, 24-bit address mode,
+ * and ready to accept commands.  This can be achieved using some form of
+ * on-board logic/controller to force a device POR in response to a SoC-level
+ * reset or by making use of the device reset signal if available (limited
+ * number of devices only).
+ *
+ * Failure to take such precautions can cause problems following a warm reset.
+ * For some operations (e.g. ERASE), there is little that can be done.  For
+ * other modes of operation (e.g. 32-bit addressing), options are often
+ * available that can help minimise the window in which a reset could cause a
+ * problem.
+ *
+ */
+static bool stfsm_can_handle_soc_reset(struct stfsm *fsm)
+{
+       /* Reset signal is available on the board and supported by the device */
+       if (fsm->reset_signal && fsm->info->flags & FLASH_FLAG_RESET)
+               return true;
+
+       /* Board-level logic forces a power-on-reset */
+       if (fsm->reset_por)
+               return true;
+
+       /* Reset is not properly handled and may result in failure to reboot */
+       return false;
+}
+
+/* Configure 'addr_cfg' according to addressing mode */
+static void stfsm_prepare_erasesec_seq(struct stfsm *fsm,
+                                      struct stfsm_seq *seq)
+{
+       int addr1_cycles = fsm->info->flags & FLASH_FLAG_32BIT_ADDR ? 16 : 8;
+
+       seq->addr_cfg = (ADR_CFG_CYCLES_ADD1(addr1_cycles) |
+                        ADR_CFG_PADS_1_ADD1 |
+                        ADR_CFG_CYCLES_ADD2(16) |
+                        ADR_CFG_PADS_1_ADD2 |
+                        ADR_CFG_CSDEASSERT_ADD2);
+}
+
+/* Search for preferred configuration based on available flags */
+static struct seq_rw_config *
+stfsm_search_seq_rw_configs(struct stfsm *fsm,
+                           struct seq_rw_config cfgs[])
+{
+       struct seq_rw_config *config;
+       int flags = fsm->info->flags;
+
+       for (config = cfgs; config->cmd != 0; config++)
+               if ((config->flags & flags) == config->flags)
+                       return config;
+
+       return NULL;
+}
+
+/* Prepare a READ/WRITE sequence according to configuration parameters */
+static void stfsm_prepare_rw_seq(struct stfsm *fsm,
+                                struct stfsm_seq *seq,
+                                struct seq_rw_config *cfg)
+{
+       int addr1_cycles, addr2_cycles;
+       int i = 0;
+
+       memset(seq, 0, sizeof(*seq));
+
+       /* Add READ/WRITE OPC  */
+       seq->seq_opc[i++] = (SEQ_OPC_PADS_1 |
+                            SEQ_OPC_CYCLES(8) |
+                            SEQ_OPC_OPCODE(cfg->cmd));
+
+       /* Add WREN OPC for a WRITE sequence */
+       if (cfg->write)
+               seq->seq_opc[i++] = (SEQ_OPC_PADS_1 |
+                                    SEQ_OPC_CYCLES(8) |
+                                    SEQ_OPC_OPCODE(FLASH_CMD_WREN) |
+                                    SEQ_OPC_CSDEASSERT);
+
+       /* Address configuration (24 or 32-bit addresses) */
+       addr1_cycles  = (fsm->info->flags & FLASH_FLAG_32BIT_ADDR) ? 16 : 8;
+       addr1_cycles /= cfg->addr_pads;
+       addr2_cycles  = 16 / cfg->addr_pads;
+       seq->addr_cfg = ((addr1_cycles & 0x3f) << 0 |   /* ADD1 cycles */
+                        (cfg->addr_pads - 1) << 6 |    /* ADD1 pads */
+                        (addr2_cycles & 0x3f) << 16 |  /* ADD2 cycles */
+                        ((cfg->addr_pads - 1) << 22)); /* ADD2 pads */
+
+       /* Data/Sequence configuration */
+       seq->seq_cfg = ((cfg->data_pads - 1) << 16 |
+                       SEQ_CFG_STARTSEQ |
+                       SEQ_CFG_CSDEASSERT);
+       if (!cfg->write)
+               seq->seq_cfg |= SEQ_CFG_READNOTWRITE;
+
+       /* Mode configuration (no. of pads taken from addr cfg) */
+       seq->mode = ((cfg->mode_data & 0xff) << 0 |     /* data */
+                    (cfg->mode_cycles & 0x3f) << 16 |  /* cycles */
+                    (cfg->addr_pads - 1) << 22);       /* pads */
+
+       /* Dummy configuration (no. of pads taken from addr cfg) */
+       seq->dummy = ((cfg->dummy_cycles & 0x3f) << 16 |        /* cycles */
+                     (cfg->addr_pads - 1) << 22);              /* pads */
+
+
+       /* Instruction sequence */
+       i = 0;
+       if (cfg->write)
+               seq->seq[i++] = STFSM_INST_CMD2;
+
+       seq->seq[i++] = STFSM_INST_CMD1;
+
+       seq->seq[i++] = STFSM_INST_ADD1;
+       seq->seq[i++] = STFSM_INST_ADD2;
+
+       if (cfg->mode_cycles)
+               seq->seq[i++] = STFSM_INST_MODE;
+
+       if (cfg->dummy_cycles)
+               seq->seq[i++] = STFSM_INST_DUMMY;
+
+       seq->seq[i++] =
+               cfg->write ? STFSM_INST_DATA_WRITE : STFSM_INST_DATA_READ;
+       seq->seq[i++] = STFSM_INST_STOP;
+}
+
+static int stfsm_search_prepare_rw_seq(struct stfsm *fsm,
+                                      struct stfsm_seq *seq,
+                                      struct seq_rw_config *cfgs)
+{
+       struct seq_rw_config *config;
+
+       config = stfsm_search_seq_rw_configs(fsm, cfgs);
+       if (!config) {
+               dev_err(fsm->dev, "failed to find suitable config\n");
+               return -EINVAL;
+       }
+
+       stfsm_prepare_rw_seq(fsm, seq, config);
+
+       return 0;
+}
+
+/* Prepare a READ/WRITE/ERASE 'default' sequences */
+static int stfsm_prepare_rwe_seqs_default(struct stfsm *fsm)
+{
+       uint32_t flags = fsm->info->flags;
+       int ret;
+
+       /* Configure 'READ' sequence */
+       ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read,
+                                         default_read_configs);
+       if (ret) {
+               dev_err(fsm->dev,
+                       "failed to prep READ sequence with flags [0x%08x]\n",
+                       flags);
+               return ret;
+       }
+
+       /* Configure 'WRITE' sequence */
+       ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write,
+                                         default_write_configs);
+       if (ret) {
+               dev_err(fsm->dev,
+                       "failed to prep WRITE sequence with flags [0x%08x]\n",
+                       flags);
+               return ret;
+       }
+
+       /* Configure 'ERASE_SECTOR' sequence */
+       stfsm_prepare_erasesec_seq(fsm, &stfsm_seq_erase_sector);
+
+       return 0;
+}
+
+static int stfsm_mx25_config(struct stfsm *fsm)
+{
+       uint32_t flags = fsm->info->flags;
+       uint32_t data_pads;
+       uint8_t sta;
+       int ret;
+       bool soc_reset;
+
+       /*
+        * Use default READ/WRITE sequences
+        */
+       ret = stfsm_prepare_rwe_seqs_default(fsm);
+       if (ret)
+               return ret;
+
+       /*
+        * Configure 32-bit Address Support
+        */
+       if (flags & FLASH_FLAG_32BIT_ADDR) {
+               /* Configure 'enter_32bitaddr' FSM sequence */
+               stfsm_mx25_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr);
+
+               soc_reset = stfsm_can_handle_soc_reset(fsm);
+               if (soc_reset || !fsm->booted_from_spi) {
+                       /* If we can handle SoC resets, we enable 32-bit address
+                        * mode pervasively */
+                       stfsm_enter_32bit_addr(fsm, 1);
+
+               } else {
+                       /* Else, enable/disable 32-bit addressing before/after
+                        * each operation */
+                       fsm->configuration = (CFG_READ_TOGGLE_32BIT_ADDR |
+                                             CFG_WRITE_TOGGLE_32BIT_ADDR |
+                                             CFG_ERASESEC_TOGGLE_32BIT_ADDR);
+                       /* It seems a small delay is required after exiting
+                        * 32-bit mode following a write operation.  The issue
+                        * is under investigation.
+                        */
+                       fsm->configuration |= CFG_WRITE_EX_32BIT_ADDR_DELAY;
+               }
+       }
+
+       /* For QUAD mode, set 'QE' STATUS bit */
+       data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
+       if (data_pads == 4) {
+               stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta);
+               sta |= MX25_STATUS_QE;
+               stfsm_write_status(fsm, sta, 1);
+       }
+
+       return 0;
+}
+
+static int stfsm_n25q_config(struct stfsm *fsm)
+{
+       uint32_t flags = fsm->info->flags;
+       uint8_t vcr;
+       int ret = 0;
+       bool soc_reset;
+
+       /* Configure 'READ' sequence */
+       if (flags & FLASH_FLAG_32BIT_ADDR)
+               ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read,
+                                                 n25q_read4_configs);
+       else
+               ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read,
+                                                 n25q_read3_configs);
+       if (ret) {
+               dev_err(fsm->dev,
+                       "failed to prepare READ sequence with flags [0x%08x]\n",
+                       flags);
+               return ret;
+       }
+
+       /* Configure 'WRITE' sequence (default configs) */
+       ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write,
+                                         default_write_configs);
+       if (ret) {
+               dev_err(fsm->dev,
+                       "preparing WRITE sequence using flags [0x%08x] failed\n",
+                       flags);
+               return ret;
+       }
+
+       /* * Configure 'ERASE_SECTOR' sequence */
+       stfsm_prepare_erasesec_seq(fsm, &stfsm_seq_erase_sector);
+
+       /* Configure 32-bit address support */
+       if (flags & FLASH_FLAG_32BIT_ADDR) {
+               stfsm_n25q_en_32bit_addr_seq(&fsm->stfsm_seq_en_32bit_addr);
+
+               soc_reset = stfsm_can_handle_soc_reset(fsm);
+               if (soc_reset || !fsm->booted_from_spi) {
+                       /*
+                        * If we can handle SoC resets, we enable 32-bit
+                        * address mode pervasively
+                        */
+                       stfsm_enter_32bit_addr(fsm, 1);
+               } else {
+                       /*
+                        * If not, enable/disable for WRITE and ERASE
+                        * operations (READ uses special commands)
+                        */
+                       fsm->configuration = (CFG_WRITE_TOGGLE_32BIT_ADDR |
+                                             CFG_ERASESEC_TOGGLE_32BIT_ADDR);
+               }
+       }
+
+       /*
+        * Configure device to use 8 dummy cycles
+        */
+       vcr = (N25Q_VCR_DUMMY_CYCLES(8) | N25Q_VCR_XIP_DISABLED |
+              N25Q_VCR_WRAP_CONT);
+       stfsm_wrvcr(fsm, vcr);
+
+       return 0;
+}
+
+static void stfsm_s25fl_prepare_erasesec_seq_32(struct stfsm_seq *seq)
+{
+       seq->seq_opc[1] = (SEQ_OPC_PADS_1 |
+                          SEQ_OPC_CYCLES(8) |
+                          SEQ_OPC_OPCODE(S25FL_CMD_SE4));
+
+       seq->addr_cfg = (ADR_CFG_CYCLES_ADD1(16) |
+                        ADR_CFG_PADS_1_ADD1 |
+                        ADR_CFG_CYCLES_ADD2(16) |
+                        ADR_CFG_PADS_1_ADD2 |
+                        ADR_CFG_CSDEASSERT_ADD2);
+}
+
+static void stfsm_s25fl_read_dyb(struct stfsm *fsm, uint32_t offs, uint8_t *dby)
+{
+       uint32_t tmp;
+       struct stfsm_seq seq = {
+               .data_size = TRANSFER_SIZE(4),
+               .seq_opc[0] = (SEQ_OPC_PADS_1 |
+                              SEQ_OPC_CYCLES(8) |
+                              SEQ_OPC_OPCODE(S25FL_CMD_DYBRD)),
+               .addr_cfg = (ADR_CFG_CYCLES_ADD1(16) |
+                            ADR_CFG_PADS_1_ADD1 |
+                            ADR_CFG_CYCLES_ADD2(16) |
+                            ADR_CFG_PADS_1_ADD2),
+               .addr1 = (offs >> 16) & 0xffff,
+               .addr2 = offs & 0xffff,
+               .seq = {
+                       STFSM_INST_CMD1,
+                       STFSM_INST_ADD1,
+                       STFSM_INST_ADD2,
+                       STFSM_INST_DATA_READ,
+                       STFSM_INST_STOP,
+               },
+               .seq_cfg = (SEQ_CFG_PADS_1 |
+                           SEQ_CFG_READNOTWRITE |
+                           SEQ_CFG_CSDEASSERT |
+                           SEQ_CFG_STARTSEQ),
+       };
+
+       stfsm_load_seq(fsm, &seq);
+
+       stfsm_read_fifo(fsm, &tmp, 4);
+
+       *dby = (uint8_t)(tmp >> 24);
+
+       stfsm_wait_seq(fsm);
+}
+
+static void stfsm_s25fl_write_dyb(struct stfsm *fsm, uint32_t offs, uint8_t dby)
+{
+       struct stfsm_seq seq = {
+               .seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+                              SEQ_OPC_OPCODE(FLASH_CMD_WREN) |
+                              SEQ_OPC_CSDEASSERT),
+               .seq_opc[1] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
+                              SEQ_OPC_OPCODE(S25FL_CMD_DYBWR)),
+               .addr_cfg = (ADR_CFG_CYCLES_ADD1(16) |
+                            ADR_CFG_PADS_1_ADD1 |
+                            ADR_CFG_CYCLES_ADD2(16) |
+                            ADR_CFG_PADS_1_ADD2),
+               .status = (uint32_t)dby | STA_PADS_1 | STA_CSDEASSERT,
+               .addr1 = (offs >> 16) & 0xffff,
+               .addr2 = offs & 0xffff,
+               .seq = {
+                       STFSM_INST_CMD1,
+                       STFSM_INST_CMD2,
+                       STFSM_INST_ADD1,
+                       STFSM_INST_ADD2,
+                       STFSM_INST_STA_WR1,
+                       STFSM_INST_STOP,
+               },
+               .seq_cfg = (SEQ_CFG_PADS_1 |
+                           SEQ_CFG_READNOTWRITE |
+                           SEQ_CFG_CSDEASSERT |
+                           SEQ_CFG_STARTSEQ),
+       };
+
+       stfsm_load_seq(fsm, &seq);
+       stfsm_wait_seq(fsm);
+
+       stfsm_wait_busy(fsm);
+}
+
+static int stfsm_s25fl_clear_status_reg(struct stfsm *fsm)
+{
+       struct stfsm_seq seq = {
+               .seq_opc[0] = (SEQ_OPC_PADS_1 |
+                              SEQ_OPC_CYCLES(8) |
+                              SEQ_OPC_OPCODE(S25FL_CMD_CLSR) |
+                              SEQ_OPC_CSDEASSERT),
+               .seq_opc[1] = (SEQ_OPC_PADS_1 |
+                              SEQ_OPC_CYCLES(8) |
+                              SEQ_OPC_OPCODE(FLASH_CMD_WRDI) |
+                              SEQ_OPC_CSDEASSERT),
+               .seq = {
+                       STFSM_INST_CMD1,
+                       STFSM_INST_CMD2,
+                       STFSM_INST_WAIT,
+                       STFSM_INST_STOP,
+               },
+               .seq_cfg = (SEQ_CFG_PADS_1 |
+                           SEQ_CFG_ERASE |
+                           SEQ_CFG_READNOTWRITE |
+                           SEQ_CFG_CSDEASSERT |
+                           SEQ_CFG_STARTSEQ),
+       };
+
+       stfsm_load_seq(fsm, &seq);
+
+       stfsm_wait_seq(fsm);
+
+       return 0;
+}
+
+static int stfsm_s25fl_config(struct stfsm *fsm)
+{
+       struct flash_info *info = fsm->info;
+       uint32_t flags = info->flags;
+       uint32_t data_pads;
+       uint32_t offs;
+       uint16_t sta_wr;
+       uint8_t sr1, cr1, dyb;
+       int ret;
+
+       if (flags & FLASH_FLAG_32BIT_ADDR) {
+               /*
+                * Prepare Read/Write/Erase sequences according to S25FLxxx
+                * 32-bit address command set
+                */
+               ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_read,
+                                                 stfsm_s25fl_read4_configs);
+               if (ret)
+                       return ret;
+
+               ret = stfsm_search_prepare_rw_seq(fsm, &fsm->stfsm_seq_write,
+                                                 stfsm_s25fl_write4_configs);
+               if (ret)
+                       return ret;
+
+               stfsm_s25fl_prepare_erasesec_seq_32(&stfsm_seq_erase_sector);
+
+       } else {
+               /* Use default configurations for 24-bit addressing */
+               ret = stfsm_prepare_rwe_seqs_default(fsm);
+               if (ret)
+                       return ret;
+       }
+
+       /*
+        * For devices that support 'DYB' sector locking, check lock status and
+        * unlock sectors if necessary (some variants power-on with sectors
+        * locked by default)
+        */
+       if (flags & FLASH_FLAG_DYB_LOCKING) {
+               offs = 0;
+               for (offs = 0; offs < info->sector_size * info->n_sectors;) {
+                       stfsm_s25fl_read_dyb(fsm, offs, &dyb);
+                       if (dyb == 0x00)
+                               stfsm_s25fl_write_dyb(fsm, offs, 0xff);
+
+                       /* Handle bottom/top 4KiB parameter sectors */
+                       if ((offs < info->sector_size * 2) ||
+                           (offs >= (info->sector_size - info->n_sectors * 4)))
+                               offs += 0x1000;
+                       else
+                               offs += 0x10000;
+               }
+       }
+
+       /* Check status of 'QE' bit */
+       data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
+       stfsm_read_status(fsm, FLASH_CMD_RDSR2, &cr1);
+       if (data_pads == 4) {
+               if (!(cr1 & STFSM_S25FL_CONFIG_QE)) {
+                       /* Set 'QE' */
+                       cr1 |= STFSM_S25FL_CONFIG_QE;
+
+                       stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1);
+                       sta_wr = ((uint16_t)cr1  << 8) | sr1;
+
+                       stfsm_write_status(fsm, sta_wr, 2);
+
+                       stfsm_wait_busy(fsm);
+               }
+       } else {
+               if ((cr1 & STFSM_S25FL_CONFIG_QE)) {
+                       /* Clear 'QE' */
+                       cr1 &= ~STFSM_S25FL_CONFIG_QE;
+
+                       stfsm_read_status(fsm, FLASH_CMD_RDSR, &sr1);
+                       sta_wr = ((uint16_t)cr1  << 8) | sr1;
+
+                       stfsm_write_status(fsm, sta_wr, 2);
+
+                       stfsm_wait_busy(fsm);
+               }
+
+       }
+
+       /*
+        * S25FLxxx devices support Program and Error error flags.
+        * Configure driver to check flags and clear if necessary.
+        */
+       fsm->configuration |= CFG_S25FL_CHECK_ERROR_FLAGS;
+
+       return 0;
+}
+
+static int stfsm_w25q_config(struct stfsm *fsm)
+{
+       uint32_t data_pads;
+       uint16_t sta_wr;
+       uint8_t sta1, sta2;
+       int ret;
+
+       ret = stfsm_prepare_rwe_seqs_default(fsm);
+       if (ret)
+               return ret;
+
+       /* If using QUAD mode, set QE STATUS bit */
+       data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
+       if (data_pads == 4) {
+               stfsm_read_status(fsm, FLASH_CMD_RDSR, &sta1);
+               stfsm_read_status(fsm, FLASH_CMD_RDSR2, &sta2);
+
+               sta_wr = ((uint16_t)sta2 << 8) | sta1;
+
+               sta_wr |= W25Q_STATUS_QE;
+
+               stfsm_write_status(fsm, sta_wr, 2);
+
+               stfsm_wait_busy(fsm);
+       }
+
+       return 0;
+}
+
+static int stfsm_read(struct stfsm *fsm, uint8_t *buf, uint32_t size,
+                     uint32_t offset)
+{
+       struct stfsm_seq *seq = &fsm->stfsm_seq_read;
+       uint32_t data_pads;
+       uint32_t read_mask;
+       uint32_t size_ub;
+       uint32_t size_lb;
+       uint32_t size_mop;
+       uint32_t tmp[4];
+       uint32_t page_buf[FLASH_PAGESIZE_32];
+       uint8_t *p;
+
+       dev_dbg(fsm->dev, "reading %d bytes from 0x%08x\n", size, offset);
+
+       /* Enter 32-bit address mode, if required */
+       if (fsm->configuration & CFG_READ_TOGGLE_32BIT_ADDR)
+               stfsm_enter_32bit_addr(fsm, 1);
+
+       /* Must read in multiples of 32 cycles (or 32*pads/8 Bytes) */
+       data_pads = ((seq->seq_cfg >> 16) & 0x3) + 1;
+       read_mask = (data_pads << 2) - 1;
+
+       /* Handle non-aligned buf */
+       p = ((uint32_t)buf & 0x3) ? (uint8_t *)page_buf : buf;
+
+       /* Handle non-aligned size */
+       size_ub = (size + read_mask) & ~read_mask;
+       size_lb = size & ~read_mask;
+       size_mop = size & read_mask;
+
+       seq->data_size = TRANSFER_SIZE(size_ub);
+       seq->addr1 = (offset >> 16) & 0xffff;
+       seq->addr2 = offset & 0xffff;
+
+       stfsm_load_seq(fsm, seq);
+
+       if (size_lb)
+               stfsm_read_fifo(fsm, (uint32_t *)p, size_lb);
+
+       if (size_mop) {
+               stfsm_read_fifo(fsm, tmp, read_mask + 1);
+               memcpy(p + size_lb, &tmp, size_mop);
+       }
+
+       /* Handle non-aligned buf */
+       if ((uint32_t)buf & 0x3)
+               memcpy(buf, page_buf, size);
+
+       /* Wait for sequence to finish */
+       stfsm_wait_seq(fsm);
+
+       stfsm_clear_fifo(fsm);
+
+       /* Exit 32-bit address mode, if required */
+       if (fsm->configuration & CFG_READ_TOGGLE_32BIT_ADDR)
+               stfsm_enter_32bit_addr(fsm, 0);
+
+       return 0;
+}
+
+static int stfsm_write(struct stfsm *fsm, const uint8_t *buf,
+                      uint32_t size, uint32_t offset)
+{
+       struct stfsm_seq *seq = &fsm->stfsm_seq_write;
+       uint32_t data_pads;
+       uint32_t write_mask;
+       uint32_t size_ub;
+       uint32_t size_lb;
+       uint32_t size_mop;
+       uint32_t tmp[4];
+       uint32_t page_buf[FLASH_PAGESIZE_32];
+       uint8_t *t = (uint8_t *)&tmp;
+       const uint8_t *p;
+       int ret;
+       int i;
+
+       dev_dbg(fsm->dev, "writing %d bytes to 0x%08x\n", size, offset);
+
+       /* Enter 32-bit address mode, if required */
+       if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR)
+               stfsm_enter_32bit_addr(fsm, 1);
+
+       /* Must write in multiples of 32 cycles (or 32*pads/8 bytes) */
+       data_pads = ((seq->seq_cfg >> 16) & 0x3) + 1;
+       write_mask = (data_pads << 2) - 1;
+
+       /* Handle non-aligned buf */
+       if ((uint32_t)buf & 0x3) {
+               memcpy(page_buf, buf, size);
+               p = (uint8_t *)page_buf;
+       } else {
+               p = buf;
+       }
+
+       /* Handle non-aligned size */
+       size_ub = (size + write_mask) & ~write_mask;
+       size_lb = size & ~write_mask;
+       size_mop = size & write_mask;
+
+       seq->data_size = TRANSFER_SIZE(size_ub);
+       seq->addr1 = (offset >> 16) & 0xffff;
+       seq->addr2 = offset & 0xffff;
+
+       /* Need to set FIFO to write mode, before writing data to FIFO (see
+        * GNBvb79594)
+        */
+       writel(0x00040000, fsm->base + SPI_FAST_SEQ_CFG);
+
+       /*
+        * Before writing data to the FIFO, apply a small delay to allow a
+        * potential change of FIFO direction to complete.
+        */
+       if (fsm->fifo_dir_delay == 0)
+               readl(fsm->base + SPI_FAST_SEQ_CFG);
+       else
+               udelay(fsm->fifo_dir_delay);
+
+
+       /* Write data to FIFO, before starting sequence (see GNBvd79593) */
+       if (size_lb) {
+               stfsm_write_fifo(fsm, (uint32_t *)p, size_lb);
+               p += size_lb;
+       }
+
+       /* Handle non-aligned size */
+       if (size_mop) {
+               memset(t, 0xff, write_mask + 1);        /* fill with 0xff's */
+               for (i = 0; i < size_mop; i++)
+                       t[i] = *p++;
+
+               stfsm_write_fifo(fsm, tmp, write_mask + 1);
+       }
+
+       /* Start sequence */
+       stfsm_load_seq(fsm, seq);
+
+       /* Wait for sequence to finish */
+       stfsm_wait_seq(fsm);
+
+       /* Wait for completion */
+       ret = stfsm_wait_busy(fsm);
+       if (ret && fsm->configuration & CFG_S25FL_CHECK_ERROR_FLAGS)
+               stfsm_s25fl_clear_status_reg(fsm);
+
+       /* Exit 32-bit address mode, if required */
+       if (fsm->configuration & CFG_WRITE_TOGGLE_32BIT_ADDR) {
+               stfsm_enter_32bit_addr(fsm, 0);
+               if (fsm->configuration & CFG_WRITE_EX_32BIT_ADDR_DELAY)
+                       udelay(1);
+       }
+
+       return 0;
+}
+
+/*
+ * Read an address range from the flash chip. The address range
+ * may be any size provided it is within the physical boundaries.
+ */
+static int stfsm_mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
+                         size_t *retlen, u_char *buf)
+{
+       struct stfsm *fsm = dev_get_drvdata(mtd->dev.parent);
+       uint32_t bytes;
+
+       dev_dbg(fsm->dev, "%s from 0x%08x, len %zd\n",
+               __func__, (u32)from, len);
+
+       mutex_lock(&fsm->lock);
+
+       while (len > 0) {
+               bytes = min_t(size_t, len, FLASH_PAGESIZE);
+
+               stfsm_read(fsm, buf, bytes, from);
+
+               buf += bytes;
+               from += bytes;
+               len -= bytes;
+
+               *retlen += bytes;
+       }
+
+       mutex_unlock(&fsm->lock);
+
+       return 0;
+}
+
+static int stfsm_erase_sector(struct stfsm *fsm, uint32_t offset)
+{
+       struct stfsm_seq *seq = &stfsm_seq_erase_sector;
+       int ret;
+
+       dev_dbg(fsm->dev, "erasing sector at 0x%08x\n", offset);
+
+       /* Enter 32-bit address mode, if required */
+       if (fsm->configuration & CFG_ERASESEC_TOGGLE_32BIT_ADDR)
+               stfsm_enter_32bit_addr(fsm, 1);
+
+       seq->addr1 = (offset >> 16) & 0xffff;
+       seq->addr2 = offset & 0xffff;
+
+       stfsm_load_seq(fsm, seq);
+
+       stfsm_wait_seq(fsm);
+
+       /* Wait for completion */
+       ret = stfsm_wait_busy(fsm);
+       if (ret && fsm->configuration & CFG_S25FL_CHECK_ERROR_FLAGS)
+               stfsm_s25fl_clear_status_reg(fsm);
+
+       /* Exit 32-bit address mode, if required */
+       if (fsm->configuration & CFG_ERASESEC_TOGGLE_32BIT_ADDR)
+               stfsm_enter_32bit_addr(fsm, 0);
+
+       return ret;
+}
+
+static int stfsm_erase_chip(struct stfsm *fsm)
+{
+       const struct stfsm_seq *seq = &stfsm_seq_erase_chip;
+
+       dev_dbg(fsm->dev, "erasing chip\n");
+
+       stfsm_load_seq(fsm, seq);
+
+       stfsm_wait_seq(fsm);
+
+       return stfsm_wait_busy(fsm);
+}
+
+/*
+ * Write an address range to the flash chip.  Data must be written in
+ * FLASH_PAGESIZE chunks.  The address range may be any size provided
+ * it is within the physical boundaries.
+ */
+static int stfsm_mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
+                          size_t *retlen, const u_char *buf)
+{
+       struct stfsm *fsm = dev_get_drvdata(mtd->dev.parent);
+
+       u32 page_offs;
+       u32 bytes;
+       uint8_t *b = (uint8_t *)buf;
+       int ret = 0;
+
+       dev_dbg(fsm->dev, "%s to 0x%08x, len %zd\n", __func__, (u32)to, len);
+
+       /* Offset within page */
+       page_offs = to % FLASH_PAGESIZE;
+
+       mutex_lock(&fsm->lock);
+
+       while (len) {
+               /* Write up to page boundary */
+               bytes = min(FLASH_PAGESIZE - page_offs, len);
+
+               ret = stfsm_write(fsm, b, bytes, to);
+               if (ret)
+                       goto out1;
+
+               b += bytes;
+               len -= bytes;
+               to += bytes;
+
+               /* We are now page-aligned */
+               page_offs = 0;
+
+               *retlen += bytes;
+
+       }
+
+out1:
+       mutex_unlock(&fsm->lock);
+
+       return ret;
+}
+
+/*
+ * Erase an address range on the flash chip. The address range may extend
+ * one or more erase sectors.  Return an error is there is a problem erasing.
+ */
+static int stfsm_mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+       struct stfsm *fsm = dev_get_drvdata(mtd->dev.parent);
+       u32 addr, len;
+       int ret;
+
+       dev_dbg(fsm->dev, "%s at 0x%llx, len %lld\n", __func__,
+               (long long)instr->addr, (long long)instr->len);
+
+       addr = instr->addr;
+       len = instr->len;
+
+       mutex_lock(&fsm->lock);
+
+       /* Whole-chip erase? */
+       if (len == mtd->size) {
+               ret = stfsm_erase_chip(fsm);
+               if (ret)
+                       goto out1;
+       } else {
+               while (len) {
+                       ret = stfsm_erase_sector(fsm, addr);
+                       if (ret)
+                               goto out1;
+
+                       addr += mtd->erasesize;
+                       len -= mtd->erasesize;
+               }
+       }
+
+       mutex_unlock(&fsm->lock);
+
+       instr->state = MTD_ERASE_DONE;
+       mtd_erase_callback(instr);
+
+       return 0;
+
+out1:
+       instr->state = MTD_ERASE_FAILED;
+       mutex_unlock(&fsm->lock);
+
+       return ret;
+}
+
+static void stfsm_read_jedec(struct stfsm *fsm, uint8_t *jedec)
+{
+       const struct stfsm_seq *seq = &stfsm_seq_read_jedec;
+       uint32_t tmp[2];
+
+       stfsm_load_seq(fsm, seq);
+
+       stfsm_read_fifo(fsm, tmp, 8);
+
+       memcpy(jedec, tmp, 5);
+
+       stfsm_wait_seq(fsm);
+}
+
+static struct flash_info *stfsm_jedec_probe(struct stfsm *fsm)
+{
+       struct flash_info       *info;
+       u16                     ext_jedec;
+       u32                     jedec;
+       u8                      id[5];
+
+       stfsm_read_jedec(fsm, id);
+
+       jedec     = id[0] << 16 | id[1] << 8 | id[2];
+       /*
+        * JEDEC also defines an optional "extended device information"
+        * string for after vendor-specific data, after the three bytes
+        * we use here. Supporting some chips might require using it.
+        */
+       ext_jedec = id[3] << 8  | id[4];
+
+       dev_dbg(fsm->dev, "JEDEC =  0x%08x [%02x %02x %02x %02x %02x]\n",
+               jedec, id[0], id[1], id[2], id[3], id[4]);
+
+       for (info = flash_types; info->name; info++) {
+               if (info->jedec_id == jedec) {
+                       if (info->ext_id && info->ext_id != ext_jedec)
+                               continue;
+                       return info;
+               }
+       }
+       dev_err(fsm->dev, "Unrecognized JEDEC id %06x\n", jedec);
+
+       return NULL;
+}
+
+static int stfsm_set_mode(struct stfsm *fsm, uint32_t mode)
+{
+       int ret, timeout = 10;
+
+       /* Wait for controller to accept mode change */
+       while (--timeout) {
+               ret = readl(fsm->base + SPI_STA_MODE_CHANGE);
+               if (ret & 0x1)
+                       break;
+               udelay(1);
+       }
+
+       if (!timeout)
+               return -EBUSY;
+
+       writel(mode, fsm->base + SPI_MODESELECT);
+
+       return 0;
+}
+
+static void stfsm_set_freq(struct stfsm *fsm, uint32_t spi_freq)
+{
+       uint32_t emi_freq;
+       uint32_t clk_div;
+
+       /* TODO: Make this dynamic */
+       emi_freq = STFSM_DEFAULT_EMI_FREQ;
+
+       /*
+        * Calculate clk_div - values between 2 and 128
+        * Multiple of 2, rounded up
+        */
+       clk_div = 2 * DIV_ROUND_UP(emi_freq, 2 * spi_freq);
+       if (clk_div < 2)
+               clk_div = 2;
+       else if (clk_div > 128)
+               clk_div = 128;
+
+       /*
+        * Determine a suitable delay for the IP to complete a change of
+        * direction of the FIFO. The required delay is related to the clock
+        * divider used. The following heuristics are based on empirical tests,
+        * using a 100MHz EMI clock.
+        */
+       if (clk_div <= 4)
+               fsm->fifo_dir_delay = 0;
+       else if (clk_div <= 10)
+               fsm->fifo_dir_delay = 1;
+       else
+               fsm->fifo_dir_delay = DIV_ROUND_UP(clk_div, 10);
+
+       dev_dbg(fsm->dev, "emi_clk = %uHZ, spi_freq = %uHZ, clk_div = %u\n",
+               emi_freq, spi_freq, clk_div);
+
+       writel(clk_div, fsm->base + SPI_CLOCKDIV);
+}
+
+static int stfsm_init(struct stfsm *fsm)
+{
+       int ret;
+
+       /* Perform a soft reset of the FSM controller */
+       writel(SEQ_CFG_SWRESET, fsm->base + SPI_FAST_SEQ_CFG);
+       udelay(1);
+       writel(0, fsm->base + SPI_FAST_SEQ_CFG);
+
+       /* Set clock to 'safe' frequency initially */
+       stfsm_set_freq(fsm, STFSM_FLASH_SAFE_FREQ);
+
+       /* Switch to FSM */
+       ret = stfsm_set_mode(fsm, SPI_MODESELECT_FSM);
+       if (ret)
+               return ret;
+
+       /* Set timing parameters */
+       writel(SPI_CFG_DEVICE_ST            |
+              SPI_CFG_DEFAULT_MIN_CS_HIGH  |
+              SPI_CFG_DEFAULT_CS_SETUPHOLD |
+              SPI_CFG_DEFAULT_DATA_HOLD,
+              fsm->base + SPI_CONFIGDATA);
+       writel(STFSM_DEFAULT_WR_TIME, fsm->base + SPI_STATUS_WR_TIME_REG);
+
+       /* Clear FIFO, just in case */
+       stfsm_clear_fifo(fsm);
+
+       return 0;
+}
+
+static void stfsm_fetch_platform_configs(struct platform_device *pdev)
+{
+       struct stfsm *fsm = platform_get_drvdata(pdev);
+       struct device_node *np = pdev->dev.of_node;
+       struct regmap *regmap;
+       uint32_t boot_device_reg;
+       uint32_t boot_device_spi;
+       uint32_t boot_device;     /* Value we read from *boot_device_reg */
+       int ret;
+
+       /* Booting from SPI NOR Flash is the default */
+       fsm->booted_from_spi = true;
+
+       regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+       if (IS_ERR(regmap))
+               goto boot_device_fail;
+
+       fsm->reset_signal = of_property_read_bool(np, "st,reset-signal");
+
+       fsm->reset_por = of_property_read_bool(np, "st,reset-por");
+
+       /* Where in the syscon the boot device information lives */
+       ret = of_property_read_u32(np, "st,boot-device-reg", &boot_device_reg);
+       if (ret)
+               goto boot_device_fail;
+
+       /* Boot device value when booted from SPI NOR */
+       ret = of_property_read_u32(np, "st,boot-device-spi", &boot_device_spi);
+       if (ret)
+               goto boot_device_fail;
+
+       ret = regmap_read(regmap, boot_device_reg, &boot_device);
+       if (ret)
+               goto boot_device_fail;
+
+       if (boot_device != boot_device_spi)
+               fsm->booted_from_spi = false;
+
+       return;
+
+boot_device_fail:
+       dev_warn(&pdev->dev,
+                "failed to fetch boot device, assuming boot from SPI\n");
+}
+
+static int stfsm_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct mtd_part_parser_data ppdata;
+       struct flash_info *info;
+       struct resource *res;
+       struct stfsm *fsm;
+       int ret;
+
+       if (!np) {
+               dev_err(&pdev->dev, "No DT found\n");
+               return -EINVAL;
+       }
+       ppdata.of_node = np;
+
+       fsm = devm_kzalloc(&pdev->dev, sizeof(*fsm), GFP_KERNEL);
+       if (!fsm)
+               return -ENOMEM;
+
+       fsm->dev = &pdev->dev;
+
+       platform_set_drvdata(pdev, fsm);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "Resource not found\n");
+               return -ENODEV;
+       }
+
+       fsm->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(fsm->base)) {
+               dev_err(&pdev->dev,
+                       "Failed to reserve memory region %pR\n", res);
+               return PTR_ERR(fsm->base);
+       }
+
+       mutex_init(&fsm->lock);
+
+       ret = stfsm_init(fsm);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to initialise FSM Controller\n");
+               return ret;
+       }
+
+       stfsm_fetch_platform_configs(pdev);
+
+       /* Detect SPI FLASH device */
+       info = stfsm_jedec_probe(fsm);
+       if (!info)
+               return -ENODEV;
+       fsm->info = info;
+
+       /* Use device size to determine address width */
+       if (info->sector_size * info->n_sectors > 0x1000000)
+               info->flags |= FLASH_FLAG_32BIT_ADDR;
+
+       /*
+        * Configure READ/WRITE/ERASE sequences according to platform and
+        * device flags.
+        */
+       if (info->config) {
+               ret = info->config(fsm);
+               if (ret)
+                       return ret;
+       } else {
+               ret = stfsm_prepare_rwe_seqs_default(fsm);
+               if (ret)
+                       return ret;
+       }
+
+       fsm->mtd.name           = info->name;
+       fsm->mtd.dev.parent     = &pdev->dev;
+       fsm->mtd.type           = MTD_NORFLASH;
+       fsm->mtd.writesize      = 4;
+       fsm->mtd.writebufsize   = fsm->mtd.writesize;
+       fsm->mtd.flags          = MTD_CAP_NORFLASH;
+       fsm->mtd.size           = info->sector_size * info->n_sectors;
+       fsm->mtd.erasesize      = info->sector_size;
+
+       fsm->mtd._read  = stfsm_mtd_read;
+       fsm->mtd._write = stfsm_mtd_write;
+       fsm->mtd._erase = stfsm_mtd_erase;
+
+       dev_info(&pdev->dev,
+               "Found serial flash device: %s\n"
+               " size = %llx (%lldMiB) erasesize = 0x%08x (%uKiB)\n",
+               info->name,
+               (long long)fsm->mtd.size, (long long)(fsm->mtd.size >> 20),
+               fsm->mtd.erasesize, (fsm->mtd.erasesize >> 10));
+
+       return mtd_device_parse_register(&fsm->mtd, NULL, &ppdata, NULL, 0);
+}
+
+static int stfsm_remove(struct platform_device *pdev)
+{
+       struct stfsm *fsm = platform_get_drvdata(pdev);
+
+       return mtd_device_unregister(&fsm->mtd);
+}
+
+static struct of_device_id stfsm_match[] = {
+       { .compatible = "st,spi-fsm", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, stfsm_match);
+
+static struct platform_driver stfsm_driver = {
+       .probe          = stfsm_probe,
+       .remove         = stfsm_remove,
+       .driver         = {
+               .name   = "st-spi-fsm",
+               .owner  = THIS_MODULE,
+               .of_match_table = stfsm_match,
+       },
+};
+module_platform_driver(stfsm_driver);
+
+MODULE_AUTHOR("Angus Clark <angus.clark@st.com>");
+MODULE_DESCRIPTION("ST SPI FSM driver");
+MODULE_LICENSE("GPL");
index 4adc037..487e64f 100644 (file)
@@ -30,7 +30,6 @@
 #include <asm/uaccess.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
-#include <linux/init.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nftl.h>
 #include <linux/mtd/inftl.h>
index d38b646..018c75f 100644 (file)
@@ -55,10 +55,8 @@ struct mtd_info *lpddr_cmdset(struct map_info *map)
        int i, j;
 
        mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
-       if (!mtd) {
-               printk(KERN_ERR "Failed to allocate memory for MTD device\n");
+       if (!mtd)
                return NULL;
-       }
        mtd->priv = map;
        mtd->type = MTD_NORFLASH;
 
index 45abed6..69f2112 100644 (file)
@@ -135,11 +135,8 @@ static int lpddr_chip_setup(struct map_info *map, struct lpddr_private *lpddr)
 {
 
        lpddr->qinfo = kzalloc(sizeof(struct qinfo_chip), GFP_KERNEL);
-       if (!lpddr->qinfo) {
-               printk(KERN_WARNING "%s: no memory for LPDDR qinfo structure\n",
-                               map->name);
+       if (!lpddr->qinfo)
                return 0;
-       }
 
        /* Get the ManuID */
        lpddr->ManufactId = CMDVAL(map_read(map, map->pfow_base + PFOW_MANUFACTURER_ID));
index 310dc7c..fce23fe 100644 (file)
@@ -66,11 +66,11 @@ config MTD_PHYSMAP_BANKWIDTH
          used internally by the CFI drivers.
 
 config MTD_PHYSMAP_OF
-       tristate "Flash device in physical memory map based on OF description"
-       depends on OF && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM)
+       tristate "Memory device in physical memory map based on OF description"
+       depends on OF && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM || MTD_RAM)
        help
-         This provides a 'mapping' driver which allows the NOR Flash and
-         ROM driver code to communicate with chips which are mapped
+         This provides a 'mapping' driver which allows the NOR Flash, ROM
+         and RAM driver code to communicate with chips which are mapped
          physically into the CPU's memory. The mapping description here is
          taken from OF device tree.
 
@@ -124,7 +124,7 @@ config MTD_NETSC520
 
 config MTD_TS5500
        tristate "JEDEC Flash device mapped on Technologic Systems TS-5500"
-       depends on X86
+       depends on TS5500 || COMPILE_TEST
        select MTD_JEDECPROBE
        select MTD_CFI_AMDSTD
        help
index 5434d8d..6ea51e5 100644 (file)
@@ -14,7 +14,6 @@
  * Licensed under the GPL-2 or later.
  */
 
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mtd/mtd.h>
index 1adba86..a4c477b 100644 (file)
@@ -14,7 +14,6 @@
  */
 
 #include <linux/gpio.h>
-#include <linux/init.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
index 46d195f..5ab71f0 100644 (file)
@@ -31,7 +31,6 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/pci.h>
-#include <linux/init.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/partitions.h>
index d6b2451..6a589f1 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/err.h>
 #include <linux/module.h>
 #include <linux/types.h>
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/slab.h>
index 93c507a..7aa682c 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/kernel.h>
 #include <linux/io.h>
 #include <linux/slab.h>
-#include <linux/init.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/partitions.h>
index 98bb5d5..cadfbe0 100644 (file)
@@ -10,7 +10,6 @@
  * kind, whether express or implied.
  */
 
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mtd/mtd.h>
index 36da518..eb0242e 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/pci.h>
-#include <linux/init.h>
 #include <linux/slab.h>
 
 #include <linux/mtd/mtd.h>
index d111097..217c25d 100644 (file)
@@ -15,7 +15,6 @@
 
 #include <linux/module.h>
 #include <linux/types.h>
-#include <linux/init.h>
 #include <linux/device.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
index 10196f5..d597e89 100644 (file)
@@ -23,7 +23,6 @@
 
 #include <linux/module.h>
 #include <linux/types.h>
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/ioport.h>
@@ -138,7 +137,6 @@ static int platram_probe(struct platform_device *pdev)
 
        info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (info == NULL) {
-               dev_err(&pdev->dev, "no memory for flash info\n");
                err = -ENOMEM;
                goto exit_error;
        }
index 9aad854..cb4d92e 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
index 9352512..146b604 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/device.h>
 #include <linux/platform_device.h>
index 3051c4c..b7a22a6 100644 (file)
@@ -47,7 +47,6 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
index 39cc418..b6f1aac 100644 (file)
@@ -11,7 +11,6 @@
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/errno.h>
-#include <linux/init.h>
 #include <linux/ioport.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
index 5073cbc..0b2ccb6 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/blkpg.h>
 #include <linux/spinlock.h>
 #include <linux/hdreg.h>
-#include <linux/init.h>
 #include <linux/mutex.h>
 #include <asm/uaccess.h>
 
index 2147e73..7d4e7b9 100644 (file)
@@ -324,6 +324,15 @@ static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t c
                default:
                        ret = mtd_write(mtd, *ppos, len, &retlen, kbuf);
                }
+
+               /*
+                * Return -ENOSPC only if no data could be written at all.
+                * Otherwise just return the number of bytes that actually
+                * have been written.
+                */
+               if ((ret == -ENOSPC) && (total_retlen))
+                       break;
+
                if (!ret) {
                        *ppos += retlen;
                        total_retlen += retlen;
@@ -889,25 +898,26 @@ static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
        case OTPGETREGIONINFO:
        {
                struct otp_info *buf = kmalloc(4096, GFP_KERNEL);
+               size_t retlen;
                if (!buf)
                        return -ENOMEM;
                switch (mfi->mode) {
                case MTD_FILE_MODE_OTP_FACTORY:
-                       ret = mtd_get_fact_prot_info(mtd, buf, 4096);
+                       ret = mtd_get_fact_prot_info(mtd, 4096, &retlen, buf);
                        break;
                case MTD_FILE_MODE_OTP_USER:
-                       ret = mtd_get_user_prot_info(mtd, buf, 4096);
+                       ret = mtd_get_user_prot_info(mtd, 4096, &retlen, buf);
                        break;
                default:
                        ret = -EINVAL;
                        break;
                }
-               if (ret >= 0) {
+               if (!ret) {
                        if (cmd == OTPGETREGIONCOUNT) {
-                               int nbr = ret / sizeof(struct otp_info);
+                               int nbr = retlen / sizeof(struct otp_info);
                                ret = copy_to_user(argp, &nbr, sizeof(int));
                        } else
-                               ret = copy_to_user(argp, buf, ret);
+                               ret = copy_to_user(argp, buf, retlen);
                        if (ret)
                                ret = -EFAULT;
                }
index 34c0b16..d201fee 100644 (file)
@@ -883,14 +883,14 @@ EXPORT_SYMBOL_GPL(mtd_read_oob);
  * devices. The user data is one time programmable but the factory data is read
  * only.
  */
-int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
-                          size_t len)
+int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
+                          struct otp_info *buf)
 {
        if (!mtd->_get_fact_prot_info)
                return -EOPNOTSUPP;
        if (!len)
                return 0;
-       return mtd->_get_fact_prot_info(mtd, buf, len);
+       return mtd->_get_fact_prot_info(mtd, len, retlen, buf);
 }
 EXPORT_SYMBOL_GPL(mtd_get_fact_prot_info);
 
@@ -906,14 +906,14 @@ int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
 }
 EXPORT_SYMBOL_GPL(mtd_read_fact_prot_reg);
 
-int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf,
-                          size_t len)
+int mtd_get_user_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
+                          struct otp_info *buf)
 {
        if (!mtd->_get_user_prot_info)
                return -EOPNOTSUPP;
        if (!len)
                return 0;
-       return mtd->_get_user_prot_info(mtd, buf, len);
+       return mtd->_get_user_prot_info(mtd, len, retlen, buf);
 }
 EXPORT_SYMBOL_GPL(mtd_get_user_prot_info);
 
@@ -932,12 +932,22 @@ EXPORT_SYMBOL_GPL(mtd_read_user_prot_reg);
 int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
                            size_t *retlen, u_char *buf)
 {
+       int ret;
+
        *retlen = 0;
        if (!mtd->_write_user_prot_reg)
                return -EOPNOTSUPP;
        if (!len)
                return 0;
-       return mtd->_write_user_prot_reg(mtd, to, len, retlen, buf);
+       ret = mtd->_write_user_prot_reg(mtd, to, len, retlen, buf);
+       if (ret)
+               return ret;
+
+       /*
+        * If no data could be written at all, we are out of memory and
+        * must return -ENOSPC.
+        */
+       return (*retlen) ? 0 : -ENOSPC;
 }
 EXPORT_SYMBOL_GPL(mtd_write_user_prot_reg);
 
index 3c7d6d7..1ca9aec 100644 (file)
@@ -150,11 +150,12 @@ static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
                                                 retlen, buf);
 }
 
-static int part_get_user_prot_info(struct mtd_info *mtd,
-               struct otp_info *buf, size_t len)
+static int part_get_user_prot_info(struct mtd_info *mtd, size_t len,
+                                  size_t *retlen, struct otp_info *buf)
 {
        struct mtd_part *part = PART(mtd);
-       return part->master->_get_user_prot_info(part->master, buf, len);
+       return part->master->_get_user_prot_info(part->master, len, retlen,
+                                                buf);
 }
 
 static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
@@ -165,11 +166,12 @@ static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
                                                 retlen, buf);
 }
 
-static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
-               size_t len)
+static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len,
+                                  size_t *retlen, struct otp_info *buf)
 {
        struct mtd_part *part = PART(mtd);
-       return part->master->_get_fact_prot_info(part->master, buf, len);
+       return part->master->_get_fact_prot_info(part->master, len, retlen,
+                                                buf);
 }
 
 static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
index a4bee41..f1cf503 100644 (file)
@@ -460,6 +460,8 @@ config MTD_NAND_MXC
 config MTD_NAND_SH_FLCTL
        tristate "Support for NAND on Renesas SuperH FLCTL"
        depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
+       depends on HAS_IOMEM
+       depends on HAS_DMA
        help
          Several Renesas SuperH CPU has FLCTL. This option enables support
          for NAND Flash using FLCTL.
index 8611eb4..4936e9e 100644 (file)
@@ -17,7 +17,6 @@
  */
 
 #include <linux/slab.h>
-#include <linux/init.h>
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/mtd/mtd.h>
index c36e9b8..4ce181a 100644 (file)
@@ -430,7 +430,7 @@ err_dma:
        dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
 err_buf:
        if (err != 0)
-               dev_warn(host->dev, "Fall back to CPU I/O\n");
+               dev_dbg(host->dev, "Fall back to CPU I/O\n");
        return err;
 }
 
@@ -1220,6 +1220,7 @@ static int atmel_pmecc_nand_init_params(struct platform_device *pdev,
                goto err;
        }
 
+       nand_chip->options |= NAND_NO_SUBPAGE_WRITE;
        nand_chip->ecc.read_page = atmel_nand_pmecc_read_page;
        nand_chip->ecc.write_page = atmel_nand_pmecc_write_page;
 
@@ -1659,8 +1660,8 @@ static void nfc_select_chip(struct mtd_info *mtd, int chip)
                nfc_writel(host->nfc->hsmc_regs, CTRL, NFC_CTRL_ENABLE);
 }
 
-static int nfc_make_addr(struct mtd_info *mtd, int column, int page_addr,
-               unsigned int *addr1234, unsigned int *cycle0)
+static int nfc_make_addr(struct mtd_info *mtd, int command, int column,
+               int page_addr, unsigned int *addr1234, unsigned int *cycle0)
 {
        struct nand_chip *chip = mtd->priv;
 
@@ -1674,7 +1675,8 @@ static int nfc_make_addr(struct mtd_info *mtd, int column, int page_addr,
        *addr1234 = 0;
 
        if (column != -1) {
-               if (chip->options & NAND_BUSWIDTH_16)
+               if (chip->options & NAND_BUSWIDTH_16 &&
+                               !nand_opcode_8bits(command))
                        column >>= 1;
                addr_bytes[acycle++] = column & 0xff;
                if (mtd->writesize > 512)
@@ -1787,8 +1789,8 @@ static void nfc_nand_command(struct mtd_info *mtd, unsigned int command,
        }
 
        if (do_addr)
-               acycle = nfc_make_addr(mtd, column, page_addr, &addr1234,
-                               &cycle0);
+               acycle = nfc_make_addr(mtd, command, column, page_addr,
+                               &addr1234, &cycle0);
 
        nfc_addr_cmd = cmd1 | cmd2 | vcmd2 | acycle | csid | dataen | nfcwr;
        nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0);
index 2880d88..bc5c518 100644 (file)
@@ -11,7 +11,6 @@
 
 #include <linux/slab.h>
 #include <linux/gpio.h>
-#include <linux/init.h>
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/mtd/mtd.h>
@@ -308,7 +307,8 @@ static void au1550_command(struct mtd_info *mtd, unsigned command, int column, i
                /* Serially input address */
                if (column != -1) {
                        /* Adjust columns for 16 bit buswidth */
-                       if (this->options & NAND_BUSWIDTH_16)
+                       if (this->options & NAND_BUSWIDTH_16 &&
+                                       !nand_opcode_8bits(command))
                                column >>= 1;
                        ctx->write_byte(mtd, column);
                }
index 94f55db..b7a2494 100644 (file)
@@ -37,7 +37,6 @@
 
 #include <linux/module.h>
 #include <linux/types.h>
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/ioport.h>
index f2f64ad..4e66726 100644 (file)
@@ -627,6 +627,8 @@ static int cafe_nand_probe(struct pci_dev *pdev,
        struct cafe_priv *cafe;
        uint32_t ctrl;
        int err = 0;
+       int old_dma;
+       struct nand_buffers *nbuf;
 
        /* Very old versions shared the same PCI ident for all three
           functions on the chip. Verify the class too... */
@@ -655,13 +657,6 @@ static int cafe_nand_probe(struct pci_dev *pdev,
                err = -ENOMEM;
                goto out_free_mtd;
        }
-       cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev, 2112 + sizeof(struct nand_buffers),
-                                         &cafe->dmaaddr, GFP_KERNEL);
-       if (!cafe->dmabuf) {
-               err = -ENOMEM;
-               goto out_ior;
-       }
-       cafe->nand.buffers = (void *)cafe->dmabuf + 2112;
 
        cafe->rs = init_rs_non_canonical(12, &cafe_mul, 0, 1, 8);
        if (!cafe->rs) {
@@ -721,7 +716,7 @@ static int cafe_nand_probe(struct pci_dev *pdev,
                          "CAFE NAND", mtd);
        if (err) {
                dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq);
-               goto out_free_dma;
+               goto out_ior;
        }
 
        /* Disable master reset, enable NAND clock */
@@ -735,6 +730,32 @@ static int cafe_nand_probe(struct pci_dev *pdev,
        cafe_writel(cafe, 0x7006, GLOBAL_CTRL);
        cafe_writel(cafe, 0x700a, GLOBAL_CTRL);
 
+       /* Enable NAND IRQ in global IRQ mask register */
+       cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
+       cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n",
+               cafe_readl(cafe, GLOBAL_CTRL),
+               cafe_readl(cafe, GLOBAL_IRQ_MASK));
+
+       /* Do not use the DMA for the nand_scan_ident() */
+       old_dma = usedma;
+       usedma = 0;
+
+       /* Scan to find existence of the device */
+       if (nand_scan_ident(mtd, 2, NULL)) {
+               err = -ENXIO;
+               goto out_irq;
+       }
+
+       cafe->dmabuf = dma_alloc_coherent(&cafe->pdev->dev,
+                               2112 + sizeof(struct nand_buffers) +
+                               mtd->writesize + mtd->oobsize,
+                               &cafe->dmaaddr, GFP_KERNEL);
+       if (!cafe->dmabuf) {
+               err = -ENOMEM;
+               goto out_irq;
+       }
+       cafe->nand.buffers = nbuf = (void *)cafe->dmabuf + 2112;
+
        /* Set up DMA address */
        cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0);
        if (sizeof(cafe->dmaaddr) > 4)
@@ -746,16 +767,13 @@ static int cafe_nand_probe(struct pci_dev *pdev,
        cafe_dev_dbg(&cafe->pdev->dev, "Set DMA address to %x (virt %p)\n",
                cafe_readl(cafe, NAND_DMA_ADDR0), cafe->dmabuf);
 
-       /* Enable NAND IRQ in global IRQ mask register */
-       cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
-       cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n",
-               cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK));
+       /* this driver does not need the @ecccalc and @ecccode */
+       nbuf->ecccalc = NULL;
+       nbuf->ecccode = NULL;
+       nbuf->databuf = (uint8_t *)(nbuf + 1);
 
-       /* Scan to find existence of the device */
-       if (nand_scan_ident(mtd, 2, NULL)) {
-               err = -ENXIO;
-               goto out_irq;
-       }
+       /* Restore the DMA flag */
+       usedma = old_dma;
 
        cafe->ctl2 = 1<<27; /* Reed-Solomon ECC */
        if (mtd->writesize == 2048)
@@ -773,7 +791,7 @@ static int cafe_nand_probe(struct pci_dev *pdev,
        } else {
                printk(KERN_WARNING "Unexpected NAND flash writesize %d. Aborting\n",
                       mtd->writesize);
-               goto out_irq;
+               goto out_free_dma;
        }
        cafe->nand.ecc.mode = NAND_ECC_HW_SYNDROME;
        cafe->nand.ecc.size = mtd->writesize;
@@ -790,7 +808,7 @@ static int cafe_nand_probe(struct pci_dev *pdev,
 
        err = nand_scan_tail(mtd);
        if (err)
-               goto out_irq;
+               goto out_free_dma;
 
        pci_set_drvdata(pdev, mtd);
 
@@ -799,12 +817,15 @@ static int cafe_nand_probe(struct pci_dev *pdev,
 
        goto out;
 
+ out_free_dma:
+       dma_free_coherent(&cafe->pdev->dev,
+                       2112 + sizeof(struct nand_buffers) +
+                       mtd->writesize + mtd->oobsize,
+                       cafe->dmabuf, cafe->dmaaddr);
  out_irq:
        /* Disable NAND IRQ in global IRQ mask register */
        cafe_writel(cafe, ~1 & cafe_readl(cafe, GLOBAL_IRQ_MASK), GLOBAL_IRQ_MASK);
        free_irq(pdev->irq, mtd);
- out_free_dma:
-       dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
  out_ior:
        pci_iounmap(pdev, cafe->mmio);
  out_free_mtd:
@@ -824,7 +845,10 @@ static void cafe_nand_remove(struct pci_dev *pdev)
        nand_release(mtd);
        free_rs(cafe->rs);
        pci_iounmap(pdev, cafe->mmio);
-       dma_free_coherent(&cafe->pdev->dev, 2112, cafe->dmabuf, cafe->dmaaddr);
+       dma_free_coherent(&cafe->pdev->dev,
+                       2112 + sizeof(struct nand_buffers) +
+                       mtd->writesize + mtd->oobsize,
+                       cafe->dmabuf, cafe->dmaaddr);
        kfree(mtd);
 }
 
index 8eb6a36..4615d79 100644 (file)
@@ -24,7 +24,6 @@
  */
 
 #include <linux/kernel.h>
-#include <linux/init.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/err.h>
index babb02c..35cb17f 100644 (file)
@@ -30,24 +30,6 @@ struct denali_dt {
        struct clk              *clk;
 };
 
-static void __iomem *request_and_map(struct device *dev,
-                                    const struct resource *res)
-{
-       void __iomem *ptr;
-
-       if (!devm_request_mem_region(dev, res->start, resource_size(res),
-                                    "denali-dt")) {
-               dev_err(dev, "unable to request %s\n", res->name);
-               return NULL;
-       }
-
-       ptr = devm_ioremap_nocache(dev, res->start, resource_size(res));
-       if (!ptr)
-               dev_err(dev, "ioremap_nocache of %s failed!", res->name);
-
-       return ptr;
-}
-
 static const struct of_device_id denali_nand_dt_ids[] = {
                { .compatible = "denali,denali-nand-dt" },
                { /* sentinel */ }
@@ -78,13 +60,6 @@ static int denali_dt_probe(struct platform_device *ofdev)
                return -ENOMEM;
        denali = &dt->denali;
 
-       denali_reg = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "denali_reg");
-       nand_data = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "nand_data");
-       if (!denali_reg || !nand_data) {
-               dev_err(&ofdev->dev, "resources not completely defined\n");
-               return -EINVAL;
-       }
-
        denali->platform = DT;
        denali->dev = &ofdev->dev;
        denali->irq = platform_get_irq(ofdev, 0);
@@ -93,13 +68,15 @@ static int denali_dt_probe(struct platform_device *ofdev)
                return denali->irq;
        }
 
-       denali->flash_reg = request_and_map(&ofdev->dev, denali_reg);
-       if (!denali->flash_reg)
-               return -ENOMEM;
+       denali_reg = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "denali_reg");
+       denali->flash_reg = devm_ioremap_resource(&ofdev->dev, denali_reg);
+       if (IS_ERR(denali->flash_reg))
+               return PTR_ERR(denali->flash_reg);
 
-       denali->flash_mem = request_and_map(&ofdev->dev, nand_data);
-       if (!denali->flash_mem)
-               return -ENOMEM;
+       nand_data = platform_get_resource_byname(ofdev, IORESOURCE_MEM, "nand_data");
+       denali->flash_mem = devm_ioremap_resource(&ofdev->dev, nand_data);
+       if (IS_ERR(denali->flash_mem))
+               return PTR_ERR(denali->flash_mem);
 
        if (!of_property_read_u32(ofdev->dev.of_node,
                "dma-mask", (u32 *)&denali_dma_mask)) {
index fec31d7..f68a7bc 100644 (file)
@@ -698,7 +698,8 @@ static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int colu
                /* Serially input address */
                if (column != -1) {
                        /* Adjust columns for 16 bit buswidth */
-                       if (this->options & NAND_BUSWIDTH_16)
+                       if (this->options & NAND_BUSWIDTH_16 &&
+                                       !nand_opcode_8bits(command))
                                column >>= 1;
                        WriteDOC(column, docptr, Mplus_FlashAddress);
                }
@@ -1438,7 +1439,7 @@ static int __init doc_probe(unsigned long physadr)
        int reg, len, numchips;
        int ret = 0;
 
-       if (!request_mem_region(physadr, DOC_IOREMAP_LEN, NULL))
+       if (!request_mem_region(physadr, DOC_IOREMAP_LEN, "DiskOnChip"))
                return -EBUSY;
        virtadr = ioremap(physadr, DOC_IOREMAP_LEN);
        if (!virtadr) {
index bcf6080..ec549cd 100644 (file)
@@ -24,7 +24,6 @@
 
 #include <linux/module.h>
 #include <linux/types.h>
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/ioport.h>
index 50d9161..cb45d2f 100644 (file)
@@ -22,7 +22,6 @@
 
 #include <linux/module.h>
 #include <linux/types.h>
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/of_address.h>
 #include <linux/slab.h>
index 8e6148a..117ce33 100644 (file)
@@ -18,7 +18,6 @@
 
 #include <linux/kernel.h>
 #include <linux/err.h>
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
index ca6369f..bb77f75 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/of_device.h>
 #include <linux/of_mtd.h>
 #include "gpmi-nand.h"
+#include "bch-regs.h"
 
 /* Resource names for the GPMI NAND driver. */
 #define GPMI_NAND_GPMI_REGS_ADDR_RES_NAME  "gpmi-nand"
@@ -985,7 +986,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
        int           ret;
 
        dev_dbg(this->dev, "page number is : %d\n", page);
-       ret = read_page_prepare(this, buf, mtd->writesize,
+       ret = read_page_prepare(this, buf, nfc_geo->payload_size,
                                        this->payload_virt, this->payload_phys,
                                        nfc_geo->payload_size,
                                        &payload_virt, &payload_phys);
@@ -999,7 +1000,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
 
        /* go! */
        ret = gpmi_read_page(this, payload_phys, auxiliary_phys);
-       read_page_end(this, buf, mtd->writesize,
+       read_page_end(this, buf, nfc_geo->payload_size,
                        this->payload_virt, this->payload_phys,
                        nfc_geo->payload_size,
                        payload_virt, payload_phys);
@@ -1041,7 +1042,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
                chip->oob_poi[0] = ((uint8_t *) auxiliary_virt)[0];
        }
 
-       read_page_swap_end(this, buf, mtd->writesize,
+       read_page_swap_end(this, buf, nfc_geo->payload_size,
                        this->payload_virt, this->payload_phys,
                        nfc_geo->payload_size,
                        payload_virt, payload_phys);
@@ -1049,6 +1050,90 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
        return max_bitflips;
 }
 
+/* Fake a virtual small page for the subpage read */
+static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
+                       uint32_t offs, uint32_t len, uint8_t *buf, int page)
+{
+       struct gpmi_nand_data *this = chip->priv;
+       void __iomem *bch_regs = this->resources.bch_regs;
+       struct bch_geometry old_geo = this->bch_geometry;
+       struct bch_geometry *geo = &this->bch_geometry;
+       int size = chip->ecc.size; /* ECC chunk size */
+       int meta, n, page_size;
+       u32 r1_old, r2_old, r1_new, r2_new;
+       unsigned int max_bitflips;
+       int first, last, marker_pos;
+       int ecc_parity_size;
+       int col = 0;
+
+       /* The size of ECC parity */
+       ecc_parity_size = geo->gf_len * geo->ecc_strength / 8;
+
+       /* Align it with the chunk size */
+       first = offs / size;
+       last = (offs + len - 1) / size;
+
+       /*
+        * Find the chunk which contains the Block Marker. If this chunk is
+        * in the range of [first, last], we have to read out the whole page.
+        * Why? since we had swapped the data at the position of Block Marker
+        * to the metadata which is bound with the chunk 0.
+        */
+       marker_pos = geo->block_mark_byte_offset / size;
+       if (last >= marker_pos && first <= marker_pos) {
+               dev_dbg(this->dev, "page:%d, first:%d, last:%d, marker at:%d\n",
+                               page, first, last, marker_pos);
+               return gpmi_ecc_read_page(mtd, chip, buf, 0, page);
+       }
+
+       meta = geo->metadata_size;
+       if (first) {
+               col = meta + (size + ecc_parity_size) * first;
+               chip->cmdfunc(mtd, NAND_CMD_RNDOUT, col, -1);
+
+               meta = 0;
+               buf = buf + first * size;
+       }
+
+       /* Save the old environment */
+       r1_old = r1_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT0);
+       r2_old = r2_new = readl(bch_regs + HW_BCH_FLASH0LAYOUT1);
+
+       /* change the BCH registers and bch_geometry{} */
+       n = last - first + 1;
+       page_size = meta + (size + ecc_parity_size) * n;
+
+       r1_new &= ~(BM_BCH_FLASH0LAYOUT0_NBLOCKS |
+                       BM_BCH_FLASH0LAYOUT0_META_SIZE);
+       r1_new |= BF_BCH_FLASH0LAYOUT0_NBLOCKS(n - 1)
+                       | BF_BCH_FLASH0LAYOUT0_META_SIZE(meta);
+       writel(r1_new, bch_regs + HW_BCH_FLASH0LAYOUT0);
+
+       r2_new &= ~BM_BCH_FLASH0LAYOUT1_PAGE_SIZE;
+       r2_new |= BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(page_size);
+       writel(r2_new, bch_regs + HW_BCH_FLASH0LAYOUT1);
+
+       geo->ecc_chunk_count = n;
+       geo->payload_size = n * size;
+       geo->page_size = page_size;
+       geo->auxiliary_status_offset = ALIGN(meta, 4);
+
+       dev_dbg(this->dev, "page:%d(%d:%d)%d, chunk:(%d:%d), BCH PG size:%d\n",
+               page, offs, len, col, first, n, page_size);
+
+       /* Read the subpage now */
+       this->swap_block_mark = false;
+       max_bitflips = gpmi_ecc_read_page(mtd, chip, buf, 0, page);
+
+       /* Restore */
+       writel(r1_old, bch_regs + HW_BCH_FLASH0LAYOUT0);
+       writel(r2_old, bch_regs + HW_BCH_FLASH0LAYOUT1);
+       this->bch_geometry = old_geo;
+       this->swap_block_mark = true;
+
+       return max_bitflips;
+}
+
 static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
                                const uint8_t *buf, int oob_required)
 {
@@ -1565,6 +1650,17 @@ static int gpmi_init_last(struct gpmi_nand_data *this)
        ecc->strength   = bch_geo->ecc_strength;
        ecc->layout     = &gpmi_hw_ecclayout;
 
+       /*
+        * We only enable the subpage read when:
+        *  (1) the chip is imx6, and
+        *  (2) the size of the ECC parity is byte aligned.
+        */
+       if (GPMI_IS_MX6Q(this) &&
+               ((bch_geo->gf_len * bch_geo->ecc_strength) % 8) == 0) {
+               ecc->read_subpage = gpmi_ecc_read_subpage;
+               chip->options |= NAND_SUBPAGE_READ;
+       }
+
        /*
         * Can we enable the extra features? such as EDO or Sync mode.
         *
index 31ee7cf..e78841a 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/gfp.h>
 #include <linux/delay.h>
 #include <linux/err.h>
-#include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/mtd/mtd.h>
index e9a4835..dba262b 100644 (file)
@@ -1501,6 +1501,8 @@ static int mxcnd_probe(struct platform_device *pdev)
        init_completion(&host->op_completion);
 
        host->irq = platform_get_irq(pdev, 0);
+       if (host->irq < 0)
+               return host->irq;
 
        /*
         * Use host->devtype_data->irq_control() here instead of irq_control()
index 9715a7b..9d01c4d 100644 (file)
@@ -589,7 +589,8 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
        /* Serially input address */
        if (column != -1) {
                /* Adjust columns for 16 bit buswidth */
-               if (chip->options & NAND_BUSWIDTH_16)
+               if (chip->options & NAND_BUSWIDTH_16 &&
+                               !nand_opcode_8bits(command))
                        column >>= 1;
                chip->cmd_ctrl(mtd, column, ctrl);
                ctrl &= ~NAND_CTRL_CHANGE;
@@ -680,7 +681,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
                /* Serially input address */
                if (column != -1) {
                        /* Adjust columns for 16 bit buswidth */
-                       if (chip->options & NAND_BUSWIDTH_16)
+                       if (chip->options & NAND_BUSWIDTH_16 &&
+                                       !nand_opcode_8bits(command))
                                column >>= 1;
                        chip->cmd_ctrl(mtd, column, ctrl);
                        ctrl &= ~NAND_CTRL_CHANGE;
@@ -1160,9 +1162,11 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
  * @data_offs: offset of requested data within the page
  * @readlen: data length
  * @bufpoi: buffer to store read data
+ * @page: page number to read
  */
 static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
-                       uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
+                       uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi,
+                       int page)
 {
        int start_step, end_step, num_steps;
        uint32_t *eccpos = chip->ecc.layout->eccpos;
@@ -1170,13 +1174,14 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
        int data_col_addr, i, gaps = 0;
        int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
        int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
-       int index = 0;
+       int index;
        unsigned int max_bitflips = 0;
 
        /* Column address within the page aligned to ECC size (256bytes) */
        start_step = data_offs / chip->ecc.size;
        end_step = (data_offs + readlen - 1) / chip->ecc.size;
        num_steps = end_step - start_step + 1;
+       index = start_step * chip->ecc.bytes;
 
        /* Data size aligned to ECC ecc.size */
        datafrag_len = num_steps * chip->ecc.size;
@@ -1213,8 +1218,6 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
                 * Send the command to read the particular ECC bytes take care
                 * about buswidth alignment in read_buf.
                 */
-               index = start_step * chip->ecc.bytes;
-
                aligned_pos = eccpos[index] & ~(busw - 1);
                aligned_len = eccfrag_len;
                if (eccpos[index] & (busw - 1))
@@ -1538,7 +1541,8 @@ read_retry:
                        else if (!aligned && NAND_HAS_SUBPAGE_READ(chip) &&
                                 !oob)
                                ret = chip->ecc.read_subpage(mtd, chip,
-                                                       col, bytes, bufpoi);
+                                                       col, bytes, bufpoi,
+                                                       page);
                        else
                                ret = chip->ecc.read_page(mtd, chip, bufpoi,
                                                          oob_required, page);
@@ -2000,7 +2004,7 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd,
                        oob += chip->ecc.prepad;
                }
 
-               chip->read_buf(mtd, oob, eccbytes);
+               chip->write_buf(mtd, oob, eccbytes);
                oob += eccbytes;
 
                if (chip->ecc.postpad) {
@@ -3063,7 +3067,7 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
                                        int *busw)
 {
        struct nand_onfi_params *p = &chip->onfi_params;
-       int i;
+       int i, j;
        int val;
 
        /* Try ONFI for unknown chip or LP */
@@ -3072,18 +3076,10 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
                chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
                return 0;
 
-       /*
-        * ONFI must be probed in 8-bit mode or with NAND_BUSWIDTH_AUTO, not
-        * with NAND_BUSWIDTH_16
-        */
-       if (chip->options & NAND_BUSWIDTH_16) {
-               pr_err("ONFI cannot be probed in 16-bit mode; aborting\n");
-               return 0;
-       }
-
        chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
        for (i = 0; i < 3; i++) {
-               chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
+               for (j = 0; j < sizeof(*p); j++)
+                       ((uint8_t *)p)[j] = chip->read_byte(mtd);
                if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
                                le16_to_cpu(p->crc)) {
                        break;
@@ -3168,6 +3164,87 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip,
        return 1;
 }
 
+/*
+ * Check if the NAND chip is JEDEC compliant, returns 1 if it is, 0 otherwise.
+ */
+static int nand_flash_detect_jedec(struct mtd_info *mtd, struct nand_chip *chip,
+                                       int *busw)
+{
+       struct nand_jedec_params *p = &chip->jedec_params;
+       struct jedec_ecc_info *ecc;
+       int val;
+       int i, j;
+
+       /* Try JEDEC for unknown chip or LP */
+       chip->cmdfunc(mtd, NAND_CMD_READID, 0x40, -1);
+       if (chip->read_byte(mtd) != 'J' || chip->read_byte(mtd) != 'E' ||
+               chip->read_byte(mtd) != 'D' || chip->read_byte(mtd) != 'E' ||
+               chip->read_byte(mtd) != 'C')
+               return 0;
+
+       chip->cmdfunc(mtd, NAND_CMD_PARAM, 0x40, -1);
+       for (i = 0; i < 3; i++) {
+               for (j = 0; j < sizeof(*p); j++)
+                       ((uint8_t *)p)[j] = chip->read_byte(mtd);
+
+               if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 510) ==
+                               le16_to_cpu(p->crc))
+                       break;
+       }
+
+       if (i == 3) {
+               pr_err("Could not find valid JEDEC parameter page; aborting\n");
+               return 0;
+       }
+
+       /* Check version */
+       val = le16_to_cpu(p->revision);
+       if (val & (1 << 2))
+               chip->jedec_version = 10;
+       else if (val & (1 << 1))
+               chip->jedec_version = 1; /* vendor specific version */
+
+       if (!chip->jedec_version) {
+               pr_info("unsupported JEDEC version: %d\n", val);
+               return 0;
+       }
+
+       sanitize_string(p->manufacturer, sizeof(p->manufacturer));
+       sanitize_string(p->model, sizeof(p->model));
+       if (!mtd->name)
+               mtd->name = p->model;
+
+       mtd->writesize = le32_to_cpu(p->byte_per_page);
+
+       /* Please reference to the comment for nand_flash_detect_onfi. */
+       mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1);
+       mtd->erasesize *= mtd->writesize;
+
+       mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
+
+       /* Please reference to the comment for nand_flash_detect_onfi. */
+       chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1);
+       chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count;
+       chip->bits_per_cell = p->bits_per_cell;
+
+       if (jedec_feature(chip) & JEDEC_FEATURE_16_BIT_BUS)
+               *busw = NAND_BUSWIDTH_16;
+       else
+               *busw = 0;
+
+       /* ECC info */
+       ecc = &p->ecc_info[0];
+
+       if (ecc->codeword_size >= 9) {
+               chip->ecc_strength_ds = ecc->ecc_bits;
+               chip->ecc_step_ds = 1 << ecc->codeword_size;
+       } else {
+               pr_warn("Invalid codeword size\n");
+       }
+
+       return 1;
+}
+
 /*
  * nand_id_has_period - Check if an ID string has a given wraparound period
  * @id_data: the ID string
@@ -3474,10 +3551,10 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip,
  */
 static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                                                  struct nand_chip *chip,
-                                                 int busw,
                                                  int *maf_id, int *dev_id,
                                                  struct nand_flash_dev *type)
 {
+       int busw;
        int i, maf_idx;
        u8 id_data[8];
 
@@ -3533,6 +3610,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                /* Check is chip is ONFI compliant */
                if (nand_flash_detect_onfi(mtd, chip, &busw))
                        goto ident_done;
+
+               /* Check if the chip is JEDEC compliant */
+               if (nand_flash_detect_jedec(mtd, chip, &busw))
+                       goto ident_done;
        }
 
        if (!type->name)
@@ -3612,8 +3693,17 @@ ident_done:
 
        pr_info("device found, Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
                *maf_id, *dev_id);
-       pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
-               chip->onfi_version ? chip->onfi_params.model : type->name);
+
+       if (chip->onfi_version)
+               pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
+                               chip->onfi_params.model);
+       else if (chip->jedec_version)
+               pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
+                               chip->jedec_params.model);
+       else
+               pr_info("%s %s\n", nand_manuf_ids[maf_idx].name,
+                               type->name);
+
        pr_info("%dMiB, %s, page size: %d, OOB size: %d\n",
                (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC",
                mtd->writesize, mtd->oobsize);
@@ -3634,18 +3724,16 @@ ident_done:
 int nand_scan_ident(struct mtd_info *mtd, int maxchips,
                    struct nand_flash_dev *table)
 {
-       int i, busw, nand_maf_id, nand_dev_id;
+       int i, nand_maf_id, nand_dev_id;
        struct nand_chip *chip = mtd->priv;
        struct nand_flash_dev *type;
 
-       /* Get buswidth to select the correct functions */
-       busw = chip->options & NAND_BUSWIDTH_16;
        /* Set the default functions */
-       nand_set_defaults(chip, busw);
+       nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);
 
        /* Read the flash type */
-       type = nand_get_flash_type(mtd, chip, busw,
-                               &nand_maf_id, &nand_dev_id, table);
+       type = nand_get_flash_type(mtd, chip, &nand_maf_id,
+                                  &nand_dev_id, table);
 
        if (IS_ERR(type)) {
                if (!(chip->options & NAND_SCAN_SILENT_NODEV))
@@ -3696,15 +3784,26 @@ int nand_scan_tail(struct mtd_info *mtd)
        int i;
        struct nand_chip *chip = mtd->priv;
        struct nand_ecc_ctrl *ecc = &chip->ecc;
+       struct nand_buffers *nbuf;
 
        /* New bad blocks should be marked in OOB, flash-based BBT, or both */
        BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
                        !(chip->bbt_options & NAND_BBT_USE_FLASH));
 
-       if (!(chip->options & NAND_OWN_BUFFERS))
-               chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
-       if (!chip->buffers)
-               return -ENOMEM;
+       if (!(chip->options & NAND_OWN_BUFFERS)) {
+               nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize
+                               + mtd->oobsize * 3, GFP_KERNEL);
+               if (!nbuf)
+                       return -ENOMEM;
+               nbuf->ecccalc = (uint8_t *)(nbuf + 1);
+               nbuf->ecccode = nbuf->ecccalc + mtd->oobsize;
+               nbuf->databuf = nbuf->ecccode + mtd->oobsize;
+
+               chip->buffers = nbuf;
+       } else {
+               if (!chip->buffers)
+                       return -ENOMEM;
+       }
 
        /* Set the internal oob buffer location, just after the page data */
        chip->oob_poi = chip->buffers->databuf + mtd->writesize;
@@ -3825,7 +3924,7 @@ int nand_scan_tail(struct mtd_info *mtd)
 
        case NAND_ECC_SOFT_BCH:
                if (!mtd_nand_has_bch()) {
-                       pr_warn("CONFIG_MTD_ECC_BCH not enabled\n");
+                       pr_warn("CONFIG_MTD_NAND_ECC_BCH not enabled\n");
                        BUG();
                }
                ecc->calculate = nand_bch_calculate_ecc;
index daa2faa..3d7c89f 100644 (file)
@@ -43,6 +43,9 @@ struct nand_flash_dev nand_flash_ids[] = {
        {"TC58NVG6D2 64G 3.3V 8-bit",
                { .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} },
                  SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) },
+       {"SDTNRGAMA 64G 3.3V 8-bit",
+               { .id = {0x45, 0xde, 0x94, 0x93, 0x76, 0x50} },
+                 SZ_16K, SZ_8K, SZ_4M, 0, 6, 1280, NAND_ECC_INFO(40, SZ_1K) },
 
        LEGACY_ID_NAND("NAND 4MiB 5V 8-bit",   0x6B, 4, SZ_8K, SP_OPTIONS),
        LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS),
index 9ee09a8..e8a5fff 100644 (file)
@@ -10,7 +10,6 @@
  */
 
 #include <linux/slab.h>
-#include <linux/init.h>
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
@@ -152,7 +151,8 @@ static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command,
        if (column != -1 || page_addr != -1) {
 
                if (column != -1) {
-                       if (chip->options & NAND_BUSWIDTH_16)
+                       if (chip->options & NAND_BUSWIDTH_16 &&
+                                       !nand_opcode_8bits(command))
                                column >>= 1;
                        write_addr_reg(nand, column);
                        write_addr_reg(nand, column >> 8 | ENDADDR);
@@ -225,7 +225,7 @@ static void nuc900_nand_enable(struct nuc900_nand *nand)
        val = __raw_readl(nand->reg + REG_FMICSR);
 
        if (!(val & NAND_EN))
-               __raw_writel(val | NAND_EN, REG_FMICSR);
+               __raw_writel(val | NAND_EN, nand->reg + REG_FMICSR);
 
        val = __raw_readl(nand->reg + REG_SMCSR);
 
index bf642ce..1ff49b8 100644 (file)
 
 #define OMAP24XX_DMA_GPMC              4
 
-#define BCH8_MAX_ERROR         8       /* upto 8 bit correctable */
-#define BCH4_MAX_ERROR         4       /* upto 4 bit correctable */
-
 #define SECTOR_BYTES           512
 /* 4 bit padding to make byte aligned, 56 = 52 + 4 */
 #define BCH4_BIT_PAD           4
-#define BCH8_ECC_MAX           ((SECTOR_BYTES + BCH8_ECC_OOB_BYTES) * 8)
-#define BCH4_ECC_MAX           ((SECTOR_BYTES + BCH4_ECC_OOB_BYTES) * 8)
 
 /* GPMC ecc engine settings for read */
 #define BCH_WRAPMODE_1         1       /* BCH wrap mode 1 */
@@ -159,7 +154,7 @@ struct omap_nand_info {
 
        int                             gpmc_cs;
        unsigned long                   phys_base;
-       unsigned long                   mem_size;
+       enum omap_ecc                   ecc_opt;
        struct completion               comp;
        struct dma_chan                 *dma;
        int                             gpmc_irq_fifo;
@@ -172,7 +167,6 @@ struct omap_nand_info {
        int                                     buf_len;
        struct gpmc_nand_regs           reg;
        /* fields specific for BCHx_HW ECC scheme */
-       bool                            is_elm_used;
        struct device                   *elm_dev;
        struct device_node              *of_node;
 };
@@ -1043,9 +1037,8 @@ static int omap_dev_ready(struct mtd_info *mtd)
        }
 }
 
-#if defined(CONFIG_MTD_NAND_ECC_BCH) || defined(CONFIG_MTD_NAND_OMAP_BCH)
 /**
- * omap3_enable_hwecc_bch - Program OMAP3 GPMC to perform BCH ECC correction
+ * omap_enable_hwecc_bch - Program GPMC to perform BCH ECC calculation
  * @mtd: MTD device structure
  * @mode: Read/Write mode
  *
@@ -1056,50 +1049,73 @@ static int omap_dev_ready(struct mtd_info *mtd)
  * eccsize0 = 0  (no additional protected byte in spare area)
  * eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
  */
-static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)
+static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode)
 {
-       int nerrors;
+       unsigned int bch_type;
        unsigned int dev_width, nsectors;
        struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
                                                   mtd);
+       enum omap_ecc ecc_opt = info->ecc_opt;
        struct nand_chip *chip = mtd->priv;
        u32 val, wr_mode;
        unsigned int ecc_size1, ecc_size0;
 
-       /* Using wrapping mode 6 for writing */
-       wr_mode = BCH_WRAPMODE_6;
-
-       /*
-        * ECC engine enabled for valid ecc_size0 nibbles
-        * and disabled for ecc_size1 nibbles.
-        */
-       ecc_size0 = BCH_ECC_SIZE0;
-       ecc_size1 = BCH_ECC_SIZE1;
-
-       /* Perform ecc calculation on 512-byte sector */
-       nsectors = 1;
-
-       /* Update number of error correction */
-       nerrors = info->nand.ecc.strength;
-
-       /* Multi sector reading/writing for NAND flash with page size < 4096 */
-       if (info->is_elm_used && (mtd->writesize <= 4096)) {
+       /* GPMC configurations for calculating ECC */
+       switch (ecc_opt) {
+       case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+               bch_type = 0;
+               nsectors = 1;
+               if (mode == NAND_ECC_READ) {
+                       wr_mode   = BCH_WRAPMODE_6;
+                       ecc_size0 = BCH_ECC_SIZE0;
+                       ecc_size1 = BCH_ECC_SIZE1;
+               } else {
+                       wr_mode   = BCH_WRAPMODE_6;
+                       ecc_size0 = BCH_ECC_SIZE0;
+                       ecc_size1 = BCH_ECC_SIZE1;
+               }
+               break;
+       case OMAP_ECC_BCH4_CODE_HW:
+               bch_type = 0;
+               nsectors = chip->ecc.steps;
                if (mode == NAND_ECC_READ) {
-                       /* Using wrapping mode 1 for reading */
-                       wr_mode = BCH_WRAPMODE_1;
-
-                       /*
-                        * ECC engine enabled for ecc_size0 nibbles
-                        * and disabled for ecc_size1 nibbles.
-                        */
-                       ecc_size0 = (nerrors == 8) ?
-                               BCH8R_ECC_SIZE0 : BCH4R_ECC_SIZE0;
-                       ecc_size1 = (nerrors == 8) ?
-                               BCH8R_ECC_SIZE1 : BCH4R_ECC_SIZE1;
+                       wr_mode   = BCH_WRAPMODE_1;
+                       ecc_size0 = BCH4R_ECC_SIZE0;
+                       ecc_size1 = BCH4R_ECC_SIZE1;
+               } else {
+                       wr_mode   = BCH_WRAPMODE_6;
+                       ecc_size0 = BCH_ECC_SIZE0;
+                       ecc_size1 = BCH_ECC_SIZE1;
                }
-
-               /* Perform ecc calculation for one page (< 4096) */
-               nsectors = info->nand.ecc.steps;
+               break;
+       case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+               bch_type = 1;
+               nsectors = 1;
+               if (mode == NAND_ECC_READ) {
+                       wr_mode   = BCH_WRAPMODE_6;
+                       ecc_size0 = BCH_ECC_SIZE0;
+                       ecc_size1 = BCH_ECC_SIZE1;
+               } else {
+                       wr_mode   = BCH_WRAPMODE_6;
+                       ecc_size0 = BCH_ECC_SIZE0;
+                       ecc_size1 = BCH_ECC_SIZE1;
+               }
+               break;
+       case OMAP_ECC_BCH8_CODE_HW:
+               bch_type = 1;
+               nsectors = chip->ecc.steps;
+               if (mode == NAND_ECC_READ) {
+                       wr_mode   = BCH_WRAPMODE_1;
+                       ecc_size0 = BCH8R_ECC_SIZE0;
+                       ecc_size1 = BCH8R_ECC_SIZE1;
+               } else {
+                       wr_mode   = BCH_WRAPMODE_6;
+                       ecc_size0 = BCH_ECC_SIZE0;
+                       ecc_size1 = BCH_ECC_SIZE1;
+               }
+               break;
+       default:
+               return;
        }
 
        writel(ECC1, info->reg.gpmc_ecc_control);
@@ -1112,7 +1128,7 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)
 
        /* BCH configuration */
        val = ((1                        << 16) | /* enable BCH */
-              (((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */
+              (bch_type                 << 12) | /* BCH4/BCH8/BCH16 */
               (wr_mode                  <<  8) | /* wrap mode */
               (dev_width                <<  7) | /* bus width */
               (((nsectors-1) & 0x7)     <<  4) | /* number of sectors */
@@ -1124,132 +1140,40 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode)
        /* Clear ecc and enable bits */
        writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control);
 }
-#endif
-
-#ifdef CONFIG_MTD_NAND_ECC_BCH
-/**
- * omap3_calculate_ecc_bch4 - Generate 7 bytes of ECC bytes
- * @mtd: MTD device structure
- * @dat: The pointer to data on which ecc is computed
- * @ecc_code: The ecc_code buffer
- */
-static int omap3_calculate_ecc_bch4(struct mtd_info *mtd, const u_char *dat,
-                                   u_char *ecc_code)
-{
-       struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
-                                                  mtd);
-       unsigned long nsectors, val1, val2;
-       int i;
-
-       nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
-
-       for (i = 0; i < nsectors; i++) {
 
-               /* Read hw-computed remainder */
-               val1 = readl(info->reg.gpmc_bch_result0[i]);
-               val2 = readl(info->reg.gpmc_bch_result1[i]);
-
-               /*
-                * Add constant polynomial to remainder, in order to get an ecc
-                * sequence of 0xFFs for a buffer filled with 0xFFs; and
-                * left-justify the resulting polynomial.
-                */
-               *ecc_code++ = 0x28 ^ ((val2 >> 12) & 0xFF);
-               *ecc_code++ = 0x13 ^ ((val2 >>  4) & 0xFF);
-               *ecc_code++ = 0xcc ^ (((val2 & 0xF) << 4)|((val1 >> 28) & 0xF));
-               *ecc_code++ = 0x39 ^ ((val1 >> 20) & 0xFF);
-               *ecc_code++ = 0x96 ^ ((val1 >> 12) & 0xFF);
-               *ecc_code++ = 0xac ^ ((val1 >> 4) & 0xFF);
-               *ecc_code++ = 0x7f ^ ((val1 & 0xF) << 4);
-       }
-
-       return 0;
-}
+static u8  bch4_polynomial[] = {0x28, 0x13, 0xcc, 0x39, 0x96, 0xac, 0x7f};
+static u8  bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2,
+                               0x97, 0x79, 0xe5, 0x24, 0xb5};
 
 /**
- * omap3_calculate_ecc_bch8 - Generate 13 bytes of ECC bytes
- * @mtd: MTD device structure
- * @dat: The pointer to data on which ecc is computed
- * @ecc_code: The ecc_code buffer
- */
-static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat,
-                                   u_char *ecc_code)
-{
-       struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
-                                                  mtd);
-       unsigned long nsectors, val1, val2, val3, val4;
-       int i;
-
-       nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
-
-       for (i = 0; i < nsectors; i++) {
-
-               /* Read hw-computed remainder */
-               val1 = readl(info->reg.gpmc_bch_result0[i]);
-               val2 = readl(info->reg.gpmc_bch_result1[i]);
-               val3 = readl(info->reg.gpmc_bch_result2[i]);
-               val4 = readl(info->reg.gpmc_bch_result3[i]);
-
-               /*
-                * Add constant polynomial to remainder, in order to get an ecc
-                * sequence of 0xFFs for a buffer filled with 0xFFs.
-                */
-               *ecc_code++ = 0xef ^ (val4 & 0xFF);
-               *ecc_code++ = 0x51 ^ ((val3 >> 24) & 0xFF);
-               *ecc_code++ = 0x2e ^ ((val3 >> 16) & 0xFF);
-               *ecc_code++ = 0x09 ^ ((val3 >> 8) & 0xFF);
-               *ecc_code++ = 0xed ^ (val3 & 0xFF);
-               *ecc_code++ = 0x93 ^ ((val2 >> 24) & 0xFF);
-               *ecc_code++ = 0x9a ^ ((val2 >> 16) & 0xFF);
-               *ecc_code++ = 0xc2 ^ ((val2 >> 8) & 0xFF);
-               *ecc_code++ = 0x97 ^ (val2 & 0xFF);
-               *ecc_code++ = 0x79 ^ ((val1 >> 24) & 0xFF);
-               *ecc_code++ = 0xe5 ^ ((val1 >> 16) & 0xFF);
-               *ecc_code++ = 0x24 ^ ((val1 >> 8) & 0xFF);
-               *ecc_code++ = 0xb5 ^ (val1 & 0xFF);
-       }
-
-       return 0;
-}
-#endif /* CONFIG_MTD_NAND_ECC_BCH */
-
-#ifdef CONFIG_MTD_NAND_OMAP_BCH
-/**
- * omap3_calculate_ecc_bch - Generate bytes of ECC bytes
+ * omap_calculate_ecc_bch - Generate bytes of ECC bytes
  * @mtd:       MTD device structure
  * @dat:       The pointer to data on which ecc is computed
  * @ecc_code:  The ecc_code buffer
  *
  * Support calculating of BCH4/8 ecc vectors for the page
  */
-static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat,
-                                   u_char *ecc_code)
+static int __maybe_unused omap_calculate_ecc_bch(struct mtd_info *mtd,
+                                       const u_char *dat, u_char *ecc_calc)
 {
        struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
                                                   mtd);
+       int eccbytes    = info->nand.ecc.bytes;
+       struct gpmc_nand_regs   *gpmc_regs = &info->reg;
+       u8 *ecc_code;
        unsigned long nsectors, bch_val1, bch_val2, bch_val3, bch_val4;
-       int i, eccbchtsel;
+       int i;
 
        nsectors = ((readl(info->reg.gpmc_ecc_config) >> 4) & 0x7) + 1;
-       /*
-        * find BCH scheme used
-        * 0 -> BCH4
-        * 1 -> BCH8
-        */
-       eccbchtsel = ((readl(info->reg.gpmc_ecc_config) >> 12) & 0x3);
-
        for (i = 0; i < nsectors; i++) {
-
-               /* Read hw-computed remainder */
-               bch_val1 = readl(info->reg.gpmc_bch_result0[i]);
-               bch_val2 = readl(info->reg.gpmc_bch_result1[i]);
-               if (eccbchtsel) {
-                       bch_val3 = readl(info->reg.gpmc_bch_result2[i]);
-                       bch_val4 = readl(info->reg.gpmc_bch_result3[i]);
-               }
-
-               if (eccbchtsel) {
-                       /* BCH8 ecc scheme */
+               ecc_code = ecc_calc;
+               switch (info->ecc_opt) {
+               case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+               case OMAP_ECC_BCH8_CODE_HW:
+                       bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]);
+                       bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]);
+                       bch_val3 = readl(gpmc_regs->gpmc_bch_result2[i]);
+                       bch_val4 = readl(gpmc_regs->gpmc_bch_result3[i]);
                        *ecc_code++ = (bch_val4 & 0xFF);
                        *ecc_code++ = ((bch_val3 >> 24) & 0xFF);
                        *ecc_code++ = ((bch_val3 >> 16) & 0xFF);
@@ -1263,14 +1187,11 @@ static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat,
                        *ecc_code++ = ((bch_val1 >> 16) & 0xFF);
                        *ecc_code++ = ((bch_val1 >> 8) & 0xFF);
                        *ecc_code++ = (bch_val1 & 0xFF);
-                       /*
-                        * Setting 14th byte to zero to handle
-                        * erased page & maintain compatibility
-                        * with RBL
-                        */
-                       *ecc_code++ = 0x0;
-               } else {
-                       /* BCH4 ecc scheme */
+                       break;
+               case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+               case OMAP_ECC_BCH4_CODE_HW:
+                       bch_val1 = readl(gpmc_regs->gpmc_bch_result0[i]);
+                       bch_val2 = readl(gpmc_regs->gpmc_bch_result1[i]);
                        *ecc_code++ = ((bch_val2 >> 12) & 0xFF);
                        *ecc_code++ = ((bch_val2 >> 4) & 0xFF);
                        *ecc_code++ = ((bch_val2 & 0xF) << 4) |
@@ -1279,12 +1200,38 @@ static int omap3_calculate_ecc_bch(struct mtd_info *mtd, const u_char *dat,
                        *ecc_code++ = ((bch_val1 >> 12) & 0xFF);
                        *ecc_code++ = ((bch_val1 >> 4) & 0xFF);
                        *ecc_code++ = ((bch_val1 & 0xF) << 4);
-                       /*
-                        * Setting 8th byte to zero to handle
-                        * erased page
-                        */
-                       *ecc_code++ = 0x0;
+                       break;
+               default:
+                       return -EINVAL;
                }
+
+               /* ECC scheme specific syndrome customizations */
+               switch (info->ecc_opt) {
+               case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW:
+                       /* Add constant polynomial to remainder, so that
+                        * ECC of blank pages results in 0x0 on reading back */
+                       for (i = 0; i < eccbytes; i++)
+                               ecc_calc[i] ^= bch4_polynomial[i];
+                       break;
+               case OMAP_ECC_BCH4_CODE_HW:
+                       /* Set  8th ECC byte as 0x0 for ROM compatibility */
+                       ecc_calc[eccbytes - 1] = 0x0;
+                       break;
+               case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+                       /* Add constant polynomial to remainder, so that
+                        * ECC of blank pages results in 0x0 on reading back */
+                       for (i = 0; i < eccbytes; i++)
+                               ecc_calc[i] ^= bch8_polynomial[i];
+                       break;
+               case OMAP_ECC_BCH8_CODE_HW:
+                       /* Set 14th ECC byte as 0x0 for ROM compatibility */
+                       ecc_calc[eccbytes - 1] = 0x0;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+       ecc_calc += eccbytes;
        }
 
        return 0;
@@ -1329,6 +1276,7 @@ static int erased_sector_bitflips(u_char *data, u_char *oob,
        return flip_bits;
 }
 
+#ifdef CONFIG_MTD_NAND_OMAP_BCH
 /**
  * omap_elm_correct_data - corrects page data area in case error reported
  * @mtd:       MTD device structure
@@ -1337,55 +1285,46 @@ static int erased_sector_bitflips(u_char *data, u_char *oob,
  * @calc_ecc:  ecc read from HW ECC registers
  *
  * Calculated ecc vector reported as zero in case of non-error pages.
- * In case of error/erased pages non-zero error vector is reported.
- * In case of non-zero ecc vector, check read_ecc at fixed offset
- * (x = 13/7 in case of BCH8/4 == 0) to find page programmed or not.
- * To handle bit flips in this data, count the number of 0's in
- * read_ecc[x] and check if it greater than 4. If it is less, it is
- * programmed page, else erased page.
- *
- * 1. If page is erased, check with standard ecc vector (ecc vector
- * for erased page to find any bit flip). If check fails, bit flip
- * is present in erased page. Count the bit flips in erased page and
- * if it falls under correctable level, report page with 0xFF and
- * update the correctable bit information.
- * 2. If error is reported on programmed page, update elm error
- * vector and correct the page with ELM error correction routine.
- *
+ * In case of non-zero ecc vector, first filter out erased-pages, and
+ * then process data via ELM to detect bit-flips.
  */
 static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
                                u_char *read_ecc, u_char *calc_ecc)
 {
        struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
                        mtd);
+       struct nand_ecc_ctrl *ecc = &info->nand.ecc;
        int eccsteps = info->nand.ecc.steps;
        int i , j, stat = 0;
-       int eccsize, eccflag, ecc_vector_size;
+       int eccflag, actual_eccbytes;
        struct elm_errorvec err_vec[ERROR_VECTOR_MAX];
        u_char *ecc_vec = calc_ecc;
        u_char *spare_ecc = read_ecc;
        u_char *erased_ecc_vec;
-       enum bch_ecc type;
+       u_char *buf;
+       int bitflip_count;
        bool is_error_reported = false;
+       u32 bit_pos, byte_pos, error_max, pos;
+       int err;
 
-       /* Initialize elm error vector to zero */
-       memset(err_vec, 0, sizeof(err_vec));
-
-       if (info->nand.ecc.strength == BCH8_MAX_ERROR) {
-               type = BCH8_ECC;
-               erased_ecc_vec = bch8_vector;
-       } else {
-               type = BCH4_ECC;
+       switch (info->ecc_opt) {
+       case OMAP_ECC_BCH4_CODE_HW:
+               /* omit  7th ECC byte reserved for ROM code compatibility */
+               actual_eccbytes = ecc->bytes - 1;
                erased_ecc_vec = bch4_vector;
+               break;
+       case OMAP_ECC_BCH8_CODE_HW:
+               /* omit 14th ECC byte reserved for ROM code compatibility */
+               actual_eccbytes = ecc->bytes - 1;
+               erased_ecc_vec = bch8_vector;
+               break;
+       default:
+               pr_err("invalid driver configuration\n");
+               return -EINVAL;
        }
 
-       ecc_vector_size = info->nand.ecc.bytes;
-
-       /*
-        * Remove extra byte padding for BCH8 RBL
-        * compatibility and erased page handling
-        */
-       eccsize = ecc_vector_size - 1;
+       /* Initialize elm error vector to zero */
+       memset(err_vec, 0, sizeof(err_vec));
 
        for (i = 0; i < eccsteps ; i++) {
                eccflag = 0;    /* initialize eccflag */
@@ -1394,8 +1333,7 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
                 * Check any error reported,
                 * In case of error, non zero ecc reported.
                 */
-
-               for (j = 0; (j < eccsize); j++) {
+               for (j = 0; j < actual_eccbytes; j++) {
                        if (calc_ecc[j] != 0) {
                                eccflag = 1; /* non zero ecc, error present */
                                break;
@@ -1403,50 +1341,43 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
                }
 
                if (eccflag == 1) {
-                       /*
-                        * Set threshold to minimum of 4, half of ecc.strength/2
-                        * to allow max bit flip in byte to 4
-                        */
-                       unsigned int threshold = min_t(unsigned int, 4,
-                                       info->nand.ecc.strength / 2);
-
-                       /*
-                        * Check data area is programmed by counting
-                        * number of 0's at fixed offset in spare area.
-                        * Checking count of 0's against threshold.
-                        * In case programmed page expects at least threshold
-                        * zeros in byte.
-                        * If zeros are less than threshold for programmed page/
-                        * zeros are more than threshold erased page, either
-                        * case page reported as uncorrectable.
-                        */
-                       if (hweight8(~read_ecc[eccsize]) >= threshold) {
+                       if (memcmp(calc_ecc, erased_ecc_vec,
+                                               actual_eccbytes) == 0) {
                                /*
-                                * Update elm error vector as
-                                * data area is programmed
+                                * calc_ecc[] matches pattern for ECC(all 0xff)
+                                * so this is definitely an erased-page
                                 */
-                               err_vec[i].error_reported = true;
-                               is_error_reported = true;
                        } else {
-                               /* Error reported in erased page */
-                               int bitflip_count;
-                               u_char *buf = &data[info->nand.ecc.size * i];
-
-                               if (memcmp(calc_ecc, erased_ecc_vec, eccsize)) {
-                                       bitflip_count = erased_sector_bitflips(
-                                                       buf, read_ecc, info);
-
-                                       if (bitflip_count)
-                                               stat += bitflip_count;
-                                       else
-                                               return -EINVAL;
+                               buf = &data[info->nand.ecc.size * i];
+                               /*
+                                * count number of 0-bits in read_buf.
+                                * This check can be removed once a similar
+                                * check is introduced in generic NAND driver
+                                */
+                               bitflip_count = erased_sector_bitflips(
+                                               buf, read_ecc, info);
+                               if (bitflip_count) {
+                                       /*
+                                        * number of 0-bits within ECC limits
+                                        * So this may be an erased-page
+                                        */
+                                       stat += bitflip_count;
+                               } else {
+                                       /*
+                                        * Too many 0-bits. It may be a
+                                        * - programmed-page, OR
+                                        * - erased-page with many bit-flips
+                                        * So this page requires check by ELM
+                                        */
+                                       err_vec[i].error_reported = true;
+                                       is_error_reported = true;
                                }
                        }
                }
 
                /* Update the ecc vector */
-               calc_ecc += ecc_vector_size;
-               read_ecc += ecc_vector_size;
+               calc_ecc += ecc->bytes;
+               read_ecc += ecc->bytes;
        }
 
        /* Check if any error reported */
@@ -1456,23 +1387,26 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
        /* Decode BCH error using ELM module */
        elm_decode_bch_error_page(info->elm_dev, ecc_vec, err_vec);
 
+       err = 0;
        for (i = 0; i < eccsteps; i++) {
-               if (err_vec[i].error_reported) {
+               if (err_vec[i].error_uncorrectable) {
+                       pr_err("nand: uncorrectable bit-flips found\n");
+                       err = -EBADMSG;
+               } else if (err_vec[i].error_reported) {
                        for (j = 0; j < err_vec[i].error_count; j++) {
-                               u32 bit_pos, byte_pos, error_max, pos;
-
-                               if (type == BCH8_ECC)
-                                       error_max = BCH8_ECC_MAX;
-                               else
-                                       error_max = BCH4_ECC_MAX;
-
-                               if (info->nand.ecc.strength == BCH8_MAX_ERROR)
-                                       pos = err_vec[i].error_loc[j];
-                               else
-                                       /* Add 4 to take care 4 bit padding */
+                               switch (info->ecc_opt) {
+                               case OMAP_ECC_BCH4_CODE_HW:
+                                       /* Add 4 bits to take care of padding */
                                        pos = err_vec[i].error_loc[j] +
                                                BCH4_BIT_PAD;
-
+                                       break;
+                               case OMAP_ECC_BCH8_CODE_HW:
+                                       pos = err_vec[i].error_loc[j];
+                                       break;
+                               default:
+                                       return -EINVAL;
+                               }
+                               error_max = (ecc->size + actual_eccbytes) * 8;
                                /* Calculate bit position of error */
                                bit_pos = pos % 8;
 
@@ -1480,13 +1414,22 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
                                byte_pos = (error_max - pos - 1) / 8;
 
                                if (pos < error_max) {
-                                       if (byte_pos < 512)
+                                       if (byte_pos < 512) {
+                                               pr_debug("bitflip@dat[%d]=%x\n",
+                                                    byte_pos, data[byte_pos]);
                                                data[byte_pos] ^= 1 << bit_pos;
-                                       else
+                                       } else {
+                                               pr_debug("bitflip@oob[%d]=%x\n",
+                                                       (byte_pos - 512),
+                                                    spare_ecc[byte_pos - 512]);
                                                spare_ecc[byte_pos - 512] ^=
                                                        1 << bit_pos;
+                                       }
+                               } else {
+                                       pr_err("invalid bit-flip @ %d:%d\n",
+                                                        byte_pos, bit_pos);
+                                       err = -EBADMSG;
                                }
-                               /* else, not interested to correct ecc */
                        }
                }
 
@@ -1494,16 +1437,11 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data,
                stat += err_vec[i].error_count;
 
                /* Update page data with sector size */
-               data += info->nand.ecc.size;
-               spare_ecc += ecc_vector_size;
+               data += ecc->size;
+               spare_ecc += ecc->bytes;
        }
 
-       for (i = 0; i < eccsteps; i++)
-               /* Return error if uncorrectable error present */
-               if (err_vec[i].error_uncorrectable)
-                       return -EINVAL;
-
-       return stat;
+       return (err) ? err : stat;
 }
 
 /**
@@ -1601,7 +1539,8 @@ static int is_elm_present(struct omap_nand_info *info,
                        struct device_node *elm_node, enum bch_ecc bch_type)
 {
        struct platform_device *pdev;
-       info->is_elm_used = false;
+       struct nand_ecc_ctrl *ecc = &info->nand.ecc;
+       int err;
        /* check whether elm-id is passed via DT */
        if (!elm_node) {
                pr_err("nand: error: ELM DT node not found\n");
@@ -1615,10 +1554,10 @@ static int is_elm_present(struct omap_nand_info *info,
        }
        /* ELM module available, now configure it */
        info->elm_dev = &pdev->dev;
-       if (elm_config(info->elm_dev, bch_type))
-               return -ENODEV;
-       info->is_elm_used = true;
-       return 0;
+       err = elm_config(info->elm_dev, bch_type,
+               (info->mtd.writesize / ecc->size), ecc->size, ecc->bytes);
+
+       return err;
 }
 #endif /* CONFIG_MTD_NAND_ECC_BCH */
 
@@ -1657,6 +1596,7 @@ static int omap_nand_probe(struct platform_device *pdev)
        info->gpmc_cs           = pdata->cs;
        info->reg               = pdata->reg;
        info->of_node           = pdata->of_node;
+       info->ecc_opt           = pdata->ecc_opt;
        mtd                     = &info->mtd;
        mtd->priv               = &info->nand;
        mtd->name               = dev_name(&pdev->dev);
@@ -1666,27 +1606,11 @@ static int omap_nand_probe(struct platform_device *pdev)
        nand_chip->options      |= NAND_SKIP_BBTSCAN;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (res == NULL) {
-               err = -EINVAL;
-               dev_err(&pdev->dev, "error getting memory resource\n");
-               goto return_error;
-       }
+       nand_chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(nand_chip->IO_ADDR_R))
+               return PTR_ERR(nand_chip->IO_ADDR_R);
 
        info->phys_base = res->start;
-       info->mem_size = resource_size(res);
-
-       if (!devm_request_mem_region(&pdev->dev, info->phys_base,
-                               info->mem_size, pdev->dev.driver->name)) {
-               err = -EBUSY;
-               goto return_error;
-       }
-
-       nand_chip->IO_ADDR_R = devm_ioremap(&pdev->dev, info->phys_base,
-                                               info->mem_size);
-       if (!nand_chip->IO_ADDR_R) {
-               err = -ENOMEM;
-               goto return_error;
-       }
 
        nand_chip->controller = &info->controller;
 
@@ -1812,7 +1736,7 @@ static int omap_nand_probe(struct platform_device *pdev)
        /* populate MTD interface based on ECC scheme */
        nand_chip->ecc.layout   = &omap_oobinfo;
        ecclayout               = &omap_oobinfo;
-       switch (pdata->ecc_opt) {
+       switch (info->ecc_opt) {
        case OMAP_ECC_HAM1_CODE_HW:
                pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n");
                nand_chip->ecc.mode             = NAND_ECC_HW;
@@ -1844,9 +1768,9 @@ static int omap_nand_probe(struct platform_device *pdev)
                nand_chip->ecc.size             = 512;
                nand_chip->ecc.bytes            = 7;
                nand_chip->ecc.strength         = 4;
-               nand_chip->ecc.hwctl            = omap3_enable_hwecc_bch;
+               nand_chip->ecc.hwctl            = omap_enable_hwecc_bch;
                nand_chip->ecc.correct          = nand_bch_correct_data;
-               nand_chip->ecc.calculate        = omap3_calculate_ecc_bch4;
+               nand_chip->ecc.calculate        = omap_calculate_ecc_bch;
                /* define ECC layout */
                ecclayout->eccbytes             = nand_chip->ecc.bytes *
                                                        (mtd->writesize /
@@ -1884,9 +1808,9 @@ static int omap_nand_probe(struct platform_device *pdev)
                /* 14th bit is kept reserved for ROM-code compatibility */
                nand_chip->ecc.bytes            = 7 + 1;
                nand_chip->ecc.strength         = 4;
-               nand_chip->ecc.hwctl            = omap3_enable_hwecc_bch;
+               nand_chip->ecc.hwctl            = omap_enable_hwecc_bch;
                nand_chip->ecc.correct          = omap_elm_correct_data;
-               nand_chip->ecc.calculate        = omap3_calculate_ecc_bch;
+               nand_chip->ecc.calculate        = omap_calculate_ecc_bch;
                nand_chip->ecc.read_page        = omap_read_page_bch;
                nand_chip->ecc.write_page       = omap_write_page_bch;
                /* define ECC layout */
@@ -1919,9 +1843,9 @@ static int omap_nand_probe(struct platform_device *pdev)
                nand_chip->ecc.size             = 512;
                nand_chip->ecc.bytes            = 13;
                nand_chip->ecc.strength         = 8;
-               nand_chip->ecc.hwctl            = omap3_enable_hwecc_bch;
+               nand_chip->ecc.hwctl            = omap_enable_hwecc_bch;
                nand_chip->ecc.correct          = nand_bch_correct_data;
-               nand_chip->ecc.calculate        = omap3_calculate_ecc_bch8;
+               nand_chip->ecc.calculate        = omap_calculate_ecc_bch;
                /* define ECC layout */
                ecclayout->eccbytes             = nand_chip->ecc.bytes *
                                                        (mtd->writesize /
@@ -1960,9 +1884,9 @@ static int omap_nand_probe(struct platform_device *pdev)
                /* 14th bit is kept reserved for ROM-code compatibility */
                nand_chip->ecc.bytes            = 13 + 1;
                nand_chip->ecc.strength         = 8;
-               nand_chip->ecc.hwctl            = omap3_enable_hwecc_bch;
+               nand_chip->ecc.hwctl            = omap_enable_hwecc_bch;
                nand_chip->ecc.correct          = omap_elm_correct_data;
-               nand_chip->ecc.calculate        = omap3_calculate_ecc_bch;
+               nand_chip->ecc.calculate        = omap_calculate_ecc_bch;
                nand_chip->ecc.read_page        = omap_read_page_bch;
                nand_chip->ecc.write_page       = omap_write_page_bch;
                /* This ECC scheme requires ELM H/W block */
index 90f871a..2c98f9d 100644 (file)
@@ -23,7 +23,6 @@
 #undef DEBUG
 
 #include <linux/slab.h>
-#include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
index 2a7a0b2..7588fe2 100644 (file)
@@ -38,7 +38,6 @@
 
 #include <linux/platform_data/mtd-nand-pxa3xx.h>
 
-#define NAND_DEV_READY_TIMEOUT  50
 #define        CHIP_DELAY_TIMEOUT      (2 * HZ/10)
 #define NAND_STOP_DELAY                (2 * HZ/50)
 #define PAGE_CHUNK_SIZE                (2048)
@@ -1531,7 +1530,7 @@ KEEP_CONFIG:
        if (!ret) {
                dev_err(&info->pdev->dev,
                        "ECC strength %d at page size %d is not supported\n",
-                       chip->ecc_strength_ds, mtd->writesize);
+                       ecc_strength, mtd->writesize);
                return -ENODEV;
        }
 
index f0918e7..79acbb8 100644 (file)
@@ -29,7 +29,6 @@
 
 #include <linux/module.h>
 #include <linux/types.h>
-#include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/io.h>
index 8e1919b..093c29a 100644 (file)
@@ -13,7 +13,6 @@
  */
 
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/platform_device.h>
 #include <linux/mtd/mtd.h>
index 6547c84..d945473 100644 (file)
@@ -25,7 +25,6 @@
 
 #include <linux/device.h>
 #include <linux/module.h>
-#include <linux/init.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/onenand.h>
 #include <linux/mtd/partitions.h>
index 1de33b5..635ee00 100644 (file)
@@ -24,7 +24,6 @@
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/slab.h>
-#include <linux/init.h>
 #include <linux/sched.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
@@ -3238,20 +3237,17 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
 /**
  * onenand_get_fact_prot_info - [MTD Interface] Read factory OTP info
  * @param mtd          MTD device structure
- * @param buf          the databuffer to put/get data
  * @param len          number of bytes to read
+ * @param retlen       pointer to variable to store the number of read bytes
+ * @param buf          the databuffer to put/get data
  *
  * Read factory OTP info.
  */
-static int onenand_get_fact_prot_info(struct mtd_info *mtd,
-                       struct otp_info *buf, size_t len)
+static int onenand_get_fact_prot_info(struct mtd_info *mtd, size_t len,
+                                     size_t *retlen, struct otp_info *buf)
 {
-       size_t retlen;
-       int ret;
-
-       ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_FACTORY);
-
-       return ret ? : retlen;
+       return onenand_otp_walk(mtd, 0, len, retlen, (u_char *) buf, NULL,
+                               MTD_OTP_FACTORY);
 }
 
 /**
@@ -3273,20 +3269,17 @@ static int onenand_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
 /**
  * onenand_get_user_prot_info - [MTD Interface] Read user OTP info
  * @param mtd          MTD device structure
- * @param buf          the databuffer to put/get data
+ * @param retlen       pointer to variable to store the number of read bytes
  * @param len          number of bytes to read
+ * @param buf          the databuffer to put/get data
  *
  * Read user OTP info.
  */
-static int onenand_get_user_prot_info(struct mtd_info *mtd,
-                       struct otp_info *buf, size_t len)
+static int onenand_get_user_prot_info(struct mtd_info *mtd, size_t len,
+                                     size_t *retlen, struct otp_info *buf)
 {
-       size_t retlen;
-       int ret;
-
-       ret = onenand_otp_walk(mtd, 0, len, &retlen, (u_char *) buf, NULL, MTD_OTP_USER);
-
-       return ret ? : retlen;
+       return onenand_otp_walk(mtd, 0, len, retlen, (u_char *) buf, NULL,
+                               MTD_OTP_USER);
 }
 
 /**
@@ -3995,11 +3988,8 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
        /* Allocate buffers, if necessary */
        if (!this->page_buf) {
                this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL);
-               if (!this->page_buf) {
-                       printk(KERN_ERR "%s: Can't allocate page_buf\n",
-                               __func__);
+               if (!this->page_buf)
                        return -ENOMEM;
-               }
 #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE
                this->verify_buf = kzalloc(mtd->writesize, GFP_KERNEL);
                if (!this->verify_buf) {
@@ -4012,8 +4002,6 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
        if (!this->oob_buf) {
                this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL);
                if (!this->oob_buf) {
-                       printk(KERN_ERR "%s: Can't allocate oob_buf\n",
-                               __func__);
                        if (this->options & ONENAND_PAGEBUF_ALLOC) {
                                this->options &= ~ONENAND_PAGEBUF_ALLOC;
                                kfree(this->page_buf);
index df7400d..b1a792f 100644 (file)
@@ -872,10 +872,8 @@ static int s3c_onenand_probe(struct platform_device *pdev)
 
        size = sizeof(struct mtd_info) + sizeof(struct onenand_chip);
        mtd = kzalloc(size, GFP_KERNEL);
-       if (!mtd) {
-               dev_err(&pdev->dev, "failed to allocate memory\n");
+       if (!mtd)
                return -ENOMEM;
-       }
 
        onenand = kzalloc(sizeof(struct s3c_onenand), GFP_KERNEL);
        if (!onenand) {
index 233b946..d1cbf26 100644 (file)
@@ -602,8 +602,7 @@ static int mark_sector_deleted(struct partition *part, u_long old_addr)
        if (rc) {
                printk(KERN_ERR PREFIX "error writing '%s' at "
                        "0x%lx\n", part->mbd.mtd->name, addr);
-               if (rc)
-                       goto err;
+               goto err;
        }
        if (block == part->current_block)
                part->header_cache[offset + HEADER_MAP_OFFSET] = del;
@@ -675,8 +674,7 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf,
        if (rc) {
                printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
                                part->mbd.mtd->name, addr);
-               if (rc)
-                       goto err;
+               goto err;
        }
 
        part->sector_map[sector] = addr;
@@ -695,8 +693,7 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf,
        if (rc) {
                printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
                                part->mbd.mtd->name, addr);
-               if (rc)
-                       goto err;
+               goto err;
        }
        block->used_sectors++;
        block->free_sectors--;
index 4b8e895..cf49c22 100644 (file)
@@ -59,15 +59,12 @@ static struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl)
        struct attribute_group *attr_group;
        struct attribute **attributes;
        struct sm_sysfs_attribute *vendor_attribute;
+       char *vendor;
 
-       int vendor_len = strnlen(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET,
-                                       SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET);
-
-       char *vendor = kmalloc(vendor_len, GFP_KERNEL);
+       vendor = kstrndup(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET,
+                         SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET, GFP_KERNEL);
        if (!vendor)
                goto error1;
-       memcpy(vendor, ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, vendor_len);
-       vendor[vendor_len] = 0;
 
        /* Initialize sysfs attributes */
        vendor_attribute =
@@ -78,7 +75,7 @@ static struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl)
        sysfs_attr_init(&vendor_attribute->dev_attr.attr);
 
        vendor_attribute->data = vendor;
-       vendor_attribute->len = vendor_len;
+       vendor_attribute->len = strlen(vendor);
        vendor_attribute->dev_attr.attr.name = "vendor";
        vendor_attribute->dev_attr.attr.mode = S_IRUGO;
        vendor_attribute->dev_attr.show = sm_attr_show;
index c818a63..111ee46 100644 (file)
@@ -1,6 +1,5 @@
 #define pr_fmt(fmt) "mtd_test: " fmt
 
-#include <linux/init.h>
 #include <linux/module.h>
 #include <linux/sched.h>
 #include <linux/printk.h>
index 0ba8b0a..7bf4163 100644 (file)
@@ -22,7 +22,6 @@
 #ifndef __UBI_UBI_H__
 #define __UBI_UBI_H__
 
-#include <linux/init.h>
 #include <linux/types.h>
 #include <linux/list.h>
 #include <linux/rbtree.h>
index a27ec94..b7361ed 100644 (file)
@@ -49,6 +49,40 @@ int of_get_nand_ecc_mode(struct device_node *np)
 }
 EXPORT_SYMBOL_GPL(of_get_nand_ecc_mode);
 
+/**
+ * of_get_nand_ecc_step_size - Get ECC step size associated to
+ * the required ECC strength (see below).
+ * @np:        Pointer to the given device_node
+ *
+ * return the ECC step size, or errno in error case.
+ */
+int of_get_nand_ecc_step_size(struct device_node *np)
+{
+       int ret;
+       u32 val;
+
+       ret = of_property_read_u32(np, "nand-ecc-step-size", &val);
+       return ret ? ret : val;
+}
+EXPORT_SYMBOL_GPL(of_get_nand_ecc_step_size);
+
+/**
+ * of_get_nand_ecc_strength - Get required ECC strength over the
+ * correspnding step size as defined by 'nand-ecc-size'
+ * @np:        Pointer to the given device_node
+ *
+ * return the ECC strength, or errno in error case.
+ */
+int of_get_nand_ecc_strength(struct device_node *np)
+{
+       int ret;
+       u32 val;
+
+       ret = of_property_read_u32(np, "nand-ecc-strength", &val);
+       return ret ? ret : val;
+}
+EXPORT_SYMBOL_GPL(of_get_nand_ecc_strength);
+
 /**
  * of_get_nand_bus_width - Get nand bus witdh for given device_node
  * @np:        Pointer to the given device_node
index 16a5047..406d9cc 100644 (file)
@@ -33,7 +33,7 @@ static int jffs2_rtime_compress(unsigned char *data_in,
                                unsigned char *cpage_out,
                                uint32_t *sourcelen, uint32_t *dstlen)
 {
-       short positions[256];
+       unsigned short positions[256];
        int outpos = 0;
        int pos=0;
 
@@ -74,7 +74,7 @@ static int jffs2_rtime_decompress(unsigned char *data_in,
                                  unsigned char *cpage_out,
                                  uint32_t srclen, uint32_t destlen)
 {
-       short positions[256];
+       unsigned short positions[256];
        int outpos = 0;
        int pos=0;
 
index f739915..601afd1 100644 (file)
@@ -457,12 +457,14 @@ struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode, struct jffs2_r
           The umask is only applied if there's no default ACL */
        ret = jffs2_init_acl_pre(dir_i, inode, &mode);
        if (ret) {
-           make_bad_inode(inode);
-           iput(inode);
-           return ERR_PTR(ret);
+               mutex_unlock(&f->sem);
+               make_bad_inode(inode);
+               iput(inode);
+               return ERR_PTR(ret);
        }
        ret = jffs2_do_new_inode (c, f, mode, ri);
        if (ret) {
+               mutex_unlock(&f->sem);
                make_bad_inode(inode);
                iput(inode);
                return ERR_PTR(ret);
@@ -479,6 +481,7 @@ struct inode *jffs2_new_inode (struct inode *dir_i, umode_t mode, struct jffs2_r
        inode->i_size = 0;
 
        if (insert_inode_locked(inode) < 0) {
+               mutex_unlock(&f->sem);
                make_bad_inode(inode);
                iput(inode);
                return ERR_PTR(-EINVAL);
index e4619b0..fa35ff7 100644 (file)
@@ -231,7 +231,7 @@ struct jffs2_tmp_dnode_info
        uint32_t version;
        uint32_t data_crc;
        uint32_t partial_crc;
-       uint16_t csize;
+       uint32_t csize;
        uint16_t overlapped;
 };
 
index 0331072..b6bd4af 100644 (file)
@@ -179,6 +179,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
                                        spin_unlock(&c->erase_completion_lock);
 
                                        schedule();
+                                       remove_wait_queue(&c->erase_wait, &wait);
                                } else
                                        spin_unlock(&c->erase_completion_lock);
                        } else if (ret)
@@ -211,20 +212,25 @@ out:
 int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize,
                           uint32_t *len, uint32_t sumsize)
 {
-       int ret = -EAGAIN;
+       int ret;
        minsize = PAD(minsize);
 
        jffs2_dbg(1, "%s(): Requested 0x%x bytes\n", __func__, minsize);
 
-       spin_lock(&c->erase_completion_lock);
-       while(ret == -EAGAIN) {
+       while (true) {
+               spin_lock(&c->erase_completion_lock);
                ret = jffs2_do_reserve_space(c, minsize, len, sumsize);
                if (ret) {
                        jffs2_dbg(1, "%s(): looping, ret is %d\n",
                                  __func__, ret);
                }
+               spin_unlock(&c->erase_completion_lock);
+
+               if (ret == -EAGAIN)
+                       cond_resched();
+               else
+                       break;
        }
-       spin_unlock(&c->erase_completion_lock);
        if (!ret)
                ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
 
index 8cc0e2f..a1b0b4c 100644 (file)
@@ -204,12 +204,12 @@ struct mtd_info {
                          struct mtd_oob_ops *ops);
        int (*_write_oob) (struct mtd_info *mtd, loff_t to,
                           struct mtd_oob_ops *ops);
-       int (*_get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
-                                   size_t len);
+       int (*_get_fact_prot_info) (struct mtd_info *mtd, size_t len,
+                                   size_t *retlen, struct otp_info *buf);
        int (*_read_fact_prot_reg) (struct mtd_info *mtd, loff_t from,
                                    size_t len, size_t *retlen, u_char *buf);
-       int (*_get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
-                                   size_t len);
+       int (*_get_user_prot_info) (struct mtd_info *mtd, size_t len,
+                                   size_t *retlen, struct otp_info *buf);
        int (*_read_user_prot_reg) (struct mtd_info *mtd, loff_t from,
                                    size_t len, size_t *retlen, u_char *buf);
        int (*_write_user_prot_reg) (struct mtd_info *mtd, loff_t to,
@@ -278,12 +278,12 @@ static inline int mtd_write_oob(struct mtd_info *mtd, loff_t to,
        return mtd->_write_oob(mtd, to, ops);
 }
 
-int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
-                          size_t len);
+int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
+                          struct otp_info *buf);
 int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
                           size_t *retlen, u_char *buf);
-int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf,
-                          size_t len);
+int mtd_get_user_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
+                          struct otp_info *buf);
 int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
                           size_t *retlen, u_char *buf);
 int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
index 32f8612..450d61e 100644 (file)
@@ -51,14 +51,6 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
 /* The maximum number of NAND chips in an array */
 #define NAND_MAX_CHIPS         8
 
-/*
- * This constant declares the max. oobsize / page, which
- * is supported now. If you add a chip with bigger oobsize/page
- * adjust this accordingly.
- */
-#define NAND_MAX_OOBSIZE       744
-#define NAND_MAX_PAGESIZE      8192
-
 /*
  * Constants for hardware specific CLE/ALE/NCE function
  *
@@ -350,6 +342,84 @@ struct nand_onfi_vendor_micron {
        u8 param_revision;
 } __packed;
 
+struct jedec_ecc_info {
+       u8 ecc_bits;
+       u8 codeword_size;
+       __le16 bb_per_lun;
+       __le16 block_endurance;
+       u8 reserved[2];
+} __packed;
+
+/* JEDEC features */
+#define JEDEC_FEATURE_16_BIT_BUS       (1 << 0)
+
+struct nand_jedec_params {
+       /* rev info and features block */
+       /* 'J' 'E' 'S' 'D'  */
+       u8 sig[4];
+       __le16 revision;
+       __le16 features;
+       u8 opt_cmd[3];
+       __le16 sec_cmd;
+       u8 num_of_param_pages;
+       u8 reserved0[18];
+
+       /* manufacturer information block */
+       char manufacturer[12];
+       char model[20];
+       u8 jedec_id[6];
+       u8 reserved1[10];
+
+       /* memory organization block */
+       __le32 byte_per_page;
+       __le16 spare_bytes_per_page;
+       u8 reserved2[6];
+       __le32 pages_per_block;
+       __le32 blocks_per_lun;
+       u8 lun_count;
+       u8 addr_cycles;
+       u8 bits_per_cell;
+       u8 programs_per_page;
+       u8 multi_plane_addr;
+       u8 multi_plane_op_attr;
+       u8 reserved3[38];
+
+       /* electrical parameter block */
+       __le16 async_sdr_speed_grade;
+       __le16 toggle_ddr_speed_grade;
+       __le16 sync_ddr_speed_grade;
+       u8 async_sdr_features;
+       u8 toggle_ddr_features;
+       u8 sync_ddr_features;
+       __le16 t_prog;
+       __le16 t_bers;
+       __le16 t_r;
+       __le16 t_r_multi_plane;
+       __le16 t_ccs;
+       __le16 io_pin_capacitance_typ;
+       __le16 input_pin_capacitance_typ;
+       __le16 clk_pin_capacitance_typ;
+       u8 driver_strength_support;
+       __le16 t_ald;
+       u8 reserved4[36];
+
+       /* ECC and endurance block */
+       u8 guaranteed_good_blocks;
+       __le16 guaranteed_block_endurance;
+       struct jedec_ecc_info ecc_info[4];
+       u8 reserved5[29];
+
+       /* reserved */
+       u8 reserved6[148];
+
+       /* vendor */
+       __le16 vendor_rev_num;
+       u8 reserved7[88];
+
+       /* CRC for Parameter Page */
+       __le16 crc;
+} __packed;
+
 /**
  * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
  * @lock:               protection lock
@@ -418,7 +488,7 @@ struct nand_ecc_ctrl {
        int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
                        uint8_t *buf, int oob_required, int page);
        int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
-                       uint32_t offs, uint32_t len, uint8_t *buf);
+                       uint32_t offs, uint32_t len, uint8_t *buf, int page);
        int (*write_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
                        uint32_t offset, uint32_t data_len,
                        const uint8_t *data_buf, int oob_required);
@@ -435,17 +505,17 @@ struct nand_ecc_ctrl {
 
 /**
  * struct nand_buffers - buffer structure for read/write
- * @ecccalc:   buffer for calculated ECC
- * @ecccode:   buffer for ECC read from flash
- * @databuf:   buffer for data - dynamically sized
+ * @ecccalc:   buffer pointer for calculated ECC, size is oobsize.
+ * @ecccode:   buffer pointer for ECC read from flash, size is oobsize.
+ * @databuf:   buffer pointer for data, size is (page size + oobsize).
  *
  * Do not change the order of buffers. databuf and oobrbuf must be in
  * consecutive order.
  */
 struct nand_buffers {
-       uint8_t ecccalc[NAND_MAX_OOBSIZE];
-       uint8_t ecccode[NAND_MAX_OOBSIZE];
-       uint8_t databuf[NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE];
+       uint8_t *ecccalc;
+       uint8_t *ecccode;
+       uint8_t *databuf;
 };
 
 /**
@@ -523,8 +593,12 @@ struct nand_buffers {
  * @subpagesize:       [INTERN] holds the subpagesize
  * @onfi_version:      [INTERN] holds the chip ONFI version (BCD encoded),
  *                     non 0 if ONFI supported.
+ * @jedec_version:     [INTERN] holds the chip JEDEC version (BCD encoded),
+ *                     non 0 if JEDEC supported.
  * @onfi_params:       [INTERN] holds the ONFI page parameter when ONFI is
  *                     supported, 0 otherwise.
+ * @jedec_params:      [INTERN] holds the JEDEC parameter page when JEDEC is
+ *                     supported, 0 otherwise.
  * @read_retries:      [INTERN] the number of read retry modes supported
  * @onfi_set_features: [REPLACEABLE] set the features for ONFI nand
  * @onfi_get_features: [REPLACEABLE] get the features for ONFI nand
@@ -597,7 +671,11 @@ struct nand_chip {
        int badblockbits;
 
        int onfi_version;
-       struct nand_onfi_params onfi_params;
+       int jedec_version;
+       union {
+               struct nand_onfi_params onfi_params;
+               struct nand_jedec_params jedec_params;
+       };
 
        int read_retries;
 
@@ -840,4 +918,29 @@ static inline bool nand_is_slc(struct nand_chip *chip)
 {
        return chip->bits_per_cell == 1;
 }
+
+/**
+ * Check if the opcode's address should be sent only on the lower 8 bits
+ * @command: opcode to check
+ */
+static inline int nand_opcode_8bits(unsigned int command)
+{
+       switch (command) {
+       case NAND_CMD_READID:
+       case NAND_CMD_PARAM:
+       case NAND_CMD_GET_FEATURES:
+       case NAND_CMD_SET_FEATURES:
+               return 1;
+       default:
+               break;
+       }
+       return 0;
+}
+
+/* return the supported JEDEC features. */
+static inline int jedec_feature(struct nand_chip *chip)
+{
+       return chip->jedec_version ? le16_to_cpu(chip->jedec_params.features)
+               : 0;
+}
 #endif /* __LINUX_MTD_NAND_H */
index cb32d9c..e266caa 100644 (file)
@@ -13,6 +13,8 @@
 
 #include <linux/of.h>
 int of_get_nand_ecc_mode(struct device_node *np);
+int of_get_nand_ecc_step_size(struct device_node *np);
+int of_get_nand_ecc_strength(struct device_node *np);
 int of_get_nand_bus_width(struct device_node *np);
 bool of_get_nand_on_flash_bbt(struct device_node *np);
 
@@ -23,6 +25,16 @@ static inline int of_get_nand_ecc_mode(struct device_node *np)
        return -ENOSYS;
 }
 
+static inline int of_get_nand_ecc_step_size(struct device_node *np)
+{
+       return -ENOSYS;
+}
+
+static inline int of_get_nand_ecc_strength(struct device_node *np)
+{
+       return -ENOSYS;
+}
+
 static inline int of_get_nand_bus_width(struct device_node *np)
 {
        return -ENOSYS;
index bf0a83b..4edb406 100644 (file)
@@ -26,13 +26,6 @@ enum bch_ecc {
 /* ELM support 8 error syndrome process */
 #define ERROR_VECTOR_MAX               8
 
-#define BCH8_ECC_OOB_BYTES             13
-#define BCH4_ECC_OOB_BYTES             7
-/* RBL requires 14 byte even though BCH8 uses only 13 byte */
-#define BCH8_SIZE                      (BCH8_ECC_OOB_BYTES + 1)
-/* Uses 1 extra byte to handle erased pages */
-#define BCH4_SIZE                      (BCH4_ECC_OOB_BYTES + 1)
-
 /**
  * struct elm_errorvec - error vector for elm
  * @error_reported:            set true for vectors error is reported
@@ -50,5 +43,6 @@ struct elm_errorvec {
 
 void elm_decode_bch_error_page(struct device *dev, u8 *ecc_calc,
                struct elm_errorvec *err_vec);
-int elm_config(struct device *dev, enum bch_ecc bch_type);
+int elm_config(struct device *dev, enum bch_ecc bch_type,
+       int ecc_steps, int ecc_step_size, int ecc_syndrome_size);
 #endif /* __ELM_H */
index b64115f..36bb921 100644 (file)
@@ -1,5 +1,4 @@
-/* arch/arm/mach-s3c2410/include/mach/nand.h
- *
+/*
  * Copyright (c) 2004 Simtec Electronics
  *     Ben Dooks <ben@simtec.co.uk>
  *
@@ -10,6 +9,9 @@
  * published by the Free Software Foundation.
 */
 
+#ifndef __MTD_NAND_S3C2410_H
+#define __MTD_NAND_S3C2410_H
+
 /**
  * struct s3c2410_nand_set - define a set of one or more nand chips
  * @disable_ecc:       Entirely disable ECC - Dangerous
@@ -65,3 +67,5 @@ struct s3c2410_platform_nand {
  * it with the s3c_device_nand. This allows @nand to be __initdata.
 */
 extern void s3c_nand_set_platdata(struct s3c2410_platform_nand *nand);
+
+#endif /*__MTD_NAND_S3C2410_H */