]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
Merge tag 'for-linus-20150216' of git://git.infradead.org/linux-mtd
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 18 Feb 2015 16:01:44 +0000 (08:01 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 18 Feb 2015 16:01:44 +0000 (08:01 -0800)
Pull MTD updates from Brian Norris:
 "NAND:

   - Add new Hisilicon NAND driver for Hip04
   - Add default reboot handler, to ensure all outstanding erase
     transactions complete in time
   - jz4740: convert to use GPIO descriptor API
   - Atmel: add support for sama5d4
   - Change default bitflip threshold to 75% of correction strength
   - Miscellaneous cleanups and bugfixes

  SPI NOR:

   - Freescale QuadSPI:
   - Fix a few probe() and remove() issues
   - Add a MAINTAINERS entry for this driver
   - Tweak transfer size to increase read performance
   - Add suspend/resume support
   - Add Micron quad I/O support
   - ST FSM SPI: miscellaneous fixes

  JFFS2:

   - gracefully handle corrupted 'offset' field found on flash

  Other:

   - bcm47xxpart: add tweaks for a few new devices
   - mtdconcat: set return lengths properly for mtd_write_oob()
   - map_ram: enable use with mtdoops
   - maps: support fallback to ROM/UBI for write-protected NOR flash"

* tag 'for-linus-20150216' of git://git.infradead.org/linux-mtd: (46 commits)
  mtd: hisilicon: && vs & typo
  jffs2: fix handling of corrupted summary length
  mtd: hisilicon: add device tree binding documentation
  mtd: hisilicon: add a new NAND controller driver for hisilicon hip04 Soc
  mtd: avoid registering reboot notifier twice
  mtd: concat: set the return lengths properly
  mtd: kconfig: replace PPC_OF with PPC
  mtd: denali: remove unnecessary stubs
  mtd: nand: remove redundant local variable
  MAINTAINERS: add maintainer entry for FREESCALE QUAD SPI driver
  mtd: fsl-quadspi: improve read performance by increase AHB transfer size
  mtd: fsl-quadspi: Remove unnecessary 'map_failed' label
  mtd: fsl-quadspi: Remove unneeded success/error messages
  mtd: fsl-quadspi: Fix the error paths
  mtd: nand: omap: drop condition with no effect
  mtd: nand: jz4740: Convert to GPIO descriptor API
  mtd: nand: Request strength instead of bytes for soft BCH
  mtd: nand: default bitflip-reporting threshold to 75% of correction strength
  mtd: atmel_nand: introduce a new compatible string for sama5d4 chip
  mtd: atmel_nand: return max bitflips in all sectors in pmecc_correction()
  ...

35 files changed:
Documentation/devicetree/bindings/mtd/atmel-nand.txt
Documentation/devicetree/bindings/mtd/fsl-quadspi.txt
Documentation/devicetree/bindings/mtd/gpmi-nand.txt
Documentation/devicetree/bindings/mtd/hisi504-nand.txt [new file with mode: 0644]
Documentation/devicetree/bindings/mtd/mtd-physmap.txt
MAINTAINERS
arch/mips/include/asm/mach-jz4740/jz4740_nand.h
arch/mips/jz4740/board-qi_lb60.c
drivers/mtd/bcm47xxpart.c
drivers/mtd/chips/map_ram.c
drivers/mtd/chips/map_rom.c
drivers/mtd/devices/st_spi_fsm.c
drivers/mtd/maps/physmap_of.c
drivers/mtd/mtdblock.c
drivers/mtd/mtdconcat.c
drivers/mtd/mtdcore.c
drivers/mtd/nand/Kconfig
drivers/mtd/nand/Makefile
drivers/mtd/nand/ams-delta.c
drivers/mtd/nand/atmel_nand.c
drivers/mtd/nand/denali.c
drivers/mtd/nand/gpmi-nand/gpmi-nand.c
drivers/mtd/nand/hisi504_nand.c [new file with mode: 0644]
drivers/mtd/nand/jz4740_nand.c
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/nandsim.c
drivers/mtd/nand/omap2.c
drivers/mtd/nand/sunxi_nand.c
drivers/mtd/nftlmount.c
drivers/mtd/spi-nor/fsl-quadspi.c
drivers/mtd/spi-nor/spi-nor.c
fs/jffs2/compr_rubin.c
fs/jffs2/scan.c
include/linux/mtd/mtd.h
include/linux/mtd/spi-nor.h

index 1fe6dde9849942a51bb515f360489d96591365a9..7d4c8eb775a5fff5da9861b632d11e18a6786eee 100644 (file)
@@ -1,7 +1,7 @@
 Atmel NAND flash
 
 Required properties:
-- compatible : "atmel,at91rm9200-nand".
+- compatible : should be "atmel,at91rm9200-nand" or "atmel,sama5d4-nand".
 - reg : should specify localbus address and size used for the chip,
        and hardware ECC controller if available.
        If the hardware ECC is PMECC, it should contain address and size for
index 823d134121956362414c546134dde3744e03fb5e..4461dc71cb10097efc887240c9f319660d43972a 100644 (file)
@@ -1,7 +1,7 @@
 * Freescale Quad Serial Peripheral Interface(QuadSPI)
 
 Required properties:
-  - compatible : Should be "fsl,vf610-qspi"
+  - compatible : Should be "fsl,vf610-qspi" or "fsl,imx6sx-qspi"
   - reg : the first contains the register location and length,
           the second contains the memory mapping address and length
   - reg-names: Should contain the reg names "QuadSPI" and "QuadSPI-memory"
index a011fdf61dbfaeac39ab76bb48db143443243549..d02acaff3c35e98476b91c26757f30355736cb3b 100644 (file)
@@ -1,7 +1,7 @@
 * Freescale General-Purpose Media Interface (GPMI)
 
 The GPMI nand controller provides an interface to control the
-NAND flash chips. We support only one NAND chip now.
+NAND flash chips.
 
 Required properties:
   - compatible : should be "fsl,<chip>-gpmi-nand"
diff --git a/Documentation/devicetree/bindings/mtd/hisi504-nand.txt b/Documentation/devicetree/bindings/mtd/hisi504-nand.txt
new file mode 100644 (file)
index 0000000..2e35f06
--- /dev/null
@@ -0,0 +1,47 @@
+Hisilicon Hip04 Soc NAND controller DT binding
+
+Required properties:
+
+- compatible:          Should be "hisilicon,504-nfc".
+- reg:                 The first contains base physical address and size of
+                       NAND controller's registers. The second contains base
+                       physical address and size of NAND controller's buffer.
+- interrupts:          Interrupt number for nfc.
+- nand-bus-width:      See nand.txt.
+- nand-ecc-mode:       Support none and hw ecc mode.
+- #address-cells:      Partition address, should be set 1.
+- #size-cells:         Partition size, should be set 1.
+
+Optional properties:
+
+- nand-ecc-strength:   Number of bits to correct per ECC step.
+- nand-ecc-step-size:  Number of data bytes covered by a single ECC step.
+
+The following ECC strength and step size are currently supported:
+
+ - nand-ecc-strength = <16>, nand-ecc-step-size = <1024>
+
+Flash chip may optionally contain additional sub-nodes describing partitions of
+the address space. See partition.txt for more detail.
+
+Example:
+
+       nand: nand@4020000 {
+               compatible = "hisilicon,504-nfc";
+               reg = <0x4020000 0x10000>, <0x5000000 0x1000>;
+               interrupts = <0 379 4>;
+               nand-bus-width = <8>;
+               nand-ecc-mode = "hw";
+               nand-ecc-strength = <16>;
+               nand-ecc-step-size = <1024>;
+               #address-cells = <1>;
+               #size-cells = <1>;
+
+               partition@0 {
+                       label = "nand_text";
+                       reg = <0x00000000 0x00400000>;
+               };
+
+               ...
+
+       };
index 6b9f680cb579bd7fbca4f1ee17f610e6c0b079bf..4a0a48bf4ecb831d0fc1e2b0263208d0b42cbb36 100644 (file)
@@ -36,6 +36,11 @@ are defined:
  - vendor-id : Contains the flash chip's vendor id (1 byte).
  - device-id : Contains the flash chip's device id (1 byte).
 
+For ROM compatible devices (and ROM fallback from cfi-flash), the following
+additional (optional) property is defined:
+
+ - erase-size : The chip's physical erase block size in bytes.
+
 The device tree may optionally contain sub-nodes describing partitions of the
 address space. See partition.txt for more detail.
 
index 0597c5b3c4ee0a27fde7e0d189cee46be9822181..7ac95f8ba6ca618dfd73c91f0c631902e5a6861a 100644 (file)
@@ -4092,6 +4092,12 @@ S:       Maintained
 F:     include/linux/platform_data/video-imxfb.h
 F:     drivers/video/fbdev/imxfb.c
 
+FREESCALE QUAD SPI DRIVER
+M:     Han Xu <han.xu@freescale.com>
+L:     linux-mtd@lists.infradead.org
+S:     Maintained
+F:     drivers/mtd/spi-nor/fsl-quadspi.c
+
 FREESCALE SOC FS_ENET DRIVER
 M:     Pantelis Antoniou <pantelis.antoniou@gmail.com>
 M:     Vitaly Bordug <vbordug@ru.mvista.com>
index 986982db7c38c95cf8ca0e4beac9bcee6570f8fe..79cff26d8b36f16cb333d9af2e56383db72b4222 100644 (file)
@@ -27,8 +27,6 @@ struct jz_nand_platform_data {
 
        struct nand_ecclayout   *ecc_layout;
 
-       unsigned int busy_gpio;
-
        unsigned char banks[JZ_NAND_NUM_BANKS];
 
        void (*ident_callback)(struct platform_device *, struct nand_chip *,
index c454525e7695704de74a777a7d1801e60ea20a1d..9dd051edb411f2a5b68f2f5b4ae77bfbe6115d57 100644 (file)
@@ -140,10 +140,18 @@ static void qi_lb60_nand_ident(struct platform_device *pdev,
 
 static struct jz_nand_platform_data qi_lb60_nand_pdata = {
        .ident_callback = qi_lb60_nand_ident,
-       .busy_gpio = 94,
        .banks = { 1 },
 };
 
+static struct gpiod_lookup_table qi_lb60_nand_gpio_table = {
+       .dev_id = "jz4740-nand.0",
+       .table = {
+               GPIO_LOOKUP("Bank C", 30, "busy", 0),
+               { },
+       },
+};
+
+
 /* Keyboard*/
 
 #define KEY_QI_QI      KEY_F13
@@ -472,6 +480,7 @@ static int __init qi_lb60_init_platform_devices(void)
        jz4740_mmc_device.dev.platform_data = &qi_lb60_mmc_pdata;
 
        gpiod_add_lookup_table(&qi_lb60_audio_gpio_table);
+       gpiod_add_lookup_table(&qi_lb60_nand_gpio_table);
 
        jz4740_serial_device_register();
 
index cc13ea5ce4d58f6cb532d151d5f22d9aa46e8c2c..c0720c1ee4c9607d0887e7b4caacecdac96088af 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 
+#include <uapi/linux/magic.h>
+
 /*
  * NAND flash on Netgear R6250 was verified to contain 15 partitions.
  * This will result in allocating too big array for some old devices, but the
@@ -39,7 +41,8 @@
 #define ML_MAGIC1                      0x39685a42
 #define ML_MAGIC2                      0x26594131
 #define TRX_MAGIC                      0x30524448
-#define SQSH_MAGIC                     0x71736873      /* shsq */
+#define SHSQ_MAGIC                     0x71736873      /* shsq (weird ZTE H218N endianness) */
+#define UBI_EC_MAGIC                   0x23494255      /* UBI# */
 
 struct trx_header {
        uint32_t magic;
@@ -50,7 +53,7 @@ struct trx_header {
        uint32_t offset[3];
 } __packed;
 
-static void bcm47xxpart_add_part(struct mtd_partition *part, char *name,
+static void bcm47xxpart_add_part(struct mtd_partition *part, const char *name,
                                 u64 offset, uint32_t mask_flags)
 {
        part->name = name;
@@ -58,6 +61,26 @@ static void bcm47xxpart_add_part(struct mtd_partition *part, char *name,
        part->mask_flags = mask_flags;
 }
 
+static const char *bcm47xxpart_trx_data_part_name(struct mtd_info *master,
+                                                 size_t offset)
+{
+       uint32_t buf;
+       size_t bytes_read;
+
+       if (mtd_read(master, offset, sizeof(buf), &bytes_read,
+                    (uint8_t *)&buf) < 0) {
+               pr_err("mtd_read error while parsing (offset: 0x%X)!\n",
+                       offset);
+               goto out_default;
+       }
+
+       if (buf == UBI_EC_MAGIC)
+               return "ubi";
+
+out_default:
+       return "rootfs";
+}
+
 static int bcm47xxpart_parse(struct mtd_info *master,
                             struct mtd_partition **pparts,
                             struct mtd_part_parser_data *data)
@@ -73,8 +96,12 @@ static int bcm47xxpart_parse(struct mtd_info *master,
        int last_trx_part = -1;
        int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, };
 
-       if (blocksize <= 0x10000)
-               blocksize = 0x10000;
+       /*
+        * Some really old flashes (like AT45DB*) had smaller erasesize-s, but
+        * partitions were aligned to at least 0x1000 anyway.
+        */
+       if (blocksize < 0x1000)
+               blocksize = 0x1000;
 
        /* Alloc */
        parts = kzalloc(sizeof(struct mtd_partition) * BCM47XXPART_MAX_PARTS,
@@ -186,8 +213,11 @@ static int bcm47xxpart_parse(struct mtd_info *master,
                         * we want to have jffs2 (overlay) in the same mtd.
                         */
                        if (trx->offset[i]) {
+                               const char *name;
+
+                               name = bcm47xxpart_trx_data_part_name(master, offset + trx->offset[i]);
                                bcm47xxpart_add_part(&parts[curr_part++],
-                                                    "rootfs",
+                                                    name,
                                                     offset + trx->offset[i],
                                                     0);
                                i++;
@@ -205,7 +235,8 @@ static int bcm47xxpart_parse(struct mtd_info *master,
                }
 
                /* Squashfs on devices not using TRX */
-               if (buf[0x000 / 4] == SQSH_MAGIC) {
+               if (le32_to_cpu(buf[0x000 / 4]) == SQUASHFS_MAGIC ||
+                   buf[0x000 / 4] == SHSQ_MAGIC) {
                        bcm47xxpart_add_part(&parts[curr_part++], "rootfs",
                                             offset, 0);
                        continue;
index 991c2a1c05d364f33ac72cee2164f0ba6567e554..afb43d5e178269d33443a59a439245263cb263c0 100644 (file)
@@ -68,6 +68,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map)
        mtd->_get_unmapped_area = mapram_unmapped_area;
        mtd->_read = mapram_read;
        mtd->_write = mapram_write;
+       mtd->_panic_write = mapram_write;
        mtd->_sync = mapram_nop;
        mtd->flags = MTD_CAP_RAM;
        mtd->writesize = 1;
index 47a43cf7e5c60fc162934b6da893b32dbd0cb717..e67f73ab44c9db23eae052c206139a03aafe15b9 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/init.h>
+#include <linux/of.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 
@@ -28,6 +29,15 @@ static struct mtd_chip_driver maprom_chipdrv = {
        .module = THIS_MODULE
 };
 
+static unsigned int default_erasesize(struct map_info *map)
+{
+       const __be32 *erase_size = NULL;
+
+       erase_size = of_get_property(map->device_node, "erase-size", NULL);
+
+       return !erase_size ? map->size : be32_to_cpu(*erase_size);
+}
+
 static struct mtd_info *map_rom_probe(struct map_info *map)
 {
        struct mtd_info *mtd;
@@ -47,8 +57,9 @@ static struct mtd_info *map_rom_probe(struct map_info *map)
        mtd->_sync = maprom_nop;
        mtd->_erase = maprom_erase;
        mtd->flags = MTD_CAP_ROM;
-       mtd->erasesize = map->size;
+       mtd->erasesize = default_erasesize(map);
        mtd->writesize = 1;
+       mtd->writebufsize = 1;
 
        __module_get(THIS_MODULE);
        return mtd;
index 54ffe5223e64294558c1189316d61d3e23b50aed..3060025c8af47772cd1d9cc50fdcc277de1521ae 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/of.h>
+#include <linux/clk.h>
 
 #include "serial_flash_cmds.h"
 
@@ -262,6 +263,7 @@ struct stfsm {
        struct mtd_info         mtd;
        struct mutex            lock;
        struct flash_info       *info;
+       struct clk              *clk;
 
        uint32_t                configuration;
        uint32_t                fifo_dir_delay;
@@ -663,6 +665,23 @@ static struct stfsm_seq stfsm_seq_write_status = {
                    SEQ_CFG_STARTSEQ),
 };
 
+/* Dummy sequence to read one byte of data from flash into the FIFO */
+static const struct stfsm_seq stfsm_seq_load_fifo_byte = {
+       .data_size = TRANSFER_SIZE(1),
+       .seq_opc[0] = (SEQ_OPC_PADS_1 |
+                      SEQ_OPC_CYCLES(8) |
+                      SEQ_OPC_OPCODE(SPINOR_OP_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 int stfsm_n25q_en_32bit_addr_seq(struct stfsm_seq *seq)
 {
        seq->seq_opc[0] = (SEQ_OPC_PADS_1 | SEQ_OPC_CYCLES(8) |
@@ -695,22 +714,6 @@ 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)
 {
@@ -772,6 +775,68 @@ static void stfsm_read_fifo(struct stfsm *fsm, uint32_t *buf, uint32_t size)
        }
 }
 
+/*
+ * Clear the data FIFO
+ *
+ * Typically, this is only required during driver initialisation, where no
+ * assumptions can be made regarding the state of the FIFO.
+ *
+ * The process of clearing the FIFO is complicated by fact that while it is
+ * possible for the FIFO to contain an arbitrary number of bytes [1], the
+ * SPI_FAST_SEQ_STA register only reports the number of complete 32-bit words
+ * present.  Furthermore, data can only be drained from the FIFO by reading
+ * complete 32-bit words.
+ *
+ * With this in mind, a two stage process is used to the clear the FIFO:
+ *
+ *     1. Read any complete 32-bit words from the FIFO, as reported by the
+ *        SPI_FAST_SEQ_STA register.
+ *
+ *     2. Mop up any remaining bytes.  At this point, it is not known if there
+ *        are 0, 1, 2, or 3 bytes in the FIFO.  To handle all cases, a dummy FSM
+ *        sequence is used to load one byte at a time, until a complete 32-bit
+ *        word is formed; at most, 4 bytes will need to be loaded.
+ *
+ * [1] It is theoretically possible for the FIFO to contain an arbitrary number
+ *     of bits.  However, since there are no known use-cases that leave
+ *     incomplete bytes in the FIFO, only words and bytes are considered here.
+ */
+static void stfsm_clear_fifo(struct stfsm *fsm)
+{
+       const struct stfsm_seq *seq = &stfsm_seq_load_fifo_byte;
+       uint32_t words, i;
+
+       /* 1. Clear any 32-bit words */
+       words = stfsm_fifo_available(fsm);
+       if (words) {
+               for (i = 0; i < words; i++)
+                       readl(fsm->base + SPI_FAST_SEQ_DATA_REG);
+               dev_dbg(fsm->dev, "cleared %d words from FIFO\n", words);
+       }
+
+       /*
+        * 2. Clear any remaining bytes
+        *    - Load the FIFO, one byte at a time, until a complete 32-bit word
+        *      is available.
+        */
+       for (i = 0, words = 0; i < 4 && !words; i++) {
+               stfsm_load_seq(fsm, seq);
+               stfsm_wait_seq(fsm);
+               words = stfsm_fifo_available(fsm);
+       }
+
+       /*    - A single word must be available now */
+       if (words != 1) {
+               dev_err(fsm->dev, "failed to clear bytes from the data FIFO\n");
+               return;
+       }
+
+       /*    - Read the 32-bit word */
+       readl(fsm->base + SPI_FAST_SEQ_DATA_REG);
+
+       dev_dbg(fsm->dev, "cleared %d byte(s) from the data FIFO\n", 4 - i);
+}
+
 static int stfsm_write_fifo(struct stfsm *fsm, const uint32_t *buf,
                            uint32_t size)
 {
@@ -1521,11 +1586,11 @@ static int stfsm_write(struct stfsm *fsm, const uint8_t *buf,
        uint32_t size_lb;
        uint32_t size_mop;
        uint32_t tmp[4];
+       uint32_t i;
        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);
 
@@ -1843,8 +1908,7 @@ 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;
+       emi_freq = clk_get_rate(fsm->clk);
 
        /*
         * Calculate clk_div - values between 2 and 128
@@ -1994,6 +2058,18 @@ static int stfsm_probe(struct platform_device *pdev)
                return PTR_ERR(fsm->base);
        }
 
+       fsm->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(fsm->clk)) {
+               dev_err(fsm->dev, "Couldn't find EMI clock.\n");
+               return PTR_ERR(fsm->clk);
+       }
+
+       ret = clk_prepare_enable(fsm->clk);
+       if (ret) {
+               dev_err(fsm->dev, "Failed to enable EMI clock.\n");
+               return ret;
+       }
+
        mutex_init(&fsm->lock);
 
        ret = stfsm_init(fsm);
@@ -2058,6 +2134,28 @@ static int stfsm_remove(struct platform_device *pdev)
        return mtd_device_unregister(&fsm->mtd);
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int stfsmfsm_suspend(struct device *dev)
+{
+       struct stfsm *fsm = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(fsm->clk);
+
+       return 0;
+}
+
+static int stfsmfsm_resume(struct device *dev)
+{
+       struct stfsm *fsm = dev_get_drvdata(dev);
+
+       clk_prepare_enable(fsm->clk);
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(stfsm_pm_ops, stfsmfsm_suspend, stfsmfsm_resume);
+
 static const struct of_device_id stfsm_match[] = {
        { .compatible = "st,spi-fsm", },
        {},
@@ -2070,6 +2168,7 @@ static struct platform_driver stfsm_driver = {
        .driver         = {
                .name   = "st-spi-fsm",
                .of_match_table = stfsm_match,
+               .pm     = &stfsm_pm_ops,
        },
 };
 module_platform_driver(stfsm_driver);
index f35cd2081314d79f0c8742a7965f8868d624c764..ff26e979b1a17c243e1525adfceed3886d479ba1 100644 (file)
@@ -269,6 +269,16 @@ static int of_flash_probe(struct platform_device *dev)
                        info->list[i].mtd = obsolete_probe(dev,
                                                           &info->list[i].map);
                }
+
+               /* Fall back to mapping region as ROM */
+               if (!info->list[i].mtd) {
+                       dev_warn(&dev->dev,
+                               "do_map_probe() failed for type %s\n",
+                                probe_type);
+
+                       info->list[i].mtd = do_map_probe("map_rom",
+                                                        &info->list[i].map);
+               }
                mtd_list[i] = info->list[i].mtd;
 
                err = -ENXIO;
index 485ea751c7f9b6ab18530e2d7ffcec3f328146c0..bb4c14f83c75acd0c070fca721e2516447fe633c 100644 (file)
@@ -45,8 +45,6 @@ struct mtdblk_dev {
        enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;
 };
 
-static DEFINE_MUTEX(mtdblks_lock);
-
 /*
  * Cache stuff...
  *
@@ -286,10 +284,8 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd)
 
        pr_debug("mtdblock_open\n");
 
-       mutex_lock(&mtdblks_lock);
        if (mtdblk->count) {
                mtdblk->count++;
-               mutex_unlock(&mtdblks_lock);
                return 0;
        }
 
@@ -302,8 +298,6 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd)
                mtdblk->cache_data = NULL;
        }
 
-       mutex_unlock(&mtdblks_lock);
-
        pr_debug("ok\n");
 
        return 0;
@@ -315,8 +309,6 @@ static void mtdblock_release(struct mtd_blktrans_dev *mbd)
 
        pr_debug("mtdblock_release\n");
 
-       mutex_lock(&mtdblks_lock);
-
        mutex_lock(&mtdblk->cache_mutex);
        write_cached_data(mtdblk);
        mutex_unlock(&mtdblk->cache_mutex);
@@ -331,8 +323,6 @@ static void mtdblock_release(struct mtd_blktrans_dev *mbd)
                vfree(mtdblk->cache_data);
        }
 
-       mutex_unlock(&mtdblks_lock);
-
        pr_debug("ok\n");
 }
 
index eacc3aac732789085b1bbfc86531c6fa5b0dbe17..239a8c806b6772df642bf6cb1385616147e975c8 100644 (file)
@@ -311,7 +311,8 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
                        devops.len = subdev->size - to;
 
                err = mtd_write_oob(subdev, to, &devops);
-               ops->retlen += devops.oobretlen;
+               ops->retlen += devops.retlen;
+               ops->oobretlen += devops.oobretlen;
                if (err)
                        return err;
 
index 0ec4d6ea1e4b8d7729a2299702391ab3f504e914..11883bd26d9d35e8234cc7dc74f51b7ed57d7566 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/backing-dev.h>
 #include <linux/gfp.h>
 #include <linux/slab.h>
+#include <linux/reboot.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
@@ -356,6 +357,17 @@ unsigned mtd_mmap_capabilities(struct mtd_info *mtd)
 EXPORT_SYMBOL_GPL(mtd_mmap_capabilities);
 #endif
 
+static int mtd_reboot_notifier(struct notifier_block *n, unsigned long state,
+                              void *cmd)
+{
+       struct mtd_info *mtd;
+
+       mtd = container_of(n, struct mtd_info, reboot_notifier);
+       mtd->_reboot(mtd);
+
+       return NOTIFY_DONE;
+}
+
 /**
  *     add_mtd_device - register an MTD device
  *     @mtd: pointer to new MTD device info structure
@@ -544,6 +556,19 @@ int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
                        err = -ENODEV;
        }
 
+       /*
+        * FIXME: some drivers unfortunately call this function more than once.
+        * So we have to check if we've already assigned the reboot notifier.
+        *
+        * Generally, we can make multiple calls work for most cases, but it
+        * does cause problems with parse_mtd_partitions() above (e.g.,
+        * cmdlineparts will register partitions more than once).
+        */
+       if (mtd->_reboot && !mtd->reboot_notifier.notifier_call) {
+               mtd->reboot_notifier.notifier_call = mtd_reboot_notifier;
+               register_reboot_notifier(&mtd->reboot_notifier);
+       }
+
        return err;
 }
 EXPORT_SYMBOL_GPL(mtd_device_parse_register);
@@ -558,6 +583,9 @@ int mtd_device_unregister(struct mtd_info *master)
 {
        int err;
 
+       if (master->_reboot)
+               unregister_reboot_notifier(&master->reboot_notifier);
+
        err = del_mtd_partitions(master);
        if (err)
                return err;
index 7d0150d2043201523bcd5882237fc28157dd13b9..5b76a173cd95d6d59c41c47d15c081ff39473c53 100644 (file)
@@ -421,7 +421,7 @@ config MTD_NAND_ORION
 
 config MTD_NAND_FSL_ELBC
        tristate "NAND support for Freescale eLBC controllers"
-       depends on PPC_OF
+       depends on PPC
        select FSL_LBC
        help
          Various Freescale chips, including the 8313, include a NAND Flash
@@ -524,4 +524,9 @@ config MTD_NAND_SUNXI
        help
          Enables support for NAND Flash chips on Allwinner SoCs.
 
+config MTD_NAND_HISI504
+       tristate "Support for NAND controller on Hisilicon SoC Hip04"
+       help
+         Enables support for NAND controller on Hisilicon SoC Hip04.
+
 endif # MTD_NAND
index bd38f21d2e28e8b622ba42a1f0c0c2c6562e4bfb..582bbd05aff7ab7df0a20c282bc25faa1532437a 100644 (file)
@@ -51,5 +51,6 @@ obj-$(CONFIG_MTD_NAND_GPMI_NAND)      += gpmi-nand/
 obj-$(CONFIG_MTD_NAND_XWAY)            += xway_nand.o
 obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)   += bcm47xxnflash/
 obj-$(CONFIG_MTD_NAND_SUNXI)           += sunxi_nand.o
+obj-$(CONFIG_MTD_NAND_HISI504)         += hisi504_nand.o
 
 nand-objs := nand_base.o nand_bbt.o nand_timings.o
index f1d555cfb332f17a91ef4c53eb3ca5c617477b70..842f8fe91b56cb2ef248c1c6e868cedaf2113766 100644 (file)
@@ -183,7 +183,7 @@ static int ams_delta_init(struct platform_device *pdev)
                return -ENXIO;
 
        /* Allocate memory for MTD device structure and private data */
-       ams_delta_mtd = kmalloc(sizeof(struct mtd_info) +
+       ams_delta_mtd = kzalloc(sizeof(struct mtd_info) +
                                sizeof(struct nand_chip), GFP_KERNEL);
        if (!ams_delta_mtd) {
                printk (KERN_WARNING "Unable to allocate E3 NAND MTD device structure.\n");
@@ -196,10 +196,6 @@ static int ams_delta_init(struct platform_device *pdev)
        /* Get pointer to private data */
        this = (struct nand_chip *) (&ams_delta_mtd[1]);
 
-       /* Initialize structures */
-       memset(ams_delta_mtd, 0, sizeof(struct mtd_info));
-       memset(this, 0, sizeof(struct nand_chip));
-
        /* Link the private data with the MTD structure */
        ams_delta_mtd->priv = this;
 
index a345e7b2463a3f80fb806fe12a19fcf2e72bf13e..d93c849b70b5cf299864a356e955a792e35bd802 100644 (file)
@@ -63,6 +63,10 @@ module_param(on_flash_bbt, int, 0);
 #include "atmel_nand_ecc.h"    /* Hardware ECC registers */
 #include "atmel_nand_nfc.h"    /* Nand Flash Controller definition */
 
+struct atmel_nand_caps {
+       bool pmecc_correct_erase_page;
+};
+
 /* oob layout for large page size
  * bad block info is on bytes 0 and 1
  * the bytes have to be consecutives to avoid
@@ -124,6 +128,7 @@ struct atmel_nand_host {
 
        struct atmel_nfc        *nfc;
 
+       struct atmel_nand_caps  *caps;
        bool                    has_pmecc;
        u8                      pmecc_corr_cap;
        u16                     pmecc_sector_size;
@@ -847,7 +852,11 @@ static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf,
        struct atmel_nand_host *host = nand_chip->priv;
        int i, err_nbr;
        uint8_t *buf_pos;
-       int total_err = 0;
+       int max_bitflips = 0;
+
+       /* If can correct bitfilps from erased page, do the normal check */
+       if (host->caps->pmecc_correct_erase_page)
+               goto normal_check;
 
        for (i = 0; i < nand_chip->ecc.total; i++)
                if (ecc[i] != 0xff)
@@ -874,13 +883,13 @@ normal_check:
                                pmecc_correct_data(mtd, buf_pos, ecc, i,
                                        nand_chip->ecc.bytes, err_nbr);
                                mtd->ecc_stats.corrected += err_nbr;
-                               total_err += err_nbr;
+                               max_bitflips = max_t(int, max_bitflips, err_nbr);
                        }
                }
                pmecc_stat >>= 1;
        }
 
-       return total_err;
+       return max_bitflips;
 }
 
 static void pmecc_enable(struct atmel_nand_host *host, int ecc_op)
@@ -1474,6 +1483,8 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
                ecc_writel(host->ecc, CR, ATMEL_ECC_RST);
 }
 
+static const struct of_device_id atmel_nand_dt_ids[];
+
 static int atmel_of_init_port(struct atmel_nand_host *host,
                              struct device_node *np)
 {
@@ -1483,6 +1494,9 @@ static int atmel_of_init_port(struct atmel_nand_host *host,
        struct atmel_nand_data *board = &host->board;
        enum of_gpio_flags flags = 0;
 
+       host->caps = (struct atmel_nand_caps *)
+               of_match_device(atmel_nand_dt_ids, host->dev)->data;
+
        if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) {
                if (val >= 32) {
                        dev_err(host->dev, "invalid addr-offset %u\n", val);
@@ -2288,8 +2302,17 @@ static int atmel_nand_remove(struct platform_device *pdev)
        return 0;
 }
 
+static struct atmel_nand_caps at91rm9200_caps = {
+       .pmecc_correct_erase_page = false,
+};
+
+static struct atmel_nand_caps sama5d4_caps = {
+       .pmecc_correct_erase_page = true,
+};
+
 static const struct of_device_id atmel_nand_dt_ids[] = {
-       { .compatible = "atmel,at91rm9200-nand" },
+       { .compatible = "atmel,at91rm9200-nand", .data = &at91rm9200_caps },
+       { .compatible = "atmel,sama5d4-nand", .data = &sama5d4_caps },
        { /* sentinel */ }
 };
 
index b3b7ca1bafb807f8828b3af853aa7886b87d7e90..f44c6061536a830e66fa7f50b139e6bd0fbf0050 100644 (file)
@@ -1041,7 +1041,7 @@ static void denali_setup_dma(struct denali_nand_info *denali, int op)
        index_addr(denali, mode | ((addr >> 16) << 8), 0x2200);
 
        /* 3. set memory low address bits 23:8 */
-       index_addr(denali, mode | ((addr & 0xff) << 8), 0x2300);
+       index_addr(denali, mode | ((addr & 0xffff) << 8), 0x2300);
 
        /* 4. interrupt when complete, burst len = 64 bytes */
        index_addr(denali, mode | 0x14000, 0x2400);
@@ -1328,35 +1328,6 @@ static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
                break;
        }
 }
-
-/* stubs for ECC functions not used by the NAND core */
-static int denali_ecc_calculate(struct mtd_info *mtd, const uint8_t *data,
-                               uint8_t *ecc_code)
-{
-       struct denali_nand_info *denali = mtd_to_denali(mtd);
-
-       dev_err(denali->dev, "denali_ecc_calculate called unexpectedly\n");
-       BUG();
-       return -EIO;
-}
-
-static int denali_ecc_correct(struct mtd_info *mtd, uint8_t *data,
-                               uint8_t *read_ecc, uint8_t *calc_ecc)
-{
-       struct denali_nand_info *denali = mtd_to_denali(mtd);
-
-       dev_err(denali->dev, "denali_ecc_correct called unexpectedly\n");
-       BUG();
-       return -EIO;
-}
-
-static void denali_ecc_hwctl(struct mtd_info *mtd, int mode)
-{
-       struct denali_nand_info *denali = mtd_to_denali(mtd);
-
-       dev_err(denali->dev, "denali_ecc_hwctl called unexpectedly\n");
-       BUG();
-}
 /* end NAND core entry points */
 
 /* Initialization code to bring the device up to a known good state */
@@ -1609,15 +1580,6 @@ int denali_init(struct denali_nand_info *denali)
        denali->totalblks = denali->mtd.size >> denali->nand.phys_erase_shift;
        denali->blksperchip = denali->totalblks / denali->nand.numchips;
 
-       /*
-        * These functions are required by the NAND core framework, otherwise,
-        * the NAND core will assert. However, we don't need them, so we'll stub
-        * them out.
-        */
-       denali->nand.ecc.calculate = denali_ecc_calculate;
-       denali->nand.ecc.correct = denali_ecc_correct;
-       denali->nand.ecc.hwctl = denali_ecc_hwctl;
-
        /* override the default read operations */
        denali->nand.ecc.size = ECC_SECTOR_SIZE * denali->devnum;
        denali->nand.ecc.read_page = denali_read_page;
index 4f3851a24bb2df62cc749da60510ecd8aca0bfc7..33f3c3c54dbc769f1259335722461d742556e3e9 100644 (file)
@@ -1294,14 +1294,6 @@ exit_auxiliary:
  * ecc.read_page or ecc.read_page_raw function. Thus, the fact that MTD wants an
  * ECC-based or raw view of the page is implicit in which function it calls
  * (there is a similar pair of ECC-based/raw functions for writing).
- *
- * FIXME: The following paragraph is incorrect, now that there exist
- * ecc.read_oob_raw and ecc.write_oob_raw functions.
- *
- * Since MTD assumes the OOB is not covered by ECC, there is no pair of
- * ECC-based/raw functions for reading or or writing the OOB. The fact that the
- * caller wants an ECC-based or raw view of the page is not propagated down to
- * this driver.
  */
 static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
                                int page)
@@ -2029,7 +2021,6 @@ static int gpmi_nand_probe(struct platform_device *pdev)
 exit_nfc_init:
        release_resources(this);
 exit_acquire_resources:
-       dev_err(this->dev, "driver registration failed: %d\n", ret);
 
        return ret;
 }
diff --git a/drivers/mtd/nand/hisi504_nand.c b/drivers/mtd/nand/hisi504_nand.c
new file mode 100644 (file)
index 0000000..289ad3a
--- /dev/null
@@ -0,0 +1,891 @@
+/*
+ * Hisilicon NAND Flash controller driver
+ *
+ * Copyright Â© 2012-2014 HiSilicon Technologies Co., Ltd.
+ *              http://www.hisilicon.com
+ *
+ * Author: Zhou Wang <wangzhou.bry@gmail.com>
+ * The initial developer of the original code is Zhiyong Cai
+ * <caizhiyong@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/of.h>
+#include <linux/of_mtd.h>
+#include <linux/mtd/mtd.h>
+#include <linux/sizes.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/mtd/nand.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/partitions.h>
+
+#define HINFC504_MAX_CHIP                               (4)
+#define HINFC504_W_LATCH                                (5)
+#define HINFC504_R_LATCH                                (7)
+#define HINFC504_RW_LATCH                               (3)
+
+#define HINFC504_NFC_TIMEOUT                           (2 * HZ)
+#define HINFC504_NFC_PM_TIMEOUT                                (1 * HZ)
+#define HINFC504_NFC_DMA_TIMEOUT                       (5 * HZ)
+#define HINFC504_CHIP_DELAY                            (25)
+
+#define HINFC504_REG_BASE_ADDRESS_LEN                  (0x100)
+#define HINFC504_BUFFER_BASE_ADDRESS_LEN               (2048 + 128)
+
+#define HINFC504_ADDR_CYCLE_MASK                       0x4
+
+#define HINFC504_CON                                   0x00
+#define HINFC504_CON_OP_MODE_NORMAL                    BIT(0)
+#define HINFC504_CON_PAGEISZE_SHIFT                    (1)
+#define HINFC504_CON_PAGESIZE_MASK                     (0x07)
+#define HINFC504_CON_BUS_WIDTH                         BIT(4)
+#define HINFC504_CON_READY_BUSY_SEL                    BIT(8)
+#define HINFC504_CON_ECCTYPE_SHIFT                     (9)
+#define HINFC504_CON_ECCTYPE_MASK                      (0x07)
+
+#define HINFC504_PWIDTH                                        0x04
+#define SET_HINFC504_PWIDTH(_w_lcnt, _r_lcnt, _rw_hcnt) \
+       ((_w_lcnt) | (((_r_lcnt) & 0x0F) << 4) | (((_rw_hcnt) & 0x0F) << 8))
+
+#define HINFC504_CMD                                   0x0C
+#define HINFC504_ADDRL                                 0x10
+#define HINFC504_ADDRH                                 0x14
+#define HINFC504_DATA_NUM                              0x18
+
+#define HINFC504_OP                                    0x1C
+#define HINFC504_OP_READ_DATA_EN                       BIT(1)
+#define HINFC504_OP_WAIT_READY_EN                      BIT(2)
+#define HINFC504_OP_CMD2_EN                            BIT(3)
+#define HINFC504_OP_WRITE_DATA_EN                      BIT(4)
+#define HINFC504_OP_ADDR_EN                            BIT(5)
+#define HINFC504_OP_CMD1_EN                            BIT(6)
+#define HINFC504_OP_NF_CS_SHIFT                         (7)
+#define HINFC504_OP_NF_CS_MASK                         (3)
+#define HINFC504_OP_ADDR_CYCLE_SHIFT                   (9)
+#define HINFC504_OP_ADDR_CYCLE_MASK                    (7)
+
+#define HINFC504_STATUS                                 0x20
+#define HINFC504_READY                                 BIT(0)
+
+#define HINFC504_INTEN                                 0x24
+#define HINFC504_INTEN_DMA                             BIT(9)
+#define HINFC504_INTEN_UE                              BIT(6)
+#define HINFC504_INTEN_CE                              BIT(5)
+
+#define HINFC504_INTS                                  0x28
+#define HINFC504_INTS_DMA                              BIT(9)
+#define HINFC504_INTS_UE                               BIT(6)
+#define HINFC504_INTS_CE                               BIT(5)
+
+#define HINFC504_INTCLR                                 0x2C
+#define HINFC504_INTCLR_DMA                            BIT(9)
+#define HINFC504_INTCLR_UE                             BIT(6)
+#define HINFC504_INTCLR_CE                             BIT(5)
+
+#define HINFC504_ECC_STATUS                             0x5C
+#define HINFC504_ECC_16_BIT_SHIFT                       12
+
+#define HINFC504_DMA_CTRL                              0x60
+#define HINFC504_DMA_CTRL_DMA_START                    BIT(0)
+#define HINFC504_DMA_CTRL_WE                           BIT(1)
+#define HINFC504_DMA_CTRL_DATA_AREA_EN                 BIT(2)
+#define HINFC504_DMA_CTRL_OOB_AREA_EN                  BIT(3)
+#define HINFC504_DMA_CTRL_BURST4_EN                    BIT(4)
+#define HINFC504_DMA_CTRL_BURST8_EN                    BIT(5)
+#define HINFC504_DMA_CTRL_BURST16_EN                   BIT(6)
+#define HINFC504_DMA_CTRL_ADDR_NUM_SHIFT               (7)
+#define HINFC504_DMA_CTRL_ADDR_NUM_MASK                 (1)
+#define HINFC504_DMA_CTRL_CS_SHIFT                     (8)
+#define HINFC504_DMA_CTRL_CS_MASK                      (0x03)
+
+#define HINFC504_DMA_ADDR_DATA                         0x64
+#define HINFC504_DMA_ADDR_OOB                          0x68
+
+#define HINFC504_DMA_LEN                               0x6C
+#define HINFC504_DMA_LEN_OOB_SHIFT                     (16)
+#define HINFC504_DMA_LEN_OOB_MASK                      (0xFFF)
+
+#define HINFC504_DMA_PARA                              0x70
+#define HINFC504_DMA_PARA_DATA_RW_EN                   BIT(0)
+#define HINFC504_DMA_PARA_OOB_RW_EN                    BIT(1)
+#define HINFC504_DMA_PARA_DATA_EDC_EN                  BIT(2)
+#define HINFC504_DMA_PARA_OOB_EDC_EN                   BIT(3)
+#define HINFC504_DMA_PARA_DATA_ECC_EN                  BIT(4)
+#define HINFC504_DMA_PARA_OOB_ECC_EN                   BIT(5)
+
+#define HINFC_VERSION                                   0x74
+#define HINFC504_LOG_READ_ADDR                         0x7C
+#define HINFC504_LOG_READ_LEN                          0x80
+
+#define HINFC504_NANDINFO_LEN                          0x10
+
+struct hinfc_host {
+       struct nand_chip        chip;
+       struct mtd_info         mtd;
+       struct device           *dev;
+       void __iomem            *iobase;
+       void __iomem            *mmio;
+       struct completion       cmd_complete;
+       unsigned int            offset;
+       unsigned int            command;
+       int                     chipselect;
+       unsigned int            addr_cycle;
+       u32                     addr_value[2];
+       u32                     cache_addr_value[2];
+       char                    *buffer;
+       dma_addr_t              dma_buffer;
+       dma_addr_t              dma_oob;
+       int                     version;
+       unsigned int            irq_status; /* interrupt status */
+};
+
+static inline unsigned int hinfc_read(struct hinfc_host *host, unsigned int reg)
+{
+       return readl(host->iobase + reg);
+}
+
+static inline void hinfc_write(struct hinfc_host *host, unsigned int value,
+                              unsigned int reg)
+{
+       writel(value, host->iobase + reg);
+}
+
+static void wait_controller_finished(struct hinfc_host *host)
+{
+       unsigned long timeout = jiffies + HINFC504_NFC_TIMEOUT;
+       int val;
+
+       while (time_before(jiffies, timeout)) {
+               val = hinfc_read(host, HINFC504_STATUS);
+               if (host->command == NAND_CMD_ERASE2) {
+                       /* nfc is ready */
+                       while (!(val & HINFC504_READY)) {
+                               usleep_range(500, 1000);
+                               val = hinfc_read(host, HINFC504_STATUS);
+                       }
+                       return;
+               }
+
+               if (val & HINFC504_READY)
+                       return;
+       }
+
+       /* wait cmd timeout */
+       dev_err(host->dev, "Wait NAND controller exec cmd timeout.\n");
+}
+
+static void hisi_nfc_dma_transfer(struct hinfc_host *host, int todev)
+{
+       struct mtd_info *mtd = &host->mtd;
+       struct nand_chip *chip = mtd->priv;
+       unsigned long val;
+       int ret;
+
+       hinfc_write(host, host->dma_buffer, HINFC504_DMA_ADDR_DATA);
+       hinfc_write(host, host->dma_oob, HINFC504_DMA_ADDR_OOB);
+
+       if (chip->ecc.mode == NAND_ECC_NONE) {
+               hinfc_write(host, ((mtd->oobsize & HINFC504_DMA_LEN_OOB_MASK)
+                       << HINFC504_DMA_LEN_OOB_SHIFT), HINFC504_DMA_LEN);
+
+               hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN
+                       | HINFC504_DMA_PARA_OOB_RW_EN, HINFC504_DMA_PARA);
+       } else {
+               if (host->command == NAND_CMD_READOOB)
+                       hinfc_write(host, HINFC504_DMA_PARA_OOB_RW_EN
+                       | HINFC504_DMA_PARA_OOB_EDC_EN
+                       | HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA);
+               else
+                       hinfc_write(host, HINFC504_DMA_PARA_DATA_RW_EN
+                       | HINFC504_DMA_PARA_OOB_RW_EN
+                       | HINFC504_DMA_PARA_DATA_EDC_EN
+                       | HINFC504_DMA_PARA_OOB_EDC_EN
+                       | HINFC504_DMA_PARA_DATA_ECC_EN
+                       | HINFC504_DMA_PARA_OOB_ECC_EN, HINFC504_DMA_PARA);
+
+       }
+
+       val = (HINFC504_DMA_CTRL_DMA_START | HINFC504_DMA_CTRL_BURST4_EN
+               | HINFC504_DMA_CTRL_BURST8_EN | HINFC504_DMA_CTRL_BURST16_EN
+               | HINFC504_DMA_CTRL_DATA_AREA_EN | HINFC504_DMA_CTRL_OOB_AREA_EN
+               | ((host->addr_cycle == 4 ? 1 : 0)
+                       << HINFC504_DMA_CTRL_ADDR_NUM_SHIFT)
+               | ((host->chipselect & HINFC504_DMA_CTRL_CS_MASK)
+                       << HINFC504_DMA_CTRL_CS_SHIFT));
+
+       if (todev)
+               val |= HINFC504_DMA_CTRL_WE;
+
+       init_completion(&host->cmd_complete);
+
+       hinfc_write(host, val, HINFC504_DMA_CTRL);
+       ret = wait_for_completion_timeout(&host->cmd_complete,
+                       HINFC504_NFC_DMA_TIMEOUT);
+
+       if (!ret) {
+               dev_err(host->dev, "DMA operation(irq) timeout!\n");
+               /* sanity check */
+               val = hinfc_read(host, HINFC504_DMA_CTRL);
+               if (!(val & HINFC504_DMA_CTRL_DMA_START))
+                       dev_err(host->dev, "DMA is already done but without irq ACK!\n");
+               else
+                       dev_err(host->dev, "DMA is really timeout!\n");
+       }
+}
+
+static int hisi_nfc_send_cmd_pageprog(struct hinfc_host *host)
+{
+       host->addr_value[0] &= 0xffff0000;
+
+       hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
+       hinfc_write(host, host->addr_value[1], HINFC504_ADDRH);
+       hinfc_write(host, NAND_CMD_PAGEPROG << 8 | NAND_CMD_SEQIN,
+                   HINFC504_CMD);
+
+       hisi_nfc_dma_transfer(host, 1);
+
+       return 0;
+}
+
+static int hisi_nfc_send_cmd_readstart(struct hinfc_host *host)
+{
+       struct mtd_info *mtd = &host->mtd;
+
+       if ((host->addr_value[0] == host->cache_addr_value[0]) &&
+           (host->addr_value[1] == host->cache_addr_value[1]))
+               return 0;
+
+       host->addr_value[0] &= 0xffff0000;
+
+       hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
+       hinfc_write(host, host->addr_value[1], HINFC504_ADDRH);
+       hinfc_write(host, NAND_CMD_READSTART << 8 | NAND_CMD_READ0,
+                   HINFC504_CMD);
+
+       hinfc_write(host, 0, HINFC504_LOG_READ_ADDR);
+       hinfc_write(host, mtd->writesize + mtd->oobsize,
+                   HINFC504_LOG_READ_LEN);
+
+       hisi_nfc_dma_transfer(host, 0);
+
+       host->cache_addr_value[0] = host->addr_value[0];
+       host->cache_addr_value[1] = host->addr_value[1];
+
+       return 0;
+}
+
+static int hisi_nfc_send_cmd_erase(struct hinfc_host *host)
+{
+       hinfc_write(host, host->addr_value[0], HINFC504_ADDRL);
+       hinfc_write(host, (NAND_CMD_ERASE2 << 8) | NAND_CMD_ERASE1,
+                   HINFC504_CMD);
+
+       hinfc_write(host, HINFC504_OP_WAIT_READY_EN
+               | HINFC504_OP_CMD2_EN
+               | HINFC504_OP_CMD1_EN
+               | HINFC504_OP_ADDR_EN
+               | ((host->chipselect & HINFC504_OP_NF_CS_MASK)
+                       << HINFC504_OP_NF_CS_SHIFT)
+               | ((host->addr_cycle & HINFC504_OP_ADDR_CYCLE_MASK)
+                       << HINFC504_OP_ADDR_CYCLE_SHIFT),
+               HINFC504_OP);
+
+       wait_controller_finished(host);
+
+       return 0;
+}
+
+static int hisi_nfc_send_cmd_readid(struct hinfc_host *host)
+{
+       hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM);
+       hinfc_write(host, NAND_CMD_READID, HINFC504_CMD);
+       hinfc_write(host, 0, HINFC504_ADDRL);
+
+       hinfc_write(host, HINFC504_OP_CMD1_EN | HINFC504_OP_ADDR_EN
+               | HINFC504_OP_READ_DATA_EN
+               | ((host->chipselect & HINFC504_OP_NF_CS_MASK)
+                       << HINFC504_OP_NF_CS_SHIFT)
+               | 1 << HINFC504_OP_ADDR_CYCLE_SHIFT, HINFC504_OP);
+
+       wait_controller_finished(host);
+
+       return 0;
+}
+
+static int hisi_nfc_send_cmd_status(struct hinfc_host *host)
+{
+       hinfc_write(host, HINFC504_NANDINFO_LEN, HINFC504_DATA_NUM);
+       hinfc_write(host, NAND_CMD_STATUS, HINFC504_CMD);
+       hinfc_write(host, HINFC504_OP_CMD1_EN
+               | HINFC504_OP_READ_DATA_EN
+               | ((host->chipselect & HINFC504_OP_NF_CS_MASK)
+                       << HINFC504_OP_NF_CS_SHIFT),
+               HINFC504_OP);
+
+       wait_controller_finished(host);
+
+       return 0;
+}
+
+static int hisi_nfc_send_cmd_reset(struct hinfc_host *host, int chipselect)
+{
+       hinfc_write(host, NAND_CMD_RESET, HINFC504_CMD);
+
+       hinfc_write(host, HINFC504_OP_CMD1_EN
+               | ((chipselect & HINFC504_OP_NF_CS_MASK)
+                       << HINFC504_OP_NF_CS_SHIFT)
+               | HINFC504_OP_WAIT_READY_EN,
+               HINFC504_OP);
+
+       wait_controller_finished(host);
+
+       return 0;
+}
+
+static void hisi_nfc_select_chip(struct mtd_info *mtd, int chipselect)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct hinfc_host *host = chip->priv;
+
+       if (chipselect < 0)
+               return;
+
+       host->chipselect = chipselect;
+}
+
+static uint8_t hisi_nfc_read_byte(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct hinfc_host *host = chip->priv;
+
+       if (host->command == NAND_CMD_STATUS)
+               return *(uint8_t *)(host->mmio);
+
+       host->offset++;
+
+       if (host->command == NAND_CMD_READID)
+               return *(uint8_t *)(host->mmio + host->offset - 1);
+
+       return *(uint8_t *)(host->buffer + host->offset - 1);
+}
+
+static u16 hisi_nfc_read_word(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct hinfc_host *host = chip->priv;
+
+       host->offset += 2;
+       return *(u16 *)(host->buffer + host->offset - 2);
+}
+
+static void
+hisi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct hinfc_host *host = chip->priv;
+
+       memcpy(host->buffer + host->offset, buf, len);
+       host->offset += len;
+}
+
+static void hisi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct hinfc_host *host = chip->priv;
+
+       memcpy(buf, host->buffer + host->offset, len);
+       host->offset += len;
+}
+
+static void set_addr(struct mtd_info *mtd, int column, int page_addr)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct hinfc_host *host = chip->priv;
+       unsigned int command = host->command;
+
+       host->addr_cycle    = 0;
+       host->addr_value[0] = 0;
+       host->addr_value[1] = 0;
+
+       /* Serially input address */
+       if (column != -1) {
+               /* Adjust columns for 16 bit buswidth */
+               if (chip->options & NAND_BUSWIDTH_16 &&
+                               !nand_opcode_8bits(command))
+                       column >>= 1;
+
+               host->addr_value[0] = column & 0xffff;
+               host->addr_cycle    = 2;
+       }
+       if (page_addr != -1) {
+               host->addr_value[0] |= (page_addr & 0xffff)
+                       << (host->addr_cycle * 8);
+               host->addr_cycle    += 2;
+               /* One more address cycle for devices > 128MiB */
+               if (chip->chipsize > (128 << 20)) {
+                       host->addr_cycle += 1;
+                       if (host->command == NAND_CMD_ERASE1)
+                               host->addr_value[0] |= ((page_addr >> 16) & 0xff) << 16;
+                       else
+                               host->addr_value[1] |= ((page_addr >> 16) & 0xff);
+               }
+       }
+}
+
+static void hisi_nfc_cmdfunc(struct mtd_info *mtd, unsigned command, int column,
+               int page_addr)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct hinfc_host *host = chip->priv;
+       int is_cache_invalid = 1;
+       unsigned int flag = 0;
+
+       host->command =  command;
+
+       switch (command) {
+       case NAND_CMD_READ0:
+       case NAND_CMD_READOOB:
+               if (command == NAND_CMD_READ0)
+                       host->offset = column;
+               else
+                       host->offset = column + mtd->writesize;
+
+               is_cache_invalid = 0;
+               set_addr(mtd, column, page_addr);
+               hisi_nfc_send_cmd_readstart(host);
+               break;
+
+       case NAND_CMD_SEQIN:
+               host->offset = column;
+               set_addr(mtd, column, page_addr);
+               break;
+
+       case NAND_CMD_ERASE1:
+               set_addr(mtd, column, page_addr);
+               break;
+
+       case NAND_CMD_PAGEPROG:
+               hisi_nfc_send_cmd_pageprog(host);
+               break;
+
+       case NAND_CMD_ERASE2:
+               hisi_nfc_send_cmd_erase(host);
+               break;
+
+       case NAND_CMD_READID:
+               host->offset = column;
+               memset(host->mmio, 0, 0x10);
+               hisi_nfc_send_cmd_readid(host);
+               break;
+
+       case NAND_CMD_STATUS:
+               flag = hinfc_read(host, HINFC504_CON);
+               if (chip->ecc.mode == NAND_ECC_HW)
+                       hinfc_write(host,
+                                   flag & ~(HINFC504_CON_ECCTYPE_MASK <<
+                                   HINFC504_CON_ECCTYPE_SHIFT), HINFC504_CON);
+
+               host->offset = 0;
+               memset(host->mmio, 0, 0x10);
+               hisi_nfc_send_cmd_status(host);
+               hinfc_write(host, flag, HINFC504_CON);
+               break;
+
+       case NAND_CMD_RESET:
+               hisi_nfc_send_cmd_reset(host, host->chipselect);
+               break;
+
+       default:
+               dev_err(host->dev, "Error: unsupported cmd(cmd=%x, col=%x, page=%x)\n",
+                       command, column, page_addr);
+       }
+
+       if (is_cache_invalid) {
+               host->cache_addr_value[0] = ~0;
+               host->cache_addr_value[1] = ~0;
+       }
+}
+
+static irqreturn_t hinfc_irq_handle(int irq, void *devid)
+{
+       struct hinfc_host *host = devid;
+       unsigned int flag;
+
+       flag = hinfc_read(host, HINFC504_INTS);
+       /* store interrupts state */
+       host->irq_status |= flag;
+
+       if (flag & HINFC504_INTS_DMA) {
+               hinfc_write(host, HINFC504_INTCLR_DMA, HINFC504_INTCLR);
+               complete(&host->cmd_complete);
+       } else if (flag & HINFC504_INTS_CE) {
+               hinfc_write(host, HINFC504_INTCLR_CE, HINFC504_INTCLR);
+       } else if (flag & HINFC504_INTS_UE) {
+               hinfc_write(host, HINFC504_INTCLR_UE, HINFC504_INTCLR);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int hisi_nand_read_page_hwecc(struct mtd_info *mtd,
+       struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+{
+       struct hinfc_host *host = chip->priv;
+       int max_bitflips = 0, stat = 0, stat_max = 0, status_ecc;
+       int stat_1, stat_2;
+
+       chip->read_buf(mtd, buf, mtd->writesize);
+       chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+       /* errors which can not be corrected by ECC */
+       if (host->irq_status & HINFC504_INTS_UE) {
+               mtd->ecc_stats.failed++;
+       } else if (host->irq_status & HINFC504_INTS_CE) {
+               /* TODO: need add other ECC modes! */
+               switch (chip->ecc.strength) {
+               case 16:
+                       status_ecc = hinfc_read(host, HINFC504_ECC_STATUS) >>
+                                       HINFC504_ECC_16_BIT_SHIFT & 0x0fff;
+                       stat_2 = status_ecc & 0x3f;
+                       stat_1 = status_ecc >> 6 & 0x3f;
+                       stat = stat_1 + stat_2;
+                       stat_max = max_t(int, stat_1, stat_2);
+               }
+               mtd->ecc_stats.corrected += stat;
+               max_bitflips = max_t(int, max_bitflips, stat_max);
+       }
+       host->irq_status = 0;
+
+       return max_bitflips;
+}
+
+static int hisi_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+                               int page)
+{
+       struct hinfc_host *host = chip->priv;
+
+       chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+       chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+       if (host->irq_status & HINFC504_INTS_UE) {
+               host->irq_status = 0;
+               return -EBADMSG;
+       }
+
+       host->irq_status = 0;
+       return 0;
+}
+
+static int hisi_nand_write_page_hwecc(struct mtd_info *mtd,
+               struct nand_chip *chip, const uint8_t *buf, int oob_required)
+{
+       chip->write_buf(mtd, buf, mtd->writesize);
+       if (oob_required)
+               chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+       return 0;
+}
+
+static void hisi_nfc_host_init(struct hinfc_host *host)
+{
+       struct nand_chip *chip = &host->chip;
+       unsigned int flag = 0;
+
+       host->version = hinfc_read(host, HINFC_VERSION);
+       host->addr_cycle                = 0;
+       host->addr_value[0]             = 0;
+       host->addr_value[1]             = 0;
+       host->cache_addr_value[0]       = ~0;
+       host->cache_addr_value[1]       = ~0;
+       host->chipselect                = 0;
+
+       /* default page size: 2K, ecc_none. need modify */
+       flag = HINFC504_CON_OP_MODE_NORMAL | HINFC504_CON_READY_BUSY_SEL
+               | ((0x001 & HINFC504_CON_PAGESIZE_MASK)
+                       << HINFC504_CON_PAGEISZE_SHIFT)
+               | ((0x0 & HINFC504_CON_ECCTYPE_MASK)
+                       << HINFC504_CON_ECCTYPE_SHIFT)
+               | ((chip->options & NAND_BUSWIDTH_16) ?
+                       HINFC504_CON_BUS_WIDTH : 0);
+       hinfc_write(host, flag, HINFC504_CON);
+
+       memset(host->mmio, 0xff, HINFC504_BUFFER_BASE_ADDRESS_LEN);
+
+       hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH,
+                   HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH);
+
+       /* enable DMA irq */
+       hinfc_write(host, HINFC504_INTEN_DMA, HINFC504_INTEN);
+}
+
+static struct nand_ecclayout nand_ecc_2K_16bits = {
+       .oobavail = 6,
+       .oobfree = { {2, 6} },
+};
+
+static int hisi_nfc_ecc_probe(struct hinfc_host *host)
+{
+       unsigned int flag;
+       int size, strength, ecc_bits;
+       struct device *dev = host->dev;
+       struct nand_chip *chip = &host->chip;
+       struct mtd_info *mtd = &host->mtd;
+       struct device_node *np = host->dev->of_node;
+
+       size = of_get_nand_ecc_step_size(np);
+       strength = of_get_nand_ecc_strength(np);
+       if (size != 1024) {
+               dev_err(dev, "error ecc size: %d\n", size);
+               return -EINVAL;
+       }
+
+       if ((size == 1024) && ((strength != 8) && (strength != 16) &&
+                               (strength != 24) && (strength != 40))) {
+               dev_err(dev, "ecc size and strength do not match\n");
+               return -EINVAL;
+       }
+
+       chip->ecc.size = size;
+       chip->ecc.strength = strength;
+
+       chip->ecc.read_page = hisi_nand_read_page_hwecc;
+       chip->ecc.read_oob = hisi_nand_read_oob;
+       chip->ecc.write_page = hisi_nand_write_page_hwecc;
+
+       switch (chip->ecc.strength) {
+       case 16:
+               ecc_bits = 6;
+               if (mtd->writesize == 2048)
+                       chip->ecc.layout = &nand_ecc_2K_16bits;
+
+               /* TODO: add more page size support */
+               break;
+
+       /* TODO: add more ecc strength support */
+       default:
+               dev_err(dev, "not support strength: %d\n", chip->ecc.strength);
+               return -EINVAL;
+       }
+
+       flag = hinfc_read(host, HINFC504_CON);
+       /* add ecc type configure */
+       flag |= ((ecc_bits & HINFC504_CON_ECCTYPE_MASK)
+                                               << HINFC504_CON_ECCTYPE_SHIFT);
+       hinfc_write(host, flag, HINFC504_CON);
+
+       /* enable ecc irq */
+       flag = hinfc_read(host, HINFC504_INTEN) & 0xfff;
+       hinfc_write(host, flag | HINFC504_INTEN_UE | HINFC504_INTEN_CE,
+                   HINFC504_INTEN);
+
+       return 0;
+}
+
+static int hisi_nfc_probe(struct platform_device *pdev)
+{
+       int ret = 0, irq, buswidth, flag, max_chips = HINFC504_MAX_CHIP;
+       struct device *dev = &pdev->dev;
+       struct hinfc_host *host;
+       struct nand_chip  *chip;
+       struct mtd_info   *mtd;
+       struct resource   *res;
+       struct device_node *np = dev->of_node;
+       struct mtd_part_parser_data ppdata;
+
+       host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
+       if (!host)
+               return -ENOMEM;
+       host->dev = dev;
+
+       platform_set_drvdata(pdev, host);
+       chip = &host->chip;
+       mtd  = &host->mtd;
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(dev, "no IRQ resource defined\n");
+               ret = -ENXIO;
+               goto err_res;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       host->iobase = devm_ioremap_resource(dev, res);
+       if (IS_ERR(host->iobase)) {
+               ret = PTR_ERR(host->iobase);
+               goto err_res;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       host->mmio = devm_ioremap_resource(dev, res);
+       if (IS_ERR(host->mmio)) {
+               ret = PTR_ERR(host->mmio);
+               dev_err(dev, "devm_ioremap_resource[1] fail\n");
+               goto err_res;
+       }
+
+       mtd->priv               = chip;
+       mtd->owner              = THIS_MODULE;
+       mtd->name               = "hisi_nand";
+       mtd->dev.parent         = &pdev->dev;
+
+       chip->priv              = host;
+       chip->cmdfunc           = hisi_nfc_cmdfunc;
+       chip->select_chip       = hisi_nfc_select_chip;
+       chip->read_byte         = hisi_nfc_read_byte;
+       chip->read_word         = hisi_nfc_read_word;
+       chip->write_buf         = hisi_nfc_write_buf;
+       chip->read_buf          = hisi_nfc_read_buf;
+       chip->chip_delay        = HINFC504_CHIP_DELAY;
+
+       chip->ecc.mode = of_get_nand_ecc_mode(np);
+
+       buswidth = of_get_nand_bus_width(np);
+       if (buswidth == 16)
+               chip->options |= NAND_BUSWIDTH_16;
+
+       hisi_nfc_host_init(host);
+
+       ret = devm_request_irq(dev, irq, hinfc_irq_handle, IRQF_DISABLED,
+                               "nandc", host);
+       if (ret) {
+               dev_err(dev, "failed to request IRQ\n");
+               goto err_res;
+       }
+
+       ret = nand_scan_ident(mtd, max_chips, NULL);
+       if (ret) {
+               ret = -ENODEV;
+               goto err_res;
+       }
+
+       host->buffer = dmam_alloc_coherent(dev, mtd->writesize + mtd->oobsize,
+               &host->dma_buffer, GFP_KERNEL);
+       if (!host->buffer) {
+               ret = -ENOMEM;
+               goto err_res;
+       }
+
+       host->dma_oob = host->dma_buffer + mtd->writesize;
+       memset(host->buffer, 0xff, mtd->writesize + mtd->oobsize);
+
+       flag = hinfc_read(host, HINFC504_CON);
+       flag &= ~(HINFC504_CON_PAGESIZE_MASK << HINFC504_CON_PAGEISZE_SHIFT);
+       switch (mtd->writesize) {
+       case 2048:
+               flag |= (0x001 << HINFC504_CON_PAGEISZE_SHIFT); break;
+       /*
+        * TODO: add more pagesize support,
+        * default pagesize has been set in hisi_nfc_host_init
+        */
+       default:
+               dev_err(dev, "NON-2KB page size nand flash\n");
+               ret = -EINVAL;
+               goto err_res;
+       }
+       hinfc_write(host, flag, HINFC504_CON);
+
+       if (chip->ecc.mode == NAND_ECC_HW)
+               hisi_nfc_ecc_probe(host);
+
+       ret = nand_scan_tail(mtd);
+       if (ret) {
+               dev_err(dev, "nand_scan_tail failed: %d\n", ret);
+               goto err_res;
+       }
+
+       ppdata.of_node = np;
+       ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+       if (ret) {
+               dev_err(dev, "Err MTD partition=%d\n", ret);
+               goto err_mtd;
+       }
+
+       return 0;
+
+err_mtd:
+       nand_release(mtd);
+err_res:
+       return ret;
+}
+
+static int hisi_nfc_remove(struct platform_device *pdev)
+{
+       struct hinfc_host *host = platform_get_drvdata(pdev);
+       struct mtd_info *mtd = &host->mtd;
+
+       nand_release(mtd);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int hisi_nfc_suspend(struct device *dev)
+{
+       struct hinfc_host *host = dev_get_drvdata(dev);
+       unsigned long timeout = jiffies + HINFC504_NFC_PM_TIMEOUT;
+
+       while (time_before(jiffies, timeout)) {
+               if (((hinfc_read(host, HINFC504_STATUS) & 0x1) == 0x0) &&
+                   (hinfc_read(host, HINFC504_DMA_CTRL) &
+                    HINFC504_DMA_CTRL_DMA_START)) {
+                       cond_resched();
+                       return 0;
+               }
+       }
+
+       dev_err(host->dev, "nand controller suspend timeout.\n");
+
+       return -EAGAIN;
+}
+
+static int hisi_nfc_resume(struct device *dev)
+{
+       int cs;
+       struct hinfc_host *host = dev_get_drvdata(dev);
+       struct nand_chip *chip = &host->chip;
+
+       for (cs = 0; cs < chip->numchips; cs++)
+               hisi_nfc_send_cmd_reset(host, cs);
+       hinfc_write(host, SET_HINFC504_PWIDTH(HINFC504_W_LATCH,
+                   HINFC504_R_LATCH, HINFC504_RW_LATCH), HINFC504_PWIDTH);
+
+       return 0;
+}
+#endif
+static SIMPLE_DEV_PM_OPS(hisi_nfc_pm_ops, hisi_nfc_suspend, hisi_nfc_resume);
+
+static const struct of_device_id nfc_id_table[] = {
+       { .compatible = "hisilicon,504-nfc" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, nfc_id_table);
+
+static struct platform_driver hisi_nfc_driver = {
+       .driver = {
+               .name  = "hisi_nand",
+               .of_match_table = nfc_id_table,
+               .pm = &hisi_nfc_pm_ops,
+       },
+       .probe          = hisi_nfc_probe,
+       .remove         = hisi_nfc_remove,
+};
+
+module_platform_driver(hisi_nfc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Zhou Wang");
+MODULE_AUTHOR("Zhiyong Cai");
+MODULE_DESCRIPTION("Hisilicon Nand Flash Controller Driver");
index 1633ec9c51082fb6ad33b90c053ee41886bec332..ebf2cce04cba14a3caa3baf9d503aa17a730e969 100644 (file)
@@ -69,7 +69,7 @@ struct jz_nand {
 
        int selected_bank;
 
-       struct jz_nand_platform_data *pdata;
+       struct gpio_desc *busy_gpio;
        bool is_reading;
 };
 
@@ -131,7 +131,7 @@ static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
 static int jz_nand_dev_ready(struct mtd_info *mtd)
 {
        struct jz_nand *nand = mtd_to_jz_nand(mtd);
-       return gpio_get_value_cansleep(nand->pdata->busy_gpio);
+       return gpiod_get_value_cansleep(nand->busy_gpio);
 }
 
 static void jz_nand_hwctl(struct mtd_info *mtd, int mode)
@@ -423,14 +423,12 @@ static int jz_nand_probe(struct platform_device *pdev)
        if (ret)
                goto err_free;
 
-       if (pdata && gpio_is_valid(pdata->busy_gpio)) {
-               ret = gpio_request(pdata->busy_gpio, "NAND busy pin");
-               if (ret) {
-                       dev_err(&pdev->dev,
-                               "Failed to request busy gpio %d: %d\n",
-                               pdata->busy_gpio, ret);
-                       goto err_iounmap_mmio;
-               }
+       nand->busy_gpio = devm_gpiod_get_optional(&pdev->dev, "busy", GPIOD_IN);
+       if (IS_ERR(nand->busy_gpio)) {
+               ret = PTR_ERR(nand->busy_gpio);
+               dev_err(&pdev->dev, "Failed to request busy gpio %d\n",
+                   ret);
+               goto err_iounmap_mmio;
        }
 
        mtd             = &nand->mtd;
@@ -454,10 +452,9 @@ static int jz_nand_probe(struct platform_device *pdev)
        chip->cmd_ctrl = jz_nand_cmd_ctrl;
        chip->select_chip = jz_nand_select_chip;
 
-       if (pdata && gpio_is_valid(pdata->busy_gpio))
+       if (nand->busy_gpio)
                chip->dev_ready = jz_nand_dev_ready;
 
-       nand->pdata = pdata;
        platform_set_drvdata(pdev, nand);
 
        /* We are going to autodetect NAND chips in the banks specified in the
@@ -496,7 +493,7 @@ static int jz_nand_probe(struct platform_device *pdev)
        }
        if (chipnr == 0) {
                dev_err(&pdev->dev, "No NAND chips found\n");
-               goto err_gpio_busy;
+               goto err_iounmap_mmio;
        }
 
        if (pdata && pdata->ident_callback) {
@@ -533,9 +530,6 @@ err_unclaim_banks:
                                         nand->bank_base[bank - 1]);
        }
        writel(0, nand->base + JZ_REG_NAND_CTRL);
-err_gpio_busy:
-       if (pdata && gpio_is_valid(pdata->busy_gpio))
-               gpio_free(pdata->busy_gpio);
 err_iounmap_mmio:
        jz_nand_iounmap_resource(nand->mem, nand->base);
 err_free:
@@ -546,7 +540,6 @@ err_free:
 static int jz_nand_remove(struct platform_device *pdev)
 {
        struct jz_nand *nand = platform_get_drvdata(pdev);
-       struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev);
        size_t i;
 
        nand_release(&nand->mtd);
@@ -562,8 +555,6 @@ static int jz_nand_remove(struct platform_device *pdev)
                        gpio_free(JZ_GPIO_MEM_CS0 + bank - 1);
                }
        }
-       if (pdata && gpio_is_valid(pdata->busy_gpio))
-               gpio_free(pdata->busy_gpio);
 
        jz_nand_iounmap_resource(nand->mem, nand->base);
 
index 41585dfb206f05caef275c95799ccf53508abb94..df7eb4ff07d156ec213b3656f9840f6e99a35710 100644 (file)
@@ -156,7 +156,6 @@ static uint8_t nand_read_byte(struct mtd_info *mtd)
 }
 
 /**
- * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip
  * nand_read_byte16 - [DEFAULT] read one byte endianness aware from the chip
  * @mtd: MTD device structure
  *
@@ -1751,11 +1750,10 @@ static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
 static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
                                  int page)
 {
-       uint8_t *buf = chip->oob_poi;
        int length = mtd->oobsize;
        int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
        int eccsize = chip->ecc.size;
-       uint8_t *bufpoi = buf;
+       uint8_t *bufpoi = chip->oob_poi;
        int i, toread, sndrnd = 0, pos;
 
        chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page);
@@ -2944,6 +2942,16 @@ static void nand_resume(struct mtd_info *mtd)
                        __func__);
 }
 
+/**
+ * nand_shutdown - [MTD Interface] Finish the current NAND operation and
+ *                 prevent further operations
+ * @mtd: MTD device structure
+ */
+static void nand_shutdown(struct mtd_info *mtd)
+{
+       nand_get_device(mtd, FL_SHUTDOWN);
+}
+
 /* Set default functions */
 static void nand_set_defaults(struct nand_chip *chip, int busw)
 {
@@ -4028,22 +4036,24 @@ int nand_scan_tail(struct mtd_info *mtd)
                ecc->read_oob = nand_read_oob_std;
                ecc->write_oob = nand_write_oob_std;
                /*
-                * Board driver should supply ecc.size and ecc.bytes values to
-                * select how many bits are correctable; see nand_bch_init()
-                * for details. Otherwise, default to 4 bits for large page
-                * devices.
+                * Board driver should supply ecc.size and ecc.strength values
+                * to select how many bits are correctable. Otherwise, default
+                * to 4 bits for large page devices.
                 */
                if (!ecc->size && (mtd->oobsize >= 64)) {
                        ecc->size = 512;
-                       ecc->bytes = DIV_ROUND_UP(13 * ecc->strength, 8);
+                       ecc->strength = 4;
                }
+
+               /* See nand_bch_init() for details. */
+               ecc->bytes = DIV_ROUND_UP(
+                               ecc->strength * fls(8 * ecc->size), 8);
                ecc->priv = nand_bch_init(mtd, ecc->size, ecc->bytes,
                                               &ecc->layout);
                if (!ecc->priv) {
                        pr_warn("BCH ECC initialization failed!\n");
                        BUG();
                }
-               ecc->strength = ecc->bytes * 8 / fls(8 * ecc->size);
                break;
 
        case NAND_ECC_NONE:
@@ -4146,6 +4156,7 @@ int nand_scan_tail(struct mtd_info *mtd)
        mtd->_unlock = NULL;
        mtd->_suspend = nand_suspend;
        mtd->_resume = nand_resume;
+       mtd->_reboot = nand_shutdown;
        mtd->_block_isreserved = nand_block_isreserved;
        mtd->_block_isbad = nand_block_isbad;
        mtd->_block_markbad = nand_block_markbad;
@@ -4161,7 +4172,7 @@ int nand_scan_tail(struct mtd_info *mtd)
         * properly set.
         */
        if (!mtd->bitflip_threshold)
-               mtd->bitflip_threshold = mtd->ecc_strength;
+               mtd->bitflip_threshold = DIV_ROUND_UP(mtd->ecc_strength * 3, 4);
 
        /* Check, if we should skip the bad block table scan */
        if (chip->options & NAND_SKIP_BBTSCAN)
index ab5bbf5674394438555dcb43df7ea2a0ccfb1cec..f2324271b94e9d19cc39aafae95cc0ccf934fc42 100644 (file)
@@ -245,7 +245,6 @@ MODULE_PARM_DESC(bch,                "Enable BCH ecc and set how many bits should "
 #define STATE_DATAOUT          0x00001000 /* waiting for page data output */
 #define STATE_DATAOUT_ID       0x00002000 /* waiting for ID bytes output */
 #define STATE_DATAOUT_STATUS   0x00003000 /* waiting for status output */
-#define STATE_DATAOUT_STATUS_M 0x00004000 /* waiting for multi-plane status output */
 #define STATE_DATAOUT_MASK     0x00007000 /* data output states mask */
 
 /* Previous operation is done, ready to accept new requests */
@@ -269,7 +268,6 @@ MODULE_PARM_DESC(bch,                "Enable BCH ecc and set how many bits should "
 #define OPT_ANY          0xFFFFFFFF /* any chip supports this operation */
 #define OPT_PAGE512      0x00000002 /* 512-byte  page chips */
 #define OPT_PAGE2048     0x00000008 /* 2048-byte page chips */
-#define OPT_SMARTMEDIA   0x00000010 /* SmartMedia technology chips */
 #define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */
 #define OPT_PAGE4096     0x00000080 /* 4096-byte page chips */
 #define OPT_LARGEPAGE    (OPT_PAGE2048 | OPT_PAGE4096) /* 2048 & 4096-byte page chips */
@@ -1096,8 +1094,6 @@ static char *get_state_name(uint32_t state)
                        return "STATE_DATAOUT_ID";
                case STATE_DATAOUT_STATUS:
                        return "STATE_DATAOUT_STATUS";
-               case STATE_DATAOUT_STATUS_M:
-                       return "STATE_DATAOUT_STATUS_M";
                case STATE_READY:
                        return "STATE_READY";
                case STATE_UNKNOWN:
@@ -1865,7 +1861,6 @@ static void switch_state(struct nandsim *ns)
                                break;
 
                        case STATE_DATAOUT_STATUS:
-                       case STATE_DATAOUT_STATUS_M:
                                ns->regs.count = ns->regs.num = 0;
                                break;
 
@@ -2005,7 +2000,6 @@ static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
                }
 
                if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS
-                       || NS_STATE(ns->state) == STATE_DATAOUT_STATUS_M
                        || NS_STATE(ns->state) == STATE_DATAOUT) {
                        int row = ns->regs.row;
 
@@ -2343,6 +2337,7 @@ static int __init ns_init_module(void)
                }
                chip->ecc.mode = NAND_ECC_SOFT_BCH;
                chip->ecc.size = 512;
+               chip->ecc.strength = bch;
                chip->ecc.bytes = eccbytes;
                NS_INFO("using %u-bit/%u bytes BCH ECC\n", bch, chip->ecc.size);
        }
index 63f858e6bf3987a03c7564c3fc3db0da0c3f106f..60fa89939c24861e487354d033fb6c2b8d95eeaf 100644 (file)
@@ -1048,10 +1048,9 @@ static int omap_dev_ready(struct mtd_info *mtd)
  * @mtd: MTD device structure
  * @mode: Read/Write mode
  *
- * When using BCH, sector size is hardcoded to 512 bytes.
- * Using wrapping mode 6 both for reading and writing if ELM module not uses
- * for error correction.
- * On writing,
+ * When using BCH with SW correction (i.e. no ELM), sector size is set
+ * to 512 bytes and we use BCH_WRAPMODE_6 wrapping mode
+ * for both reading and writing with:
  * eccsize0 = 0  (no additional protected byte in spare area)
  * eccsize1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
  */
@@ -1071,15 +1070,9 @@ static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode)
        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;
-               }
+               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;
@@ -1097,15 +1090,9 @@ static void __maybe_unused omap_enable_hwecc_bch(struct mtd_info *mtd, int mode)
        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;
-               }
+               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;
index ccaa8e28338855effbf10724f9b6fae2df215466..6f93b2990d250e76007ee355aed8054752aa33f9 100644 (file)
@@ -1110,8 +1110,6 @@ static int sunxi_nand_ecc_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc,
 
        switch (ecc->mode) {
        case NAND_ECC_SOFT_BCH:
-               ecc->bytes = DIV_ROUND_UP(ecc->strength * fls(8 * ecc->size),
-                                         8);
                break;
        case NAND_ECC_HW:
                ret = sunxi_nand_hw_ecc_ctrl_init(mtd, ecc, np);
index 51b9d6af307f616193ebc006b492a5fecedd535b..a5dfbfbebfcafe7a2d4b7161a186cea565fe9ff1 100644 (file)
@@ -89,9 +89,10 @@ static int find_boot_record(struct NFTLrecord *nftl)
                }
 
                /* To be safer with BIOS, also use erase mark as discriminant */
-               if ((ret = nftl_read_oob(mtd, block * nftl->EraseSize +
+               ret = nftl_read_oob(mtd, block * nftl->EraseSize +
                                         SECTORSIZE + 8, 8, &retlen,
-                                        (char *)&h1) < 0)) {
+                                        (char *)&h1);
+               if (ret < 0) {
                        printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n",
                               block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
                        continue;
@@ -109,8 +110,9 @@ static int find_boot_record(struct NFTLrecord *nftl)
                }
 
                /* Finally reread to check ECC */
-               if ((ret = mtd->read(mtd, block * nftl->EraseSize, SECTORSIZE,
-                                    &retlen, buf) < 0)) {
+               ret = mtd->read(mtd, block * nftl->EraseSize, SECTORSIZE,
+                               &retlen, buf);
+               if (ret < 0) {
                        printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n",
                               block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
                        continue;
@@ -228,9 +230,11 @@ device is already correct.
 The new DiskOnChip driver already scanned the bad block table.  Just query it.
                        if ((i & (SECTORSIZE - 1)) == 0) {
                                /* read one sector for every SECTORSIZE of blocks */
-                               if ((ret = mtd->read(nftl->mbd.mtd, block * nftl->EraseSize +
-                                                    i + SECTORSIZE, SECTORSIZE, &retlen,
-                                                    buf)) < 0) {
+                               ret = mtd->read(nftl->mbd.mtd,
+                                               block * nftl->EraseSize + i +
+                                               SECTORSIZE, SECTORSIZE,
+                                               &retlen, buf);
+                               if (ret < 0) {
                                        printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n",
                                               ret);
                                        kfree(nftl->ReplUnitTable);
index 39763b94f67d48bcc4ec9a83b5481b3f12b33ceb..1c7308c2c77d9b54beb22ad626a0b9d813052a48 100644 (file)
@@ -57,7 +57,9 @@
 
 #define QUADSPI_BUF3CR                 0x1c
 #define QUADSPI_BUF3CR_ALLMST_SHIFT    31
-#define QUADSPI_BUF3CR_ALLMST          (1 << QUADSPI_BUF3CR_ALLMST_SHIFT)
+#define QUADSPI_BUF3CR_ALLMST_MASK     (1 << QUADSPI_BUF3CR_ALLMST_SHIFT)
+#define QUADSPI_BUF3CR_ADATSZ_SHIFT            8
+#define QUADSPI_BUF3CR_ADATSZ_MASK     (0xFF << QUADSPI_BUF3CR_ADATSZ_SHIFT)
 
 #define QUADSPI_BFGENCR                        0x20
 #define QUADSPI_BFGENCR_PAR_EN_SHIFT   16
@@ -198,18 +200,21 @@ struct fsl_qspi_devtype_data {
        enum fsl_qspi_devtype devtype;
        int rxfifo;
        int txfifo;
+       int ahb_buf_size;
 };
 
 static struct fsl_qspi_devtype_data vybrid_data = {
        .devtype = FSL_QUADSPI_VYBRID,
        .rxfifo = 128,
-       .txfifo = 64
+       .txfifo = 64,
+       .ahb_buf_size = 1024
 };
 
 static struct fsl_qspi_devtype_data imx6sx_data = {
        .devtype = FSL_QUADSPI_IMX6SX,
        .rxfifo = 128,
-       .txfifo = 512
+       .txfifo = 512,
+       .ahb_buf_size = 1024
 };
 
 #define FSL_QSPI_MAX_CHIP      4
@@ -227,6 +232,7 @@ struct fsl_qspi {
        u32 nor_num;
        u32 clk_rate;
        unsigned int chip_base_addr; /* We may support two chips. */
+       bool has_second_chip;
 };
 
 static inline int is_vybrid_qspi(struct fsl_qspi *q)
@@ -583,7 +589,12 @@ static void fsl_qspi_init_abh_read(struct fsl_qspi *q)
        writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF0CR);
        writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF1CR);
        writel(QUADSPI_BUFXCR_INVALID_MSTRID, base + QUADSPI_BUF2CR);
-       writel(QUADSPI_BUF3CR_ALLMST, base + QUADSPI_BUF3CR);
+       /*
+        * Set ADATSZ with the maximum AHB buffer size to improve the
+        * read performance.
+        */
+       writel(QUADSPI_BUF3CR_ALLMST_MASK | ((q->devtype_data->ahb_buf_size / 8)
+                       << QUADSPI_BUF3CR_ADATSZ_SHIFT), base + QUADSPI_BUF3CR);
 
        /* We only use the buffer3 */
        writel(0, base + QUADSPI_BUF0IND);
@@ -783,7 +794,6 @@ static int fsl_qspi_probe(struct platform_device *pdev)
        struct spi_nor *nor;
        struct mtd_info *mtd;
        int ret, i = 0;
-       bool has_second_chip = false;
        const struct of_device_id *of_id =
                        of_match_device(fsl_qspi_dt_ids, &pdev->dev);
 
@@ -798,37 +808,30 @@ static int fsl_qspi_probe(struct platform_device *pdev)
        /* find the resources */
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "QuadSPI");
        q->iobase = devm_ioremap_resource(dev, res);
-       if (IS_ERR(q->iobase)) {
-               ret = PTR_ERR(q->iobase);
-               goto map_failed;
-       }
+       if (IS_ERR(q->iobase))
+               return PTR_ERR(q->iobase);
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
                                        "QuadSPI-memory");
        q->ahb_base = devm_ioremap_resource(dev, res);
-       if (IS_ERR(q->ahb_base)) {
-               ret = PTR_ERR(q->ahb_base);
-               goto map_failed;
-       }
+       if (IS_ERR(q->ahb_base))
+               return PTR_ERR(q->ahb_base);
+
        q->memmap_phy = res->start;
 
        /* find the clocks */
        q->clk_en = devm_clk_get(dev, "qspi_en");
-       if (IS_ERR(q->clk_en)) {
-               ret = PTR_ERR(q->clk_en);
-               goto map_failed;
-       }
+       if (IS_ERR(q->clk_en))
+               return PTR_ERR(q->clk_en);
 
        q->clk = devm_clk_get(dev, "qspi");
-       if (IS_ERR(q->clk)) {
-               ret = PTR_ERR(q->clk);
-               goto map_failed;
-       }
+       if (IS_ERR(q->clk))
+               return PTR_ERR(q->clk);
 
        ret = clk_prepare_enable(q->clk_en);
        if (ret) {
                dev_err(dev, "can not enable the qspi_en clock\n");
-               goto map_failed;
+               return ret;
        }
 
        ret = clk_prepare_enable(q->clk);
@@ -860,14 +863,14 @@ static int fsl_qspi_probe(struct platform_device *pdev)
                goto irq_failed;
 
        if (of_get_property(np, "fsl,qspi-has-second-chip", NULL))
-               has_second_chip = true;
+               q->has_second_chip = true;
 
        /* iterate the subnodes. */
        for_each_available_child_of_node(dev->of_node, np) {
                char modalias[40];
 
                /* skip the holes */
-               if (!has_second_chip)
+               if (!q->has_second_chip)
                        i *= 2;
 
                nor = &q->nor[i];
@@ -890,24 +893,24 @@ static int fsl_qspi_probe(struct platform_device *pdev)
 
                ret = of_modalias_node(np, modalias, sizeof(modalias));
                if (ret < 0)
-                       goto map_failed;
+                       goto irq_failed;
 
                ret = of_property_read_u32(np, "spi-max-frequency",
                                &q->clk_rate);
                if (ret < 0)
-                       goto map_failed;
+                       goto irq_failed;
 
                /* set the chip address for READID */
                fsl_qspi_set_base_addr(q, nor);
 
                ret = spi_nor_scan(nor, modalias, SPI_NOR_QUAD);
                if (ret)
-                       goto map_failed;
+                       goto irq_failed;
 
                ppdata.of_node = np;
                ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
                if (ret)
-                       goto map_failed;
+                       goto irq_failed;
 
                /* Set the correct NOR size now. */
                if (q->nor_size == 0) {
@@ -939,19 +942,19 @@ static int fsl_qspi_probe(struct platform_device *pdev)
 
        clk_disable(q->clk);
        clk_disable(q->clk_en);
-       dev_info(dev, "QuadSPI SPI NOR flash driver\n");
        return 0;
 
 last_init_failed:
-       for (i = 0; i < q->nor_num; i++)
+       for (i = 0; i < q->nor_num; i++) {
+               /* skip the holes */
+               if (!q->has_second_chip)
+                       i *= 2;
                mtd_device_unregister(&q->mtd[i]);
-
+       }
 irq_failed:
        clk_disable_unprepare(q->clk);
 clk_failed:
        clk_disable_unprepare(q->clk_en);
-map_failed:
-       dev_err(dev, "Freescale QuadSPI probe failed\n");
        return ret;
 }
 
@@ -960,8 +963,12 @@ static int fsl_qspi_remove(struct platform_device *pdev)
        struct fsl_qspi *q = platform_get_drvdata(pdev);
        int i;
 
-       for (i = 0; i < q->nor_num; i++)
+       for (i = 0; i < q->nor_num; i++) {
+               /* skip the holes */
+               if (!q->has_second_chip)
+                       i *= 2;
                mtd_device_unregister(&q->mtd[i]);
+       }
 
        /* disable the hardware */
        writel(QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR);
@@ -972,6 +979,22 @@ static int fsl_qspi_remove(struct platform_device *pdev)
        return 0;
 }
 
+static int fsl_qspi_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       return 0;
+}
+
+static int fsl_qspi_resume(struct platform_device *pdev)
+{
+       struct fsl_qspi *q = platform_get_drvdata(pdev);
+
+       fsl_qspi_nor_setup(q);
+       fsl_qspi_set_map_addr(q);
+       fsl_qspi_nor_setup_last(q);
+
+       return 0;
+}
+
 static struct platform_driver fsl_qspi_driver = {
        .driver = {
                .name   = "fsl-quadspi",
@@ -980,6 +1003,8 @@ static struct platform_driver fsl_qspi_driver = {
        },
        .probe          = fsl_qspi_probe,
        .remove         = fsl_qspi_remove,
+       .suspend        = fsl_qspi_suspend,
+       .resume         = fsl_qspi_resume,
 };
 module_platform_driver(fsl_qspi_driver);
 
index 0f8ec3c2d015815673c5a6e51f351fe55cbe48de..b6a5a0c269e1d29f2dbf46f2ab2f2117dbe96417 100644 (file)
@@ -538,6 +538,7 @@ static const struct spi_device_id spi_nor_ids[] = {
        /* GigaDevice */
        { "gd25q32", INFO(0xc84016, 0, 64 * 1024,  64, SECT_4K) },
        { "gd25q64", INFO(0xc84017, 0, 64 * 1024, 128, SECT_4K) },
+       { "gd25q128", INFO(0xc84018, 0, 64 * 1024, 256, SECT_4K) },
 
        /* Intel/Numonyx -- xxxs33b */
        { "160s33b",  INFO(0x898911, 0, 64 * 1024,  32, 0) },
@@ -560,14 +561,14 @@ static const struct spi_device_id spi_nor_ids[] = {
        { "mx66l1g55g",  INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
 
        /* Micron */
-       { "n25q032",     INFO(0x20ba16, 0, 64 * 1024,   64, 0) },
-       { "n25q064",     INFO(0x20ba17, 0, 64 * 1024,  128, 0) },
-       { "n25q128a11",  INFO(0x20bb18, 0, 64 * 1024,  256, 0) },
-       { "n25q128a13",  INFO(0x20ba18, 0, 64 * 1024,  256, 0) },
-       { "n25q256a",    INFO(0x20ba19, 0, 64 * 1024,  512, SECT_4K) },
-       { "n25q512a",    INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) },
-       { "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, USE_FSR) },
-       { "n25q00",      INFO(0x20ba21, 0, 64 * 1024, 2048, USE_FSR) },
+       { "n25q032",     INFO(0x20ba16, 0, 64 * 1024,   64, SPI_NOR_QUAD_READ) },
+       { "n25q064",     INFO(0x20ba17, 0, 64 * 1024,  128, SPI_NOR_QUAD_READ) },
+       { "n25q128a11",  INFO(0x20bb18, 0, 64 * 1024,  256, SPI_NOR_QUAD_READ) },
+       { "n25q128a13",  INFO(0x20ba18, 0, 64 * 1024,  256, SPI_NOR_QUAD_READ) },
+       { "n25q256a",    INFO(0x20ba19, 0, 64 * 1024,  512, SECT_4K | SPI_NOR_QUAD_READ) },
+       { "n25q512a",    INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
+       { "n25q512ax3",  INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
+       { "n25q00",      INFO(0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
 
        /* PMC */
        { "pm25lv512",   INFO(0,        0, 32 * 1024,    2, SECT_4K_PMC) },
@@ -891,6 +892,45 @@ static int spansion_quad_enable(struct spi_nor *nor)
        return 0;
 }
 
+static int micron_quad_enable(struct spi_nor *nor)
+{
+       int ret;
+       u8 val;
+
+       ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1);
+       if (ret < 0) {
+               dev_err(nor->dev, "error %d reading EVCR\n", ret);
+               return ret;
+       }
+
+       write_enable(nor);
+
+       /* set EVCR, enable quad I/O */
+       nor->cmd_buf[0] = val & ~EVCR_QUAD_EN_MICRON;
+       ret = nor->write_reg(nor, SPINOR_OP_WD_EVCR, nor->cmd_buf, 1, 0);
+       if (ret < 0) {
+               dev_err(nor->dev, "error while writing EVCR register\n");
+               return ret;
+       }
+
+       ret = spi_nor_wait_till_ready(nor);
+       if (ret)
+               return ret;
+
+       /* read EVCR and check it */
+       ret = nor->read_reg(nor, SPINOR_OP_RD_EVCR, &val, 1);
+       if (ret < 0) {
+               dev_err(nor->dev, "error %d reading EVCR\n", ret);
+               return ret;
+       }
+       if (val & EVCR_QUAD_EN_MICRON) {
+               dev_err(nor->dev, "Micron EVCR Quad bit not clear\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int set_quad_mode(struct spi_nor *nor, struct flash_info *info)
 {
        int status;
@@ -903,6 +943,13 @@ static int set_quad_mode(struct spi_nor *nor, struct flash_info *info)
                        return -EINVAL;
                }
                return status;
+       case CFI_MFR_ST:
+               status = micron_quad_enable(nor);
+               if (status) {
+                       dev_err(nor->dev, "Micron quad-read not enabled\n");
+                       return -EINVAL;
+               }
+               return status;
        default:
                status = spansion_quad_enable(nor);
                if (status) {
index 92e0644bf8673d91c091edf437e5a8e5e2941d64..556de100ebd5a5318fec58cc47c6187315ed2b7b 100644 (file)
@@ -84,11 +84,6 @@ static inline int pullbit(struct pushpull *pp)
        return bit;
 }
 
-static inline int pulledbits(struct pushpull *pp)
-{
-       return pp->ofs;
-}
-
 
 static void init_rubin(struct rubin_state *rs, int div, int *bits)
 {
index 7654e87b042869ef43aff269a10e88a4088d59c3..9ad5ba4b299be2f41cca834e97480a309c4e7b16 100644 (file)
@@ -510,6 +510,10 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
                                sumlen = c->sector_size - je32_to_cpu(sm->offset);
                                sumptr = buf + buf_size - sumlen;
 
+                               /* sm->offset maybe wrong but MAGIC maybe right */
+                               if (sumlen > c->sector_size)
+                                       goto full_scan;
+
                                /* Now, make sure the summary itself is available */
                                if (sumlen > buf_size) {
                                        /* Need to kmalloc for this. */
@@ -544,6 +548,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
                }
        }
 
+full_scan:
        buf_ofs = jeb->offset;
 
        if (!buf_size) {
index 3301c4c289d625987efb33e6471a70ce4987ff2e..f17fa75809aa1d05a6164fabb4f02b87106c4657 100644 (file)
@@ -227,6 +227,7 @@ struct mtd_info {
        int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
        int (*_suspend) (struct mtd_info *mtd);
        void (*_resume) (struct mtd_info *mtd);
+       void (*_reboot) (struct mtd_info *mtd);
        /*
         * If the driver is something smart, like UBI, it may need to maintain
         * its own reference counting. The below functions are only for driver.
index 63aeccf9ddc8cbae4b40d06705e5ebb3658d510d..4720b86ee73dce54e39516f3d3371409d1254d3a 100644 (file)
 /* Used for Spansion flashes only. */
 #define SPINOR_OP_BRWR         0x17    /* Bank register write */
 
+/* Used for Micron flashes only. */
+#define SPINOR_OP_RD_EVCR      0x65    /* Read EVCR register */
+#define SPINOR_OP_WD_EVCR      0x61    /* Write EVCR register */
+
 /* Status Register bits. */
 #define SR_WIP                 1       /* Write in progress */
 #define SR_WEL                 2       /* Write enable latch */
@@ -67,6 +71,9 @@
 
 #define SR_QUAD_EN_MX          0x40    /* Macronix Quad I/O */
 
+/* Enhanced Volatile Configuration Register bits */
+#define EVCR_QUAD_EN_MICRON    0x80    /* Micron Quad I/O */
+
 /* Flag Status Register bits */
 #define FSR_READY              0x80