Merge tag 'spi-nor/for-4.21' of git://git.infradead.org/linux-mtd into mtd/next
authorBoris Brezillon <boris.brezillon@bootlin.com>
Tue, 18 Dec 2018 19:00:52 +0000 (20:00 +0100)
committerBoris Brezillon <boris.brezillon@bootlin.com>
Tue, 18 Dec 2018 19:00:52 +0000 (20:00 +0100)
Core changes:
- Parse the 4BAIT SFDP section
- Add a bunch of SPI NOR entries to the flash_info table
- Add the concept of SFDP fixups and use it to fix a bug on MX25L25635F
- A bunch of minor cleanups/comestic changes

98 files changed:
.mailmap
Documentation/devicetree/bindings/mtd/mtd-physmap.txt
Documentation/devicetree/bindings/mtd/partitions/redboot-fis.txt [new file with mode: 0644]
MAINTAINERS
arch/arm/mach-omap1/board-ams-delta.c
drivers/mtd/Kconfig
drivers/mtd/Makefile
drivers/mtd/chips/cfi_cmdset_0020.c
drivers/mtd/devices/block2mtd.c
drivers/mtd/devices/docg3.c
drivers/mtd/devices/docg3.h
drivers/mtd/maps/Kconfig
drivers/mtd/maps/Makefile
drivers/mtd/maps/gpio-addr-flash.c [deleted file]
drivers/mtd/maps/latch-addr-flash.c [deleted file]
drivers/mtd/maps/physmap-core.c [new file with mode: 0644]
drivers/mtd/maps/physmap-gemini.c [new file with mode: 0644]
drivers/mtd/maps/physmap-gemini.h [new file with mode: 0644]
drivers/mtd/maps/physmap-versatile.c [new file with mode: 0644]
drivers/mtd/maps/physmap-versatile.h [new file with mode: 0644]
drivers/mtd/maps/physmap.c [deleted file]
drivers/mtd/maps/physmap_of_core.c [deleted file]
drivers/mtd/maps/physmap_of_gemini.c [deleted file]
drivers/mtd/maps/physmap_of_gemini.h [deleted file]
drivers/mtd/maps/physmap_of_versatile.c [deleted file]
drivers/mtd/maps/physmap_of_versatile.h [deleted file]
drivers/mtd/mtdblock.c
drivers/mtd/mtdcore.c
drivers/mtd/mtdpart.c
drivers/mtd/mtdswap.c
drivers/mtd/nand/raw/Kconfig
drivers/mtd/nand/raw/ams-delta.c
drivers/mtd/nand/raw/atmel/nand-controller.c
drivers/mtd/nand/raw/au1550nd.c
drivers/mtd/nand/raw/bcm47xxnflash/ops_bcm4706.c
drivers/mtd/nand/raw/cafe_nand.c
drivers/mtd/nand/raw/davinci_nand.c
drivers/mtd/nand/raw/denali.c
drivers/mtd/nand/raw/denali.h
drivers/mtd/nand/raw/diskonchip.c
drivers/mtd/nand/raw/fsl_elbc_nand.c
drivers/mtd/nand/raw/fsl_ifc_nand.c
drivers/mtd/nand/raw/fsl_upm.c
drivers/mtd/nand/raw/fsmc_nand.c
drivers/mtd/nand/raw/gpmi-nand/gpmi-nand.c
drivers/mtd/nand/raw/hisi504_nand.c
drivers/mtd/nand/raw/internals.h
drivers/mtd/nand/raw/jz4740_nand.c
drivers/mtd/nand/raw/jz4780_bch.c
drivers/mtd/nand/raw/jz4780_nand.c
drivers/mtd/nand/raw/lpc32xx_mlc.c
drivers/mtd/nand/raw/lpc32xx_slc.c
drivers/mtd/nand/raw/marvell_nand.c
drivers/mtd/nand/raw/mpc5121_nfc.c
drivers/mtd/nand/raw/mtk_nand.c
drivers/mtd/nand/raw/mxc_nand.c
drivers/mtd/nand/raw/nand_base.c
drivers/mtd/nand/raw/nand_bbt.c
drivers/mtd/nand/raw/nand_hynix.c
drivers/mtd/nand/raw/nand_jedec.c
drivers/mtd/nand/raw/nand_legacy.c
drivers/mtd/nand/raw/nand_macronix.c
drivers/mtd/nand/raw/nandsim.c
drivers/mtd/nand/raw/ndfc.c
drivers/mtd/nand/raw/omap2.c
drivers/mtd/nand/raw/plat_nand.c
drivers/mtd/nand/raw/qcom_nandc.c
drivers/mtd/nand/raw/r852.c
drivers/mtd/nand/raw/s3c2410.c
drivers/mtd/nand/raw/sh_flctl.c
drivers/mtd/nand/raw/sm_common.c
drivers/mtd/nand/raw/sunxi_nand.c
drivers/mtd/nand/raw/tango_nand.c
drivers/mtd/nand/raw/tegra_nand.c
drivers/mtd/nand/raw/vf610_nfc.c
drivers/mtd/nand/raw/xway_nand.c
drivers/mtd/nand/spi/Makefile
drivers/mtd/nand/spi/core.c
drivers/mtd/nand/spi/gigadevice.c [new file with mode: 0644]
drivers/mtd/nand/spi/toshiba.c [new file with mode: 0644]
drivers/mtd/nand/spi/winbond.c
drivers/mtd/nftlmount.c
drivers/mtd/parsers/Kconfig
drivers/mtd/parsers/Makefile
drivers/mtd/parsers/redboot.c [new file with mode: 0644]
drivers/mtd/redboot.c [deleted file]
drivers/staging/Kconfig
drivers/staging/Makefile
drivers/staging/mt29f_spinand/Kconfig [deleted file]
drivers/staging/mt29f_spinand/Makefile [deleted file]
drivers/staging/mt29f_spinand/TODO [deleted file]
drivers/staging/mt29f_spinand/mt29f_spinand.c [deleted file]
drivers/staging/mt29f_spinand/mt29f_spinand.h [deleted file]
fs/jffs2/super.c
include/linux/mtd/mtd.h
include/linux/mtd/rawnand.h
include/linux/mtd/sh_flctl.h
include/linux/mtd/spinand.h

index 28fecaf..b4b0b0b 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -36,9 +36,10 @@ Bart Van Assche <bvanassche@acm.org> <bart.vanassche@sandisk.com>
 Ben Gardner <bgardner@wabtec.com>
 Ben M Cahill <ben.m.cahill@intel.com>
 Björn Steinbrink <B.Steinbrink@gmx.de>
-Boris Brezillon <boris.brezillon@bootlin.com> <boris.brezillon@free-electrons.com>
-Boris Brezillon <boris.brezillon@bootlin.com> <b.brezillon.dev@gmail.com>
-Boris Brezillon <boris.brezillon@bootlin.com> <b.brezillon@overkiz.com>
+Boris Brezillon <bbrezillon@kernel.org> <boris.brezillon@bootlin.com>
+Boris Brezillon <bbrezillon@kernel.org> <boris.brezillon@free-electrons.com>
+Boris Brezillon <bbrezillon@kernel.org> <b.brezillon.dev@gmail.com>
+Boris Brezillon <bbrezillon@kernel.org> <b.brezillon@overkiz.com>
 Brian Avery <b.avery@hp.com>
 Brian King <brking@us.ibm.com>
 Christoph Hellwig <hch@lst.de>
index 232fa12..7df0dca 100644 (file)
@@ -29,6 +29,8 @@ file systems on embedded devices.
  - use-advanced-sector-protection: boolean to enable support for the
    advanced sector protection (Spansion: PPB - Persistent Protection
    Bits) locking.
+ - addr-gpios : (optional) List of GPIO descriptors that will be used to
+   address the MSBs address lines. The order goes from LSB to MSB.
 
 For JEDEC compatible devices, the following additional properties
 are defined:
diff --git a/Documentation/devicetree/bindings/mtd/partitions/redboot-fis.txt b/Documentation/devicetree/bindings/mtd/partitions/redboot-fis.txt
new file mode 100644 (file)
index 0000000..fd0ebe4
--- /dev/null
@@ -0,0 +1,27 @@
+RedBoot FLASH Image System (FIS) Partitions
+===========================================
+
+The FLASH Image System (FIS) directory is a flash description
+format closely associated with the RedBoot boot loader.
+
+It uses one single flash eraseblock in the flash to store an index of
+all images in the flash.
+
+This block size will vary depending on flash but is typically
+32 KB in size.
+
+Required properties:
+- compatible : (required) must be "redboot-fis"
+- fis-index-block : (required) a index to the eraseblock containing
+  the FIS directory on this device. On a flash memory with 32KB
+  eraseblocks, 0 means the first eraseblock at 0x00000000, 1 means the
+  second eraseblock at 0x00008000 and so on.
+
+Example:
+
+flash@0 {
+       partitions {
+               compatible = "redboot-fis";
+               fis-index-block = <0>;
+       };
+};
index 6682420..cd2813d 100644 (file)
@@ -4886,7 +4886,7 @@ F:        Documentation/gpu/meson.rst
 T:     git git://anongit.freedesktop.org/drm/drm-misc
 
 DRM DRIVERS FOR ATMEL HLCDC
-M:     Boris Brezillon <boris.brezillon@bootlin.com>
+M:     Boris Brezillon <bbrezillon@kernel.org>
 L:     dri-devel@lists.freedesktop.org
 S:     Supported
 F:     drivers/gpu/drm/atmel-hlcdc/
@@ -8948,7 +8948,7 @@ F:        include/uapi/drm/armada_drm.h
 F:     Documentation/devicetree/bindings/display/armada/
 
 MARVELL CRYPTO DRIVER
-M:     Boris Brezillon <boris.brezillon@bootlin.com>
+M:     Boris Brezillon <bbrezillon@kernel.org>
 M:     Arnaud Ebalard <arno@natisbad.org>
 F:     drivers/crypto/marvell/
 S:     Maintained
@@ -9652,7 +9652,7 @@ F:        mm/
 MEMORY TECHNOLOGY DEVICES (MTD)
 M:     David Woodhouse <dwmw2@infradead.org>
 M:     Brian Norris <computersforpeace@gmail.com>
-M:     Boris Brezillon <boris.brezillon@bootlin.com>
+M:     Boris Brezillon <bbrezillon@kernel.org>
 M:     Marek Vasut <marek.vasut@gmail.com>
 M:     Richard Weinberger <richard@nod.at>
 L:     linux-mtd@lists.infradead.org
@@ -10242,7 +10242,7 @@ S:      Supported
 F:     drivers/net/ethernet/myricom/myri10ge/
 
 NAND FLASH SUBSYSTEM
-M:     Boris Brezillon <boris.brezillon@bootlin.com>
+M:     Boris Brezillon <bbrezillon@kernel.org>
 M:     Miquel Raynal <miquel.raynal@bootlin.com>
 R:     Richard Weinberger <richard@nod.at>
 L:     linux-mtd@lists.infradead.org
index 1788674..691a8da 100644 (file)
@@ -296,23 +296,13 @@ struct modem_private_data {
 
 static struct modem_private_data modem_priv;
 
-static struct resource ams_delta_nand_resources[] = {
-       [0] = {
-               .start  = OMAP1_MPUIO_BASE,
-               .end    = OMAP1_MPUIO_BASE +
-                               OMAP_MPUIO_IO_CNTL + sizeof(u32) - 1,
-               .flags  = IORESOURCE_MEM,
-       },
-};
-
 static struct platform_device ams_delta_nand_device = {
        .name   = "ams-delta-nand",
        .id     = -1,
-       .num_resources  = ARRAY_SIZE(ams_delta_nand_resources),
-       .resource       = ams_delta_nand_resources,
 };
 
-#define OMAP_GPIO_LABEL        "gpio-0-15"
+#define OMAP_GPIO_LABEL                "gpio-0-15"
+#define OMAP_MPUIO_LABEL       "mpuio"
 
 static struct gpiod_lookup_table ams_delta_nand_gpio_table = {
        .table = {
@@ -324,6 +314,14 @@ static struct gpiod_lookup_table ams_delta_nand_gpio_table = {
                GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_NWE, "nwe", 0),
                GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_ALE, "ale", 0),
                GPIO_LOOKUP(LATCH2_LABEL, LATCH2_PIN_NAND_CLE, "cle", 0),
+               GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 0, "data", 0, 0),
+               GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 1, "data", 1, 0),
+               GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 2, "data", 2, 0),
+               GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 3, "data", 3, 0),
+               GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 4, "data", 4, 0),
+               GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 5, "data", 5, 0),
+               GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 6, "data", 6, 0),
+               GPIO_LOOKUP_IDX(OMAP_MPUIO_LABEL, 7, "data", 7, 0),
                { },
        },
 };
index c77f537..1e18c96 100644 (file)
@@ -22,56 +22,6 @@ config MTD_TESTS
          WARNING: some of the tests will ERASE entire MTD device which they
          test. Do not use these tests unless you really know what you do.
 
-config MTD_REDBOOT_PARTS
-       tristate "RedBoot partition table parsing"
-       help
-         RedBoot is a ROM monitor and bootloader which deals with multiple
-         'images' in flash devices by putting a table one of the erase
-         blocks on the device, similar to a partition table, which gives
-         the offsets, lengths and names of all the images stored in the
-         flash.
-
-         If you need code which can detect and parse this table, and register
-         MTD 'partitions' corresponding to each image in the table, enable
-         this option.
-
-         You will still need the parsing functions to be called by the driver
-         for your particular device. It won't happen automatically. The
-         SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for
-         example.
-
-if MTD_REDBOOT_PARTS
-
-config MTD_REDBOOT_DIRECTORY_BLOCK
-       int "Location of RedBoot partition table"
-       default "-1"
-       help
-         This option is the Linux counterpart to the
-         CYGNUM_REDBOOT_FIS_DIRECTORY_BLOCK RedBoot compile time
-         option.
-
-         The option specifies which Flash sectors holds the RedBoot
-         partition table.  A zero or positive value gives an absolute
-         erase block number. A negative value specifies a number of
-         sectors before the end of the device.
-
-         For example "2" means block number 2, "-1" means the last
-         block and "-2" means the penultimate block.
-
-config MTD_REDBOOT_PARTS_UNALLOCATED
-       bool "Include unallocated flash regions"
-       help
-         If you need to register each unallocated flash region as a MTD
-         'partition', enable this option.
-
-config MTD_REDBOOT_PARTS_READONLY
-       bool "Force read-only for RedBoot system images"
-       help
-         If you need to force read-only for 'RedBoot', 'RedBoot Config' and
-         'FIS directory' images, enable this option.
-
-endif # MTD_REDBOOT_PARTS
-
 config MTD_CMDLINE_PARTS
        tristate "Command line partition table parsing"
        depends on MTD
@@ -144,7 +94,7 @@ config MTD_BCM63XX_PARTS
        depends on BCM63XX || BMIPS_GENERIC || COMPILE_TEST
        select CRC32
        help
-         This provides partions parsing for BCM63xx devices with CFE
+         This provides partition parsing for BCM63xx devices with CFE
          bootloaders.
 
 config MTD_BCM47XX_PARTS
index 93473d2..58fc327 100644 (file)
@@ -8,7 +8,6 @@ obj-$(CONFIG_MTD)               += mtd.o
 mtd-y                          := mtdcore.o mtdsuper.o mtdconcat.o mtdpart.o mtdchar.o
 
 obj-$(CONFIG_MTD_OF_PARTS)     += ofpart.o
-obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
 obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
 obj-$(CONFIG_MTD_AFS_PARTS)    += afs.o
 obj-$(CONFIG_MTD_AR7_PARTS)    += ar7part.o
index 35aa72b..e752067 100644 (file)
@@ -324,6 +324,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof
        case FL_JEDEC_QUERY:
                map_write(map, CMD(0x70), cmd_addr);
                chip->state = FL_STATUS;
+               /* Fall through */
 
        case FL_STATUS:
                status = map_read(map, cmd_addr);
@@ -461,6 +462,7 @@ static int do_write_buffer(struct map_info *map, struct flchip *chip,
 #ifdef DEBUG_CFI_FEATURES
        printk("%s: 1 status[%x]\n", __func__, map_read(map, cmd_adr));
 #endif
+               /* Fall through */
 
        case FL_STATUS:
                status = map_read(map, cmd_adr);
@@ -754,6 +756,7 @@ retry:
        case FL_READY:
                map_write(map, CMD(0x70), adr);
                chip->state = FL_STATUS;
+               /* Fall through */
 
        case FL_STATUS:
                status = map_read(map, adr);
@@ -995,6 +998,7 @@ static void cfi_staa_sync (struct mtd_info *mtd)
                         * as the whole point is that nobody can do anything
                         * with the chip now anyway.
                         */
+                       /* Fall through */
                case FL_SYNCING:
                        mutex_unlock(&chip->mutex);
                        break;
@@ -1050,6 +1054,7 @@ retry:
        case FL_READY:
                map_write(map, CMD(0x70), adr);
                chip->state = FL_STATUS;
+               /* Fall through */
 
        case FL_STATUS:
                status = map_read(map, adr);
@@ -1196,6 +1201,7 @@ retry:
        case FL_READY:
                map_write(map, CMD(0x70), adr);
                chip->state = FL_STATUS;
+               /* Fall through */
 
        case FL_STATUS:
                status = map_read(map, adr);
index c9e4249..410a321 100644 (file)
@@ -329,8 +329,10 @@ static int ustrtoul(const char *cp, char **endp, unsigned int base)
        switch (**endp) {
        case 'G' :
                result *= 1024;
+               /* fall through */
        case 'M':
                result *= 1024;
+               /* fall through */
        case 'K':
        case 'k':
                result *= 1024;
index 512bd4c..4c94fc0 100644 (file)
@@ -1603,7 +1603,7 @@ static void doc_unregister_sysfs(struct platform_device *pdev,
 /*
  * Debug sysfs entries
  */
-static int dbg_flashctrl_show(struct seq_file *s, void *p)
+static int flashcontrol_show(struct seq_file *s, void *p)
 {
        struct docg3 *docg3 = (struct docg3 *)s->private;
 
@@ -1623,9 +1623,9 @@ static int dbg_flashctrl_show(struct seq_file *s, void *p)
 
        return 0;
 }
-DEBUGFS_RO_ATTR(flashcontrol, dbg_flashctrl_show);
+DEFINE_SHOW_ATTRIBUTE(flashcontrol);
 
-static int dbg_asicmode_show(struct seq_file *s, void *p)
+static int asic_mode_show(struct seq_file *s, void *p)
 {
        struct docg3 *docg3 = (struct docg3 *)s->private;
 
@@ -1660,9 +1660,9 @@ static int dbg_asicmode_show(struct seq_file *s, void *p)
        seq_puts(s, ")\n");
        return 0;
 }
-DEBUGFS_RO_ATTR(asic_mode, dbg_asicmode_show);
+DEFINE_SHOW_ATTRIBUTE(asic_mode);
 
-static int dbg_device_id_show(struct seq_file *s, void *p)
+static int device_id_show(struct seq_file *s, void *p)
 {
        struct docg3 *docg3 = (struct docg3 *)s->private;
        int id;
@@ -1674,9 +1674,9 @@ static int dbg_device_id_show(struct seq_file *s, void *p)
        seq_printf(s, "DeviceId = %d\n", id);
        return 0;
 }
-DEBUGFS_RO_ATTR(device_id, dbg_device_id_show);
+DEFINE_SHOW_ATTRIBUTE(device_id);
 
-static int dbg_protection_show(struct seq_file *s, void *p)
+static int protection_show(struct seq_file *s, void *p)
 {
        struct docg3 *docg3 = (struct docg3 *)s->private;
        int protect, dps0, dps0_low, dps0_high, dps1, dps1_low, dps1_high;
@@ -1726,7 +1726,7 @@ static int dbg_protection_show(struct seq_file *s, void *p)
                   !!(dps1 & DOC_DPS_KEY_OK));
        return 0;
 }
-DEBUGFS_RO_ATTR(protection, dbg_protection_show);
+DEFINE_SHOW_ATTRIBUTE(protection);
 
 static void __init doc_dbg_register(struct mtd_info *floor)
 {
index e999465..e16dca2 100644 (file)
@@ -317,17 +317,6 @@ struct docg3 {
 #define doc_info(fmt, arg...) dev_info(docg3->dev, (fmt), ## arg)
 #define doc_dbg(fmt, arg...) dev_dbg(docg3->dev, (fmt), ## arg)
 #define doc_vdbg(fmt, arg...) dev_vdbg(docg3->dev, (fmt), ## arg)
-
-#define DEBUGFS_RO_ATTR(name, show_fct) \
-       static int name##_open(struct inode *inode, struct file *file) \
-       { return single_open(file, show_fct, inode->i_private); }      \
-       static const struct file_operations name##_fops = { \
-               .owner = THIS_MODULE, \
-               .open = name##_open, \
-               .llseek = seq_lseek, \
-               .read = seq_read, \
-               .release = single_release \
-       };
 #endif
 
 /*
index afb36bf..e0cf869 100644 (file)
@@ -66,15 +66,15 @@ config MTD_PHYSMAP_BANKWIDTH
          used internally by the CFI drivers.
 
 config MTD_PHYSMAP_OF
-       tristate "Memory device in physical memory map based on OF description"
-       depends on OF && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM || MTD_RAM)
+       bool "Memory device in physical memory map based on OF description"
+       depends on OF && MTD_PHYSMAP
        help
          This provides a 'mapping' driver which allows the NOR Flash, ROM
          and RAM driver code to communicate with chips which are mapped
          physically into the CPU's memory. The mapping description here is
          taken from OF device tree.
 
-config MTD_PHYSMAP_OF_VERSATILE
+config MTD_PHYSMAP_VERSATILE
        bool "ARM Versatile OF-based physical memory map handling"
        depends on MTD_PHYSMAP_OF
        depends on MFD_SYSCON
@@ -84,16 +84,26 @@ config MTD_PHYSMAP_OF_VERSATILE
          platforms, basically to add a VPP (write protection) callback so
          the flash can be taken out of write protection.
 
-config MTD_PHYSMAP_OF_GEMINI
+config MTD_PHYSMAP_GEMINI
        bool "Cortina Gemini OF-based physical memory map handling"
        depends on MTD_PHYSMAP_OF
        depends on MFD_SYSCON
+       select MTD_COMPLEX_MAPPINGS
        default ARCH_GEMINI
        help
          This provides some extra DT physmap parsing for the Gemini
          platforms, some detection and setting up parallel mode on the
          external interface.
 
+config MTD_PHYSMAP_GPIO_ADDR
+       bool "GPIO-assisted Flash Chip Support"
+       depends on MTD_PHYSMAP
+       depends on GPIOLIB || COMPILE_TEST
+       depends on MTD_COMPLEX_MAPPINGS
+       help
+         Extend the physmap driver to allow flashes to be partially
+         physically addressed and assisted by GPIOs.
+
 config MTD_PMC_MSP_EVM
        tristate "CFI Flash device mapped on PMC-Sierra MSP"
        depends on PMC_MSP && MTD_CFI
@@ -334,16 +344,6 @@ config MTD_PCMCIA_ANONYMOUS
 
          If unsure, say N.
 
-config MTD_GPIO_ADDR
-       tristate "GPIO-assisted Flash Chip Support"
-       depends on GPIOLIB || COMPILE_TEST
-       depends on MTD_COMPLEX_MAPPINGS
-       help
-         Map driver which allows flashes to be partially physically addressed
-         and assisted by GPIOs.
-
-         If compiled as a module, it will be called gpio-addr-flash.
-
 config MTD_UCLINUX
        bool "Generic uClinux RAM/ROM filesystem support"
        depends on (MTD_RAM=y || MTD_ROM=y) && (!MMU || COLDFIRE)
@@ -400,13 +400,4 @@ config MTD_PISMO
 
          When built as a module, it will be called pismo.ko
 
-config MTD_LATCH_ADDR
-       tristate "Latch-assisted Flash Chip Support"
-       depends on MTD_COMPLEX_MAPPINGS
-       help
-         Map driver which allows flashes to be partially physically addressed
-         and have the upper address lines set by a board specific code.
-
-         If compiled as a module, it will be called latch-addr-flash.
-
 endmenu
index 51acf1f..1146009 100644 (file)
@@ -17,12 +17,11 @@ obj-$(CONFIG_MTD_ICHXROM)   += ichxrom.o
 obj-$(CONFIG_MTD_CK804XROM)    += ck804xrom.o
 obj-$(CONFIG_MTD_TSUNAMI)      += tsunami_flash.o
 obj-$(CONFIG_MTD_PXA2XX)       += pxa2xx-flash.o
+physmap-objs-y                 += physmap-core.o
+physmap-objs-$(CONFIG_MTD_PHYSMAP_VERSATILE) += physmap-versatile.o
+physmap-objs-$(CONFIG_MTD_PHYSMAP_GEMINI) += physmap-gemini.o
+physmap-objs                   := $(physmap-objs-y)
 obj-$(CONFIG_MTD_PHYSMAP)      += physmap.o
-physmap_of-objs-y              += physmap_of_core.o
-physmap_of-objs-$(CONFIG_MTD_PHYSMAP_OF_VERSATILE) += physmap_of_versatile.o
-physmap_of-objs-$(CONFIG_MTD_PHYSMAP_OF_GEMINI) += physmap_of_gemini.o
-physmap_of-objs                        := $(physmap_of-objs-y)
-obj-$(CONFIG_MTD_PHYSMAP_OF)   += physmap_of.o
 obj-$(CONFIG_MTD_PISMO)                += pismo.o
 obj-$(CONFIG_MTD_PMC_MSP_EVM)   += pmcmsp-flash.o
 obj-$(CONFIG_MTD_PCMCIA)       += pcmciamtd.o
@@ -44,6 +43,4 @@ obj-$(CONFIG_MTD_PLATRAM)     += plat-ram.o
 obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
 obj-$(CONFIG_MTD_RBTX4939)     += rbtx4939-flash.o
 obj-$(CONFIG_MTD_VMU)          += vmu-flash.o
-obj-$(CONFIG_MTD_GPIO_ADDR)    += gpio-addr-flash.o
-obj-$(CONFIG_MTD_LATCH_ADDR)   += latch-addr-flash.o
 obj-$(CONFIG_MTD_LANTIQ)       += lantiq-flash.o
diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c
deleted file mode 100644 (file)
index a20e85a..0000000
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * drivers/mtd/maps/gpio-addr-flash.c
- *
- * Handle the case where a flash device is mostly addressed using physical
- * line and supplemented by GPIOs.  This way you can hook up say a 8MiB flash
- * to a 2MiB memory range and use the GPIOs to select a particular range.
- *
- * Copyright © 2000 Nicolas Pitre <nico@cam.org>
- * Copyright © 2005-2009 Analog Devices Inc.
- *
- * Enter bugs at http://blackfin.uclinux.org/
- *
- * Licensed under the GPL-2 or later.
- */
-
-#include <linux/gpio.h>
-#include <linux/gpio/consumer.h>
-#include <linux/io.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/physmap.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-
-#define win_mask(x) ((BIT(x)) - 1)
-
-#define DRIVER_NAME "gpio-addr-flash"
-
-/**
- * struct async_state - keep GPIO flash state
- *     @mtd:         MTD state for this mapping
- *     @map:         MTD map state for this flash
- *     @gpios:       Struct containing the array of GPIO descriptors
- *     @gpio_values: cached GPIO values
- *     @win_order:   dedicated memory size (if no GPIOs)
- */
-struct async_state {
-       struct mtd_info *mtd;
-       struct map_info map;
-       struct gpio_descs *gpios;
-       unsigned int gpio_values;
-       unsigned int win_order;
-};
-#define gf_map_info_to_state(mi) ((struct async_state *)(mi)->map_priv_1)
-
-/**
- * gf_set_gpios() - set GPIO address lines to access specified flash offset
- *     @state: GPIO flash state
- *     @ofs:   desired offset to access
- *
- * Rather than call the GPIO framework every time, cache the last-programmed
- * value.  This speeds up sequential accesses (which are by far the most common
- * type).
- */
-static void gf_set_gpios(struct async_state *state, unsigned long ofs)
-{
-       int i;
-
-       ofs >>= state->win_order;
-
-       if (ofs == state->gpio_values)
-               return;
-
-       for (i = 0; i < state->gpios->ndescs; i++) {
-               if ((ofs & BIT(i)) == (state->gpio_values & BIT(i)))
-                       continue;
-
-               gpiod_set_value(state->gpios->desc[i], !!(ofs & BIT(i)));
-       }
-
-       state->gpio_values = ofs;
-}
-
-/**
- * gf_read() - read a word at the specified offset
- *     @map: MTD map state
- *     @ofs: desired offset to read
- */
-static map_word gf_read(struct map_info *map, unsigned long ofs)
-{
-       struct async_state *state = gf_map_info_to_state(map);
-       uint16_t word;
-       map_word test;
-
-       gf_set_gpios(state, ofs);
-
-       word = readw(map->virt + (ofs & win_mask(state->win_order)));
-       test.x[0] = word;
-       return test;
-}
-
-/**
- * gf_copy_from() - copy a chunk of data from the flash
- *     @map:  MTD map state
- *     @to:   memory to copy to
- *     @from: flash offset to copy from
- *     @len:  how much to copy
- *
- * The "from" region may straddle more than one window, so toggle the GPIOs for
- * each window region before reading its data.
- */
-static void gf_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-       struct async_state *state = gf_map_info_to_state(map);
-
-       int this_len;
-
-       while (len) {
-               this_len = from & win_mask(state->win_order);
-               this_len = BIT(state->win_order) - this_len;
-               this_len = min_t(int, len, this_len);
-
-               gf_set_gpios(state, from);
-               memcpy_fromio(to,
-                             map->virt + (from & win_mask(state->win_order)),
-                             this_len);
-               len -= this_len;
-               from += this_len;
-               to += this_len;
-       }
-}
-
-/**
- * gf_write() - write a word at the specified offset
- *     @map: MTD map state
- *     @ofs: desired offset to write
- */
-static void gf_write(struct map_info *map, map_word d1, unsigned long ofs)
-{
-       struct async_state *state = gf_map_info_to_state(map);
-       uint16_t d;
-
-       gf_set_gpios(state, ofs);
-
-       d = d1.x[0];
-       writew(d, map->virt + (ofs & win_mask(state->win_order)));
-}
-
-/**
- * gf_copy_to() - copy a chunk of data to the flash
- *     @map:  MTD map state
- *     @to:   flash offset to copy to
- *     @from: memory to copy from
- *     @len:  how much to copy
- *
- * See gf_copy_from() caveat.
- */
-static void gf_copy_to(struct map_info *map, unsigned long to,
-                      const void *from, ssize_t len)
-{
-       struct async_state *state = gf_map_info_to_state(map);
-
-       int this_len;
-
-       while (len) {
-               this_len = to & win_mask(state->win_order);
-               this_len = BIT(state->win_order) - this_len;
-               this_len = min_t(int, len, this_len);
-
-               gf_set_gpios(state, to);
-               memcpy_toio(map->virt + (to & win_mask(state->win_order)),
-                           from, len);
-
-               len -= this_len;
-               to += this_len;
-               from += this_len;
-       }
-}
-
-static const char * const part_probe_types[] = {
-       "cmdlinepart", "RedBoot", NULL };
-
-/**
- * gpio_flash_probe() - setup a mapping for a GPIO assisted flash
- *     @pdev: platform device
- *
- * The platform resource layout expected looks something like:
- * struct mtd_partition partitions[] = { ... };
- * struct physmap_flash_data flash_data = { ... };
- * static struct gpiod_lookup_table addr_flash_gpios = {
- *             .dev_id = "gpio-addr-flash.0",
- *             .table = {
- *             GPIO_LOOKUP_IDX("gpio.0", 15, "addr", 0, GPIO_ACTIVE_HIGH),
- *             GPIO_LOOKUP_IDX("gpio.0", 16, "addr", 1, GPIO_ACTIVE_HIGH),
- *             );
- * };
- * gpiod_add_lookup_table(&addr_flash_gpios);
- *
- * struct resource flash_resource[] = {
- *     {
- *             .name  = "cfi_probe",
- *             .start = 0x20000000,
- *             .end   = 0x201fffff,
- *             .flags = IORESOURCE_MEM,
- *     },
- * };
- * struct platform_device flash_device = {
- *     .name          = "gpio-addr-flash",
- *     .dev           = { .platform_data = &flash_data, },
- *     .num_resources = ARRAY_SIZE(flash_resource),
- *     .resource      = flash_resource,
- *     ...
- * };
- */
-static int gpio_flash_probe(struct platform_device *pdev)
-{
-       struct physmap_flash_data *pdata;
-       struct resource *memory;
-       struct async_state *state;
-
-       pdata = dev_get_platdata(&pdev->dev);
-       memory = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
-       if (!memory)
-               return -EINVAL;
-
-       state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL);
-       if (!state)
-               return -ENOMEM;
-
-       state->gpios = devm_gpiod_get_array(&pdev->dev, "addr", GPIOD_OUT_LOW);
-       if (IS_ERR(state->gpios))
-               return PTR_ERR(state->gpios);
-
-       state->win_order      = get_bitmask_order(resource_size(memory)) - 1;
-
-       state->map.name       = DRIVER_NAME;
-       state->map.read       = gf_read;
-       state->map.copy_from  = gf_copy_from;
-       state->map.write      = gf_write;
-       state->map.copy_to    = gf_copy_to;
-       state->map.bankwidth  = pdata->width;
-       state->map.size       = BIT(state->win_order + state->gpios->ndescs);
-       state->map.virt       = devm_ioremap_resource(&pdev->dev, memory);
-       if (IS_ERR(state->map.virt))
-               return PTR_ERR(state->map.virt);
-
-       state->map.phys       = NO_XIP;
-       state->map.map_priv_1 = (unsigned long)state;
-
-       platform_set_drvdata(pdev, state);
-
-       dev_notice(&pdev->dev, "probing %d-bit flash bus\n",
-                  state->map.bankwidth * 8);
-       state->mtd = do_map_probe(memory->name, &state->map);
-       if (!state->mtd)
-               return -ENXIO;
-       state->mtd->dev.parent = &pdev->dev;
-
-       mtd_device_parse_register(state->mtd, part_probe_types, NULL,
-                                 pdata->parts, pdata->nr_parts);
-
-       return 0;
-}
-
-static int gpio_flash_remove(struct platform_device *pdev)
-{
-       struct async_state *state = platform_get_drvdata(pdev);
-
-       mtd_device_unregister(state->mtd);
-       map_destroy(state->mtd);
-       return 0;
-}
-
-static struct platform_driver gpio_flash_driver = {
-       .probe          = gpio_flash_probe,
-       .remove         = gpio_flash_remove,
-       .driver         = {
-               .name   = DRIVER_NAME,
-       },
-};
-
-module_platform_driver(gpio_flash_driver);
-
-MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
-MODULE_DESCRIPTION("MTD map driver for flashes addressed physically and with gpios");
-MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/maps/latch-addr-flash.c b/drivers/mtd/maps/latch-addr-flash.c
deleted file mode 100644 (file)
index 51db24b..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Interface for NOR flash driver whose high address lines are latched
- *
- * Copyright © 2000 Nicolas Pitre <nico@cam.org>
- * Copyright © 2005-2008 Analog Devices Inc.
- * Copyright © 2008 MontaVista Software, Inc. <source@mvista.com>
- *
- * This file is licensed under the terms of the GNU General Public License
- * version 2. This program is licensed "as is" without any warranty of any
- * kind, whether express or implied.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-#include <linux/platform_device.h>
-#include <linux/mtd/latch-addr-flash.h>
-#include <linux/slab.h>
-
-#define DRIVER_NAME "latch-addr-flash"
-
-struct latch_addr_flash_info {
-       struct mtd_info         *mtd;
-       struct map_info         map;
-       struct resource         *res;
-
-       void                    (*set_window)(unsigned long offset, void *data);
-       void                    *data;
-
-       /* cache; could be found out of res */
-       unsigned long           win_mask;
-
-       spinlock_t              lock;
-};
-
-static map_word lf_read(struct map_info *map, unsigned long ofs)
-{
-       struct latch_addr_flash_info *info;
-       map_word datum;
-
-       info = (struct latch_addr_flash_info *)map->map_priv_1;
-
-       spin_lock(&info->lock);
-
-       info->set_window(ofs, info->data);
-       datum = inline_map_read(map, info->win_mask & ofs);
-
-       spin_unlock(&info->lock);
-
-       return datum;
-}
-
-static void lf_write(struct map_info *map, map_word datum, unsigned long ofs)
-{
-       struct latch_addr_flash_info *info;
-
-       info = (struct latch_addr_flash_info *)map->map_priv_1;
-
-       spin_lock(&info->lock);
-
-       info->set_window(ofs, info->data);
-       inline_map_write(map, datum, info->win_mask & ofs);
-
-       spin_unlock(&info->lock);
-}
-
-static void lf_copy_from(struct map_info *map, void *to,
-               unsigned long from, ssize_t len)
-{
-       struct latch_addr_flash_info *info =
-               (struct latch_addr_flash_info *) map->map_priv_1;
-       unsigned n;
-
-       while (len > 0) {
-               n = info->win_mask + 1 - (from & info->win_mask);
-               if (n > len)
-                       n = len;
-
-               spin_lock(&info->lock);
-
-               info->set_window(from, info->data);
-               memcpy_fromio(to, map->virt + (from & info->win_mask), n);
-
-               spin_unlock(&info->lock);
-
-               to += n;
-               from += n;
-               len -= n;
-       }
-}
-
-static char *rom_probe_types[] = { "cfi_probe", NULL };
-
-static int latch_addr_flash_remove(struct platform_device *dev)
-{
-       struct latch_addr_flash_info *info;
-       struct latch_addr_flash_data *latch_addr_data;
-
-       info = platform_get_drvdata(dev);
-       if (info == NULL)
-               return 0;
-
-       latch_addr_data = dev_get_platdata(&dev->dev);
-
-       if (info->mtd != NULL) {
-               mtd_device_unregister(info->mtd);
-               map_destroy(info->mtd);
-       }
-
-       if (info->map.virt != NULL)
-               iounmap(info->map.virt);
-
-       if (info->res != NULL)
-               release_mem_region(info->res->start, resource_size(info->res));
-
-       kfree(info);
-
-       if (latch_addr_data->done)
-               latch_addr_data->done(latch_addr_data->data);
-
-       return 0;
-}
-
-static int latch_addr_flash_probe(struct platform_device *dev)
-{
-       struct latch_addr_flash_data *latch_addr_data;
-       struct latch_addr_flash_info *info;
-       resource_size_t win_base = dev->resource->start;
-       resource_size_t win_size = resource_size(dev->resource);
-       char **probe_type;
-       int chipsel;
-       int err;
-
-       latch_addr_data = dev_get_platdata(&dev->dev);
-       if (latch_addr_data == NULL)
-               return -ENODEV;
-
-       pr_notice("latch-addr platform flash device: %#llx byte "
-                 "window at %#.8llx\n",
-                 (unsigned long long)win_size, (unsigned long long)win_base);
-
-       chipsel = dev->id;
-
-       if (latch_addr_data->init) {
-               err = latch_addr_data->init(latch_addr_data->data, chipsel);
-               if (err != 0)
-                       return err;
-       }
-
-       info = kzalloc(sizeof(struct latch_addr_flash_info), GFP_KERNEL);
-       if (info == NULL) {
-               err = -ENOMEM;
-               goto done;
-       }
-
-       platform_set_drvdata(dev, info);
-
-       info->res = request_mem_region(win_base, win_size, DRIVER_NAME);
-       if (info->res == NULL) {
-               dev_err(&dev->dev, "Could not reserve memory region\n");
-               err = -EBUSY;
-               goto free_info;
-       }
-
-       info->map.name          = DRIVER_NAME;
-       info->map.size          = latch_addr_data->size;
-       info->map.bankwidth     = latch_addr_data->width;
-
-       info->map.phys          = NO_XIP;
-       info->map.virt          = ioremap(win_base, win_size);
-       if (!info->map.virt) {
-               err = -ENOMEM;
-               goto free_res;
-       }
-
-       info->map.map_priv_1    = (unsigned long)info;
-
-       info->map.read          = lf_read;
-       info->map.copy_from     = lf_copy_from;
-       info->map.write         = lf_write;
-       info->set_window        = latch_addr_data->set_window;
-       info->data              = latch_addr_data->data;
-       info->win_mask          = win_size - 1;
-
-       spin_lock_init(&info->lock);
-
-       for (probe_type = rom_probe_types; !info->mtd && *probe_type;
-               probe_type++)
-               info->mtd = do_map_probe(*probe_type, &info->map);
-
-       if (info->mtd == NULL) {
-               dev_err(&dev->dev, "map_probe failed\n");
-               err = -ENODEV;
-               goto iounmap;
-       }
-       info->mtd->dev.parent = &dev->dev;
-
-       mtd_device_register(info->mtd, latch_addr_data->parts,
-                           latch_addr_data->nr_parts);
-       return 0;
-
-iounmap:
-       iounmap(info->map.virt);
-free_res:
-       release_mem_region(info->res->start, resource_size(info->res));
-free_info:
-       kfree(info);
-done:
-       if (latch_addr_data->done)
-               latch_addr_data->done(latch_addr_data->data);
-       return err;
-}
-
-static struct platform_driver latch_addr_flash_driver = {
-       .probe          = latch_addr_flash_probe,
-       .remove         = latch_addr_flash_remove,
-       .driver         = {
-               .name   = DRIVER_NAME,
-       },
-};
-
-module_platform_driver(latch_addr_flash_driver);
-
-MODULE_AUTHOR("David Griego <dgriego@mvista.com>");
-MODULE_DESCRIPTION("MTD map driver for flashes addressed physically with upper "
-               "address lines being set board specifically");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mtd/maps/physmap-core.c b/drivers/mtd/maps/physmap-core.c
new file mode 100644 (file)
index 0000000..d9a3e4b
--- /dev/null
@@ -0,0 +1,665 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Normal mappings of chips in physical memory
+ *
+ * Copyright (C) 2003 MontaVista Software Inc.
+ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
+ *
+ * 031022 - [jsun] add run-time configure and partition setup
+ *
+ * Device tree support:
+ *    Copyright (C) 2006 MontaVista Software Inc.
+ *    Author: Vitaly Wool <vwool@ru.mvista.com>
+ *
+ *    Revised to handle newer style flash binding by:
+ *    Copyright (C) 2007 David Gibson, IBM Corporation.
+ *
+ * GPIO address extension:
+ *    Handle the case where a flash device is mostly addressed using physical
+ *    line and supplemented by GPIOs.  This way you can hook up say a 8MiB flash
+ *    to a 2MiB memory range and use the GPIOs to select a particular range.
+ *
+ *    Copyright © 2000 Nicolas Pitre <nico@cam.org>
+ *    Copyright © 2005-2009 Analog Devices Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/physmap.h>
+#include <linux/mtd/concat.h>
+#include <linux/mtd/cfi_endian.h>
+#include <linux/io.h>
+#include <linux/of_device.h>
+#include <linux/gpio/consumer.h>
+
+#include "physmap-gemini.h"
+#include "physmap-versatile.h"
+
+struct physmap_flash_info {
+       unsigned int            nmaps;
+       struct mtd_info         **mtds;
+       struct mtd_info         *cmtd;
+       struct map_info         *maps;
+       spinlock_t              vpp_lock;
+       int                     vpp_refcnt;
+       const char              *probe_type;
+       const char * const      *part_types;
+       unsigned int            nparts;
+       const struct mtd_partition *parts;
+       struct gpio_descs       *gpios;
+       unsigned int            gpio_values;
+       unsigned int            win_order;
+};
+
+static int physmap_flash_remove(struct platform_device *dev)
+{
+       struct physmap_flash_info *info;
+       struct physmap_flash_data *physmap_data;
+       int i, err;
+
+       info = platform_get_drvdata(dev);
+       if (!info)
+               return 0;
+
+       if (info->cmtd) {
+               err = mtd_device_unregister(info->cmtd);
+               if (err)
+                       return err;
+
+               if (info->cmtd != info->mtds[0])
+                       mtd_concat_destroy(info->cmtd);
+       }
+
+       for (i = 0; i < info->nmaps; i++) {
+               if (info->mtds[i])
+                       map_destroy(info->mtds[i]);
+       }
+
+       physmap_data = dev_get_platdata(&dev->dev);
+       if (physmap_data && physmap_data->exit)
+               physmap_data->exit(dev);
+
+       return 0;
+}
+
+static void physmap_set_vpp(struct map_info *map, int state)
+{
+       struct platform_device *pdev;
+       struct physmap_flash_data *physmap_data;
+       struct physmap_flash_info *info;
+       unsigned long flags;
+
+       pdev = (struct platform_device *)map->map_priv_1;
+       physmap_data = dev_get_platdata(&pdev->dev);
+
+       if (!physmap_data->set_vpp)
+               return;
+
+       info = platform_get_drvdata(pdev);
+
+       spin_lock_irqsave(&info->vpp_lock, flags);
+       if (state) {
+               if (++info->vpp_refcnt == 1)    /* first nested 'on' */
+                       physmap_data->set_vpp(pdev, 1);
+       } else {
+               if (--info->vpp_refcnt == 0)    /* last nested 'off' */
+                       physmap_data->set_vpp(pdev, 0);
+       }
+       spin_unlock_irqrestore(&info->vpp_lock, flags);
+}
+
+#if IS_ENABLED(CONFIG_MTD_PHYSMAP_GPIO_ADDR)
+static void physmap_set_addr_gpios(struct physmap_flash_info *info,
+                                  unsigned long ofs)
+{
+       unsigned int i;
+
+       ofs >>= info->win_order;
+       if (info->gpio_values == ofs)
+               return;
+
+       for (i = 0; i < info->gpios->ndescs; i++) {
+               if ((BIT(i) & ofs) == (BIT(i) & info->gpio_values))
+                       continue;
+
+               gpiod_set_value(info->gpios->desc[i], !!(BIT(i) & ofs));
+       }
+}
+
+#define win_mask(order)                (BIT(order) - 1)
+
+static map_word physmap_addr_gpios_read(struct map_info *map,
+                                       unsigned long ofs)
+{
+       struct platform_device *pdev;
+       struct physmap_flash_info *info;
+       map_word mw;
+       u16 word;
+
+       pdev = (struct platform_device *)map->map_priv_1;
+       info = platform_get_drvdata(pdev);
+       physmap_set_addr_gpios(info, ofs);
+
+       word = readw(map->virt + (ofs & win_mask(info->win_order)));
+       mw.x[0] = word;
+       return mw;
+}
+
+static void physmap_addr_gpios_copy_from(struct map_info *map, void *buf,
+                                        unsigned long ofs, ssize_t len)
+{
+       struct platform_device *pdev;
+       struct physmap_flash_info *info;
+
+       pdev = (struct platform_device *)map->map_priv_1;
+       info = platform_get_drvdata(pdev);
+
+       while (len) {
+               unsigned int winofs = ofs & win_mask(info->win_order);
+               unsigned int chunklen = min_t(unsigned int, len,
+                                             BIT(info->win_order) - winofs);
+
+               physmap_set_addr_gpios(info, ofs);
+               memcpy_fromio(buf, map->virt + winofs, chunklen);
+               len -= chunklen;
+               buf += chunklen;
+               ofs += chunklen;
+       }
+}
+
+static void physmap_addr_gpios_write(struct map_info *map, map_word mw,
+                                    unsigned long ofs)
+{
+       struct platform_device *pdev;
+       struct physmap_flash_info *info;
+       u16 word;
+
+       pdev = (struct platform_device *)map->map_priv_1;
+       info = platform_get_drvdata(pdev);
+       physmap_set_addr_gpios(info, ofs);
+
+       word = mw.x[0];
+       writew(word, map->virt + (ofs & win_mask(info->win_order)));
+}
+
+static void physmap_addr_gpios_copy_to(struct map_info *map, unsigned long ofs,
+                                      const void *buf, ssize_t len)
+{
+       struct platform_device *pdev;
+       struct physmap_flash_info *info;
+
+       pdev = (struct platform_device *)map->map_priv_1;
+       info = platform_get_drvdata(pdev);
+
+       while (len) {
+               unsigned int winofs = ofs & win_mask(info->win_order);
+               unsigned int chunklen = min_t(unsigned int, len,
+                                             BIT(info->win_order) - winofs);
+
+               physmap_set_addr_gpios(info, ofs);
+               memcpy_toio(map->virt + winofs, buf, chunklen);
+               len -= chunklen;
+               buf += chunklen;
+               ofs += chunklen;
+       }
+}
+
+static int physmap_addr_gpios_map_init(struct map_info *map)
+{
+       map->phys = NO_XIP;
+       map->read = physmap_addr_gpios_read;
+       map->copy_from = physmap_addr_gpios_copy_from;
+       map->write = physmap_addr_gpios_write;
+       map->copy_to = physmap_addr_gpios_copy_to;
+
+       return 0;
+}
+#else
+static int physmap_addr_gpios_map_init(struct map_info *map)
+{
+       return -ENOTSUPP;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_MTD_PHYSMAP_OF)
+static const struct of_device_id of_flash_match[] = {
+       {
+               .compatible = "cfi-flash",
+               .data = "cfi_probe",
+       },
+       {
+               /*
+                * FIXME: JEDEC chips can't be safely and reliably
+                * probed, although the mtd code gets it right in
+                * practice most of the time.  We should use the
+                * vendor and device ids specified by the binding to
+                * bypass the heuristic probe code, but the mtd layer
+                * provides, at present, no interface for doing so
+                * :(.
+                */
+               .compatible = "jedec-flash",
+               .data = "jedec_probe",
+       },
+       {
+               .compatible = "mtd-ram",
+               .data = "map_ram",
+       },
+       {
+               .compatible = "mtd-rom",
+               .data = "map_rom",
+       },
+       {
+               .type = "rom",
+               .compatible = "direct-mapped"
+       },
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, of_flash_match);
+
+static const char * const of_default_part_probes[] = {
+       "cmdlinepart", "RedBoot", "ofpart", "ofoldpart", NULL
+};
+
+static const char * const *of_get_part_probes(struct platform_device *dev)
+{
+       struct device_node *dp = dev->dev.of_node;
+       const char **res;
+       int count;
+
+       count = of_property_count_strings(dp, "linux,part-probe");
+       if (count < 0)
+               return of_default_part_probes;
+
+       res = devm_kcalloc(&dev->dev, count + 1, sizeof(*res), GFP_KERNEL);
+       if (!res)
+               return NULL;
+
+       count = of_property_read_string_array(dp, "linux,part-probe", res,
+                                             count);
+       if (count < 0)
+               return NULL;
+
+       return res;
+}
+
+static const char *of_select_probe_type(struct platform_device *dev)
+{
+       struct device_node *dp = dev->dev.of_node;
+       const struct of_device_id *match;
+       const char *probe_type;
+
+       match = of_match_device(of_flash_match, &dev->dev);
+       probe_type = match->data;
+       if (probe_type)
+               return probe_type;
+
+       dev_warn(&dev->dev,
+                "Device tree uses obsolete \"direct-mapped\" flash binding\n");
+
+       of_property_read_string(dp, "probe-type", &probe_type);
+       if (!probe_type)
+               return NULL;
+
+       if (!strcmp(probe_type, "CFI")) {
+               probe_type = "cfi_probe";
+       } else if (!strcmp(probe_type, "JEDEC")) {
+               probe_type = "jedec_probe";
+       } else if (!strcmp(probe_type, "ROM")) {
+               probe_type = "map_rom";
+       } else {
+               dev_warn(&dev->dev,
+                        "obsolete_probe: don't know probe type '%s', mapping as rom\n",
+                        probe_type);
+               probe_type = "map_rom";
+       }
+
+       return probe_type;
+}
+
+static int physmap_flash_of_init(struct platform_device *dev)
+{
+       struct physmap_flash_info *info = platform_get_drvdata(dev);
+       struct device_node *dp = dev->dev.of_node;
+       const char *mtd_name = NULL;
+       int err, swap = 0;
+       bool map_indirect;
+       unsigned int i;
+       u32 bankwidth;
+
+       if (!dp)
+               return -EINVAL;
+
+       info->probe_type = of_select_probe_type(dev);
+
+       info->part_types = of_get_part_probes(dev);
+       if (!info->part_types)
+               return -ENOMEM;
+
+       of_property_read_string(dp, "linux,mtd-name", &mtd_name);
+
+       map_indirect = of_property_read_bool(dp, "no-unaligned-direct-access");
+
+       err = of_property_read_u32(dp, "bank-width", &bankwidth);
+       if (err) {
+               dev_err(&dev->dev, "Can't get bank width from device tree\n");
+               return err;
+       }
+
+       if (of_property_read_bool(dp, "big-endian"))
+               swap = CFI_BIG_ENDIAN;
+       else if (of_property_read_bool(dp, "little-endian"))
+               swap = CFI_LITTLE_ENDIAN;
+
+       for (i = 0; i < info->nmaps; i++) {
+               info->maps[i].name = mtd_name;
+               info->maps[i].swap = swap;
+               info->maps[i].bankwidth = bankwidth;
+               info->maps[i].device_node = dp;
+
+               err = of_flash_probe_gemini(dev, dp, &info->maps[i]);
+               if (err)
+                       return err;
+
+               err = of_flash_probe_versatile(dev, dp, &info->maps[i]);
+               if (err)
+                       return err;
+
+               /*
+                * On some platforms (e.g. MPC5200) a direct 1:1 mapping
+                * may cause problems with JFFS2 usage, as the local bus (LPB)
+                * doesn't support unaligned accesses as implemented in the
+                * JFFS2 code via memcpy(). By setting NO_XIP, the
+                * flash will not be exposed directly to the MTD users
+                * (e.g. JFFS2) any more.
+                */
+               if (map_indirect)
+                       info->maps[i].phys = NO_XIP;
+       }
+
+       return 0;
+}
+#else /* IS_ENABLED(CONFIG_MTD_PHYSMAP_OF) */
+#define of_flash_match NULL
+
+static int physmap_flash_of_init(struct platform_device *dev)
+{
+       return -ENOTSUPP;
+}
+#endif /* IS_ENABLED(CONFIG_MTD_PHYSMAP_OF) */
+
+static const char * const rom_probe_types[] = {
+       "cfi_probe", "jedec_probe", "qinfo_probe", "map_rom",
+};
+
+static const char * const part_probe_types[] = {
+       "cmdlinepart", "RedBoot", "afs", NULL
+};
+
+static int physmap_flash_pdata_init(struct platform_device *dev)
+{
+       struct physmap_flash_info *info = platform_get_drvdata(dev);
+       struct physmap_flash_data *physmap_data;
+       unsigned int i;
+       int err;
+
+       physmap_data = dev_get_platdata(&dev->dev);
+       if (!physmap_data)
+               return -EINVAL;
+
+       info->probe_type = physmap_data->probe_type;
+       info->part_types = physmap_data->part_probe_types ? : part_probe_types;
+       info->parts = physmap_data->parts;
+       info->nparts = physmap_data->nr_parts;
+
+       if (physmap_data->init) {
+               err = physmap_data->init(dev);
+               if (err)
+                       return err;
+       }
+
+       for (i = 0; i < info->nmaps; i++) {
+               info->maps[i].bankwidth = physmap_data->width;
+               info->maps[i].pfow_base = physmap_data->pfow_base;
+               info->maps[i].set_vpp = physmap_set_vpp;
+       }
+
+       return 0;
+}
+
+static int physmap_flash_probe(struct platform_device *dev)
+{
+       struct physmap_flash_info *info;
+       int err = 0;
+       int i;
+
+       if (!dev->dev.of_node && !dev_get_platdata(&dev->dev))
+               return -EINVAL;
+
+       info = devm_kzalloc(&dev->dev, sizeof(*info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       while (platform_get_resource(dev, IORESOURCE_MEM, info->nmaps))
+               info->nmaps++;
+
+       if (!info->nmaps)
+               return -ENODEV;
+
+       info->maps = devm_kzalloc(&dev->dev,
+                                 sizeof(*info->maps) * info->nmaps,
+                                 GFP_KERNEL);
+       if (!info->maps)
+               return -ENOMEM;
+
+       info->mtds = devm_kzalloc(&dev->dev,
+                                 sizeof(*info->mtds) * info->nmaps,
+                                 GFP_KERNEL);
+       if (!info->mtds)
+               return -ENOMEM;
+
+       platform_set_drvdata(dev, info);
+
+       info->gpios = devm_gpiod_get_array_optional(&dev->dev, "addr",
+                                                   GPIOD_OUT_LOW);
+       if (IS_ERR(info->gpios))
+               return PTR_ERR(info->gpios);
+
+       if (info->gpios && info->nmaps > 1) {
+               dev_err(&dev->dev, "addr-gpios only supported for nmaps == 1\n");
+               return -EINVAL;
+       }
+
+       if (dev->dev.of_node)
+               err = physmap_flash_of_init(dev);
+       else
+               err = physmap_flash_pdata_init(dev);
+
+       if (err)
+               return err;
+
+       for (i = 0; i < info->nmaps; i++) {
+               struct resource *res;
+
+               res = platform_get_resource(dev, IORESOURCE_MEM, i);
+               info->maps[i].virt = devm_ioremap_resource(&dev->dev, res);
+               if (IS_ERR(info->maps[i].virt)) {
+                       err = PTR_ERR(info->maps[i].virt);
+                       goto err_out;
+               }
+
+               dev_notice(&dev->dev, "physmap platform flash device: %pR\n",
+                          res);
+
+               info->maps[i].name = dev_name(&dev->dev);
+
+               if (!info->maps[i].phys)
+                       info->maps[i].phys = res->start;
+
+               info->win_order = get_bitmask_order(resource_size(res)) - 1;
+               info->maps[i].size = BIT(info->win_order +
+                                        (info->gpios ?
+                                         info->gpios->ndescs : 0));
+
+               info->maps[i].map_priv_1 = (unsigned long)dev;
+
+               if (info->gpios) {
+                       err = physmap_addr_gpios_map_init(&info->maps[i]);
+                       if (err)
+                               goto err_out;
+               }
+
+#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
+               /*
+                * Only use the simple_map implementation if map hooks are not
+                * implemented. Since map->read() is mandatory checking for its
+                * presence is enough.
+                */
+               if (!info->maps[i].read)
+                       simple_map_init(&info->maps[i]);
+#else
+               simple_map_init(&info->maps[i]);
+#endif
+
+               if (info->probe_type) {
+                       info->mtds[i] = do_map_probe(info->probe_type,
+                                                    &info->maps[i]);
+               } else {
+                       int j;
+
+                       for (j = 0; j < ARRAY_SIZE(rom_probe_types); j++) {
+                               info->mtds[i] = do_map_probe(rom_probe_types[j],
+                                                            &info->maps[i]);
+                               if (info->mtds[i])
+                                       break;
+                       }
+               }
+
+               if (!info->mtds[i]) {
+                       dev_err(&dev->dev, "map_probe failed\n");
+                       err = -ENXIO;
+                       goto err_out;
+               }
+               info->mtds[i]->dev.parent = &dev->dev;
+       }
+
+       if (info->nmaps == 1) {
+               info->cmtd = info->mtds[0];
+       } else {
+               /*
+                * We detected multiple devices. Concatenate them together.
+                */
+               info->cmtd = mtd_concat_create(info->mtds, info->nmaps,
+                                              dev_name(&dev->dev));
+               if (!info->cmtd)
+                       err = -ENXIO;
+       }
+       if (err)
+               goto err_out;
+
+       spin_lock_init(&info->vpp_lock);
+
+       mtd_set_of_node(info->cmtd, dev->dev.of_node);
+       err = mtd_device_parse_register(info->cmtd, info->part_types, NULL,
+                                       info->parts, info->nparts);
+       if (err)
+               goto err_out;
+
+       return 0;
+
+err_out:
+       physmap_flash_remove(dev);
+       return err;
+}
+
+#ifdef CONFIG_PM
+static void physmap_flash_shutdown(struct platform_device *dev)
+{
+       struct physmap_flash_info *info = platform_get_drvdata(dev);
+       int i;
+
+       for (i = 0; i < info->nmaps && info->mtds[i]; i++)
+               if (mtd_suspend(info->mtds[i]) == 0)
+                       mtd_resume(info->mtds[i]);
+}
+#else
+#define physmap_flash_shutdown NULL
+#endif
+
+static struct platform_driver physmap_flash_driver = {
+       .probe          = physmap_flash_probe,
+       .remove         = physmap_flash_remove,
+       .shutdown       = physmap_flash_shutdown,
+       .driver         = {
+               .name   = "physmap-flash",
+               .of_match_table = of_flash_match,
+       },
+};
+
+#ifdef CONFIG_MTD_PHYSMAP_COMPAT
+static struct physmap_flash_data physmap_flash_data = {
+       .width          = CONFIG_MTD_PHYSMAP_BANKWIDTH,
+};
+
+static struct resource physmap_flash_resource = {
+       .start          = CONFIG_MTD_PHYSMAP_START,
+       .end            = CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN - 1,
+       .flags          = IORESOURCE_MEM,
+};
+
+static struct platform_device physmap_flash = {
+       .name           = "physmap-flash",
+       .id             = 0,
+       .dev            = {
+               .platform_data  = &physmap_flash_data,
+       },
+       .num_resources  = 1,
+       .resource       = &physmap_flash_resource,
+};
+#endif
+
+static int __init physmap_init(void)
+{
+       int err;
+
+       err = platform_driver_register(&physmap_flash_driver);
+#ifdef CONFIG_MTD_PHYSMAP_COMPAT
+       if (err == 0) {
+               err = platform_device_register(&physmap_flash);
+               if (err)
+                       platform_driver_unregister(&physmap_flash_driver);
+       }
+#endif
+
+       return err;
+}
+
+static void __exit physmap_exit(void)
+{
+#ifdef CONFIG_MTD_PHYSMAP_COMPAT
+       platform_device_unregister(&physmap_flash);
+#endif
+       platform_driver_unregister(&physmap_flash_driver);
+}
+
+module_init(physmap_init);
+module_exit(physmap_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_AUTHOR("Vitaly Wool <vwool@ru.mvista.com>");
+MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
+MODULE_DESCRIPTION("Generic configurable MTD map driver");
+
+/* legacy platform drivers can't hotplug or coldplg */
+#ifndef CONFIG_MTD_PHYSMAP_COMPAT
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:physmap-flash");
+#endif
diff --git a/drivers/mtd/maps/physmap-gemini.c b/drivers/mtd/maps/physmap-gemini.c
new file mode 100644 (file)
index 0000000..60775b2
--- /dev/null
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cortina Systems Gemini OF physmap add-on
+ * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
+ *
+ * This SoC has an elaborate flash control register, so we need to
+ * detect and set it up when booting on this platform.
+ */
+#include <linux/export.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/xip.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/bitops.h>
+#include <linux/pinctrl/consumer.h>
+#include "physmap-gemini.h"
+
+/*
+ * The Flash-relevant parts of the global status register
+ * These would also be relevant for a NAND driver.
+ */
+#define GLOBAL_STATUS                  0x04
+#define FLASH_TYPE_MASK                        (0x3 << 24)
+#define FLASH_TYPE_NAND_2K             (0x3 << 24)
+#define FLASH_TYPE_NAND_512            (0x2 << 24)
+#define FLASH_TYPE_PARALLEL            (0x1 << 24)
+#define FLASH_TYPE_SERIAL              (0x0 << 24)
+/* if parallel */
+#define FLASH_WIDTH_16BIT              (1 << 23)       /* else 8 bit */
+/* if serial */
+#define FLASH_ATMEL                    (1 << 23)       /* else STM */
+
+#define FLASH_SIZE_MASK                        (0x3 << 21)
+#define NAND_256M                      (0x3 << 21)     /* and more */
+#define NAND_128M                      (0x2 << 21)
+#define NAND_64M                       (0x1 << 21)
+#define NAND_32M                       (0x0 << 21)
+#define ATMEL_16M                      (0x3 << 21)     /* and more */
+#define ATMEL_8M                       (0x2 << 21)
+#define ATMEL_4M_2M                    (0x1 << 21)
+#define ATMEL_1M                       (0x0 << 21)     /* and less */
+#define STM_32M                                (1 << 22)       /* and more */
+#define STM_16M                                (0 << 22)       /* and less */
+
+#define FLASH_PARALLEL_HIGH_PIN_CNT    (1 << 20)       /* else low pin cnt */
+
+static const struct of_device_id syscon_match[] = {
+       { .compatible = "cortina,gemini-syscon" },
+       { },
+};
+
+struct gemini_flash {
+       struct device *dev;
+       struct pinctrl *p;
+       struct pinctrl_state *enabled_state;
+       struct pinctrl_state *disabled_state;
+};
+
+/* Static local state */
+static struct gemini_flash *gf;
+
+static void gemini_flash_enable_pins(void)
+{
+       int ret;
+
+       if (IS_ERR(gf->enabled_state))
+               return;
+       ret = pinctrl_select_state(gf->p, gf->enabled_state);
+       if (ret)
+               dev_err(gf->dev, "failed to enable pins\n");
+}
+
+static void gemini_flash_disable_pins(void)
+{
+       int ret;
+
+       if (IS_ERR(gf->disabled_state))
+               return;
+       ret = pinctrl_select_state(gf->p, gf->disabled_state);
+       if (ret)
+               dev_err(gf->dev, "failed to disable pins\n");
+}
+
+static map_word __xipram gemini_flash_map_read(struct map_info *map,
+                                              unsigned long ofs)
+{
+       map_word __xipram ret;
+
+       gemini_flash_enable_pins();
+       ret = inline_map_read(map, ofs);
+       gemini_flash_disable_pins();
+
+       return ret;
+}
+
+static void __xipram gemini_flash_map_write(struct map_info *map,
+                                           const map_word datum,
+                                           unsigned long ofs)
+{
+       gemini_flash_enable_pins();
+       inline_map_write(map, datum, ofs);
+       gemini_flash_disable_pins();
+}
+
+static void __xipram gemini_flash_map_copy_from(struct map_info *map,
+                                               void *to, unsigned long from,
+                                               ssize_t len)
+{
+       gemini_flash_enable_pins();
+       inline_map_copy_from(map, to, from, len);
+       gemini_flash_disable_pins();
+}
+
+static void __xipram gemini_flash_map_copy_to(struct map_info *map,
+                                             unsigned long to,
+                                             const void *from, ssize_t len)
+{
+       gemini_flash_enable_pins();
+       inline_map_copy_to(map, to, from, len);
+       gemini_flash_disable_pins();
+}
+
+int of_flash_probe_gemini(struct platform_device *pdev,
+                         struct device_node *np,
+                         struct map_info *map)
+{
+       struct regmap *rmap;
+       struct device *dev = &pdev->dev;
+       u32 val;
+       int ret;
+
+       /* Multiplatform guard */
+       if (!of_device_is_compatible(np, "cortina,gemini-flash"))
+               return 0;
+
+       gf = devm_kzalloc(dev, sizeof(*gf), GFP_KERNEL);
+       if (!gf)
+               return -ENOMEM;
+       gf->dev = dev;
+
+       rmap = syscon_regmap_lookup_by_phandle(np, "syscon");
+       if (IS_ERR(rmap)) {
+               dev_err(dev, "no syscon\n");
+               return PTR_ERR(rmap);
+       }
+
+       ret = regmap_read(rmap, GLOBAL_STATUS, &val);
+       if (ret) {
+               dev_err(dev, "failed to read global status register\n");
+               return -ENODEV;
+       }
+       dev_dbg(dev, "global status reg: %08x\n", val);
+
+       /*
+        * It would be contradictory if a physmap flash was NOT parallel.
+        */
+       if ((val & FLASH_TYPE_MASK) != FLASH_TYPE_PARALLEL) {
+               dev_err(dev, "flash is not parallel\n");
+               return -ENODEV;
+       }
+
+       /*
+        * Complain if DT data and hardware definition is different.
+        */
+       if (val & FLASH_WIDTH_16BIT) {
+               if (map->bankwidth != 2)
+                       dev_warn(dev, "flash hardware say flash is 16 bit wide but DT says it is %d bits wide\n",
+                                map->bankwidth * 8);
+       } else {
+               if (map->bankwidth != 1)
+                       dev_warn(dev, "flash hardware say flash is 8 bit wide but DT says it is %d bits wide\n",
+                                map->bankwidth * 8);
+       }
+
+       gf->p = devm_pinctrl_get(dev);
+       if (IS_ERR(gf->p)) {
+               dev_err(dev, "no pinctrl handle\n");
+               ret = PTR_ERR(gf->p);
+               return ret;
+       }
+
+       gf->enabled_state = pinctrl_lookup_state(gf->p, "enabled");
+       if (IS_ERR(gf->enabled_state))
+               dev_err(dev, "no enabled pin control state\n");
+
+       gf->disabled_state = pinctrl_lookup_state(gf->p, "disabled");
+       if (IS_ERR(gf->enabled_state)) {
+               dev_err(dev, "no disabled pin control state\n");
+       } else {
+               ret = pinctrl_select_state(gf->p, gf->disabled_state);
+               if (ret)
+                       dev_err(gf->dev, "failed to disable pins\n");
+       }
+
+       map->read = gemini_flash_map_read;
+       map->write = gemini_flash_map_write;
+       map->copy_from = gemini_flash_map_copy_from;
+       map->copy_to = gemini_flash_map_copy_to;
+
+       dev_info(dev, "initialized Gemini-specific physmap control\n");
+
+       return 0;
+}
diff --git a/drivers/mtd/maps/physmap-gemini.h b/drivers/mtd/maps/physmap-gemini.h
new file mode 100644 (file)
index 0000000..72bd04c
--- /dev/null
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <linux/of.h>
+#include <linux/mtd/map.h>
+
+#ifdef CONFIG_MTD_PHYSMAP_GEMINI
+int of_flash_probe_gemini(struct platform_device *pdev,
+                         struct device_node *np,
+                         struct map_info *map);
+#else
+static inline
+int of_flash_probe_gemini(struct platform_device *pdev,
+                         struct device_node *np,
+                         struct map_info *map)
+{
+       return 0;
+}
+#endif
diff --git a/drivers/mtd/maps/physmap-versatile.c b/drivers/mtd/maps/physmap-versatile.c
new file mode 100644 (file)
index 0000000..0179d71
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * Versatile OF physmap driver add-on
+ *
+ * Copyright (c) 2016, Linaro Limited
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/mtd/map.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/bitops.h>
+#include "physmap-versatile.h"
+
+static struct regmap *syscon_regmap;
+
+enum versatile_flashprot {
+       INTEGRATOR_AP_FLASHPROT,
+       INTEGRATOR_CP_FLASHPROT,
+       VERSATILE_FLASHPROT,
+       REALVIEW_FLASHPROT,
+};
+
+static const struct of_device_id syscon_match[] = {
+       {
+               .compatible = "arm,integrator-ap-syscon",
+               .data = (void *)INTEGRATOR_AP_FLASHPROT,
+       },
+       {
+               .compatible = "arm,integrator-cp-syscon",
+               .data = (void *)INTEGRATOR_CP_FLASHPROT,
+       },
+       {
+               .compatible = "arm,core-module-versatile",
+               .data = (void *)VERSATILE_FLASHPROT,
+       },
+       {
+               .compatible = "arm,realview-eb-syscon",
+               .data = (void *)REALVIEW_FLASHPROT,
+       },
+       {
+               .compatible = "arm,realview-pb1176-syscon",
+               .data = (void *)REALVIEW_FLASHPROT,
+       },
+       {
+               .compatible = "arm,realview-pb11mp-syscon",
+               .data = (void *)REALVIEW_FLASHPROT,
+       },
+       {
+               .compatible = "arm,realview-pba8-syscon",
+               .data = (void *)REALVIEW_FLASHPROT,
+       },
+       {
+               .compatible = "arm,realview-pbx-syscon",
+               .data = (void *)REALVIEW_FLASHPROT,
+       },
+       {},
+};
+
+/*
+ * Flash protection handling for the Integrator/AP
+ */
+#define INTEGRATOR_SC_CTRLS_OFFSET     0x08
+#define INTEGRATOR_SC_CTRLC_OFFSET     0x0C
+#define INTEGRATOR_SC_CTRL_FLVPPEN     BIT(1)
+#define INTEGRATOR_SC_CTRL_FLWP                BIT(2)
+
+#define INTEGRATOR_EBI_CSR1_OFFSET     0x04
+/* The manual says bit 2, the code says bit 3, trust the code */
+#define INTEGRATOR_EBI_WRITE_ENABLE    BIT(3)
+#define INTEGRATOR_EBI_LOCK_OFFSET     0x20
+#define INTEGRATOR_EBI_LOCK_VAL                0xA05F
+
+static const struct of_device_id ebi_match[] = {
+       { .compatible = "arm,external-bus-interface"},
+       { },
+};
+
+static int ap_flash_init(struct platform_device *pdev)
+{
+       struct device_node *ebi;
+       void __iomem *ebi_base;
+       u32 val;
+       int ret;
+
+       /* Look up the EBI */
+       ebi = of_find_matching_node(NULL, ebi_match);
+       if (!ebi) {
+               return -ENODEV;
+       }
+       ebi_base = of_iomap(ebi, 0);
+       if (!ebi_base)
+               return -ENODEV;
+
+       /* Clear VPP and write protection bits */
+       ret = regmap_write(syscon_regmap,
+               INTEGRATOR_SC_CTRLC_OFFSET,
+               INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP);
+       if (ret)
+               dev_err(&pdev->dev, "error clearing Integrator VPP/WP\n");
+
+       /* Unlock the EBI */
+       writel(INTEGRATOR_EBI_LOCK_VAL, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET);
+
+       /* Enable write cycles on the EBI, CSR1 (flash) */
+       val = readl(ebi_base + INTEGRATOR_EBI_CSR1_OFFSET);
+       val |= INTEGRATOR_EBI_WRITE_ENABLE;
+       writel(val, ebi_base + INTEGRATOR_EBI_CSR1_OFFSET);
+
+       /* Lock the EBI again */
+       writel(0, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET);
+       iounmap(ebi_base);
+
+       return 0;
+}
+
+static void ap_flash_set_vpp(struct map_info *map, int on)
+{
+       int ret;
+
+       if (on) {
+               ret = regmap_write(syscon_regmap,
+                       INTEGRATOR_SC_CTRLS_OFFSET,
+                       INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP);
+               if (ret)
+                       pr_err("error enabling AP VPP\n");
+       } else {
+               ret = regmap_write(syscon_regmap,
+                       INTEGRATOR_SC_CTRLC_OFFSET,
+                       INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP);
+               if (ret)
+                       pr_err("error disabling AP VPP\n");
+       }
+}
+
+/*
+ * Flash protection handling for the Integrator/CP
+ */
+
+#define INTCP_FLASHPROG_OFFSET         0x04
+#define CINTEGRATOR_FLVPPEN            BIT(0)
+#define CINTEGRATOR_FLWREN             BIT(1)
+#define CINTEGRATOR_FLMASK             BIT(0)|BIT(1)
+
+static void cp_flash_set_vpp(struct map_info *map, int on)
+{
+       int ret;
+
+       if (on) {
+               ret = regmap_update_bits(syscon_regmap,
+                               INTCP_FLASHPROG_OFFSET,
+                               CINTEGRATOR_FLMASK,
+                               CINTEGRATOR_FLVPPEN | CINTEGRATOR_FLWREN);
+               if (ret)
+                       pr_err("error setting CP VPP\n");
+       } else {
+               ret = regmap_update_bits(syscon_regmap,
+                               INTCP_FLASHPROG_OFFSET,
+                               CINTEGRATOR_FLMASK,
+                               0);
+               if (ret)
+                       pr_err("error setting CP VPP\n");
+       }
+}
+
+/*
+ * Flash protection handling for the Versatiles and RealViews
+ */
+
+#define VERSATILE_SYS_FLASH_OFFSET            0x4C
+
+static void versatile_flash_set_vpp(struct map_info *map, int on)
+{
+       int ret;
+
+       ret = regmap_update_bits(syscon_regmap, VERSATILE_SYS_FLASH_OFFSET,
+                                0x01, !!on);
+       if (ret)
+               pr_err("error setting Versatile VPP\n");
+}
+
+int of_flash_probe_versatile(struct platform_device *pdev,
+                            struct device_node *np,
+                            struct map_info *map)
+{
+       struct device_node *sysnp;
+       const struct of_device_id *devid;
+       struct regmap *rmap;
+       static enum versatile_flashprot versatile_flashprot;
+       int ret;
+
+       /* Not all flash chips use this protection line */
+       if (!of_device_is_compatible(np, "arm,versatile-flash"))
+               return 0;
+
+       /* For first chip probed, look up the syscon regmap */
+       if (!syscon_regmap) {
+               sysnp = of_find_matching_node_and_match(NULL,
+                                                       syscon_match,
+                                                       &devid);
+               if (!sysnp)
+                       return -ENODEV;
+
+               versatile_flashprot = (enum versatile_flashprot)devid->data;
+               rmap = syscon_node_to_regmap(sysnp);
+               if (IS_ERR(rmap))
+                       return PTR_ERR(rmap);
+
+               syscon_regmap = rmap;
+       }
+
+       switch (versatile_flashprot) {
+       case INTEGRATOR_AP_FLASHPROT:
+               ret = ap_flash_init(pdev);
+               if (ret)
+                       return ret;
+               map->set_vpp = ap_flash_set_vpp;
+               dev_info(&pdev->dev, "Integrator/AP flash protection\n");
+               break;
+       case INTEGRATOR_CP_FLASHPROT:
+               map->set_vpp = cp_flash_set_vpp;
+               dev_info(&pdev->dev, "Integrator/CP flash protection\n");
+               break;
+       case VERSATILE_FLASHPROT:
+       case REALVIEW_FLASHPROT:
+               map->set_vpp = versatile_flash_set_vpp;
+               dev_info(&pdev->dev, "versatile/realview flash protection\n");
+               break;
+       default:
+               dev_info(&pdev->dev, "device marked as Versatile flash "
+                        "but no system controller was found\n");
+               break;
+       }
+
+       return 0;
+}
diff --git a/drivers/mtd/maps/physmap-versatile.h b/drivers/mtd/maps/physmap-versatile.h
new file mode 100644 (file)
index 0000000..9cf39d0
--- /dev/null
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <linux/of.h>
+#include <linux/mtd/map.h>
+
+#ifdef CONFIG_MTD_PHYSMAP_VERSATILE
+int of_flash_probe_versatile(struct platform_device *pdev,
+                            struct device_node *np,
+                            struct map_info *map);
+#else
+static inline
+int of_flash_probe_versatile(struct platform_device *pdev,
+                            struct device_node *np,
+                            struct map_info *map)
+{
+       return 0;
+}
+#endif
diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c
deleted file mode 100644 (file)
index cc2adbb..0000000
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Normal mappings of chips in physical memory
- *
- * Copyright (C) 2003 MontaVista Software Inc.
- * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
- *
- * 031022 - [jsun] add run-time configure and partition setup
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/physmap.h>
-#include <linux/mtd/concat.h>
-#include <linux/io.h>
-
-#define MAX_RESOURCES          4
-
-struct physmap_flash_info {
-       struct mtd_info         *mtd[MAX_RESOURCES];
-       struct mtd_info         *cmtd;
-       struct map_info         map[MAX_RESOURCES];
-       spinlock_t              vpp_lock;
-       int                     vpp_refcnt;
-};
-
-static int physmap_flash_remove(struct platform_device *dev)
-{
-       struct physmap_flash_info *info;
-       struct physmap_flash_data *physmap_data;
-       int i;
-
-       info = platform_get_drvdata(dev);
-       if (info == NULL)
-               return 0;
-
-       physmap_data = dev_get_platdata(&dev->dev);
-
-       if (info->cmtd) {
-               mtd_device_unregister(info->cmtd);
-               if (info->cmtd != info->mtd[0])
-                       mtd_concat_destroy(info->cmtd);
-       }
-
-       for (i = 0; i < MAX_RESOURCES; i++) {
-               if (info->mtd[i] != NULL)
-                       map_destroy(info->mtd[i]);
-       }
-
-       if (physmap_data->exit)
-               physmap_data->exit(dev);
-
-       return 0;
-}
-
-static void physmap_set_vpp(struct map_info *map, int state)
-{
-       struct platform_device *pdev;
-       struct physmap_flash_data *physmap_data;
-       struct physmap_flash_info *info;
-       unsigned long flags;
-
-       pdev = (struct platform_device *)map->map_priv_1;
-       physmap_data = dev_get_platdata(&pdev->dev);
-
-       if (!physmap_data->set_vpp)
-               return;
-
-       info = platform_get_drvdata(pdev);
-
-       spin_lock_irqsave(&info->vpp_lock, flags);
-       if (state) {
-               if (++info->vpp_refcnt == 1)    /* first nested 'on' */
-                       physmap_data->set_vpp(pdev, 1);
-       } else {
-               if (--info->vpp_refcnt == 0)    /* last nested 'off' */
-                       physmap_data->set_vpp(pdev, 0);
-       }
-       spin_unlock_irqrestore(&info->vpp_lock, flags);
-}
-
-static const char * const rom_probe_types[] = {
-       "cfi_probe", "jedec_probe", "qinfo_probe", "map_rom", NULL };
-
-static const char * const part_probe_types[] = {
-       "cmdlinepart", "RedBoot", "afs", NULL };
-
-static int physmap_flash_probe(struct platform_device *dev)
-{
-       struct physmap_flash_data *physmap_data;
-       struct physmap_flash_info *info;
-       const char * const *probe_type;
-       const char * const *part_types;
-       int err = 0;
-       int i;
-       int devices_found = 0;
-
-       physmap_data = dev_get_platdata(&dev->dev);
-       if (physmap_data == NULL)
-               return -ENODEV;
-
-       info = devm_kzalloc(&dev->dev, sizeof(struct physmap_flash_info),
-                           GFP_KERNEL);
-       if (info == NULL) {
-               err = -ENOMEM;
-               goto err_out;
-       }
-
-       if (physmap_data->init) {
-               err = physmap_data->init(dev);
-               if (err)
-                       goto err_out;
-       }
-
-       platform_set_drvdata(dev, info);
-
-       for (i = 0; i < dev->num_resources; i++) {
-               printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n",
-                      (unsigned long long)resource_size(&dev->resource[i]),
-                      (unsigned long long)dev->resource[i].start);
-
-               if (!devm_request_mem_region(&dev->dev,
-                       dev->resource[i].start,
-                       resource_size(&dev->resource[i]),
-                       dev_name(&dev->dev))) {
-                       dev_err(&dev->dev, "Could not reserve memory region\n");
-                       err = -ENOMEM;
-                       goto err_out;
-               }
-
-               info->map[i].name = dev_name(&dev->dev);
-               info->map[i].phys = dev->resource[i].start;
-               info->map[i].size = resource_size(&dev->resource[i]);
-               info->map[i].bankwidth = physmap_data->width;
-               info->map[i].set_vpp = physmap_set_vpp;
-               info->map[i].pfow_base = physmap_data->pfow_base;
-               info->map[i].map_priv_1 = (unsigned long)dev;
-
-               info->map[i].virt = devm_ioremap(&dev->dev, info->map[i].phys,
-                                                info->map[i].size);
-               if (info->map[i].virt == NULL) {
-                       dev_err(&dev->dev, "Failed to ioremap flash region\n");
-                       err = -EIO;
-                       goto err_out;
-               }
-
-               simple_map_init(&info->map[i]);
-
-               probe_type = rom_probe_types;
-               if (physmap_data->probe_type == NULL) {
-                       for (; info->mtd[i] == NULL && *probe_type != NULL; probe_type++)
-                               info->mtd[i] = do_map_probe(*probe_type, &info->map[i]);
-               } else
-                       info->mtd[i] = do_map_probe(physmap_data->probe_type, &info->map[i]);
-
-               if (info->mtd[i] == NULL) {
-                       dev_err(&dev->dev, "map_probe failed\n");
-                       err = -ENXIO;
-                       goto err_out;
-               } else {
-                       devices_found++;
-               }
-               info->mtd[i]->dev.parent = &dev->dev;
-       }
-
-       if (devices_found == 1) {
-               info->cmtd = info->mtd[0];
-       } else if (devices_found > 1) {
-               /*
-                * We detected multiple devices. Concatenate them together.
-                */
-               info->cmtd = mtd_concat_create(info->mtd, devices_found, dev_name(&dev->dev));
-               if (info->cmtd == NULL)
-                       err = -ENXIO;
-       }
-       if (err)
-               goto err_out;
-
-       spin_lock_init(&info->vpp_lock);
-
-       part_types = physmap_data->part_probe_types ? : part_probe_types;
-
-       mtd_device_parse_register(info->cmtd, part_types, NULL,
-                                 physmap_data->parts, physmap_data->nr_parts);
-       return 0;
-
-err_out:
-       physmap_flash_remove(dev);
-       return err;
-}
-
-#ifdef CONFIG_PM
-static void physmap_flash_shutdown(struct platform_device *dev)
-{
-       struct physmap_flash_info *info = platform_get_drvdata(dev);
-       int i;
-
-       for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++)
-               if (mtd_suspend(info->mtd[i]) == 0)
-                       mtd_resume(info->mtd[i]);
-}
-#else
-#define physmap_flash_shutdown NULL
-#endif
-
-static struct platform_driver physmap_flash_driver = {
-       .probe          = physmap_flash_probe,
-       .remove         = physmap_flash_remove,
-       .shutdown       = physmap_flash_shutdown,
-       .driver         = {
-               .name   = "physmap-flash",
-       },
-};
-
-
-#ifdef CONFIG_MTD_PHYSMAP_COMPAT
-static struct physmap_flash_data physmap_flash_data = {
-       .width          = CONFIG_MTD_PHYSMAP_BANKWIDTH,
-};
-
-static struct resource physmap_flash_resource = {
-       .start          = CONFIG_MTD_PHYSMAP_START,
-       .end            = CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN - 1,
-       .flags          = IORESOURCE_MEM,
-};
-
-static struct platform_device physmap_flash = {
-       .name           = "physmap-flash",
-       .id             = 0,
-       .dev            = {
-               .platform_data  = &physmap_flash_data,
-       },
-       .num_resources  = 1,
-       .resource       = &physmap_flash_resource,
-};
-#endif
-
-static int __init physmap_init(void)
-{
-       int err;
-
-       err = platform_driver_register(&physmap_flash_driver);
-#ifdef CONFIG_MTD_PHYSMAP_COMPAT
-       if (err == 0) {
-               err = platform_device_register(&physmap_flash);
-               if (err)
-                       platform_driver_unregister(&physmap_flash_driver);
-       }
-#endif
-
-       return err;
-}
-
-static void __exit physmap_exit(void)
-{
-#ifdef CONFIG_MTD_PHYSMAP_COMPAT
-       platform_device_unregister(&physmap_flash);
-#endif
-       platform_driver_unregister(&physmap_flash_driver);
-}
-
-module_init(physmap_init);
-module_exit(physmap_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
-MODULE_DESCRIPTION("Generic configurable MTD map driver");
-
-/* legacy platform drivers can't hotplug or coldplg */
-#ifndef CONFIG_MTD_PHYSMAP_COMPAT
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:physmap-flash");
-#endif
diff --git a/drivers/mtd/maps/physmap_of_core.c b/drivers/mtd/maps/physmap_of_core.c
deleted file mode 100644 (file)
index ece605d..0000000
+++ /dev/null
@@ -1,368 +0,0 @@
-/*
- * Flash mappings described by the OF (or flattened) device tree
- *
- * Copyright (C) 2006 MontaVista Software Inc.
- * Author: Vitaly Wool <vwool@ru.mvista.com>
- *
- * Revised to handle newer style flash binding by:
- *   Copyright (C) 2007 David Gibson, IBM Corporation.
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of  the GNU General  Public License as published by the
- * Free Software Foundation;  either version 2 of the  License, or (at your
- * option) any later version.
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/device.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/map.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/concat.h>
-#include <linux/mtd/cfi_endian.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_platform.h>
-#include <linux/slab.h>
-#include "physmap_of_gemini.h"
-#include "physmap_of_versatile.h"
-
-struct of_flash_list {
-       struct mtd_info *mtd;
-       struct map_info map;
-};
-
-struct of_flash {
-       struct mtd_info         *cmtd;
-       int list_size; /* number of elements in of_flash_list */
-       struct of_flash_list    list[0];
-};
-
-static int of_flash_remove(struct platform_device *dev)
-{
-       struct of_flash *info;
-       int i;
-
-       info = dev_get_drvdata(&dev->dev);
-       if (!info)
-               return 0;
-       dev_set_drvdata(&dev->dev, NULL);
-
-       if (info->cmtd) {
-               mtd_device_unregister(info->cmtd);
-               if (info->cmtd != info->list[0].mtd)
-                       mtd_concat_destroy(info->cmtd);
-       }
-
-       for (i = 0; i < info->list_size; i++)
-               if (info->list[i].mtd)
-                       map_destroy(info->list[i].mtd);
-
-       return 0;
-}
-
-static const char * const rom_probe_types[] = {
-       "cfi_probe", "jedec_probe", "map_rom" };
-
-/* Helper function to handle probing of the obsolete "direct-mapped"
- * compatible binding, which has an extra "probe-type" property
- * describing the type of flash probe necessary. */
-static struct mtd_info *obsolete_probe(struct platform_device *dev,
-                                      struct map_info *map)
-{
-       struct device_node *dp = dev->dev.of_node;
-       const char *of_probe;
-       struct mtd_info *mtd;
-       int i;
-
-       dev_warn(&dev->dev, "Device tree uses obsolete \"direct-mapped\" "
-                "flash binding\n");
-
-       of_probe = of_get_property(dp, "probe-type", NULL);
-       if (!of_probe) {
-               for (i = 0; i < ARRAY_SIZE(rom_probe_types); i++) {
-                       mtd = do_map_probe(rom_probe_types[i], map);
-                       if (mtd)
-                               return mtd;
-               }
-               return NULL;
-       } else if (strcmp(of_probe, "CFI") == 0) {
-               return do_map_probe("cfi_probe", map);
-       } else if (strcmp(of_probe, "JEDEC") == 0) {
-               return do_map_probe("jedec_probe", map);
-       } else {
-               if (strcmp(of_probe, "ROM") != 0)
-                       dev_warn(&dev->dev, "obsolete_probe: don't know probe "
-                                "type '%s', mapping as rom\n", of_probe);
-               return do_map_probe("map_rom", map);
-       }
-}
-
-/* When partitions are set we look for a linux,part-probe property which
-   specifies the list of partition probers to use. If none is given then the
-   default is use. These take precedence over other device tree
-   information. */
-static const char * const part_probe_types_def[] = {
-       "cmdlinepart", "RedBoot", "ofpart", "ofoldpart", NULL };
-
-static const char * const *of_get_probes(struct device_node *dp)
-{
-       const char **res;
-       int count;
-
-       count = of_property_count_strings(dp, "linux,part-probe");
-       if (count < 0)
-               return part_probe_types_def;
-
-       res = kcalloc(count + 1, sizeof(*res), GFP_KERNEL);
-       if (!res)
-               return NULL;
-
-       count = of_property_read_string_array(dp, "linux,part-probe", res,
-                                             count);
-       if (count < 0)
-               return NULL;
-
-       return res;
-}
-
-static void of_free_probes(const char * const *probes)
-{
-       if (probes != part_probe_types_def)
-               kfree(probes);
-}
-
-static const struct of_device_id of_flash_match[];
-static int of_flash_probe(struct platform_device *dev)
-{
-       const char * const *part_probe_types;
-       const struct of_device_id *match;
-       struct device_node *dp = dev->dev.of_node;
-       struct resource res;
-       struct of_flash *info;
-       const char *probe_type;
-       const __be32 *width;
-       int err;
-       int i;
-       int count;
-       const __be32 *p;
-       int reg_tuple_size;
-       struct mtd_info **mtd_list = NULL;
-       resource_size_t res_size;
-       bool map_indirect;
-       const char *mtd_name = NULL;
-
-       match = of_match_device(of_flash_match, &dev->dev);
-       if (!match)
-               return -EINVAL;
-       probe_type = match->data;
-
-       reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32);
-
-       of_property_read_string(dp, "linux,mtd-name", &mtd_name);
-
-       /*
-        * Get number of "reg" tuples. Scan for MTD devices on area's
-        * described by each "reg" region. This makes it possible (including
-        * the concat support) to support the Intel P30 48F4400 chips which
-        * consists internally of 2 non-identical NOR chips on one die.
-        */
-       p = of_get_property(dp, "reg", &count);
-       if (!p || count % reg_tuple_size != 0) {
-               dev_err(&dev->dev, "Malformed reg property on %pOF\n",
-                               dev->dev.of_node);
-               err = -EINVAL;
-               goto err_flash_remove;
-       }
-       count /= reg_tuple_size;
-
-       map_indirect = of_property_read_bool(dp, "no-unaligned-direct-access");
-
-       err = -ENOMEM;
-       info = devm_kzalloc(&dev->dev,
-                           sizeof(struct of_flash) +
-                           sizeof(struct of_flash_list) * count, GFP_KERNEL);
-       if (!info)
-               goto err_flash_remove;
-
-       dev_set_drvdata(&dev->dev, info);
-
-       mtd_list = kcalloc(count, sizeof(*mtd_list), GFP_KERNEL);
-       if (!mtd_list)
-               goto err_flash_remove;
-
-       for (i = 0; i < count; i++) {
-               err = -ENXIO;
-               if (of_address_to_resource(dp, i, &res)) {
-                       /*
-                        * Continue with next register tuple if this
-                        * one is not mappable
-                        */
-                       continue;
-               }
-
-               dev_dbg(&dev->dev, "of_flash device: %pR\n", &res);
-
-               err = -EBUSY;
-               res_size = resource_size(&res);
-               info->list[i].map.virt = devm_ioremap_resource(&dev->dev, &res);
-               if (IS_ERR(info->list[i].map.virt)) {
-                       err = PTR_ERR(info->list[i].map.virt);
-                       goto err_out;
-               }
-
-               err = -ENXIO;
-               width = of_get_property(dp, "bank-width", NULL);
-               if (!width) {
-                       dev_err(&dev->dev, "Can't get bank width from device"
-                               " tree\n");
-                       goto err_out;
-               }
-
-               info->list[i].map.name = mtd_name ?: dev_name(&dev->dev);
-               info->list[i].map.phys = res.start;
-               info->list[i].map.size = res_size;
-               info->list[i].map.bankwidth = be32_to_cpup(width);
-               info->list[i].map.device_node = dp;
-
-               if (of_property_read_bool(dp, "big-endian"))
-                       info->list[i].map.swap = CFI_BIG_ENDIAN;
-               else if (of_property_read_bool(dp, "little-endian"))
-                       info->list[i].map.swap = CFI_LITTLE_ENDIAN;
-
-               err = of_flash_probe_gemini(dev, dp, &info->list[i].map);
-               if (err)
-                       goto err_out;
-               err = of_flash_probe_versatile(dev, dp, &info->list[i].map);
-               if (err)
-                       goto err_out;
-
-               simple_map_init(&info->list[i].map);
-
-               /*
-                * On some platforms (e.g. MPC5200) a direct 1:1 mapping
-                * may cause problems with JFFS2 usage, as the local bus (LPB)
-                * doesn't support unaligned accesses as implemented in the
-                * JFFS2 code via memcpy(). By setting NO_XIP, the
-                * flash will not be exposed directly to the MTD users
-                * (e.g. JFFS2) any more.
-                */
-               if (map_indirect)
-                       info->list[i].map.phys = NO_XIP;
-
-               if (probe_type) {
-                       info->list[i].mtd = do_map_probe(probe_type,
-                                                        &info->list[i].map);
-               } else {
-                       info->list[i].mtd = obsolete_probe(dev,
-                                                          &info->list[i].map);
-               }
-
-               /* Fall back to mapping region as ROM */
-               if (!info->list[i].mtd) {
-                       dev_warn(&dev->dev,
-                               "do_map_probe() failed for type %s\n",
-                                probe_type);
-
-                       info->list[i].mtd = do_map_probe("map_rom",
-                                                        &info->list[i].map);
-               }
-               mtd_list[i] = info->list[i].mtd;
-
-               err = -ENXIO;
-               if (!info->list[i].mtd) {
-                       dev_err(&dev->dev, "do_map_probe() failed\n");
-                       goto err_out;
-               } else {
-                       info->list_size++;
-               }
-               info->list[i].mtd->dev.parent = &dev->dev;
-       }
-
-       err = 0;
-       info->cmtd = NULL;
-       if (info->list_size == 1) {
-               info->cmtd = info->list[0].mtd;
-       } else if (info->list_size > 1) {
-               /*
-                * We detected multiple devices. Concatenate them together.
-                */
-               info->cmtd = mtd_concat_create(mtd_list, info->list_size,
-                                              dev_name(&dev->dev));
-       }
-       if (info->cmtd == NULL)
-               err = -ENXIO;
-
-       if (err)
-               goto err_out;
-
-       info->cmtd->dev.parent = &dev->dev;
-       mtd_set_of_node(info->cmtd, dp);
-       part_probe_types = of_get_probes(dp);
-       if (!part_probe_types) {
-               err = -ENOMEM;
-               goto err_out;
-       }
-       mtd_device_parse_register(info->cmtd, part_probe_types, NULL,
-                       NULL, 0);
-       of_free_probes(part_probe_types);
-
-       kfree(mtd_list);
-
-       return 0;
-
-err_out:
-       kfree(mtd_list);
-err_flash_remove:
-       of_flash_remove(dev);
-
-       return err;
-}
-
-static const struct of_device_id of_flash_match[] = {
-       {
-               .compatible     = "cfi-flash",
-               .data           = (void *)"cfi_probe",
-       },
-       {
-               /* FIXME: JEDEC chips can't be safely and reliably
-                * probed, although the mtd code gets it right in
-                * practice most of the time.  We should use the
-                * vendor and device ids specified by the binding to
-                * bypass the heuristic probe code, but the mtd layer
-                * provides, at present, no interface for doing so
-                * :(. */
-               .compatible     = "jedec-flash",
-               .data           = (void *)"jedec_probe",
-       },
-       {
-               .compatible     = "mtd-ram",
-               .data           = (void *)"map_ram",
-       },
-       {
-               .compatible     = "mtd-rom",
-               .data           = (void *)"map_rom",
-       },
-       {
-               .type           = "rom",
-               .compatible     = "direct-mapped"
-       },
-       { },
-};
-MODULE_DEVICE_TABLE(of, of_flash_match);
-
-static struct platform_driver of_flash_driver = {
-       .driver = {
-               .name = "of-flash",
-               .of_match_table = of_flash_match,
-       },
-       .probe          = of_flash_probe,
-       .remove         = of_flash_remove,
-};
-
-module_platform_driver(of_flash_driver);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Vitaly Wool <vwool@ru.mvista.com>");
-MODULE_DESCRIPTION("Device tree based MTD map driver");
diff --git a/drivers/mtd/maps/physmap_of_gemini.c b/drivers/mtd/maps/physmap_of_gemini.c
deleted file mode 100644 (file)
index 9df62ca..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Cortina Systems Gemini OF physmap add-on
- * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
- *
- * This SoC has an elaborate flash control register, so we need to
- * detect and set it up when booting on this platform.
- */
-#include <linux/export.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/mtd/map.h>
-#include <linux/mfd/syscon.h>
-#include <linux/regmap.h>
-#include <linux/bitops.h>
-#include "physmap_of_gemini.h"
-
-/*
- * The Flash-relevant parts of the global status register
- * These would also be relevant for a NAND driver.
- */
-#define GLOBAL_STATUS                  0x04
-#define FLASH_TYPE_MASK                        (0x3 << 24)
-#define FLASH_TYPE_NAND_2K             (0x3 << 24)
-#define FLASH_TYPE_NAND_512            (0x2 << 24)
-#define FLASH_TYPE_PARALLEL            (0x1 << 24)
-#define FLASH_TYPE_SERIAL              (0x0 << 24)
-/* if parallel */
-#define FLASH_WIDTH_16BIT              (1 << 23)       /* else 8 bit */
-/* if serial */
-#define FLASH_ATMEL                    (1 << 23)       /* else STM */
-
-#define FLASH_SIZE_MASK                        (0x3 << 21)
-#define NAND_256M                      (0x3 << 21)     /* and more */
-#define NAND_128M                      (0x2 << 21)
-#define NAND_64M                       (0x1 << 21)
-#define NAND_32M                       (0x0 << 21)
-#define ATMEL_16M                      (0x3 << 21)     /* and more */
-#define ATMEL_8M                       (0x2 << 21)
-#define ATMEL_4M_2M                    (0x1 << 21)
-#define ATMEL_1M                       (0x0 << 21)     /* and less */
-#define STM_32M                                (1 << 22)       /* and more */
-#define STM_16M                                (0 << 22)       /* and less */
-
-#define FLASH_PARALLEL_HIGH_PIN_CNT    (1 << 20)       /* else low pin cnt */
-
-int of_flash_probe_gemini(struct platform_device *pdev,
-                         struct device_node *np,
-                         struct map_info *map)
-{
-       struct regmap *rmap;
-       struct device *dev = &pdev->dev;
-       u32 val;
-       int ret;
-
-       /* Multiplatform guard */
-       if (!of_device_is_compatible(np, "cortina,gemini-flash"))
-               return 0;
-
-       rmap = syscon_regmap_lookup_by_phandle(np, "syscon");
-       if (IS_ERR(rmap)) {
-               dev_err(dev, "no syscon\n");
-               return PTR_ERR(rmap);
-       }
-
-       ret = regmap_read(rmap, GLOBAL_STATUS, &val);
-       if (ret) {
-               dev_err(dev, "failed to read global status register\n");
-               return -ENODEV;
-       }
-       dev_dbg(dev, "global status reg: %08x\n", val);
-
-       /*
-        * It would be contradictory if a physmap flash was NOT parallel.
-        */
-       if ((val & FLASH_TYPE_MASK) != FLASH_TYPE_PARALLEL) {
-               dev_err(dev, "flash is not parallel\n");
-               return -ENODEV;
-       }
-
-       /*
-        * Complain if DT data and hardware definition is different.
-        */
-       if (val & FLASH_WIDTH_16BIT) {
-               if (map->bankwidth != 2)
-                       dev_warn(dev, "flash hardware say flash is 16 bit wide but DT says it is %d bits wide\n",
-                                map->bankwidth * 8);
-       } else {
-               if (map->bankwidth != 1)
-                       dev_warn(dev, "flash hardware say flash is 8 bit wide but DT says it is %d bits wide\n",
-                                map->bankwidth * 8);
-       }
-
-       dev_info(&pdev->dev, "initialized Gemini-specific physmap control\n");
-
-       return 0;
-}
diff --git a/drivers/mtd/maps/physmap_of_gemini.h b/drivers/mtd/maps/physmap_of_gemini.h
deleted file mode 100644 (file)
index 60e13a6..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#include <linux/of.h>
-#include <linux/mtd/map.h>
-
-#ifdef CONFIG_MTD_PHYSMAP_OF_GEMINI
-int of_flash_probe_gemini(struct platform_device *pdev,
-                         struct device_node *np,
-                         struct map_info *map);
-#else
-static inline
-int of_flash_probe_gemini(struct platform_device *pdev,
-                         struct device_node *np,
-                         struct map_info *map)
-{
-       return 0;
-}
-#endif
diff --git a/drivers/mtd/maps/physmap_of_versatile.c b/drivers/mtd/maps/physmap_of_versatile.c
deleted file mode 100644 (file)
index 03f2b6e..0000000
+++ /dev/null
@@ -1,254 +0,0 @@
-/*
- * Versatile OF physmap driver add-on
- *
- * Copyright (c) 2016, Linaro Limited
- * Author: Linus Walleij <linus.walleij@linaro.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-#include <linux/export.h>
-#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/mtd/map.h>
-#include <linux/mfd/syscon.h>
-#include <linux/regmap.h>
-#include <linux/bitops.h>
-#include "physmap_of_versatile.h"
-
-static struct regmap *syscon_regmap;
-
-enum versatile_flashprot {
-       INTEGRATOR_AP_FLASHPROT,
-       INTEGRATOR_CP_FLASHPROT,
-       VERSATILE_FLASHPROT,
-       REALVIEW_FLASHPROT,
-};
-
-static const struct of_device_id syscon_match[] = {
-       {
-               .compatible = "arm,integrator-ap-syscon",
-               .data = (void *)INTEGRATOR_AP_FLASHPROT,
-       },
-       {
-               .compatible = "arm,integrator-cp-syscon",
-               .data = (void *)INTEGRATOR_CP_FLASHPROT,
-       },
-       {
-               .compatible = "arm,core-module-versatile",
-               .data = (void *)VERSATILE_FLASHPROT,
-       },
-       {
-               .compatible = "arm,realview-eb-syscon",
-               .data = (void *)REALVIEW_FLASHPROT,
-       },
-       {
-               .compatible = "arm,realview-pb1176-syscon",
-               .data = (void *)REALVIEW_FLASHPROT,
-       },
-       {
-               .compatible = "arm,realview-pb11mp-syscon",
-               .data = (void *)REALVIEW_FLASHPROT,
-       },
-       {
-               .compatible = "arm,realview-pba8-syscon",
-               .data = (void *)REALVIEW_FLASHPROT,
-       },
-       {
-               .compatible = "arm,realview-pbx-syscon",
-               .data = (void *)REALVIEW_FLASHPROT,
-       },
-       {},
-};
-
-/*
- * Flash protection handling for the Integrator/AP
- */
-#define INTEGRATOR_SC_CTRLS_OFFSET     0x08
-#define INTEGRATOR_SC_CTRLC_OFFSET     0x0C
-#define INTEGRATOR_SC_CTRL_FLVPPEN     BIT(1)
-#define INTEGRATOR_SC_CTRL_FLWP                BIT(2)
-
-#define INTEGRATOR_EBI_CSR1_OFFSET     0x04
-/* The manual says bit 2, the code says bit 3, trust the code */
-#define INTEGRATOR_EBI_WRITE_ENABLE    BIT(3)
-#define INTEGRATOR_EBI_LOCK_OFFSET     0x20
-#define INTEGRATOR_EBI_LOCK_VAL                0xA05F
-
-static const struct of_device_id ebi_match[] = {
-       { .compatible = "arm,external-bus-interface"},
-       { },
-};
-
-static int ap_flash_init(struct platform_device *pdev)
-{
-       struct device_node *ebi;
-       void __iomem *ebi_base;
-       u32 val;
-       int ret;
-
-       /* Look up the EBI */
-       ebi = of_find_matching_node(NULL, ebi_match);
-       if (!ebi) {
-               return -ENODEV;
-       }
-       ebi_base = of_iomap(ebi, 0);
-       if (!ebi_base)
-               return -ENODEV;
-
-       /* Clear VPP and write protection bits */
-       ret = regmap_write(syscon_regmap,
-               INTEGRATOR_SC_CTRLC_OFFSET,
-               INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP);
-       if (ret)
-               dev_err(&pdev->dev, "error clearing Integrator VPP/WP\n");
-
-       /* Unlock the EBI */
-       writel(INTEGRATOR_EBI_LOCK_VAL, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET);
-
-       /* Enable write cycles on the EBI, CSR1 (flash) */
-       val = readl(ebi_base + INTEGRATOR_EBI_CSR1_OFFSET);
-       val |= INTEGRATOR_EBI_WRITE_ENABLE;
-       writel(val, ebi_base + INTEGRATOR_EBI_CSR1_OFFSET);
-
-       /* Lock the EBI again */
-       writel(0, ebi_base + INTEGRATOR_EBI_LOCK_OFFSET);
-       iounmap(ebi_base);
-
-       return 0;
-}
-
-static void ap_flash_set_vpp(struct map_info *map, int on)
-{
-       int ret;
-
-       if (on) {
-               ret = regmap_write(syscon_regmap,
-                       INTEGRATOR_SC_CTRLS_OFFSET,
-                       INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP);
-               if (ret)
-                       pr_err("error enabling AP VPP\n");
-       } else {
-               ret = regmap_write(syscon_regmap,
-                       INTEGRATOR_SC_CTRLC_OFFSET,
-                       INTEGRATOR_SC_CTRL_FLVPPEN | INTEGRATOR_SC_CTRL_FLWP);
-               if (ret)
-                       pr_err("error disabling AP VPP\n");
-       }
-}
-
-/*
- * Flash protection handling for the Integrator/CP
- */
-
-#define INTCP_FLASHPROG_OFFSET         0x04
-#define CINTEGRATOR_FLVPPEN            BIT(0)
-#define CINTEGRATOR_FLWREN             BIT(1)
-#define CINTEGRATOR_FLMASK             BIT(0)|BIT(1)
-
-static void cp_flash_set_vpp(struct map_info *map, int on)
-{
-       int ret;
-
-       if (on) {
-               ret = regmap_update_bits(syscon_regmap,
-                               INTCP_FLASHPROG_OFFSET,
-                               CINTEGRATOR_FLMASK,
-                               CINTEGRATOR_FLVPPEN | CINTEGRATOR_FLWREN);
-               if (ret)
-                       pr_err("error setting CP VPP\n");
-       } else {
-               ret = regmap_update_bits(syscon_regmap,
-                               INTCP_FLASHPROG_OFFSET,
-                               CINTEGRATOR_FLMASK,
-                               0);
-               if (ret)
-                       pr_err("error setting CP VPP\n");
-       }
-}
-
-/*
- * Flash protection handling for the Versatiles and RealViews
- */
-
-#define VERSATILE_SYS_FLASH_OFFSET            0x4C
-
-static void versatile_flash_set_vpp(struct map_info *map, int on)
-{
-       int ret;
-
-       ret = regmap_update_bits(syscon_regmap, VERSATILE_SYS_FLASH_OFFSET,
-                                0x01, !!on);
-       if (ret)
-               pr_err("error setting Versatile VPP\n");
-}
-
-int of_flash_probe_versatile(struct platform_device *pdev,
-                            struct device_node *np,
-                            struct map_info *map)
-{
-       struct device_node *sysnp;
-       const struct of_device_id *devid;
-       struct regmap *rmap;
-       static enum versatile_flashprot versatile_flashprot;
-       int ret;
-
-       /* Not all flash chips use this protection line */
-       if (!of_device_is_compatible(np, "arm,versatile-flash"))
-               return 0;
-
-       /* For first chip probed, look up the syscon regmap */
-       if (!syscon_regmap) {
-               sysnp = of_find_matching_node_and_match(NULL,
-                                                       syscon_match,
-                                                       &devid);
-               if (!sysnp)
-                       return -ENODEV;
-
-               versatile_flashprot = (enum versatile_flashprot)devid->data;
-               rmap = syscon_node_to_regmap(sysnp);
-               if (IS_ERR(rmap))
-                       return PTR_ERR(rmap);
-
-               syscon_regmap = rmap;
-       }
-
-       switch (versatile_flashprot) {
-       case INTEGRATOR_AP_FLASHPROT:
-               ret = ap_flash_init(pdev);
-               if (ret)
-                       return ret;
-               map->set_vpp = ap_flash_set_vpp;
-               dev_info(&pdev->dev, "Integrator/AP flash protection\n");
-               break;
-       case INTEGRATOR_CP_FLASHPROT:
-               map->set_vpp = cp_flash_set_vpp;
-               dev_info(&pdev->dev, "Integrator/CP flash protection\n");
-               break;
-       case VERSATILE_FLASHPROT:
-       case REALVIEW_FLASHPROT:
-               map->set_vpp = versatile_flash_set_vpp;
-               dev_info(&pdev->dev, "versatile/realview flash protection\n");
-               break;
-       default:
-               dev_info(&pdev->dev, "device marked as Versatile flash "
-                        "but no system controller was found\n");
-               break;
-       }
-
-       return 0;
-}
diff --git a/drivers/mtd/maps/physmap_of_versatile.h b/drivers/mtd/maps/physmap_of_versatile.h
deleted file mode 100644 (file)
index 0302502..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#include <linux/of.h>
-#include <linux/mtd/map.h>
-
-#ifdef CONFIG_MTD_PHYSMAP_OF_VERSATILE
-int of_flash_probe_versatile(struct platform_device *pdev,
-                            struct device_node *np,
-                            struct map_info *map);
-#else
-static inline
-int of_flash_probe_versatile(struct platform_device *pdev,
-                            struct device_node *np,
-                            struct map_info *map)
-{
-       return 0;
-}
-#endif
index a5b1933..b2d5ed1 100644 (file)
@@ -56,7 +56,7 @@ struct mtdblk_dev {
  */
 
 static int erase_write (struct mtd_info *mtd, unsigned long pos,
-                       int len, const char *buf)
+                       unsigned int len, const char *buf)
 {
        struct erase_info erase;
        size_t retlen;
index 97ac219..b6b9329 100644 (file)
@@ -665,6 +665,8 @@ static void mtd_set_dev_defaults(struct mtd_info *mtd)
        } else {
                pr_debug("mtd device won't show a device symlink in sysfs\n");
        }
+
+       mtd->orig_flags = mtd->flags;
 }
 
 /**
@@ -1136,13 +1138,13 @@ static int mtd_check_oob_ops(struct mtd_info *mtd, loff_t offs,
                return -EINVAL;
 
        if (ops->ooblen) {
-               u64 maxooblen;
+               size_t maxooblen;
 
                if (ops->ooboffs >= mtd_oobavail(mtd, ops))
                        return -EINVAL;
 
-               maxooblen = ((mtd_div_by_ws(mtd->size, mtd) -
-                             mtd_div_by_ws(offs, mtd)) *
+               maxooblen = ((size_t)(mtd_div_by_ws(mtd->size, mtd) -
+                                     mtd_div_by_ws(offs, mtd)) *
                             mtd_oobavail(mtd, ops)) - ops->ooboffs;
                if (ops->ooblen > maxooblen)
                        return -EINVAL;
index 99c460f..b6af41b 100644 (file)
@@ -61,6 +61,15 @@ static inline struct mtd_part *mtd_to_part(const struct mtd_info *mtd)
        return container_of(mtd, struct mtd_part, mtd);
 }
 
+static u64 part_absolute_offset(struct mtd_info *mtd)
+{
+       struct mtd_part *part = mtd_to_part(mtd);
+
+       if (!mtd_is_partition(mtd))
+               return 0;
+
+       return part_absolute_offset(part->parent) + part->offset;
+}
 
 /*
  * MTD methods which simply translate the effective address and pass through
@@ -346,7 +355,8 @@ static struct mtd_part *allocate_partition(struct mtd_info *parent,
 
        /* set up the MTD object for this partition */
        slave->mtd.type = parent->type;
-       slave->mtd.flags = parent->flags & ~part->mask_flags;
+       slave->mtd.flags = parent->orig_flags & ~part->mask_flags;
+       slave->mtd.orig_flags = slave->mtd.flags;
        slave->mtd.size = part->size;
        slave->mtd.writesize = parent->writesize;
        slave->mtd.writebufsize = parent->writebufsize;
@@ -513,7 +523,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *parent,
        if (!(slave->mtd.flags & MTD_NO_ERASE))
                wr_alignment = slave->mtd.erasesize;
 
-       tmp = slave->offset;
+       tmp = part_absolute_offset(parent) + slave->offset;
        remainder = do_div(tmp, wr_alignment);
        if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) {
                /* Doesn't start on a boundary of major erase size */
@@ -524,7 +534,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *parent,
                        part->name);
        }
 
-       tmp = slave->mtd.size;
+       tmp = part_absolute_offset(parent) + slave->mtd.size;
        remainder = do_div(tmp, wr_alignment);
        if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) {
                slave->mtd.flags &= ~MTD_WRITEABLE;
index d9dcb2d..d162d17 100644 (file)
@@ -1265,18 +1265,7 @@ static int mtdswap_show(struct seq_file *s, void *data)
 
        return 0;
 }
-
-static int mtdswap_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, mtdswap_show, inode->i_private);
-}
-
-static const struct file_operations mtdswap_fops = {
-       .open           = mtdswap_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(mtdswap);
 
 static int mtdswap_add_debugfs(struct mtdswap_dev *d)
 {
index c7efc31..1a55d3e 100644 (file)
@@ -70,7 +70,7 @@ config MTD_NAND_GPIO
 
 config MTD_NAND_AMS_DELTA
        tristate "NAND Flash device on Amstrad E3"
-       depends on MACH_AMS_DELTA
+       depends on MACH_AMS_DELTA || COMPILE_TEST
        default y
        help
          Support for NAND flash on Amstrad E3 (Delta).
index 5ba180a..8312182 100644 (file)
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  *  Copyright (C) 2006 Jonathan McDowell <noodles@earth.li>
  *
@@ -8,10 +9,6 @@
  *  Converted to platform driver by Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
  *  Partially stolen from plat_nand.c
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
  *  Overview:
  *   This is a device driver for the NAND flash device found on the
  *   Amstrad E3 (Delta).
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/rawnand.h>
 #include <linux/mtd/partitions.h>
-#include <linux/platform_data/gpio-omap.h>
-
-#include <asm/io.h>
-#include <asm/sizes.h>
-
-#include <mach/hardware.h>
+#include <linux/platform_device.h>
+#include <linux/sizes.h>
 
 /*
  * MTD structure for E3 (Delta)
  */
-
 struct ams_delta_nand {
+       struct nand_controller  base;
        struct nand_chip        nand_chip;
        struct gpio_desc        *gpiod_rdy;
        struct gpio_desc        *gpiod_nce;
@@ -44,7 +37,7 @@ struct ams_delta_nand {
        struct gpio_desc        *gpiod_nwe;
        struct gpio_desc        *gpiod_ale;
        struct gpio_desc        *gpiod_cle;
-       void __iomem            *io_base;
+       struct gpio_descs       *data_gpiods;
        bool                    data_in;
 };
 
@@ -73,99 +66,154 @@ static const struct mtd_partition partition_info[] = {
          .size         =  3 * SZ_256K },
 };
 
-static void ams_delta_io_write(struct ams_delta_nand *priv, u_char byte)
+static void ams_delta_write_commit(struct ams_delta_nand *priv)
 {
-       writew(byte, priv->nand_chip.legacy.IO_ADDR_W);
        gpiod_set_value(priv->gpiod_nwe, 0);
        ndelay(40);
        gpiod_set_value(priv->gpiod_nwe, 1);
 }
 
-static u_char ams_delta_io_read(struct ams_delta_nand *priv)
+static void ams_delta_io_write(struct ams_delta_nand *priv, u8 byte)
+{
+       struct gpio_descs *data_gpiods = priv->data_gpiods;
+       DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, };
+
+       gpiod_set_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
+                                 data_gpiods->info, values);
+
+       ams_delta_write_commit(priv);
+}
+
+static void ams_delta_dir_output(struct ams_delta_nand *priv, u8 byte)
+{
+       struct gpio_descs *data_gpiods = priv->data_gpiods;
+       DECLARE_BITMAP(values, BITS_PER_TYPE(byte)) = { byte, };
+       int i;
+
+       for (i = 0; i < data_gpiods->ndescs; i++)
+               gpiod_direction_output_raw(data_gpiods->desc[i],
+                                          test_bit(i, values));
+
+       ams_delta_write_commit(priv);
+
+       priv->data_in = false;
+}
+
+static u8 ams_delta_io_read(struct ams_delta_nand *priv)
 {
-       u_char res;
+       u8 res;
+       struct gpio_descs *data_gpiods = priv->data_gpiods;
+       DECLARE_BITMAP(values, BITS_PER_TYPE(res)) = { 0, };
 
        gpiod_set_value(priv->gpiod_nre, 0);
        ndelay(40);
-       res = readw(priv->nand_chip.legacy.IO_ADDR_R);
+
+       gpiod_get_raw_array_value(data_gpiods->ndescs, data_gpiods->desc,
+                                 data_gpiods->info, values);
+
        gpiod_set_value(priv->gpiod_nre, 1);
 
+       res = values[0];
        return res;
 }
 
-static void ams_delta_dir_input(struct ams_delta_nand *priv, bool in)
+static void ams_delta_dir_input(struct ams_delta_nand *priv)
 {
-       writew(in ? ~0 : 0, priv->io_base + OMAP_MPUIO_IO_CNTL);
-       priv->data_in = in;
+       struct gpio_descs *data_gpiods = priv->data_gpiods;
+       int i;
+
+       for (i = 0; i < data_gpiods->ndescs; i++)
+               gpiod_direction_input(data_gpiods->desc[i]);
+
+       priv->data_in = true;
 }
 
-static void ams_delta_write_buf(struct nand_chip *this, const u_char *buf,
+static void ams_delta_write_buf(struct ams_delta_nand *priv, const u8 *buf,
                                int len)
 {
-       struct ams_delta_nand *priv = nand_get_controller_data(this);
-       int i;
+       int i = 0;
 
-       if (priv->data_in)
-               ams_delta_dir_input(priv, false);
+       if (len > 0 && priv->data_in)
+               ams_delta_dir_output(priv, buf[i++]);
 
-       for (i = 0; i < len; i++)
-               ams_delta_io_write(priv, buf[i]);
+       while (i < len)
+               ams_delta_io_write(priv, buf[i++]);
 }
 
-static void ams_delta_read_buf(struct nand_chip *this, u_char *buf, int len)
+static void ams_delta_read_buf(struct ams_delta_nand *priv, u8 *buf, int len)
 {
-       struct ams_delta_nand *priv = nand_get_controller_data(this);
        int i;
 
        if (!priv->data_in)
-               ams_delta_dir_input(priv, true);
+               ams_delta_dir_input(priv);
 
        for (i = 0; i < len; i++)
                buf[i] = ams_delta_io_read(priv);
 }
 
-static u_char ams_delta_read_byte(struct nand_chip *this)
+static void ams_delta_ctrl_cs(struct ams_delta_nand *priv, bool assert)
 {
-       u_char res;
-
-       ams_delta_read_buf(this, &res, 1);
-
-       return res;
+       gpiod_set_value(priv->gpiod_nce, assert ? 0 : 1);
 }
 
-/*
- * Command control function
- *
- * ctrl:
- * NAND_NCE: bit 0 -> bit 2
- * NAND_CLE: bit 1 -> bit 7
- * NAND_ALE: bit 2 -> bit 6
- */
-static void ams_delta_hwcontrol(struct nand_chip *this, int cmd,
-                               unsigned int ctrl)
+static int ams_delta_exec_op(struct nand_chip *this,
+                            const struct nand_operation *op, bool check_only)
 {
        struct ams_delta_nand *priv = nand_get_controller_data(this);
-
-       if (ctrl & NAND_CTRL_CHANGE) {
-               gpiod_set_value(priv->gpiod_nce, !(ctrl & NAND_NCE));
-               gpiod_set_value(priv->gpiod_cle, !!(ctrl & NAND_CLE));
-               gpiod_set_value(priv->gpiod_ale, !!(ctrl & NAND_ALE));
+       const struct nand_op_instr *instr;
+       int ret = 0;
+
+       if (check_only)
+               return 0;
+
+       ams_delta_ctrl_cs(priv, 1);
+
+       for (instr = op->instrs; instr < op->instrs + op->ninstrs; instr++) {
+               switch (instr->type) {
+               case NAND_OP_CMD_INSTR:
+                       gpiod_set_value(priv->gpiod_cle, 1);
+                       ams_delta_write_buf(priv, &instr->ctx.cmd.opcode, 1);
+                       gpiod_set_value(priv->gpiod_cle, 0);
+                       break;
+
+               case NAND_OP_ADDR_INSTR:
+                       gpiod_set_value(priv->gpiod_ale, 1);
+                       ams_delta_write_buf(priv, instr->ctx.addr.addrs,
+                                           instr->ctx.addr.naddrs);
+                       gpiod_set_value(priv->gpiod_ale, 0);
+                       break;
+
+               case NAND_OP_DATA_IN_INSTR:
+                       ams_delta_read_buf(priv, instr->ctx.data.buf.in,
+                                          instr->ctx.data.len);
+                       break;
+
+               case NAND_OP_DATA_OUT_INSTR:
+                       ams_delta_write_buf(priv, instr->ctx.data.buf.out,
+                                           instr->ctx.data.len);
+                       break;
+
+               case NAND_OP_WAITRDY_INSTR:
+                       ret = priv->gpiod_rdy ?
+                             nand_gpio_waitrdy(this, priv->gpiod_rdy,
+                                               instr->ctx.waitrdy.timeout_ms) :
+                             nand_soft_waitrdy(this,
+                                               instr->ctx.waitrdy.timeout_ms);
+                       break;
+               }
+
+               if (ret)
+                       break;
        }
 
-       if (cmd != NAND_CMD_NONE) {
-               u_char byte = cmd;
+       ams_delta_ctrl_cs(priv, 0);
 
-               ams_delta_write_buf(this, &byte, 1);
-       }
-}
-
-static int ams_delta_nand_ready(struct nand_chip *this)
-{
-       struct ams_delta_nand *priv = nand_get_controller_data(this);
-
-       return gpiod_get_value(priv->gpiod_rdy);
+       return ret;
 }
 
+static const struct nand_controller_ops ams_delta_ops = {
+       .exec_op = ams_delta_exec_op,
+};
 
 /*
  * Main initialization routine
@@ -175,61 +223,29 @@ static int ams_delta_init(struct platform_device *pdev)
        struct ams_delta_nand *priv;
        struct nand_chip *this;
        struct mtd_info *mtd;
-       struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       void __iomem *io_base;
+       struct gpio_descs *data_gpiods;
        int err = 0;
 
-       if (!res)
-               return -ENXIO;
-
        /* Allocate memory for MTD device structure and private data */
        priv = devm_kzalloc(&pdev->dev, sizeof(struct ams_delta_nand),
                            GFP_KERNEL);
-       if (!priv) {
-               pr_warn("Unable to allocate E3 NAND MTD device structure.\n");
+       if (!priv)
                return -ENOMEM;
-       }
+
        this = &priv->nand_chip;
 
        mtd = nand_to_mtd(this);
        mtd->dev.parent = &pdev->dev;
 
-       /*
-        * Don't try to request the memory region from here,
-        * it should have been already requested from the
-        * gpio-omap driver and requesting it again would fail.
-        */
-
-       io_base = ioremap(res->start, resource_size(res));
-       if (io_base == NULL) {
-               dev_err(&pdev->dev, "ioremap failed\n");
-               err = -EIO;
-               goto out_free;
-       }
-
-       priv->io_base = io_base;
        nand_set_controller_data(this, priv);
 
-       /* Set address of NAND IO lines */
-       this->legacy.IO_ADDR_R = io_base + OMAP_MPUIO_INPUT_LATCH;
-       this->legacy.IO_ADDR_W = io_base + OMAP_MPUIO_OUTPUT;
-       this->legacy.read_byte = ams_delta_read_byte;
-       this->legacy.write_buf = ams_delta_write_buf;
-       this->legacy.read_buf = ams_delta_read_buf;
-       this->legacy.cmd_ctrl = ams_delta_hwcontrol;
-
        priv->gpiod_rdy = devm_gpiod_get_optional(&pdev->dev, "rdy", GPIOD_IN);
        if (IS_ERR(priv->gpiod_rdy)) {
                err = PTR_ERR(priv->gpiod_rdy);
                dev_warn(&pdev->dev, "RDY GPIO request failed (%d)\n", err);
-               goto out_mtd;
+               return err;
        }
 
-       if (priv->gpiod_rdy)
-               this->legacy.dev_ready = ams_delta_nand_ready;
-
-       /* 25 us command delay time */
-       this->legacy.chip_delay = 30;
        this->ecc.mode = NAND_ECC_SOFT;
        this->ecc.algo = NAND_ECC_HAMMING;
 
@@ -240,61 +256,75 @@ static int ams_delta_init(struct platform_device *pdev)
        if (IS_ERR(priv->gpiod_nwp)) {
                err = PTR_ERR(priv->gpiod_nwp);
                dev_err(&pdev->dev, "NWP GPIO request failed (%d)\n", err);
-               goto out_mtd;
+               return err;
        }
 
        priv->gpiod_nce = devm_gpiod_get(&pdev->dev, "nce", GPIOD_OUT_HIGH);
        if (IS_ERR(priv->gpiod_nce)) {
                err = PTR_ERR(priv->gpiod_nce);
                dev_err(&pdev->dev, "NCE GPIO request failed (%d)\n", err);
-               goto out_mtd;
+               return err;
        }
 
        priv->gpiod_nre = devm_gpiod_get(&pdev->dev, "nre", GPIOD_OUT_HIGH);
        if (IS_ERR(priv->gpiod_nre)) {
                err = PTR_ERR(priv->gpiod_nre);
                dev_err(&pdev->dev, "NRE GPIO request failed (%d)\n", err);
-               goto out_mtd;
+               return err;
        }
 
        priv->gpiod_nwe = devm_gpiod_get(&pdev->dev, "nwe", GPIOD_OUT_HIGH);
        if (IS_ERR(priv->gpiod_nwe)) {
                err = PTR_ERR(priv->gpiod_nwe);
                dev_err(&pdev->dev, "NWE GPIO request failed (%d)\n", err);
-               goto out_mtd;
+               return err;
        }
 
        priv->gpiod_ale = devm_gpiod_get(&pdev->dev, "ale", GPIOD_OUT_LOW);
        if (IS_ERR(priv->gpiod_ale)) {
                err = PTR_ERR(priv->gpiod_ale);
                dev_err(&pdev->dev, "ALE GPIO request failed (%d)\n", err);
-               goto out_mtd;
+               return err;
        }
 
        priv->gpiod_cle = devm_gpiod_get(&pdev->dev, "cle", GPIOD_OUT_LOW);
        if (IS_ERR(priv->gpiod_cle)) {
                err = PTR_ERR(priv->gpiod_cle);
                dev_err(&pdev->dev, "CLE GPIO request failed (%d)\n", err);
-               goto out_mtd;
+               return err;
        }
 
-       /* Initialize data port direction to a known state */
-       ams_delta_dir_input(priv, true);
+       /* Request array of data pins, initialize them as input */
+       data_gpiods = devm_gpiod_get_array(&pdev->dev, "data", GPIOD_IN);
+       if (IS_ERR(data_gpiods)) {
+               err = PTR_ERR(data_gpiods);
+               dev_err(&pdev->dev, "data GPIO request failed: %d\n", err);
+               return err;
+       }
+       priv->data_gpiods = data_gpiods;
+       priv->data_in = true;
+
+       /* Initialize the NAND controller object embedded in ams_delta_nand. */
+       priv->base.ops = &ams_delta_ops;
+       nand_controller_init(&priv->base);
+       this->controller = &priv->base;
 
        /* Scan to find existence of the device */
        err = nand_scan(this, 1);
        if (err)
-               goto out_mtd;
+               return err;
 
        /* Register the partitions */
-       mtd_device_register(mtd, partition_info, ARRAY_SIZE(partition_info));
+       err = mtd_device_register(mtd, partition_info,
+                                 ARRAY_SIZE(partition_info));
+       if (err)
+               goto err_nand_cleanup;
 
-       goto out;
+       return 0;
+
+err_nand_cleanup:
+       nand_cleanup(this);
 
- out_mtd:
-       iounmap(io_base);
-out_free:
- out:
        return err;
 }
 
@@ -305,13 +335,10 @@ static int ams_delta_cleanup(struct platform_device *pdev)
 {
        struct ams_delta_nand *priv = platform_get_drvdata(pdev);
        struct mtd_info *mtd = nand_to_mtd(&priv->nand_chip);
-       void __iomem *io_base = priv->io_base;
 
-       /* Release resources, unregister device */
+       /* Unregister device */
        nand_release(mtd_to_nand(mtd));
 
-       iounmap(io_base);
-
        return 0;
 }
 
@@ -325,6 +352,6 @@ static struct platform_driver ams_delta_nand_driver = {
 
 module_platform_driver(ams_delta_nand_driver);
 
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>");
 MODULE_DESCRIPTION("Glue layer for NAND flash on Amstrad E3 (Delta)");
index ad72049..5781fcf 100644 (file)
@@ -1477,10 +1477,10 @@ static void atmel_nand_init(struct atmel_nand_controller *nc,
        chip->legacy.write_byte = atmel_nand_write_byte;
        chip->legacy.read_buf = atmel_nand_read_buf;
        chip->legacy.write_buf = atmel_nand_write_buf;
-       chip->select_chip = atmel_nand_select_chip;
+       chip->legacy.select_chip = atmel_nand_select_chip;
 
-       if (nc->mck && nc->caps->ops->setup_data_interface)
-               chip->setup_data_interface = atmel_nand_setup_data_interface;
+       if (!nc->mck || !nc->caps->ops->setup_data_interface)
+               chip->options |= NAND_KEEP_TIMINGS;
 
        /* Some NANDs require a longer delay than the default one (20us). */
        chip->legacy.chip_delay = 40;
@@ -1525,7 +1525,7 @@ static void atmel_hsmc_nand_init(struct atmel_nand_controller *nc,
 
        /* Overload some methods for the HSMC controller. */
        chip->legacy.cmd_ctrl = atmel_hsmc_nand_cmd_ctrl;
-       chip->select_chip = atmel_hsmc_nand_select_chip;
+       chip->legacy.select_chip = atmel_hsmc_nand_select_chip;
 }
 
 static int atmel_nand_controller_remove_nand(struct atmel_nand *nand)
@@ -1908,6 +1908,7 @@ static int atmel_nand_attach_chip(struct nand_chip *chip)
 
 static const struct nand_controller_ops atmel_nand_controller_ops = {
        .attach_chip = atmel_nand_attach_chip,
+       .setup_data_interface = atmel_nand_setup_data_interface,
 };
 
 static int atmel_nand_controller_init(struct atmel_nand_controller *nc,
index 9731c1c..a963002 100644 (file)
@@ -430,7 +430,7 @@ static int au1550nd_probe(struct platform_device *pdev)
        ctx->cs = cs;
 
        this->legacy.dev_ready = au1550_device_ready;
-       this->select_chip = au1550_select_chip;
+       this->legacy.select_chip = au1550_select_chip;
        this->legacy.cmdfunc = au1550_command;
 
        /* 30 us command delay time */
index 9095a79..a37cbfe 100644 (file)
@@ -383,7 +383,7 @@ int bcm47xxnflash_ops_bcm4706_init(struct bcm47xxnflash *b47n)
        u8 tbits, col_bits, col_size, row_bits, row_bsize;
        u32 val;
 
-       b47n->nand_chip.select_chip = bcm47xxnflash_ops_bcm4706_select_chip;
+       nand_chip->legacy.select_chip = bcm47xxnflash_ops_bcm4706_select_chip;
        nand_chip->legacy.cmd_ctrl = bcm47xxnflash_ops_bcm4706_cmd_ctrl;
        nand_chip->legacy.dev_ready = bcm47xxnflash_ops_bcm4706_dev_ready;
        b47n->nand_chip.legacy.cmdfunc = bcm47xxnflash_ops_bcm4706_cmdfunc;
index c1a7459..b1c0cd6 100644 (file)
@@ -708,7 +708,7 @@ static int cafe_nand_probe(struct pci_dev *pdev,
        cafe->nand.legacy.read_byte = cafe_read_byte;
        cafe->nand.legacy.read_buf = cafe_read_buf;
        cafe->nand.legacy.write_buf = cafe_write_buf;
-       cafe->nand.select_chip = cafe_select_chip;
+       cafe->nand.legacy.select_chip = cafe_select_chip;
        cafe->nand.legacy.set_features = nand_get_set_features_notsupp;
        cafe->nand.legacy.get_features = nand_get_set_features_notsupp;
 
@@ -780,7 +780,7 @@ static int cafe_nand_probe(struct pci_dev *pdev,
        cafe->usedma = 0;
 
        /* Scan to find existence of the device */
-       cafe->nand.dummy_controller.ops = &cafe_nand_controller_ops;
+       cafe->nand.legacy.dummy_controller.ops = &cafe_nand_controller_ops;
        err = nand_scan(&cafe->nand, 2);
        if (err)
                goto out_irq;
index 80f228d..27bafa5 100644 (file)
@@ -762,7 +762,7 @@ static int nand_davinci_probe(struct platform_device *pdev)
        info->chip.legacy.IO_ADDR_R     = vaddr;
        info->chip.legacy.IO_ADDR_W     = vaddr;
        info->chip.legacy.chip_delay    = 0;
-       info->chip.select_chip  = nand_davinci_select_chip;
+       info->chip.legacy.select_chip   = nand_davinci_select_chip;
 
        /* options such as NAND_BBT_USE_FLASH */
        info->chip.bbt_options  = pdata->bbt_options;
@@ -801,7 +801,7 @@ static int nand_davinci_probe(struct platform_device *pdev)
        spin_unlock_irq(&davinci_nand_lock);
 
        /* Scan to find existence of the device(s) */
-       info->chip.dummy_controller.ops = &davinci_nand_controller_ops;
+       info->chip.legacy.dummy_controller.ops = &davinci_nand_controller_ops;
        ret = nand_scan(&info->chip, pdata->mask_chipsel ? 2 : 1);
        if (ret < 0) {
                dev_dbg(&pdev->dev, "no NAND chip(s) found\n");
index 830ea24..eebac35 100644 (file)
@@ -204,18 +204,6 @@ static uint32_t denali_wait_for_irq(struct denali_nand_info *denali,
        return denali->irq_status;
 }
 
-static uint32_t denali_check_irq(struct denali_nand_info *denali)
-{
-       unsigned long flags;
-       uint32_t irq_status;
-
-       spin_lock_irqsave(&denali->irq_lock, flags);
-       irq_status = denali->irq_status;
-       spin_unlock_irqrestore(&denali->irq_lock, flags);
-
-       return irq_status;
-}
-
 static void denali_read_buf(struct nand_chip *chip, uint8_t *buf, int len)
 {
        struct mtd_info *mtd = nand_to_mtd(chip);
@@ -288,8 +276,7 @@ static void denali_cmd_ctrl(struct nand_chip *chip, int dat, unsigned int ctrl)
                return;
 
        /*
-        * Some commands are followed by chip->legacy.dev_ready or
-        * chip->legacy.waitfunc.
+        * Some commands are followed by chip->legacy.waitfunc.
         * irq_status must be cleared here to catch the R/B# interrupt later.
         */
        if (ctrl & NAND_CTRL_CHANGE)
@@ -298,13 +285,6 @@ static void denali_cmd_ctrl(struct nand_chip *chip, int dat, unsigned int ctrl)
        denali->host_write(denali, DENALI_BANK(denali) | type, dat);
 }
 
-static int denali_dev_ready(struct nand_chip *chip)
-{
-       struct denali_nand_info *denali = mtd_to_denali(nand_to_mtd(chip));
-
-       return !!(denali_check_irq(denali) & INTR__INT_ACT);
-}
-
 static int denali_check_erased_page(struct mtd_info *mtd,
                                    struct nand_chip *chip, uint8_t *buf,
                                    unsigned long uncor_ecc_flags,
@@ -1065,29 +1045,6 @@ static int denali_setup_data_interface(struct nand_chip *chip, int chipnr,
        return 0;
 }
 
-static void denali_reset_banks(struct denali_nand_info *denali)
-{
-       u32 irq_status;
-       int i;
-
-       for (i = 0; i < denali->max_banks; i++) {
-               denali->active_bank = i;
-
-               denali_reset_irq(denali);
-
-               iowrite32(DEVICE_RESET__BANK(i),
-                         denali->reg + DEVICE_RESET);
-
-               irq_status = denali_wait_for_irq(denali,
-                       INTR__RST_COMP | INTR__INT_ACT | INTR__TIME_OUT);
-               if (!(irq_status & INTR__INT_ACT))
-                       break;
-       }
-
-       dev_dbg(denali->dev, "%d chips connected\n", i);
-       denali->max_banks = i;
-}
-
 static void denali_hw_init(struct denali_nand_info *denali)
 {
        /*
@@ -1316,6 +1273,7 @@ static void denali_detach_chip(struct nand_chip *chip)
 static const struct nand_controller_ops denali_controller_ops = {
        .attach_chip = denali_attach_chip,
        .detach_chip = denali_detach_chip,
+       .setup_data_interface = denali_setup_data_interface,
 };
 
 int denali_init(struct denali_nand_info *denali)
@@ -1341,12 +1299,6 @@ int denali_init(struct denali_nand_info *denali)
        }
 
        denali_enable_irq(denali);
-       denali_reset_banks(denali);
-       if (!denali->max_banks) {
-               /* Error out earlier if no chip is found for some reasons. */
-               ret = -ENODEV;
-               goto disable_irq;
-       }
 
        denali->active_bank = DENALI_INVALID_BANK;
 
@@ -1355,11 +1307,10 @@ int denali_init(struct denali_nand_info *denali)
        if (!mtd->name)
                mtd->name = "denali-nand";
 
-       chip->select_chip = denali_select_chip;
+       chip->legacy.select_chip = denali_select_chip;
        chip->legacy.read_byte = denali_read_byte;
        chip->legacy.write_byte = denali_write_byte;
        chip->legacy.cmd_ctrl = denali_cmd_ctrl;
-       chip->legacy.dev_ready = denali_dev_ready;
        chip->legacy.waitfunc = denali_waitfunc;
 
        if (features & FEATURES__INDEX_ADDR) {
@@ -1372,9 +1323,9 @@ int denali_init(struct denali_nand_info *denali)
 
        /* clk rate info is needed for setup_data_interface */
        if (denali->clk_rate && denali->clk_x_rate)
-               chip->setup_data_interface = denali_setup_data_interface;
+               chip->options |= NAND_KEEP_TIMINGS;
 
-       chip->dummy_controller.ops = &denali_controller_ops;
+       chip->legacy.dummy_controller.ops = &denali_controller_ops;
        ret = nand_scan(chip, denali->max_banks);
        if (ret)
                goto disable_irq;
index 57a5498..25c0060 100644 (file)
@@ -7,7 +7,7 @@
 #ifndef __DENALI_H__
 #define __DENALI_H__
 
-#include <linux/bitops.h>
+#include <linux/bits.h>
 #include <linux/completion.h>
 #include <linux/mtd/rawnand.h>
 #include <linux/spinlock_types.h>
index 3a4c373..53f57e0 100644 (file)
@@ -1390,7 +1390,7 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd)
        this->legacy.read_buf = doc2001plus_readbuf;
        doc->late_init = inftl_scan_bbt;
        this->legacy.cmd_ctrl = NULL;
-       this->select_chip = doc2001plus_select_chip;
+       this->legacy.select_chip = doc2001plus_select_chip;
        this->legacy.cmdfunc = doc2001plus_command;
        this->ecc.hwctl = doc2001plus_enable_hwecc;
 
@@ -1568,7 +1568,7 @@ static int __init doc_probe(unsigned long physadr)
        mtd_set_ooblayout(mtd, &doc200x_ooblayout_ops);
 
        nand_set_controller_data(nand, doc);
-       nand->select_chip       = doc200x_select_chip;
+       nand->legacy.select_chip        = doc200x_select_chip;
        nand->legacy.cmd_ctrl           = doc200x_hwcontrol;
        nand->legacy.dev_ready  = doc200x_dev_ready;
        nand->legacy.waitfunc   = doc200x_wait;
index d6ed697..70f0d2b 100644 (file)
@@ -779,7 +779,7 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
        chip->legacy.read_byte = fsl_elbc_read_byte;
        chip->legacy.write_buf = fsl_elbc_write_buf;
        chip->legacy.read_buf = fsl_elbc_read_buf;
-       chip->select_chip = fsl_elbc_select_chip;
+       chip->legacy.select_chip = fsl_elbc_select_chip;
        chip->legacy.cmdfunc = fsl_elbc_cmdfunc;
        chip->legacy.waitfunc = fsl_elbc_wait;
        chip->legacy.set_features = nand_get_set_features_notsupp;
index 6f4afc4..e65d274 100644 (file)
@@ -864,7 +864,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv)
 
        chip->legacy.write_buf = fsl_ifc_write_buf;
        chip->legacy.read_buf = fsl_ifc_read_buf;
-       chip->select_chip = fsl_ifc_select_chip;
+       chip->legacy.select_chip = fsl_ifc_select_chip;
        chip->legacy.cmdfunc = fsl_ifc_cmdfunc;
        chip->legacy.waitfunc = fsl_ifc_wait;
        chip->legacy.set_features = nand_get_set_features_notsupp;
index 673c5a0..5ccc28e 100644 (file)
@@ -170,7 +170,7 @@ static int fun_chip_init(struct fsl_upm_nand *fun,
        fun->chip.ecc.mode = NAND_ECC_SOFT;
        fun->chip.ecc.algo = NAND_ECC_HAMMING;
        if (fun->mchip_count > 1)
-               fun->chip.select_chip = fun_select_chip;
+               fun->chip.legacy.select_chip = fun_select_chip;
 
        if (fun->rnb_gpio[0] >= 0)
                fun->chip.legacy.dev_ready = fun_chip_ready;
index 70ac8d8..325b441 100644 (file)
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * ST Microelectronics
  * Flexible Static Memory Controller (FSMC)
  * Based on drivers/mtd/nand/nomadik_nand.c (removed in v3.8)
  *  Copyright © 2007 STMicroelectronics Pvt. Ltd.
  *  Copyright © 2009 Alessandro Rubini
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
  */
 
 #include <linux/clk.h>
 /* fsmc controller registers for NOR flash */
 #define CTRL                   0x0
        /* ctrl register definitions */
-       #define BANK_ENABLE             (1 << 0)
-       #define MUXED                   (1 << 1)
+       #define BANK_ENABLE             BIT(0)
+       #define MUXED                   BIT(1)
        #define NOR_DEV                 (2 << 2)
-       #define WIDTH_8                 (0 << 4)
-       #define WIDTH_16                (1 << 4)
-       #define RSTPWRDWN               (1 << 6)
-       #define WPROT                   (1 << 7)
-       #define WRT_ENABLE              (1 << 12)
-       #define WAIT_ENB                (1 << 13)
+       #define WIDTH_16                BIT(4)
+       #define RSTPWRDWN               BIT(6)
+       #define WPROT                   BIT(7)
+       #define WRT_ENABLE              BIT(12)
+       #define WAIT_ENB                BIT(13)
 
 #define CTRL_TIM               0x4
        /* ctrl_tim register definitions */
 #define FSMC_NOR_BANK_SZ       0x8
 #define FSMC_NOR_REG_SIZE      0x40
 
-#define FSMC_NOR_REG(base, bank, reg)          (base + \
-                                               FSMC_NOR_BANK_SZ * (bank) + \
-                                               reg)
+#define FSMC_NOR_REG(base, bank, reg)  ((base) +                       \
+                                        (FSMC_NOR_BANK_SZ * (bank)) +  \
+                                        (reg))
 
 /* fsmc controller registers for NAND flash */
 #define FSMC_PC                        0x00
        /* pc register definitions */
-       #define FSMC_RESET              (1 << 0)
-       #define FSMC_WAITON             (1 << 1)
-       #define FSMC_ENABLE             (1 << 2)
-       #define FSMC_DEVTYPE_NAND       (1 << 3)
-       #define FSMC_DEVWID_8           (0 << 4)
-       #define FSMC_DEVWID_16          (1 << 4)
-       #define FSMC_ECCEN              (1 << 6)
-       #define FSMC_ECCPLEN_512        (0 << 7)
-       #define FSMC_ECCPLEN_256        (1 << 7)
-       #define FSMC_TCLR_1             (1)
+       #define FSMC_RESET              BIT(0)
+       #define FSMC_WAITON             BIT(1)
+       #define FSMC_ENABLE             BIT(2)
+       #define FSMC_DEVTYPE_NAND       BIT(3)
+       #define FSMC_DEVWID_16          BIT(4)
+       #define FSMC_ECCEN              BIT(6)
+       #define FSMC_ECCPLEN_256        BIT(7)
        #define FSMC_TCLR_SHIFT         (9)
        #define FSMC_TCLR_MASK          (0xF)
-       #define FSMC_TAR_1              (1)
        #define FSMC_TAR_SHIFT          (13)
        #define FSMC_TAR_MASK           (0xF)
 #define STS                    0x04
        /* sts register definitions */
-       #define FSMC_CODE_RDY           (1 << 15)
+       #define FSMC_CODE_RDY           BIT(15)
 #define COMM                   0x08
        /* comm register definitions */
-       #define FSMC_TSET_0             0
        #define FSMC_TSET_SHIFT         0
        #define FSMC_TSET_MASK          0xFF
-       #define FSMC_TWAIT_6            6
        #define FSMC_TWAIT_SHIFT        8
        #define FSMC_TWAIT_MASK         0xFF
-       #define FSMC_THOLD_4            4
        #define FSMC_THOLD_SHIFT        16
        #define FSMC_THOLD_MASK         0xFF
-       #define FSMC_THIZ_1             1
        #define FSMC_THIZ_SHIFT         24
        #define FSMC_THIZ_MASK          0xFF
 #define ATTRIB                 0x0C
 #define FSMC_BUSY_WAIT_TIMEOUT (1 * HZ)
 
 struct fsmc_nand_timings {
-       uint8_t tclr;
-       uint8_t tar;
-       uint8_t thiz;
-       uint8_t thold;
-       uint8_t twait;
-       uint8_t tset;
+       u8 tclr;
+       u8 tar;
+       u8 thiz;
+       u8 thold;
+       u8 twait;
+       u8 tset;
 };
 
 enum access_mode {
@@ -122,19 +110,21 @@ enum access_mode {
 /**
  * struct fsmc_nand_data - structure for FSMC NAND device state
  *
+ * @base:              Inherit from the nand_controller struct
  * @pid:               Part ID on the AMBA PrimeCell format
- * @mtd:               MTD info for a NAND flash.
  * @nand:              Chip related info for a NAND flash.
- * @partitions:                Partition info for a NAND Flash.
- * @nr_partitions:     Total number of partition of a NAND flash.
  *
  * @bank:              Bank number for probed device.
+ * @dev:               Parent device
+ * @mode:              Access mode
  * @clk:               Clock structure for FSMC.
  *
  * @read_dma_chan:     DMA channel for read access
  * @write_dma_chan:    DMA channel for write access to NAND
  * @dma_access_complete: Completion structure
  *
+ * @dev_timings:       NAND timings
+ *
  * @data_pa:           NAND Physical port for Data.
  * @data_va:           NAND port for Data.
  * @cmd_va:            NAND port for Command.
@@ -142,6 +132,7 @@ enum access_mode {
  * @regs_va:           Registers base address for a given bank.
  */
 struct fsmc_nand_data {
+       struct nand_controller  base;
        u32                     pid;
        struct nand_chip        nand;
 
@@ -248,9 +239,9 @@ static const struct mtd_ooblayout_ops fsmc_ecc4_ooblayout_ops = {
        .free = fsmc_ecc4_ooblayout_free,
 };
 
-static inline struct fsmc_nand_data *mtd_to_fsmc(struct mtd_info *mtd)
+static inline struct fsmc_nand_data *nand_to_fsmc(struct nand_chip *chip)
 {
-       return container_of(mtd_to_nand(mtd), struct fsmc_nand_data, nand);
+       return container_of(chip, struct fsmc_nand_data, nand);
 }
 
 /*
@@ -262,8 +253,8 @@ static inline struct fsmc_nand_data *mtd_to_fsmc(struct mtd_info *mtd)
 static void fsmc_nand_setup(struct fsmc_nand_data *host,
                            struct fsmc_nand_timings *tims)
 {
-       uint32_t value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON;
-       uint32_t tclr, tar, thiz, thold, twait, tset;
+       u32 value = FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON;
+       u32 tclr, tar, thiz, thold, twait, tset;
 
        tclr = (tims->tclr & FSMC_TCLR_MASK) << FSMC_TCLR_SHIFT;
        tar = (tims->tar & FSMC_TAR_MASK) << FSMC_TAR_SHIFT;
@@ -273,13 +264,9 @@ static void fsmc_nand_setup(struct fsmc_nand_data *host,
        tset = (tims->tset & FSMC_TSET_MASK) << FSMC_TSET_SHIFT;
 
        if (host->nand.options & NAND_BUSWIDTH_16)
-               writel_relaxed(value | FSMC_DEVWID_16,
-                              host->regs_va + FSMC_PC);
-       else
-               writel_relaxed(value | FSMC_DEVWID_8, host->regs_va + FSMC_PC);
+               value |= FSMC_DEVWID_16;
 
-       writel_relaxed(readl(host->regs_va + FSMC_PC) | tclr | tar,
-                      host->regs_va + FSMC_PC);
+       writel_relaxed(value | tclr | tar, host->regs_va + FSMC_PC);
        writel_relaxed(thiz | thold | twait | tset, host->regs_va + COMM);
        writel_relaxed(thiz | thold | twait | tset, host->regs_va + ATTRIB);
 }
@@ -290,7 +277,7 @@ static int fsmc_calc_timings(struct fsmc_nand_data *host,
 {
        unsigned long hclk = clk_get_rate(host->clk);
        unsigned long hclkn = NSEC_PER_SEC / hclk;
-       uint32_t thiz, thold, twait, tset;
+       u32 thiz, thold, twait, tset;
 
        if (sdrt->tRC_min < 30000)
                return -EOPNOTSUPP;
@@ -343,7 +330,7 @@ static int fsmc_calc_timings(struct fsmc_nand_data *host,
 static int fsmc_setup_data_interface(struct nand_chip *nand, int csline,
                                     const struct nand_data_interface *conf)
 {
-       struct fsmc_nand_data *host = nand_get_controller_data(nand);
+       struct fsmc_nand_data *host = nand_to_fsmc(nand);
        struct fsmc_nand_timings tims;
        const struct nand_sdr_timings *sdrt;
        int ret;
@@ -369,7 +356,7 @@ static int fsmc_setup_data_interface(struct nand_chip *nand, int csline,
  */
 static void fsmc_enable_hwecc(struct nand_chip *chip, int mode)
 {
-       struct fsmc_nand_data *host = mtd_to_fsmc(nand_to_mtd(chip));
+       struct fsmc_nand_data *host = nand_to_fsmc(chip);
 
        writel_relaxed(readl(host->regs_va + FSMC_PC) & ~FSMC_ECCPLEN_256,
                       host->regs_va + FSMC_PC);
@@ -384,18 +371,18 @@ static void fsmc_enable_hwecc(struct nand_chip *chip, int mode)
  * FSMC. ECC is 13 bytes for 512 bytes of data (supports error correction up to
  * max of 8-bits)
  */
-static int fsmc_read_hwecc_ecc4(struct nand_chip *chip, const uint8_t *data,
-                               uint8_t *ecc)
+static int fsmc_read_hwecc_ecc4(struct nand_chip *chip, const u8 *data,
+                               u8 *ecc)
 {
-       struct fsmc_nand_data *host = mtd_to_fsmc(nand_to_mtd(chip));
-       uint32_t ecc_tmp;
+       struct fsmc_nand_data *host = nand_to_fsmc(chip);
+       u32 ecc_tmp;
        unsigned long deadline = jiffies + FSMC_BUSY_WAIT_TIMEOUT;
 
        do {
                if (readl_relaxed(host->regs_va + STS) & FSMC_CODE_RDY)
                        break;
-               else
-                       cond_resched();
+
+               cond_resched();
        } while (!time_after_eq(jiffies, deadline));
 
        if (time_after_eq(jiffies, deadline)) {
@@ -404,25 +391,25 @@ static int fsmc_read_hwecc_ecc4(struct nand_chip *chip, const uint8_t *data,
        }
 
        ecc_tmp = readl_relaxed(host->regs_va + ECC1);
-       ecc[0] = (uint8_t) (ecc_tmp >> 0);
-       ecc[1] = (uint8_t) (ecc_tmp >> 8);
-       ecc[2] = (uint8_t) (ecc_tmp >> 16);
-       ecc[3] = (uint8_t) (ecc_tmp >> 24);
+       ecc[0] = ecc_tmp;
+       ecc[1] = ecc_tmp >> 8;
+       ecc[2] = ecc_tmp >> 16;
+       ecc[3] = ecc_tmp >> 24;
 
        ecc_tmp = readl_relaxed(host->regs_va + ECC2);
-       ecc[4] = (uint8_t) (ecc_tmp >> 0);
-       ecc[5] = (uint8_t) (ecc_tmp >> 8);
-       ecc[6] = (uint8_t) (ecc_tmp >> 16);
-       ecc[7] = (uint8_t) (ecc_tmp >> 24);
+       ecc[4] = ecc_tmp;
+       ecc[5] = ecc_tmp >> 8;
+       ecc[6] = ecc_tmp >> 16;
+       ecc[7] = ecc_tmp >> 24;
 
        ecc_tmp = readl_relaxed(host->regs_va + ECC3);
-       ecc[8] = (uint8_t) (ecc_tmp >> 0);
-       ecc[9] = (uint8_t) (ecc_tmp >> 8);
-       ecc[10] = (uint8_t) (ecc_tmp >> 16);
-       ecc[11] = (uint8_t) (ecc_tmp >> 24);
+       ecc[8] = ecc_tmp;
+       ecc[9] = ecc_tmp >> 8;
+       ecc[10] = ecc_tmp >> 16;
+       ecc[11] = ecc_tmp >> 24;
 
        ecc_tmp = readl_relaxed(host->regs_va + STS);
-       ecc[12] = (uint8_t) (ecc_tmp >> 16);
+       ecc[12] = ecc_tmp >> 16;
 
        return 0;
 }
@@ -432,22 +419,22 @@ static int fsmc_read_hwecc_ecc4(struct nand_chip *chip, const uint8_t *data,
  * FSMC. ECC is 3 bytes for 512 bytes of data (supports error correction up to
  * max of 1-bit)
  */
-static int fsmc_read_hwecc_ecc1(struct nand_chip *chip, const uint8_t *data,
-                               uint8_t *ecc)
+static int fsmc_read_hwecc_ecc1(struct nand_chip *chip, const u8 *data,
+                               u8 *ecc)
 {
-       struct fsmc_nand_data *host = mtd_to_fsmc(nand_to_mtd(chip));
-       uint32_t ecc_tmp;
+       struct fsmc_nand_data *host = nand_to_fsmc(chip);
+       u32 ecc_tmp;
 
        ecc_tmp = readl_relaxed(host->regs_va + ECC1);
-       ecc[0] = (uint8_t) (ecc_tmp >> 0);
-       ecc[1] = (uint8_t) (ecc_tmp >> 8);
-       ecc[2] = (uint8_t) (ecc_tmp >> 16);
+       ecc[0] = ecc_tmp;
+       ecc[1] = ecc_tmp >> 8;
+       ecc[2] = ecc_tmp >> 16;
 
        return 0;
 }
 
 /* Count the number of 0's in buff upto a max of max_bits */
-static int count_written_bits(uint8_t *buff, int size, int max_bits)
+static int count_written_bits(u8 *buff, int size, int max_bits)
 {
        int k, written_bits = 0;
 
@@ -468,7 +455,7 @@ static void dma_complete(void *param)
 }
 
 static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
-               enum dma_data_direction direction)
+                   enum dma_data_direction direction)
 {
        struct dma_chan *chan;
        struct dma_device *dma_dev;
@@ -519,7 +506,7 @@ static int dma_xfer(struct fsmc_nand_data *host, void *buffer, int len,
 
        time_left =
        wait_for_completion_timeout(&host->dma_access_complete,
-                               msecs_to_jiffies(3000));
+                                   msecs_to_jiffies(3000));
        if (time_left == 0) {
                dmaengine_terminate_all(chan);
                dev_err(host->dev, "wait_for_completion_timeout\n");
@@ -537,18 +524,19 @@ unmap_dma:
 
 /*
  * fsmc_write_buf - write buffer to chip
- * @mtd:       MTD device structure
+ * @host:      FSMC NAND controller
  * @buf:       data buffer
  * @len:       number of bytes to write
  */
-static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+static void fsmc_write_buf(struct fsmc_nand_data *host, const u8 *buf,
+                          int len)
 {
-       struct fsmc_nand_data *host  = mtd_to_fsmc(mtd);
        int i;
 
-       if (IS_ALIGNED((uintptr_t)buf, sizeof(uint32_t)) &&
-                       IS_ALIGNED(len, sizeof(uint32_t))) {
-               uint32_t *p = (uint32_t *)buf;
+       if (IS_ALIGNED((uintptr_t)buf, sizeof(u32)) &&
+           IS_ALIGNED(len, sizeof(u32))) {
+               u32 *p = (u32 *)buf;
+
                len = len >> 2;
                for (i = 0; i < len; i++)
                        writel_relaxed(p[i], host->data_va);
@@ -560,18 +548,18 @@ static void fsmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
 
 /*
  * fsmc_read_buf - read chip data into buffer
- * @mtd:       MTD device structure
+ * @host:      FSMC NAND controller
  * @buf:       buffer to store date
  * @len:       number of bytes to read
  */
-static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+static void fsmc_read_buf(struct fsmc_nand_data *host, u8 *buf, int len)
 {
-       struct fsmc_nand_data *host  = mtd_to_fsmc(mtd);
        int i;
 
-       if (IS_ALIGNED((uintptr_t)buf, sizeof(uint32_t)) &&
-                       IS_ALIGNED(len, sizeof(uint32_t))) {
-               uint32_t *p = (uint32_t *)buf;
+       if (IS_ALIGNED((uintptr_t)buf, sizeof(u32)) &&
+           IS_ALIGNED(len, sizeof(u32))) {
+               u32 *p = (u32 *)buf;
+
                len = len >> 2;
                for (i = 0; i < len; i++)
                        p[i] = readl_relaxed(host->data_va);
@@ -583,48 +571,42 @@ static void fsmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
 
 /*
  * fsmc_read_buf_dma - read chip data into buffer
- * @mtd:       MTD device structure
+ * @host:      FSMC NAND controller
  * @buf:       buffer to store date
  * @len:       number of bytes to read
  */
-static void fsmc_read_buf_dma(struct mtd_info *mtd, uint8_t *buf, int len)
+static void fsmc_read_buf_dma(struct fsmc_nand_data *host, u8 *buf,
+                             int len)
 {
-       struct fsmc_nand_data *host  = mtd_to_fsmc(mtd);
-
        dma_xfer(host, buf, len, DMA_FROM_DEVICE);
 }
 
 /*
  * fsmc_write_buf_dma - write buffer to chip
- * @mtd:       MTD device structure
+ * @host:      FSMC NAND controller
  * @buf:       data buffer
  * @len:       number of bytes to write
  */
-static void fsmc_write_buf_dma(struct mtd_info *mtd, const uint8_t *buf,
-               int len)
+static void fsmc_write_buf_dma(struct fsmc_nand_data *host, const u8 *buf,
+                              int len)
 {
-       struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
-
        dma_xfer(host, (void *)buf, len, DMA_TO_DEVICE);
 }
 
 /* fsmc_select_chip - assert or deassert nCE */
-static void fsmc_select_chip(struct nand_chip *chip, int chipnr)
+static void fsmc_ce_ctrl(struct fsmc_nand_data *host, bool assert)
 {
-       struct fsmc_nand_data *host = mtd_to_fsmc(nand_to_mtd(chip));
-       u32 pc;
-
-       /* Support only one CS */
-       if (chipnr > 0)
-               return;
+       u32 pc = readl(host->regs_va + FSMC_PC);
 
-       pc = readl(host->regs_va + FSMC_PC);
-       if (chipnr < 0)
+       if (!assert)
                writel_relaxed(pc & ~FSMC_ENABLE, host->regs_va + FSMC_PC);
        else
                writel_relaxed(pc | FSMC_ENABLE, host->regs_va + FSMC_PC);
 
-       /* nCE line must be asserted before starting any operation */
+       /*
+        * nCE line changes must be applied before returning from this
+        * function.
+        */
        mb();
 }
 
@@ -637,14 +619,16 @@ static void fsmc_select_chip(struct nand_chip *chip, int chipnr)
 static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op,
                        bool check_only)
 {
-       struct mtd_info *mtd = nand_to_mtd(chip);
-       struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
+       struct fsmc_nand_data *host = nand_to_fsmc(chip);
        const struct nand_op_instr *instr = NULL;
        int ret = 0;
        unsigned int op_id;
        int i;
 
        pr_debug("Executing operation [%d instructions]:\n", op->ninstrs);
+
+       fsmc_ce_ctrl(host, true);
+
        for (op_id = 0; op_id < op->ninstrs; op_id++) {
                instr = &op->instrs[op_id];
 
@@ -671,10 +655,10 @@ static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op,
                                 ", force 8-bit" : "");
 
                        if (host->mode == USE_DMA_ACCESS)
-                               fsmc_read_buf_dma(mtd, instr->ctx.data.buf.in,
+                               fsmc_read_buf_dma(host, instr->ctx.data.buf.in,
                                                  instr->ctx.data.len);
                        else
-                               fsmc_read_buf(mtd, instr->ctx.data.buf.in,
+                               fsmc_read_buf(host, instr->ctx.data.buf.in,
                                              instr->ctx.data.len);
                        break;
 
@@ -684,10 +668,11 @@ static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op,
                                 ", force 8-bit" : "");
 
                        if (host->mode == USE_DMA_ACCESS)
-                               fsmc_write_buf_dma(mtd, instr->ctx.data.buf.out,
+                               fsmc_write_buf_dma(host,
+                                                  instr->ctx.data.buf.out,
                                                   instr->ctx.data.len);
                        else
-                               fsmc_write_buf(mtd, instr->ctx.data.buf.out,
+                               fsmc_write_buf(host, instr->ctx.data.buf.out,
                                               instr->ctx.data.len);
                        break;
 
@@ -701,6 +686,8 @@ static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op,
                }
        }
 
+       fsmc_ce_ctrl(host, false);
+
        return ret;
 }
 
@@ -717,34 +704,35 @@ static int fsmc_exec_op(struct nand_chip *chip, const struct nand_operation *op,
  * After this read, fsmc hardware generates and reports error data bits(up to a
  * max of 8 bits)
  */
-static int fsmc_read_page_hwecc(struct nand_chip *chip, uint8_t *buf,
+static int fsmc_read_page_hwecc(struct nand_chip *chip, u8 *buf,
                                int oob_required, int page)
 {
        struct mtd_info *mtd = nand_to_mtd(chip);
        int i, j, s, stat, eccsize = chip->ecc.size;
        int eccbytes = chip->ecc.bytes;
        int eccsteps = chip->ecc.steps;
-       uint8_t *p = buf;
-       uint8_t *ecc_calc = chip->ecc.calc_buf;
-       uint8_t *ecc_code = chip->ecc.code_buf;
-       int off, len, group = 0;
+       u8 *p = buf;
+       u8 *ecc_calc = chip->ecc.calc_buf;
+       u8 *ecc_code = chip->ecc.code_buf;
+       int off, len, ret, group = 0;
        /*
-        * ecc_oob is intentionally taken as uint16_t. In 16bit devices, we
+        * ecc_oob is intentionally taken as u16. In 16bit devices, we
         * end up reading 14 bytes (7 words) from oob. The local array is
         * to maintain word alignment
         */
-       uint16_t ecc_oob[7];
-       uint8_t *oob = (uint8_t *)&ecc_oob[0];
+       u16 ecc_oob[7];
+       u8 *oob = (u8 *)&ecc_oob[0];
        unsigned int max_bitflips = 0;
 
        for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) {
                nand_read_page_op(chip, page, s * eccsize, NULL, 0);
                chip->ecc.hwctl(chip, NAND_ECC_READ);
-               nand_read_data_op(chip, p, eccsize, false);
+               ret = nand_read_data_op(chip, p, eccsize, false);
+               if (ret)
+                       return ret;
 
                for (j = 0; j < eccbytes;) {
                        struct mtd_oob_region oobregion;
-                       int ret;
 
                        ret = mtd_ooblayout_ecc(mtd, group++, &oobregion);
                        if (ret)
@@ -788,15 +776,15 @@ static int fsmc_read_page_hwecc(struct nand_chip *chip, uint8_t *buf,
  * @calc_ecc:  ecc calculated from read data
  *
  * calc_ecc is a 104 bit information containing maximum of 8 error
- * offset informations of 13 bits each in 512 bytes of read data.
+ * offset information of 13 bits each in 512 bytes of read data.
  */
-static int fsmc_bch8_correct_data(struct nand_chip *chip, uint8_t *dat,
-                                 uint8_t *read_ecc, uint8_t *calc_ecc)
+static int fsmc_bch8_correct_data(struct nand_chip *chip, u8 *dat,
+                                 u8 *read_ecc, u8 *calc_ecc)
 {
-       struct fsmc_nand_data *host = mtd_to_fsmc(nand_to_mtd(chip));
-       uint32_t err_idx[8];
-       uint32_t num_err, i;
-       uint32_t ecc1, ecc2, ecc3, ecc4;
+       struct fsmc_nand_data *host = nand_to_fsmc(chip);
+       u32 err_idx[8];
+       u32 num_err, i;
+       u32 ecc1, ecc2, ecc3, ecc4;
 
        num_err = (readl_relaxed(host->regs_va + STS) >> 10) & 0xF;
 
@@ -837,8 +825,8 @@ static int fsmc_bch8_correct_data(struct nand_chip *chip, uint8_t *dat,
         * |---idx[7]--|--.....-----|---idx[2]--||---idx[1]--||---idx[0]--|
         *
         * calc_ecc is a 104 bit information containing maximum of 8 error
-        * offset informations of 13 bits each. calc_ecc is copied into a
-        * uint64_t array and error offset indexes are populated in err_idx
+        * offset information of 13 bits each. calc_ecc is copied into a
+        * u64 array and error offset indexes are populated in err_idx
         * array
         */
        ecc1 = readl_relaxed(host->regs_va + ECC1);
@@ -897,11 +885,13 @@ static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
                nand->options |= NAND_SKIP_BBTSCAN;
 
        host->dev_timings = devm_kzalloc(&pdev->dev,
-                               sizeof(*host->dev_timings), GFP_KERNEL);
+                                        sizeof(*host->dev_timings),
+                                        GFP_KERNEL);
        if (!host->dev_timings)
                return -ENOMEM;
+
        ret = of_property_read_u8_array(np, "timings", (u8 *)host->dev_timings,
-                                               sizeof(*host->dev_timings));
+                                       sizeof(*host->dev_timings));
        if (ret)
                host->dev_timings = NULL;
 
@@ -920,7 +910,7 @@ static int fsmc_nand_probe_config_dt(struct platform_device *pdev,
 static int fsmc_nand_attach_chip(struct nand_chip *nand)
 {
        struct mtd_info *mtd = nand_to_mtd(nand);
-       struct fsmc_nand_data *host = mtd_to_fsmc(mtd);
+       struct fsmc_nand_data *host = nand_to_fsmc(nand);
 
        if (AMBA_REV_BITS(host->pid) >= 8) {
                switch (mtd->oobsize) {
@@ -992,6 +982,8 @@ static int fsmc_nand_attach_chip(struct nand_chip *nand)
 
 static const struct nand_controller_ops fsmc_nand_controller_ops = {
        .attach_chip = fsmc_nand_attach_chip,
+       .exec_op = fsmc_exec_op,
+       .setup_data_interface = fsmc_setup_data_interface,
 };
 
 /*
@@ -1061,10 +1053,13 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
         * AMBA PrimeCell bus. However it is not a PrimeCell.
         */
        for (pid = 0, i = 0; i < 4; i++)
-               pid |= (readl(base + resource_size(res) - 0x20 + 4 * i) & 255) << (i * 8);
+               pid |= (readl(base + resource_size(res) - 0x20 + 4 * i) &
+                       255) << (i * 8);
+
        host->pid = pid;
-       dev_info(&pdev->dev, "FSMC device partno %03x, manufacturer %02x, "
-                "revision %02x, config %02x\n",
+
+       dev_info(&pdev->dev,
+                "FSMC device partno %03x, manufacturer %02x, revision %02x, config %02x\n",
                 AMBA_PART_BITS(pid), AMBA_MANF_BITS(pid),
                 AMBA_REV_BITS(pid), AMBA_CONFIG_BITS(pid));
 
@@ -1075,12 +1070,9 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
 
        /* Link all private pointers */
        mtd = nand_to_mtd(&host->nand);
-       nand_set_controller_data(nand, host);
        nand_set_flash_node(nand, pdev->dev.of_node);
 
        mtd->dev.parent = &pdev->dev;
-       nand->exec_op = fsmc_exec_op;
-       nand->select_chip = fsmc_select_chip;
 
        /*
         * Setup default ECC mode. nand_dt_init() called from nand_scan_ident()
@@ -1106,10 +1098,10 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
                }
        }
 
-       if (host->dev_timings)
+       if (host->dev_timings) {
                fsmc_nand_setup(host, host->dev_timings);
-       else
-               nand->setup_data_interface = fsmc_setup_data_interface;
+               nand->options |= NAND_KEEP_TIMINGS;
+       }
 
        if (AMBA_REV_BITS(host->pid) >= 8) {
                nand->ecc.read_page = fsmc_read_page_hwecc;
@@ -1119,10 +1111,13 @@ static int __init fsmc_nand_probe(struct platform_device *pdev)
                nand->ecc.strength = 8;
        }
 
+       nand_controller_init(&host->base);
+       host->base.ops = &fsmc_nand_controller_ops;
+       nand->controller = &host->base;
+
        /*
         * Scan to find existence of the device
         */
-       nand->dummy_controller.ops = &fsmc_nand_controller_ops;
        ret = nand_scan(nand, 1);
        if (ret)
                goto release_dma_write_chan;
@@ -1175,19 +1170,23 @@ static int fsmc_nand_remove(struct platform_device *pdev)
 static int fsmc_nand_suspend(struct device *dev)
 {
        struct fsmc_nand_data *host = dev_get_drvdata(dev);
+
        if (host)
                clk_disable_unprepare(host->clk);
+
        return 0;
 }
 
 static int fsmc_nand_resume(struct device *dev)
 {
        struct fsmc_nand_data *host = dev_get_drvdata(dev);
+
        if (host) {
                clk_prepare_enable(host->clk);
                if (host->dev_timings)
                        fsmc_nand_setup(host, host->dev_timings);
        }
+
        return 0;
 }
 #endif
@@ -1212,6 +1211,6 @@ static struct platform_driver fsmc_nand_driver = {
 
 module_platform_driver_probe(fsmc_nand_driver, fsmc_nand_probe);
 
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>, Ashish Priyadarshi");
 MODULE_DESCRIPTION("NAND driver for SPEAr Platforms");
index 94c2b75..ed405c9 100644 (file)
@@ -1549,7 +1549,7 @@ static int gpmi_block_markbad(struct nand_chip *chip, loff_t ofs)
        int column, page, chipnr;
 
        chipnr = (int)(ofs >> chip->chip_shift);
-       chip->select_chip(chip, chipnr);
+       nand_select_target(chip, chipnr);
 
        column = !GPMI_IS_MX23(this) ? mtd->writesize : 0;
 
@@ -1562,7 +1562,7 @@ static int gpmi_block_markbad(struct nand_chip *chip, loff_t ofs)
 
        ret = nand_prog_page_op(chip, page, column, block_mark, 1);
 
-       chip->select_chip(chip, -1);
+       nand_deselect_target(chip);
 
        return ret;
 }
@@ -1610,7 +1610,7 @@ static int mx23_check_transcription_stamp(struct gpmi_nand_data *this)
        search_area_size_in_strides = 1 << rom_geo->search_area_stride_exponent;
 
        saved_chip_number = this->current_chip;
-       chip->select_chip(chip, 0);
+       nand_select_target(chip, 0);
 
        /*
         * Loop through the first search area, looking for the NCB fingerprint.
@@ -1638,7 +1638,10 @@ static int mx23_check_transcription_stamp(struct gpmi_nand_data *this)
 
        }
 
-       chip->select_chip(chip, saved_chip_number);
+       if (saved_chip_number >= 0)
+               nand_select_target(chip, saved_chip_number);
+       else
+               nand_deselect_target(chip);
 
        if (found_an_ncb_fingerprint)
                dev_dbg(dev, "\tFound a fingerprint\n");
@@ -1681,7 +1684,7 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this)
 
        /* Select chip 0. */
        saved_chip_number = this->current_chip;
-       chip->select_chip(chip, 0);
+       nand_select_target(chip, 0);
 
        /* Loop over blocks in the first search area, erasing them. */
        dev_dbg(dev, "Erasing the search area...\n");
@@ -1713,7 +1716,11 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this)
        }
 
        /* Deselect chip 0. */
-       chip->select_chip(chip, saved_chip_number);
+       if (saved_chip_number >= 0)
+               nand_select_target(chip, saved_chip_number);
+       else
+               nand_deselect_target(chip);
+
        return 0;
 }
 
@@ -1762,10 +1769,10 @@ static int mx23_boot_init(struct gpmi_nand_data  *this)
                byte = block <<  chip->phys_erase_shift;
 
                /* Send the command to read the conventional block mark. */
-               chip->select_chip(chip, chipnr);
+               nand_select_target(chip, chipnr);
                nand_read_page_op(chip, page, mtd->writesize, NULL, 0);
                block_mark = chip->legacy.read_byte(chip);
-               chip->select_chip(chip, -1);
+               nand_deselect_target(chip);
 
                /*
                 * Check if the block is marked bad. If so, we need to mark it
@@ -1882,6 +1889,7 @@ static int gpmi_nand_attach_chip(struct nand_chip *chip)
 
 static const struct nand_controller_ops gpmi_nand_controller_ops = {
        .attach_chip = gpmi_nand_attach_chip,
+       .setup_data_interface = gpmi_setup_data_interface,
 };
 
 static int gpmi_nand_init(struct gpmi_nand_data *this)
@@ -1900,8 +1908,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
        /* init the nand_chip{}, we don't support a 16-bit NAND Flash bus. */
        nand_set_controller_data(chip, this);
        nand_set_flash_node(chip, this->pdev->dev.of_node);
-       chip->select_chip       = gpmi_select_chip;
-       chip->setup_data_interface = gpmi_setup_data_interface;
+       chip->legacy.select_chip        = gpmi_select_chip;
        chip->legacy.cmd_ctrl   = gpmi_cmd_ctrl;
        chip->legacy.dev_ready  = gpmi_dev_ready;
        chip->legacy.read_byte  = gpmi_read_byte;
@@ -1924,7 +1931,7 @@ static int gpmi_nand_init(struct gpmi_nand_data *this)
        if (ret)
                goto err_out;
 
-       chip->dummy_controller.ops = &gpmi_nand_controller_ops;
+       chip->legacy.dummy_controller.ops = &gpmi_nand_controller_ops;
        ret = nand_scan(chip, GPMI_IS_MX6(this) ? 2 : 1);
        if (ret)
                goto err_out;
index f043938..f3f9aa1 100644 (file)
@@ -783,7 +783,7 @@ static int hisi_nfc_probe(struct platform_device *pdev)
        nand_set_controller_data(chip, host);
        nand_set_flash_node(chip, np);
        chip->legacy.cmdfunc    = hisi_nfc_cmdfunc;
-       chip->select_chip       = hisi_nfc_select_chip;
+       chip->legacy.select_chip        = hisi_nfc_select_chip;
        chip->legacy.read_byte  = hisi_nfc_read_byte;
        chip->legacy.write_buf  = hisi_nfc_write_buf;
        chip->legacy.read_buf   = hisi_nfc_read_buf;
@@ -799,7 +799,7 @@ static int hisi_nfc_probe(struct platform_device *pdev)
                return ret;
        }
 
-       chip->dummy_controller.ops = &hisi_nfc_controller_ops;
+       chip->legacy.dummy_controller.ops = &hisi_nfc_controller_ops;
        ret = nand_scan(chip, max_chips);
        if (ret)
                return ret;
index 04c2cf7..fbf6ca0 100644 (file)
@@ -95,6 +95,39 @@ void nand_decode_ext_id(struct nand_chip *chip);
 void panic_nand_wait(struct nand_chip *chip, unsigned long timeo);
 void sanitize_string(uint8_t *s, size_t len);
 
+static inline bool nand_has_exec_op(struct nand_chip *chip)
+{
+       if (!chip->controller || !chip->controller->ops ||
+           !chip->controller->ops->exec_op)
+               return false;
+
+       return true;
+}
+
+static inline int nand_exec_op(struct nand_chip *chip,
+                              const struct nand_operation *op)
+{
+       if (!nand_has_exec_op(chip))
+               return -ENOTSUPP;
+
+       if (WARN_ON(op->cs >= chip->numchips))
+               return -EINVAL;
+
+       return chip->controller->ops->exec_op(chip, op, false);
+}
+
+static inline bool nand_has_setup_data_iface(struct nand_chip *chip)
+{
+       if (!chip->controller || !chip->controller->ops ||
+           !chip->controller->ops->setup_data_interface)
+               return false;
+
+       if (chip->options & NAND_KEEP_TIMINGS)
+               return false;
+
+       return true;
+}
+
 /* BBT functions */
 int nand_markbad_bbt(struct nand_chip *chip, loff_t offs);
 int nand_isreserved_bbt(struct nand_chip *chip, loff_t offs);
index fb59cfc..f92ae5a 100644 (file)
@@ -335,14 +335,14 @@ static int jz_nand_detect_bank(struct platform_device *pdev,
                        goto notfound_id;
 
                /* Retrieve the IDs from the first chip. */
-               chip->select_chip(chip, 0);
+               nand_select_target(chip, 0);
                nand_reset_op(chip);
                nand_readid_op(chip, 0, id, sizeof(id));
                *nand_maf_id = id[0];
                *nand_dev_id = id[1];
        } else {
                /* Detect additional chip. */
-               chip->select_chip(chip, chipnr);
+               nand_select_target(chip, chipnr);
                nand_reset_op(chip);
                nand_readid_op(chip, 0, id, sizeof(id));
                if (*nand_maf_id != id[0] || *nand_dev_id != id[1]) {
@@ -427,8 +427,8 @@ static int jz_nand_probe(struct platform_device *pdev)
 
        chip->legacy.chip_delay = 50;
        chip->legacy.cmd_ctrl = jz_nand_cmd_ctrl;
-       chip->select_chip = jz_nand_select_chip;
-       chip->dummy_controller.ops = &jz_nand_controller_ops;
+       chip->legacy.select_chip = jz_nand_select_chip;
+       chip->legacy.dummy_controller.ops = &jz_nand_controller_ops;
 
        if (nand->busy_gpio)
                chip->legacy.dev_ready = jz_nand_dev_ready;
index 731c605..7201827 100644 (file)
@@ -136,8 +136,10 @@ static void jz4780_bch_read_parity(struct jz4780_bch *bch, void *buf,
        switch (size8) {
        case 3:
                dest8[2] = (val >> 16) & 0xff;
+               /* fall through */
        case 2:
                dest8[1] = (val >> 8) & 0xff;
+               /* fall through */
        case 1:
                dest8[0] = val & 0xff;
                break;
index cdf2210..22e5897 100644 (file)
@@ -279,7 +279,7 @@ static int jz4780_nand_init_chip(struct platform_device *pdev,
        chip->legacy.IO_ADDR_W = cs->base + OFFSET_DATA;
        chip->legacy.chip_delay = RB_DELAY_US;
        chip->options = NAND_NO_SUBPAGE_WRITE;
-       chip->select_chip = jz4780_nand_select_chip;
+       chip->legacy.select_chip = jz4780_nand_select_chip;
        chip->legacy.cmd_ctrl = jz4780_nand_cmd_ctrl;
        chip->ecc.mode = NAND_ECC_HW;
        chip->controller = &nfc->controller;
index abbb655..086964f 100644 (file)
@@ -799,7 +799,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
         * Scan to find existence of the device and get the type of NAND device:
         * SMALL block or LARGE block.
         */
-       nand_chip->dummy_controller.ops = &lpc32xx_nand_controller_ops;
+       nand_chip->legacy.dummy_controller.ops = &lpc32xx_nand_controller_ops;
        res = nand_scan(nand_chip, 1);
        if (res)
                goto free_irq;
index f2f2cdb..a2c5fdc 100644 (file)
@@ -924,7 +924,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev)
        }
 
        /* Find NAND device */
-       chip->dummy_controller.ops = &lpc32xx_nand_controller_ops;
+       chip->legacy.dummy_controller.ops = &lpc32xx_nand_controller_ops;
        res = nand_scan(chip, 1);
        if (res)
                goto release_dma;
index 650f2b4..84283c6 100644 (file)
@@ -378,7 +378,7 @@ struct marvell_nfc_caps {
  * @dev:               Parent device (used to print error messages)
  * @regs:              NAND controller registers
  * @core_clk:          Core clock
- * @reg_clk:           Regiters clock
+ * @reg_clk:           Registers clock
  * @complete:          Completion object to wait for NAND controller events
  * @assigned_cs:       Bitmask describing already assigned CS lines
  * @chips:             List containing all the NAND chips attached to
@@ -514,9 +514,14 @@ static void marvell_nfc_enable_int(struct marvell_nfc *nfc, u32 int_mask)
        writel_relaxed(reg & ~int_mask, nfc->regs + NDCR);
 }
 
-static void marvell_nfc_clear_int(struct marvell_nfc *nfc, u32 int_mask)
+static u32 marvell_nfc_clear_int(struct marvell_nfc *nfc, u32 int_mask)
 {
+       u32 reg;
+
+       reg = readl_relaxed(nfc->regs + NDSR);
        writel_relaxed(int_mask, nfc->regs + NDSR);
+
+       return reg & int_mask;
 }
 
 static void marvell_nfc_force_byte_access(struct nand_chip *chip,
@@ -683,6 +688,7 @@ static int marvell_nfc_wait_cmdd(struct nand_chip *chip)
 static int marvell_nfc_wait_op(struct nand_chip *chip, unsigned int timeout_ms)
 {
        struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
+       u32 pending;
        int ret;
 
        /* Timeout is expressed in ms */
@@ -695,8 +701,13 @@ static int marvell_nfc_wait_op(struct nand_chip *chip, unsigned int timeout_ms)
        ret = wait_for_completion_timeout(&nfc->complete,
                                          msecs_to_jiffies(timeout_ms));
        marvell_nfc_disable_int(nfc, NDCR_RDYM);
-       marvell_nfc_clear_int(nfc, NDSR_RDY(0) | NDSR_RDY(1));
-       if (!ret) {
+       pending = marvell_nfc_clear_int(nfc, NDSR_RDY(0) | NDSR_RDY(1));
+
+       /*
+        * In case the interrupt was not served in the required time frame,
+        * check if the ISR was not served or if something went actually wrong.
+        */
+       if (ret && !pending) {
                dev_err(nfc->dev, "Timeout waiting for RB signal\n");
                return -ETIMEDOUT;
        }
@@ -704,7 +715,8 @@ static int marvell_nfc_wait_op(struct nand_chip *chip, unsigned int timeout_ms)
        return 0;
 }
 
-static void marvell_nfc_select_chip(struct nand_chip *chip, int die_nr)
+static void marvell_nfc_select_target(struct nand_chip *chip,
+                                     unsigned int die_nr)
 {
        struct marvell_nand_chip *marvell_nand = to_marvell_nand(chip);
        struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
@@ -713,12 +725,6 @@ static void marvell_nfc_select_chip(struct nand_chip *chip, int die_nr)
        if (chip == nfc->selected_chip && die_nr == marvell_nand->selected_die)
                return;
 
-       if (die_nr < 0 || die_nr >= marvell_nand->nsels) {
-               nfc->selected_chip = NULL;
-               marvell_nand->selected_die = -1;
-               return;
-       }
-
        writel_relaxed(marvell_nand->ndtr0, nfc->regs + NDTR0);
        writel_relaxed(marvell_nand->ndtr1, nfc->regs + NDTR1);
 
@@ -1024,13 +1030,13 @@ static int marvell_nfc_hw_ecc_hmg_do_read_page(struct nand_chip *chip,
        }
 
        ret = marvell_nfc_wait_cmdd(chip);
-
        return ret;
 }
 
 static int marvell_nfc_hw_ecc_hmg_read_page_raw(struct nand_chip *chip, u8 *buf,
                                                int oob_required, int page)
 {
+       marvell_nfc_select_target(chip, chip->cur_cs);
        return marvell_nfc_hw_ecc_hmg_do_read_page(chip, buf, chip->oob_poi,
                                                   true, page);
 }
@@ -1043,6 +1049,7 @@ static int marvell_nfc_hw_ecc_hmg_read_page(struct nand_chip *chip, u8 *buf,
        int max_bitflips = 0, ret;
        u8 *raw_buf;
 
+       marvell_nfc_select_target(chip, chip->cur_cs);
        marvell_nfc_enable_hw_ecc(chip);
        marvell_nfc_hw_ecc_hmg_do_read_page(chip, buf, chip->oob_poi, false,
                                            page);
@@ -1079,6 +1086,7 @@ static int marvell_nfc_hw_ecc_hmg_read_oob_raw(struct nand_chip *chip, int page)
        /* Invalidate page cache */
        chip->pagebuf = -1;
 
+       marvell_nfc_select_target(chip, chip->cur_cs);
        return marvell_nfc_hw_ecc_hmg_do_read_page(chip, chip->data_buf,
                                                   chip->oob_poi, true, page);
 }
@@ -1142,6 +1150,7 @@ static int marvell_nfc_hw_ecc_hmg_write_page_raw(struct nand_chip *chip,
                                                 const u8 *buf,
                                                 int oob_required, int page)
 {
+       marvell_nfc_select_target(chip, chip->cur_cs);
        return marvell_nfc_hw_ecc_hmg_do_write_page(chip, buf, chip->oob_poi,
                                                    true, page);
 }
@@ -1152,6 +1161,7 @@ static int marvell_nfc_hw_ecc_hmg_write_page(struct nand_chip *chip,
 {
        int ret;
 
+       marvell_nfc_select_target(chip, chip->cur_cs);
        marvell_nfc_enable_hw_ecc(chip);
        ret = marvell_nfc_hw_ecc_hmg_do_write_page(chip, buf, chip->oob_poi,
                                                   false, page);
@@ -1175,6 +1185,7 @@ static int marvell_nfc_hw_ecc_hmg_write_oob_raw(struct nand_chip *chip,
 
        memset(chip->data_buf, 0xFF, mtd->writesize);
 
+       marvell_nfc_select_target(chip, chip->cur_cs);
        return marvell_nfc_hw_ecc_hmg_do_write_page(chip, chip->data_buf,
                                                    chip->oob_poi, true, page);
 }
@@ -1194,6 +1205,8 @@ static int marvell_nfc_hw_ecc_bch_read_page_raw(struct nand_chip *chip, u8 *buf,
        int ecc_len = lt->ecc_bytes;
        int chunk;
 
+       marvell_nfc_select_target(chip, chip->cur_cs);
+
        if (oob_required)
                memset(chip->oob_poi, 0xFF, mtd->oobsize);
 
@@ -1304,6 +1317,8 @@ static int marvell_nfc_hw_ecc_bch_read_page(struct nand_chip *chip,
        u32 failure_mask = 0;
        int chunk, ret;
 
+       marvell_nfc_select_target(chip, chip->cur_cs);
+
        /*
         * With BCH, OOB is not fully used (and thus not read entirely), not
         * expected bytes could show up at the end of the OOB buffer if not
@@ -1448,6 +1463,8 @@ static int marvell_nfc_hw_ecc_bch_write_page_raw(struct nand_chip *chip,
                lt->last_spare_bytes;
        int chunk;
 
+       marvell_nfc_select_target(chip, chip->cur_cs);
+
        nand_prog_page_begin_op(chip, page, 0, NULL, 0);
 
        for (chunk = 0; chunk < lt->nchunks; chunk++) {
@@ -1559,6 +1576,8 @@ static int marvell_nfc_hw_ecc_bch_write_page(struct nand_chip *chip,
        int spare_len = lt->spare_bytes;
        int chunk, ret;
 
+       marvell_nfc_select_target(chip, chip->cur_cs);
+
        /* Spare data will be written anyway, so clear it to avoid garbage */
        if (!oob_required)
                memset(chip->oob_poi, 0xFF, mtd->oobsize);
@@ -2097,6 +2116,8 @@ static int marvell_nfc_exec_op(struct nand_chip *chip,
 {
        struct marvell_nfc *nfc = to_marvell_nfc(chip->controller);
 
+       marvell_nfc_select_target(chip, op->cs);
+
        if (nfc->caps->is_nfcv2)
                return nand_op_parser_exec_op(chip, &marvell_nfcv2_op_parser,
                                              op, check_only);
@@ -2495,6 +2516,8 @@ static int marvell_nand_attach_chip(struct nand_chip *chip)
 
 static const struct nand_controller_ops marvell_nand_controller_ops = {
        .attach_chip = marvell_nand_attach_chip,
+       .exec_op = marvell_nfc_exec_op,
+       .setup_data_interface = marvell_nfc_setup_data_interface,
 };
 
 static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
@@ -2617,10 +2640,8 @@ static int marvell_nand_chip_init(struct device *dev, struct marvell_nfc *nfc,
        chip->controller = &nfc->controller;
        nand_set_flash_node(chip, np);
 
-       chip->exec_op = marvell_nfc_exec_op;
-       chip->select_chip = marvell_nfc_select_chip;
        if (!of_property_read_bool(np, "marvell,nand-keep-config"))
-               chip->setup_data_interface = marvell_nfc_setup_data_interface;
+               chip->options |= NAND_KEEP_TIMINGS;
 
        mtd = nand_to_mtd(chip);
        mtd->dev.parent = dev;
index 86a0aab..062cd1e 100644 (file)
@@ -697,7 +697,7 @@ static int mpc5121_nfc_probe(struct platform_device *op)
        chip->legacy.read_byte = mpc5121_nfc_read_byte;
        chip->legacy.read_buf = mpc5121_nfc_read_buf;
        chip->legacy.write_buf = mpc5121_nfc_write_buf;
-       chip->select_chip = mpc5121_nfc_select_chip;
+       chip->legacy.select_chip = mpc5121_nfc_select_chip;
        chip->legacy.set_features = nand_get_set_features_notsupp;
        chip->legacy.get_features = nand_get_set_features_notsupp;
        chip->bbt_options = NAND_BBT_USE_FLASH;
@@ -712,7 +712,7 @@ static int mpc5121_nfc_probe(struct platform_device *op)
                        return retval;
                }
 
-               chip->select_chip = ads5121_select_chip;
+               chip->legacy.select_chip = ads5121_select_chip;
        }
 
        /* Enable NFC clock */
index 2bb0df1..b6b4602 100644 (file)
@@ -1288,6 +1288,7 @@ static int mtk_nfc_attach_chip(struct nand_chip *chip)
 
 static const struct nand_controller_ops mtk_nfc_controller_ops = {
        .attach_chip = mtk_nfc_attach_chip,
+       .setup_data_interface = mtk_nfc_setup_data_interface,
 };
 
 static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
@@ -1333,13 +1334,12 @@ static int mtk_nfc_nand_chip_init(struct device *dev, struct mtk_nfc *nfc,
 
        nand->options |= NAND_USE_BOUNCE_BUFFER | NAND_SUBPAGE_READ;
        nand->legacy.dev_ready = mtk_nfc_dev_ready;
-       nand->select_chip = mtk_nfc_select_chip;
+       nand->legacy.select_chip = mtk_nfc_select_chip;
        nand->legacy.write_byte = mtk_nfc_write_byte;
        nand->legacy.write_buf = mtk_nfc_write_buf;
        nand->legacy.read_byte = mtk_nfc_read_byte;
        nand->legacy.read_buf = mtk_nfc_read_buf;
        nand->legacy.cmd_ctrl = mtk_nfc_cmd_ctrl;
-       nand->setup_data_interface = mtk_nfc_setup_data_interface;
 
        /* set default mode in case dt entry is missing */
        nand->ecc.mode = NAND_ECC_HW;
index 88bd3f6..59554c1 100644 (file)
@@ -1738,8 +1738,17 @@ static int mxcnd_attach_chip(struct nand_chip *chip)
        return 0;
 }
 
+static int mxcnd_setup_data_interface(struct nand_chip *chip, int chipnr,
+                                     const struct nand_data_interface *conf)
+{
+       struct mxc_nand_host *host = nand_get_controller_data(chip);
+
+       return host->devtype_data->setup_data_interface(chip, chipnr, conf);
+}
+
 static const struct nand_controller_ops mxcnd_controller_ops = {
        .attach_chip = mxcnd_attach_chip,
+       .setup_data_interface = mxcnd_setup_data_interface,
 };
 
 static int mxcnd_probe(struct platform_device *pdev)
@@ -1800,7 +1809,8 @@ static int mxcnd_probe(struct platform_device *pdev)
        if (err < 0)
                return err;
 
-       this->setup_data_interface = host->devtype_data->setup_data_interface;
+       if (!host->devtype_data->setup_data_interface)
+               this->options |= NAND_KEEP_TIMINGS;
 
        if (host->devtype_data->needs_ip) {
                res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1828,7 +1838,7 @@ static int mxcnd_probe(struct platform_device *pdev)
        this->ecc.bytes = host->devtype_data->eccbytes;
        host->eccsize = host->devtype_data->eccsize;
 
-       this->select_chip = host->devtype_data->select_chip;
+       this->legacy.select_chip = host->devtype_data->select_chip;
        this->ecc.size = 512;
        mtd_set_ooblayout(mtd, host->devtype_data->ooblayout);
 
@@ -1881,7 +1891,7 @@ static int mxcnd_probe(struct platform_device *pdev)
        }
 
        /* Scan the NAND device */
-       this->dummy_controller.ops = &mxcnd_controller_ops;
+       this->legacy.dummy_controller.ops = &mxcnd_controller_ops;
        err = nand_scan(this, is_imx25_nfc(host) ? 4 : 1);
        if (err)
                goto escan;
index 71050a0..cca4b24 100644 (file)
 #include <linux/io.h>
 #include <linux/mtd/partitions.h>
 #include <linux/of.h>
+#include <linux/gpio/consumer.h>
 
 #include "internals.h"
 
-static int nand_get_device(struct mtd_info *mtd, int new_state);
-
-static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
-                            struct mtd_oob_ops *ops);
-
 /* Define default oob placement schemes for large and small page devices */
 static int nand_ooblayout_ecc_sp(struct mtd_info *mtd, int section,
                                 struct mtd_oob_region *oobregion)
@@ -213,10 +209,8 @@ static const struct mtd_ooblayout_ops nand_ooblayout_lp_hamming_ops = {
        .free = nand_ooblayout_free_lp_hamming,
 };
 
-static int check_offs_len(struct mtd_info *mtd,
-                                       loff_t ofs, uint64_t len)
+static int check_offs_len(struct nand_chip *chip, loff_t ofs, uint64_t len)
 {
-       struct nand_chip *chip = mtd_to_nand(mtd);
        int ret = 0;
 
        /* Start address must align on block boundary */
@@ -234,16 +228,55 @@ static int check_offs_len(struct mtd_info *mtd,
        return ret;
 }
 
+/**
+ * nand_select_target() - Select a NAND target (A.K.A. die)
+ * @chip: NAND chip object
+ * @cs: the CS line to select. Note that this CS id is always from the chip
+ *     PoV, not the controller one
+ *
+ * Select a NAND target so that further operations executed on @chip go to the
+ * selected NAND target.
+ */
+void nand_select_target(struct nand_chip *chip, unsigned int cs)
+{
+       /*
+        * cs should always lie between 0 and chip->numchips, when that's not
+        * the case it's a bug and the caller should be fixed.
+        */
+       if (WARN_ON(cs > chip->numchips))
+               return;
+
+       chip->cur_cs = cs;
+
+       if (chip->legacy.select_chip)
+               chip->legacy.select_chip(chip, cs);
+}
+EXPORT_SYMBOL_GPL(nand_select_target);
+
+/**
+ * nand_deselect_target() - Deselect the currently selected target
+ * @chip: NAND chip object
+ *
+ * Deselect the currently selected NAND target. The result of operations
+ * executed on @chip after the target has been deselected is undefined.
+ */
+void nand_deselect_target(struct nand_chip *chip)
+{
+       if (chip->legacy.select_chip)
+               chip->legacy.select_chip(chip, -1);
+
+       chip->cur_cs = -1;
+}
+EXPORT_SYMBOL_GPL(nand_deselect_target);
+
 /**
  * nand_release_device - [GENERIC] release chip
- * @mtd: MTD device structure
+ * @chip: NAND chip object
  *
  * Release chip lock and wake up anyone waiting on the device.
  */
-static void nand_release_device(struct mtd_info *mtd)
+static void nand_release_device(struct nand_chip *chip)
 {
-       struct nand_chip *chip = mtd_to_nand(mtd);
-
        /* Release the controller and the chip */
        spin_lock(&chip->controller->lock);
        chip->controller->active = NULL;
@@ -289,6 +322,197 @@ static int nand_block_bad(struct nand_chip *chip, loff_t ofs)
        return 0;
 }
 
+static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs)
+{
+       if (chip->legacy.block_bad)
+               return chip->legacy.block_bad(chip, ofs);
+
+       return nand_block_bad(chip, ofs);
+}
+
+/**
+ * panic_nand_get_device - [GENERIC] Get chip for selected access
+ * @chip: the nand chip descriptor
+ * @new_state: the state which is requested
+ *
+ * Used when in panic, no locks are taken.
+ */
+static void panic_nand_get_device(struct nand_chip *chip, int new_state)
+{
+       /* Hardware controller shared among independent devices */
+       chip->controller->active = chip;
+       chip->state = new_state;
+}
+
+/**
+ * nand_get_device - [GENERIC] Get chip for selected access
+ * @chip: NAND chip structure
+ * @new_state: the state which is requested
+ *
+ * Get the device and lock it for exclusive access
+ */
+static int
+nand_get_device(struct nand_chip *chip, int new_state)
+{
+       spinlock_t *lock = &chip->controller->lock;
+       wait_queue_head_t *wq = &chip->controller->wq;
+       DECLARE_WAITQUEUE(wait, current);
+retry:
+       spin_lock(lock);
+
+       /* Hardware controller shared among independent devices */
+       if (!chip->controller->active)
+               chip->controller->active = chip;
+
+       if (chip->controller->active == chip && chip->state == FL_READY) {
+               chip->state = new_state;
+               spin_unlock(lock);
+               return 0;
+       }
+       if (new_state == FL_PM_SUSPENDED) {
+               if (chip->controller->active->state == FL_PM_SUSPENDED) {
+                       chip->state = FL_PM_SUSPENDED;
+                       spin_unlock(lock);
+                       return 0;
+               }
+       }
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       add_wait_queue(wq, &wait);
+       spin_unlock(lock);
+       schedule();
+       remove_wait_queue(wq, &wait);
+       goto retry;
+}
+
+/**
+ * nand_check_wp - [GENERIC] check if the chip is write protected
+ * @chip: NAND chip object
+ *
+ * Check, if the device is write protected. The function expects, that the
+ * device is already selected.
+ */
+static int nand_check_wp(struct nand_chip *chip)
+{
+       u8 status;
+       int ret;
+
+       /* Broken xD cards report WP despite being writable */
+       if (chip->options & NAND_BROKEN_XD)
+               return 0;
+
+       /* Check the WP bit */
+       ret = nand_status_op(chip, &status);
+       if (ret)
+               return ret;
+
+       return status & NAND_STATUS_WP ? 0 : 1;
+}
+
+/**
+ * nand_fill_oob - [INTERN] Transfer client buffer to oob
+ * @oob: oob data buffer
+ * @len: oob data write length
+ * @ops: oob ops structure
+ */
+static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
+                             struct mtd_oob_ops *ops)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       int ret;
+
+       /*
+        * Initialise to all 0xFF, to avoid the possibility of left over OOB
+        * data from a previous OOB read.
+        */
+       memset(chip->oob_poi, 0xff, mtd->oobsize);
+
+       switch (ops->mode) {
+
+       case MTD_OPS_PLACE_OOB:
+       case MTD_OPS_RAW:
+               memcpy(chip->oob_poi + ops->ooboffs, oob, len);
+               return oob + len;
+
+       case MTD_OPS_AUTO_OOB:
+               ret = mtd_ooblayout_set_databytes(mtd, oob, chip->oob_poi,
+                                                 ops->ooboffs, len);
+               BUG_ON(ret);
+               return oob + len;
+
+       default:
+               BUG();
+       }
+       return NULL;
+}
+
+/**
+ * nand_do_write_oob - [MTD Interface] NAND write out-of-band
+ * @chip: NAND chip object
+ * @to: offset to write to
+ * @ops: oob operation description structure
+ *
+ * NAND write out-of-band.
+ */
+static int nand_do_write_oob(struct nand_chip *chip, loff_t to,
+                            struct mtd_oob_ops *ops)
+{
+       struct mtd_info *mtd = nand_to_mtd(chip);
+       int chipnr, page, status, len;
+
+       pr_debug("%s: to = 0x%08x, len = %i\n",
+                        __func__, (unsigned int)to, (int)ops->ooblen);
+
+       len = mtd_oobavail(mtd, ops);
+
+       /* Do not allow write past end of page */
+       if ((ops->ooboffs + ops->ooblen) > len) {
+               pr_debug("%s: attempt to write past end of page\n",
+                               __func__);
+               return -EINVAL;
+       }
+
+       chipnr = (int)(to >> chip->chip_shift);
+
+       /*
+        * Reset the chip. Some chips (like the Toshiba TC5832DC found in one
+        * of my DiskOnChip 2000 test units) will clear the whole data page too
+        * if we don't do this. I have no clue why, but I seem to have 'fixed'
+        * it in the doc2000 driver in August 1999.  dwmw2.
+        */
+       nand_reset(chip, chipnr);
+
+       nand_select_target(chip, chipnr);
+
+       /* Shift to get page */
+       page = (int)(to >> chip->page_shift);
+
+       /* Check, if it is write protected */
+       if (nand_check_wp(chip)) {
+               nand_deselect_target(chip);
+               return -EROFS;
+       }
+
+       /* Invalidate the page cache, if we write to the cached page */
+       if (page == chip->pagebuf)
+               chip->pagebuf = -1;
+
+       nand_fill_oob(chip, ops->oobbuf, ops->ooblen, ops);
+
+       if (ops->mode == MTD_OPS_RAW)
+               status = chip->ecc.write_oob_raw(chip, page & chip->pagemask);
+       else
+               status = chip->ecc.write_oob(chip, page & chip->pagemask);
+
+       nand_deselect_target(chip);
+
+       if (status)
+               return status;
+
+       ops->oobretlen = ops->ooblen;
+
+       return 0;
+}
+
 /**
  * nand_default_block_markbad - [DEFAULT] mark a block bad via bad block marker
  * @chip: NAND chip object
@@ -320,7 +544,7 @@ static int nand_default_block_markbad(struct nand_chip *chip, loff_t ofs)
        if (chip->bbt_options & NAND_BBT_SCANLASTPAGE)
                ofs += mtd->erasesize - mtd->writesize;
        do {
-               res = nand_do_write_oob(mtd, ofs, &ops);
+               res = nand_do_write_oob(chip, ofs, &ops);
                if (!ret)
                        ret = res;
 
@@ -344,17 +568,9 @@ int nand_markbad_bbm(struct nand_chip *chip, loff_t ofs)
        return nand_default_block_markbad(chip, ofs);
 }
 
-static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs)
-{
-       if (chip->legacy.block_bad)
-               return chip->legacy.block_bad(chip, ofs);
-
-       return nand_block_bad(chip, ofs);
-}
-
 /**
  * nand_block_markbad_lowlevel - mark a block bad
- * @mtd: MTD device structure
+ * @chip: NAND chip object
  * @ofs: offset from device start
  *
  * This function performs the generic NAND bad block marking steps (i.e., bad
@@ -371,9 +587,9 @@ static int nand_isbad_bbm(struct nand_chip *chip, loff_t ofs)
  * Note that we retain the first error encountered in (2) or (3), finish the
  * procedures, and dump the error in the end.
 */
-static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
+static int nand_block_markbad_lowlevel(struct nand_chip *chip, loff_t ofs)
 {
-       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct mtd_info *mtd = nand_to_mtd(chip);
        int res, ret = 0;
 
        if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) {
@@ -386,9 +602,9 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
                nand_erase_nand(chip, &einfo, 0);
 
                /* Write bad block marker to OOB */
-               nand_get_device(mtd, FL_WRITING);
+               nand_get_device(chip, FL_WRITING);
                ret = nand_markbad_bbm(chip, ofs);
-               nand_release_device(mtd);
+               nand_release_device(chip);
        }
 
        /* Mark block bad in BBT */
@@ -404,31 +620,6 @@ static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs)
        return ret;
 }
 
-/**
- * nand_check_wp - [GENERIC] check if the chip is write protected
- * @mtd: MTD device structure
- *
- * Check, if the device is write protected. The function expects, that the
- * device is already selected.
- */
-static int nand_check_wp(struct mtd_info *mtd)
-{
-       struct nand_chip *chip = mtd_to_nand(mtd);
-       u8 status;
-       int ret;
-
-       /* Broken xD cards report WP despite being writable */
-       if (chip->options & NAND_BROKEN_XD)
-               return 0;
-
-       /* Check the WP bit */
-       ret = nand_status_op(chip, &status);
-       if (ret)
-               return ret;
-
-       return status & NAND_STATUS_WP ? 0 : 1;
-}
-
 /**
  * nand_block_isreserved - [GENERIC] Check if a block is marked reserved.
  * @mtd: MTD device structure
@@ -448,17 +639,15 @@ static int nand_block_isreserved(struct mtd_info *mtd, loff_t ofs)
 
 /**
  * nand_block_checkbad - [GENERIC] Check if a block is marked bad
- * @mtd: MTD device structure
+ * @chip: NAND chip object
  * @ofs: offset from device start
  * @allowbbt: 1, if its allowed to access the bbt area
  *
  * Check, if the block is bad. Either by reading the bad block table or
  * calling of the scan function.
  */
-static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int allowbbt)
+static int nand_block_checkbad(struct nand_chip *chip, loff_t ofs, int allowbbt)
 {
-       struct nand_chip *chip = mtd_to_nand(mtd);
-
        /* Return info from the table */
        if (chip->bbt)
                return nand_isbad_bbt(chip, ofs, allowbbt);
@@ -489,7 +678,7 @@ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms)
        u8 status = 0;
        int ret;
 
-       if (!chip->exec_op)
+       if (!nand_has_exec_op(chip))
                return -ENOTSUPP;
 
        /* Wait tWB before polling the STATUS reg. */
@@ -532,61 +721,34 @@ int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms)
 EXPORT_SYMBOL_GPL(nand_soft_waitrdy);
 
 /**
- * panic_nand_get_device - [GENERIC] Get chip for selected access
- * @chip: the nand chip descriptor
- * @mtd: MTD device structure
- * @new_state: the state which is requested
+ * nand_gpio_waitrdy - Poll R/B GPIO pin until ready
+ * @chip: NAND chip structure
+ * @gpiod: GPIO descriptor of R/B pin
+ * @timeout_ms: Timeout in ms
  *
- * Used when in panic, no locks are taken.
- */
-static void panic_nand_get_device(struct nand_chip *chip,
-                     struct mtd_info *mtd, int new_state)
-{
-       /* Hardware controller shared among independent devices */
-       chip->controller->active = chip;
-       chip->state = new_state;
-}
-
-/**
- * nand_get_device - [GENERIC] Get chip for selected access
- * @mtd: MTD device structure
- * @new_state: the state which is requested
+ * Poll the R/B GPIO pin until it becomes ready. If that does not happen
+ * whitin the specified timeout, -ETIMEDOUT is returned.
  *
- * Get the device and lock it for exclusive access
+ * This helper is intended to be used when the controller has access to the
+ * NAND R/B pin over GPIO.
+ *
+ * Return 0 if the R/B pin indicates chip is ready, a negative error otherwise.
  */
-static int
-nand_get_device(struct mtd_info *mtd, int new_state)
+int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod,
+                     unsigned long timeout_ms)
 {
-       struct nand_chip *chip = mtd_to_nand(mtd);
-       spinlock_t *lock = &chip->controller->lock;
-       wait_queue_head_t *wq = &chip->controller->wq;
-       DECLARE_WAITQUEUE(wait, current);
-retry:
-       spin_lock(lock);
+       /* Wait until R/B pin indicates chip is ready or timeout occurs */
+       timeout_ms = jiffies + msecs_to_jiffies(timeout_ms);
+       do {
+               if (gpiod_get_value_cansleep(gpiod))
+                       return 0;
 
-       /* Hardware controller shared among independent devices */
-       if (!chip->controller->active)
-               chip->controller->active = chip;
+               cond_resched();
+       } while (time_before(jiffies, timeout_ms));
 
-       if (chip->controller->active == chip && chip->state == FL_READY) {
-               chip->state = new_state;
-               spin_unlock(lock);
-               return 0;
-       }
-       if (new_state == FL_PM_SUSPENDED) {
-               if (chip->controller->active->state == FL_PM_SUSPENDED) {
-                       chip->state = FL_PM_SUSPENDED;
-                       spin_unlock(lock);
-                       return 0;
-               }
-       }
-       set_current_state(TASK_UNINTERRUPTIBLE);
-       add_wait_queue(wq, &wait);
-       spin_unlock(lock);
-       schedule();
-       remove_wait_queue(wq, &wait);
-       goto retry;
-}
+       return gpiod_get_value_cansleep(gpiod) ? 0 : -ETIMEDOUT;
+};
+EXPORT_SYMBOL_GPL(nand_gpio_waitrdy);
 
 /**
  * panic_nand_wait - [GENERIC] wait until the command is done
@@ -645,7 +807,7 @@ static int nand_reset_data_interface(struct nand_chip *chip, int chipnr)
 {
        int ret;
 
-       if (!chip->setup_data_interface)
+       if (!nand_has_setup_data_iface(chip))
                return 0;
 
        /*
@@ -663,7 +825,8 @@ static int nand_reset_data_interface(struct nand_chip *chip, int chipnr)
         */
 
        onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0);
-       ret = chip->setup_data_interface(chip, chipnr, &chip->data_interface);
+       ret = chip->controller->ops->setup_data_interface(chip, chipnr,
+                                                       &chip->data_interface);
        if (ret)
                pr_err("Failed to configure data interface to SDR timing mode 0\n");
 
@@ -690,21 +853,22 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr)
        };
        int ret;
 
-       if (!chip->setup_data_interface)
+       if (!nand_has_setup_data_iface(chip))
                return 0;
 
        /* Change the mode on the chip side (if supported by the NAND chip) */
        if (nand_supports_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE)) {
-               chip->select_chip(chip, chipnr);
+               nand_select_target(chip, chipnr);
                ret = nand_set_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE,
                                        tmode_param);
-               chip->select_chip(chip, -1);
+               nand_deselect_target(chip);
                if (ret)
                        return ret;
        }
 
        /* Change the mode on the controller side */
-       ret = chip->setup_data_interface(chip, chipnr, &chip->data_interface);
+       ret = chip->controller->ops->setup_data_interface(chip, chipnr,
+                                                       &chip->data_interface);
        if (ret)
                return ret;
 
@@ -713,10 +877,10 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr)
                return 0;
 
        memset(tmode_param, 0, ONFI_SUBFEATURE_PARAM_LEN);
-       chip->select_chip(chip, chipnr);
+       nand_select_target(chip, chipnr);
        ret = nand_get_features(chip, ONFI_FEATURE_ADDR_TIMING_MODE,
                                tmode_param);
-       chip->select_chip(chip, -1);
+       nand_deselect_target(chip);
        if (ret)
                goto err_reset_chip;
 
@@ -734,9 +898,9 @@ err_reset_chip:
         * timing mode.
         */
        nand_reset_data_interface(chip, chipnr);
-       chip->select_chip(chip, chipnr);
+       nand_select_target(chip, chipnr);
        nand_reset_op(chip);
-       chip->select_chip(chip, -1);
+       nand_deselect_target(chip);
 
        return ret;
 }
@@ -759,7 +923,7 @@ static int nand_init_data_interface(struct nand_chip *chip)
 {
        int modes, mode, ret;
 
-       if (!chip->setup_data_interface)
+       if (!nand_has_setup_data_iface(chip))
                return 0;
 
        /*
@@ -785,7 +949,7 @@ static int nand_init_data_interface(struct nand_chip *chip)
                 * Pass NAND_DATA_IFACE_CHECK_ONLY to only check if the
                 * controller supports the requested timings.
                 */
-               ret = chip->setup_data_interface(chip,
+               ret = chip->controller->ops->setup_data_interface(chip,
                                                 NAND_DATA_IFACE_CHECK_ONLY,
                                                 &chip->data_interface);
                if (!ret) {
@@ -866,7 +1030,7 @@ static int nand_sp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
                                 PSEC_TO_NSEC(sdr->tRR_min)),
                NAND_OP_DATA_IN(len, buf, 0),
        };
-       struct nand_operation op = NAND_OPERATION(instrs);
+       struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
        int ret;
 
        /* Drop the DATA_IN instruction if len is set to 0. */
@@ -909,7 +1073,7 @@ static int nand_lp_exec_read_page_op(struct nand_chip *chip, unsigned int page,
                                 PSEC_TO_NSEC(sdr->tRR_min)),
                NAND_OP_DATA_IN(len, buf, 0),
        };
-       struct nand_operation op = NAND_OPERATION(instrs);
+       struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
        int ret;
 
        /* Drop the DATA_IN instruction if len is set to 0. */
@@ -955,7 +1119,7 @@ int nand_read_page_op(struct nand_chip *chip, unsigned int page,
        if (offset_in_page + len > mtd->writesize + mtd->oobsize)
                return -EINVAL;
 
-       if (chip->exec_op) {
+       if (nand_has_exec_op(chip)) {
                if (mtd->writesize > 512)
                        return nand_lp_exec_read_page_op(chip, page,
                                                         offset_in_page, buf,
@@ -994,7 +1158,7 @@ int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf,
        if (len && !buf)
                return -EINVAL;
 
-       if (chip->exec_op) {
+       if (nand_has_exec_op(chip)) {
                const struct nand_sdr_timings *sdr =
                        nand_get_sdr_timings(&chip->data_interface);
                struct nand_op_instr instrs[] = {
@@ -1004,7 +1168,7 @@ int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf,
                                         PSEC_TO_NSEC(sdr->tRR_min)),
                        NAND_OP_8BIT_DATA_IN(len, buf, 0),
                };
-               struct nand_operation op = NAND_OPERATION(instrs);
+               struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
 
                /* Drop the DATA_IN instruction if len is set to 0. */
                if (!len)
@@ -1049,7 +1213,7 @@ int nand_change_read_column_op(struct nand_chip *chip,
        if (mtd->writesize <= 512)
                return -ENOTSUPP;
 
-       if (chip->exec_op) {
+       if (nand_has_exec_op(chip)) {
                const struct nand_sdr_timings *sdr =
                        nand_get_sdr_timings(&chip->data_interface);
                u8 addrs[2] = {};
@@ -1060,7 +1224,7 @@ int nand_change_read_column_op(struct nand_chip *chip,
                                    PSEC_TO_NSEC(sdr->tCCS_min)),
                        NAND_OP_DATA_IN(len, buf, 0),
                };
-               struct nand_operation op = NAND_OPERATION(instrs);
+               struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
                int ret;
 
                ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
@@ -1108,7 +1272,7 @@ int nand_read_oob_op(struct nand_chip *chip, unsigned int page,
        if (offset_in_oob + len > mtd->oobsize)
                return -EINVAL;
 
-       if (chip->exec_op)
+       if (nand_has_exec_op(chip))
                return nand_read_page_op(chip, page,
                                         mtd->writesize + offset_in_oob,
                                         buf, len);
@@ -1142,7 +1306,7 @@ static int nand_exec_prog_page_op(struct nand_chip *chip, unsigned int page,
                NAND_OP_CMD(NAND_CMD_PAGEPROG, PSEC_TO_NSEC(sdr->tWB_max)),
                NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0),
        };
-       struct nand_operation op = NAND_OPERATION(instrs);
+       struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
        int naddrs = nand_fill_column_cycles(chip, addrs, offset_in_page);
        int ret;
        u8 status;
@@ -1221,7 +1385,7 @@ int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page,
        if (offset_in_page + len > mtd->writesize + mtd->oobsize)
                return -EINVAL;
 
-       if (chip->exec_op)
+       if (nand_has_exec_op(chip))
                return nand_exec_prog_page_op(chip, page, offset_in_page, buf,
                                              len, false);
 
@@ -1248,7 +1412,7 @@ int nand_prog_page_end_op(struct nand_chip *chip)
        int ret;
        u8 status;
 
-       if (chip->exec_op) {
+       if (nand_has_exec_op(chip)) {
                const struct nand_sdr_timings *sdr =
                        nand_get_sdr_timings(&chip->data_interface);
                struct nand_op_instr instrs[] = {
@@ -1256,7 +1420,7 @@ int nand_prog_page_end_op(struct nand_chip *chip)
                                    PSEC_TO_NSEC(sdr->tWB_max)),
                        NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tPROG_max), 0),
                };
-               struct nand_operation op = NAND_OPERATION(instrs);
+               struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
 
                ret = nand_exec_op(chip, &op);
                if (ret)
@@ -1307,7 +1471,7 @@ int nand_prog_page_op(struct nand_chip *chip, unsigned int page,
        if (offset_in_page + len > mtd->writesize + mtd->oobsize)
                return -EINVAL;
 
-       if (chip->exec_op) {
+       if (nand_has_exec_op(chip)) {
                status = nand_exec_prog_page_op(chip, page, offset_in_page, buf,
                                                len, true);
        } else {
@@ -1355,7 +1519,7 @@ int nand_change_write_column_op(struct nand_chip *chip,
        if (mtd->writesize <= 512)
                return -ENOTSUPP;
 
-       if (chip->exec_op) {
+       if (nand_has_exec_op(chip)) {
                const struct nand_sdr_timings *sdr =
                        nand_get_sdr_timings(&chip->data_interface);
                u8 addrs[2];
@@ -1364,7 +1528,7 @@ int nand_change_write_column_op(struct nand_chip *chip,
                        NAND_OP_ADDR(2, addrs, PSEC_TO_NSEC(sdr->tCCS_min)),
                        NAND_OP_DATA_OUT(len, buf, 0),
                };
-               struct nand_operation op = NAND_OPERATION(instrs);
+               struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
                int ret;
 
                ret = nand_fill_column_cycles(chip, addrs, offset_in_page);
@@ -1410,7 +1574,7 @@ int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf,
        if (len && !buf)
                return -EINVAL;
 
-       if (chip->exec_op) {
+       if (nand_has_exec_op(chip)) {
                const struct nand_sdr_timings *sdr =
                        nand_get_sdr_timings(&chip->data_interface);
                struct nand_op_instr instrs[] = {
@@ -1418,7 +1582,7 @@ int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf,
                        NAND_OP_ADDR(1, &addr, PSEC_TO_NSEC(sdr->tADL_min)),
                        NAND_OP_8BIT_DATA_IN(len, buf, 0),
                };
-               struct nand_operation op = NAND_OPERATION(instrs);
+               struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
 
                /* Drop the DATA_IN instruction if len is set to 0. */
                if (!len)
@@ -1449,7 +1613,7 @@ EXPORT_SYMBOL_GPL(nand_readid_op);
  */
 int nand_status_op(struct nand_chip *chip, u8 *status)
 {
-       if (chip->exec_op) {
+       if (nand_has_exec_op(chip)) {
                const struct nand_sdr_timings *sdr =
                        nand_get_sdr_timings(&chip->data_interface);
                struct nand_op_instr instrs[] = {
@@ -1457,7 +1621,7 @@ int nand_status_op(struct nand_chip *chip, u8 *status)
                                    PSEC_TO_NSEC(sdr->tADL_min)),
                        NAND_OP_8BIT_DATA_IN(1, status, 0),
                };
-               struct nand_operation op = NAND_OPERATION(instrs);
+               struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
 
                if (!status)
                        op.ninstrs--;
@@ -1486,11 +1650,11 @@ EXPORT_SYMBOL_GPL(nand_status_op);
  */
 int nand_exit_status_op(struct nand_chip *chip)
 {
-       if (chip->exec_op) {
+       if (nand_has_exec_op(chip)) {
                struct nand_op_instr instrs[] = {
                        NAND_OP_CMD(NAND_CMD_READ0, 0),
                };
-               struct nand_operation op = NAND_OPERATION(instrs);
+               struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
 
                return nand_exec_op(chip, &op);
        }
@@ -1518,7 +1682,7 @@ int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock)
        int ret;
        u8 status;
 
-       if (chip->exec_op) {
+       if (nand_has_exec_op(chip)) {
                const struct nand_sdr_timings *sdr =
                        nand_get_sdr_timings(&chip->data_interface);
                u8 addrs[3] = { page, page >> 8, page >> 16 };
@@ -1529,7 +1693,7 @@ int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock)
                                    PSEC_TO_MSEC(sdr->tWB_max)),
                        NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tBERS_max), 0),
                };
-               struct nand_operation op = NAND_OPERATION(instrs);
+               struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
 
                if (chip->options & NAND_ROW_ADDR_3)
                        instrs[1].ctx.addr.naddrs++;
@@ -1577,7 +1741,7 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature,
        const u8 *params = data;
        int i, ret;
 
-       if (chip->exec_op) {
+       if (nand_has_exec_op(chip)) {
                const struct nand_sdr_timings *sdr =
                        nand_get_sdr_timings(&chip->data_interface);
                struct nand_op_instr instrs[] = {
@@ -1587,7 +1751,7 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature,
                                              PSEC_TO_NSEC(sdr->tWB_max)),
                        NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tFEAT_max), 0),
                };
-               struct nand_operation op = NAND_OPERATION(instrs);
+               struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
 
                return nand_exec_op(chip, &op);
        }
@@ -1624,7 +1788,7 @@ static int nand_get_features_op(struct nand_chip *chip, u8 feature,
        u8 *params = data;
        int i;
 
-       if (chip->exec_op) {
+       if (nand_has_exec_op(chip)) {
                const struct nand_sdr_timings *sdr =
                        nand_get_sdr_timings(&chip->data_interface);
                struct nand_op_instr instrs[] = {
@@ -1635,7 +1799,7 @@ static int nand_get_features_op(struct nand_chip *chip, u8 feature,
                        NAND_OP_8BIT_DATA_IN(ONFI_SUBFEATURE_PARAM_LEN,
                                             data, 0),
                };
-               struct nand_operation op = NAND_OPERATION(instrs);
+               struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
 
                return nand_exec_op(chip, &op);
        }
@@ -1650,12 +1814,12 @@ static int nand_get_features_op(struct nand_chip *chip, u8 feature,
 static int nand_wait_rdy_op(struct nand_chip *chip, unsigned int timeout_ms,
                            unsigned int delay_ns)
 {
-       if (chip->exec_op) {
+       if (nand_has_exec_op(chip)) {
                struct nand_op_instr instrs[] = {
                        NAND_OP_WAIT_RDY(PSEC_TO_MSEC(timeout_ms),
                                         PSEC_TO_NSEC(delay_ns)),
                };
-               struct nand_operation op = NAND_OPERATION(instrs);
+               struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
 
                return nand_exec_op(chip, &op);
        }
@@ -1681,14 +1845,14 @@ static int nand_wait_rdy_op(struct nand_chip *chip, unsigned int timeout_ms,
  */
 int nand_reset_op(struct nand_chip *chip)
 {
-       if (chip->exec_op) {
+       if (nand_has_exec_op(chip)) {
                const struct nand_sdr_timings *sdr =
                        nand_get_sdr_timings(&chip->data_interface);
                struct nand_op_instr instrs[] = {
                        NAND_OP_CMD(NAND_CMD_RESET, PSEC_TO_NSEC(sdr->tWB_max)),
                        NAND_OP_WAIT_RDY(PSEC_TO_MSEC(sdr->tRST_max), 0),
                };
-               struct nand_operation op = NAND_OPERATION(instrs);
+               struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
 
                return nand_exec_op(chip, &op);
        }
@@ -1718,11 +1882,11 @@ int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len,
        if (!len || !buf)
                return -EINVAL;
 
-       if (chip->exec_op) {
+       if (nand_has_exec_op(chip)) {
                struct nand_op_instr instrs[] = {
                        NAND_OP_DATA_IN(len, buf, 0),
                };
-               struct nand_operation op = NAND_OPERATION(instrs);
+               struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
 
                instrs[0].ctx.data.force_8bit = force_8bit;
 
@@ -1762,11 +1926,11 @@ int nand_write_data_op(struct nand_chip *chip, const void *buf,
        if (!len || !buf)
                return -EINVAL;
 
-       if (chip->exec_op) {
+       if (nand_has_exec_op(chip)) {
                struct nand_op_instr instrs[] = {
                        NAND_OP_DATA_OUT(len, buf, 0),
                };
-               struct nand_operation op = NAND_OPERATION(instrs);
+               struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
 
                instrs[0].ctx.data.force_8bit = force_8bit;
 
@@ -2224,11 +2388,12 @@ int nand_reset(struct nand_chip *chip, int chipnr)
 
        /*
         * The CS line has to be released before we can apply the new NAND
-        * interface settings, hence this weird ->select_chip() dance.
+        * interface settings, hence this weird nand_select_target()
+        * nand_deselect_target() dance.
         */
-       chip->select_chip(chip, chipnr);
+       nand_select_target(chip, chipnr);
        ret = nand_reset_op(chip);
-       chip->select_chip(chip, -1);
+       nand_deselect_target(chip);
        if (ret)
                return ret;
 
@@ -2924,15 +3089,15 @@ static int nand_read_page_syndrome(struct nand_chip *chip, uint8_t *buf,
 
 /**
  * nand_transfer_oob - [INTERN] Transfer oob to client buffer
- * @mtd: mtd info structure
+ * @chip: NAND chip object
  * @oob: oob destination address
  * @ops: oob ops structure
  * @len: size of oob to transfer
  */
-static uint8_t *nand_transfer_oob(struct mtd_info *mtd, uint8_t *oob,
+static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
                                  struct mtd_oob_ops *ops, size_t len)
 {
-       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct mtd_info *mtd = nand_to_mtd(chip);
        int ret;
 
        switch (ops->mode) {
@@ -2989,17 +3154,17 @@ static void nand_wait_readrdy(struct nand_chip *chip)
 
 /**
  * nand_do_read_ops - [INTERN] Read data with ECC
- * @mtd: MTD device structure
+ * @chip: NAND chip object
  * @from: offset to read from
  * @ops: oob ops structure
  *
  * Internal function. Called with chip held.
  */
-static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
+static int nand_do_read_ops(struct nand_chip *chip, loff_t from,
                            struct mtd_oob_ops *ops)
 {
        int chipnr, page, realpage, col, bytes, aligned, oob_required;
-       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct mtd_info *mtd = nand_to_mtd(chip);
        int ret = 0;
        uint32_t readlen = ops->len;
        uint32_t oobreadlen = ops->ooblen;
@@ -3012,7 +3177,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
        bool ecc_fail = false;
 
        chipnr = (int)(from >> chip->chip_shift);
-       chip->select_chip(chip, chipnr);
+       nand_select_target(chip, chipnr);
 
        realpage = (int)(from >> chip->page_shift);
        page = realpage & chip->pagemask;
@@ -3087,8 +3252,8 @@ read_retry:
                                int toread = min(oobreadlen, max_oobsize);
 
                                if (toread) {
-                                       oob = nand_transfer_oob(mtd,
-                                               oob, ops, toread);
+                                       oob = nand_transfer_oob(chip, oob, ops,
+                                                               toread);
                                        oobreadlen -= toread;
                                }
                        }
@@ -3143,11 +3308,11 @@ read_retry:
                /* Check, if we cross a chip boundary */
                if (!page) {
                        chipnr++;
-                       chip->select_chip(chip, -1);
-                       chip->select_chip(chip, chipnr);
+                       nand_deselect_target(chip);
+                       nand_select_target(chip, chipnr);
                }
        }
-       chip->select_chip(chip, -1);
+       nand_deselect_target(chip);
 
        ops->retlen = ops->len - (size_t) readlen;
        if (oob)
@@ -3318,18 +3483,18 @@ static int nand_write_oob_syndrome(struct nand_chip *chip, int page)
 
 /**
  * nand_do_read_oob - [INTERN] NAND read out-of-band
- * @mtd: MTD device structure
+ * @chip: NAND chip object
  * @from: offset to read from
  * @ops: oob operations description structure
  *
  * NAND read out-of-band data from the spare area.
  */
-static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
+static int nand_do_read_oob(struct nand_chip *chip, loff_t from,
                            struct mtd_oob_ops *ops)
 {
+       struct mtd_info *mtd = nand_to_mtd(chip);
        unsigned int max_bitflips = 0;
        int page, realpage, chipnr;
-       struct nand_chip *chip = mtd_to_nand(mtd);
        struct mtd_ecc_stats stats;
        int readlen = ops->ooblen;
        int len;
@@ -3344,7 +3509,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
        len = mtd_oobavail(mtd, ops);
 
        chipnr = (int)(from >> chip->chip_shift);
-       chip->select_chip(chip, chipnr);
+       nand_select_target(chip, chipnr);
 
        /* Shift to get page */
        realpage = (int)(from >> chip->page_shift);
@@ -3360,7 +3525,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
                        break;
 
                len = min(len, readlen);
-               buf = nand_transfer_oob(mtd, buf, ops, len);
+               buf = nand_transfer_oob(chip, buf, ops, len);
 
                nand_wait_readrdy(chip);
 
@@ -3377,11 +3542,11 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
                /* Check, if we cross a chip boundary */
                if (!page) {
                        chipnr++;
-                       chip->select_chip(chip, -1);
-                       chip->select_chip(chip, chipnr);
+                       nand_deselect_target(chip);
+                       nand_select_target(chip, chipnr);
                }
        }
-       chip->select_chip(chip, -1);
+       nand_deselect_target(chip);
 
        ops->oobretlen = ops->ooblen - readlen;
 
@@ -3405,6 +3570,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
 static int nand_read_oob(struct mtd_info *mtd, loff_t from,
                         struct mtd_oob_ops *ops)
 {
+       struct nand_chip *chip = mtd_to_nand(mtd);
        int ret;
 
        ops->retlen = 0;
@@ -3414,14 +3580,14 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
            ops->mode != MTD_OPS_RAW)
                return -ENOTSUPP;
 
-       nand_get_device(mtd, FL_READING);
+       nand_get_device(chip, FL_READING);
 
        if (!ops->datbuf)
-               ret = nand_do_read_oob(mtd, from, ops);
+               ret = nand_do_read_oob(chip, from, ops);
        else
-               ret = nand_do_read_ops(mtd, from, ops);
+               ret = nand_do_read_ops(chip, from, ops);
 
-       nand_release_device(mtd);
+       nand_release_device(chip);
        return ret;
 }
 
@@ -3749,7 +3915,6 @@ static int nand_write_page_syndrome(struct nand_chip *chip, const uint8_t *buf,
 
 /**
  * nand_write_page - write one page
- * @mtd: MTD device structure
  * @chip: NAND chip descriptor
  * @offset: address offset within the page
  * @data_len: length of actual data to be written
@@ -3758,10 +3923,11 @@ static int nand_write_page_syndrome(struct nand_chip *chip, const uint8_t *buf,
  * @page: page number to write
  * @raw: use _raw version of write_page
  */
-static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
-               uint32_t offset, int data_len, const uint8_t *buf,
-               int oob_required, int page, int raw)
+static int nand_write_page(struct nand_chip *chip, uint32_t offset,
+                          int data_len, const uint8_t *buf, int oob_required,
+                          int page, int raw)
 {
+       struct mtd_info *mtd = nand_to_mtd(chip);
        int status, subpage;
 
        if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
@@ -3785,59 +3951,21 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
        return 0;
 }
 
-/**
- * nand_fill_oob - [INTERN] Transfer client buffer to oob
- * @mtd: MTD device structure
- * @oob: oob data buffer
- * @len: oob data write length
- * @ops: oob ops structure
- */
-static uint8_t *nand_fill_oob(struct mtd_info *mtd, uint8_t *oob, size_t len,
-                             struct mtd_oob_ops *ops)
-{
-       struct nand_chip *chip = mtd_to_nand(mtd);
-       int ret;
-
-       /*
-        * Initialise to all 0xFF, to avoid the possibility of left over OOB
-        * data from a previous OOB read.
-        */
-       memset(chip->oob_poi, 0xff, mtd->oobsize);
-
-       switch (ops->mode) {
-
-       case MTD_OPS_PLACE_OOB:
-       case MTD_OPS_RAW:
-               memcpy(chip->oob_poi + ops->ooboffs, oob, len);
-               return oob + len;
-
-       case MTD_OPS_AUTO_OOB:
-               ret = mtd_ooblayout_set_databytes(mtd, oob, chip->oob_poi,
-                                                 ops->ooboffs, len);
-               BUG_ON(ret);
-               return oob + len;
-
-       default:
-               BUG();
-       }
-       return NULL;
-}
-
 #define NOTALIGNED(x)  ((x & (chip->subpagesize - 1)) != 0)
 
 /**
  * nand_do_write_ops - [INTERN] NAND write with ECC
- * @mtd: MTD device structure
+ * @chip: NAND chip object
  * @to: offset to write to
  * @ops: oob operations description structure
  *
  * NAND write with ECC.
  */
-static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
+static int nand_do_write_ops(struct nand_chip *chip, loff_t to,
                             struct mtd_oob_ops *ops)
 {
+       struct mtd_info *mtd = nand_to_mtd(chip);
        int chipnr, realpage, page, column;
-       struct nand_chip *chip = mtd_to_nand(mtd);
        uint32_t writelen = ops->len;
 
        uint32_t oobwritelen = ops->ooblen;
@@ -3862,10 +3990,10 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
        column = to & (mtd->writesize - 1);
 
        chipnr = (int)(to >> chip->chip_shift);
-       chip->select_chip(chip, chipnr);
+       nand_select_target(chip, chipnr);
 
        /* Check, if it is write protected */
-       if (nand_check_wp(mtd)) {
+       if (nand_check_wp(chip)) {
                ret = -EIO;
                goto err_out;
        }
@@ -3913,14 +4041,14 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
 
                if (unlikely(oob)) {
                        size_t len = min(oobwritelen, oobmaxlen);
-                       oob = nand_fill_oob(mtd, oob, len, ops);
+                       oob = nand_fill_oob(chip, oob, len, ops);
                        oobwritelen -= len;
                } else {
                        /* We still need to erase leftover OOB data */
                        memset(chip->oob_poi, 0xff, mtd->oobsize);
                }
 
-               ret = nand_write_page(mtd, chip, column, bytes, wbuf,
+               ret = nand_write_page(chip, column, bytes, wbuf,
                                      oob_required, page,
                                      (ops->mode == MTD_OPS_RAW));
                if (ret)
@@ -3938,8 +4066,8 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
                /* Check, if we cross a chip boundary */
                if (!page) {
                        chipnr++;
-                       chip->select_chip(chip, -1);
-                       chip->select_chip(chip, chipnr);
+                       nand_deselect_target(chip);
+                       nand_select_target(chip, chipnr);
                }
        }
 
@@ -3948,7 +4076,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
                ops->oobretlen = ops->ooblen;
 
 err_out:
-       chip->select_chip(chip, -1);
+       nand_deselect_target(chip);
        return ret;
 }
 
@@ -3972,9 +4100,9 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
        int ret;
 
        /* Grab the device */
-       panic_nand_get_device(chip, mtd, FL_WRITING);
+       panic_nand_get_device(chip, FL_WRITING);
 
-       chip->select_chip(chip, chipnr);
+       nand_select_target(chip, chipnr);
 
        /* Wait for the device to get ready */
        panic_nand_wait(chip, 400);
@@ -3984,80 +4112,12 @@ static int panic_nand_write(struct mtd_info *mtd, loff_t to, size_t len,
        ops.datbuf = (uint8_t *)buf;
        ops.mode = MTD_OPS_PLACE_OOB;
 
-       ret = nand_do_write_ops(mtd, to, &ops);
+       ret = nand_do_write_ops(chip, to, &ops);
 
        *retlen = ops.retlen;
        return ret;
 }
 
-/**
- * nand_do_write_oob - [MTD Interface] NAND write out-of-band
- * @mtd: MTD device structure
- * @to: offset to write to
- * @ops: oob operation description structure
- *
- * NAND write out-of-band.
- */
-static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
-                            struct mtd_oob_ops *ops)
-{
-       int chipnr, page, status, len;
-       struct nand_chip *chip = mtd_to_nand(mtd);
-
-       pr_debug("%s: to = 0x%08x, len = %i\n",
-                        __func__, (unsigned int)to, (int)ops->ooblen);
-
-       len = mtd_oobavail(mtd, ops);
-
-       /* Do not allow write past end of page */
-       if ((ops->ooboffs + ops->ooblen) > len) {
-               pr_debug("%s: attempt to write past end of page\n",
-                               __func__);
-               return -EINVAL;
-       }
-
-       chipnr = (int)(to >> chip->chip_shift);
-
-       /*
-        * Reset the chip. Some chips (like the Toshiba TC5832DC found in one
-        * of my DiskOnChip 2000 test units) will clear the whole data page too
-        * if we don't do this. I have no clue why, but I seem to have 'fixed'
-        * it in the doc2000 driver in August 1999.  dwmw2.
-        */
-       nand_reset(chip, chipnr);
-
-       chip->select_chip(chip, chipnr);
-
-       /* Shift to get page */
-       page = (int)(to >> chip->page_shift);
-
-       /* Check, if it is write protected */
-       if (nand_check_wp(mtd)) {
-               chip->select_chip(chip, -1);
-               return -EROFS;
-       }
-
-       /* Invalidate the page cache, if we write to the cached page */
-       if (page == chip->pagebuf)
-               chip->pagebuf = -1;
-
-       nand_fill_oob(mtd, ops->oobbuf, ops->ooblen, ops);
-
-       if (ops->mode == MTD_OPS_RAW)
-               status = chip->ecc.write_oob_raw(chip, page & chip->pagemask);
-       else
-               status = chip->ecc.write_oob(chip, page & chip->pagemask);
-
-       chip->select_chip(chip, -1);
-
-       if (status)
-               return status;
-
-       ops->oobretlen = ops->ooblen;
-
-       return 0;
-}
-
 /**
  * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band
  * @mtd: MTD device structure
@@ -4067,11 +4127,12 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
 static int nand_write_oob(struct mtd_info *mtd, loff_t to,
                          struct mtd_oob_ops *ops)
 {
+       struct nand_chip *chip = mtd_to_nand(mtd);
        int ret = -ENOTSUPP;
 
        ops->retlen = 0;
 
-       nand_get_device(mtd, FL_WRITING);
+       nand_get_device(chip, FL_WRITING);
 
        switch (ops->mode) {
        case MTD_OPS_PLACE_OOB:
@@ -4084,12 +4145,12 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
        }
 
        if (!ops->datbuf)
-               ret = nand_do_write_oob(mtd, to, ops);
+               ret = nand_do_write_oob(chip, to, ops);
        else
-               ret = nand_do_write_ops(mtd, to, ops);
+               ret = nand_do_write_ops(chip, to, ops);
 
 out:
-       nand_release_device(mtd);
+       nand_release_device(chip);
        return ret;
 }
 
@@ -4133,7 +4194,6 @@ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
 int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
                    int allowbbt)
 {
-       struct mtd_info *mtd = nand_to_mtd(chip);
        int page, status, pages_per_block, ret, chipnr;
        loff_t len;
 
@@ -4141,11 +4201,11 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
                        __func__, (unsigned long long)instr->addr,
                        (unsigned long long)instr->len);
 
-       if (check_offs_len(mtd, instr->addr, instr->len))
+       if (check_offs_len(chip, instr->addr, instr->len))
                return -EINVAL;
 
        /* Grab the lock and see if the device is available */
-       nand_get_device(mtd, FL_ERASING);
+       nand_get_device(chip, FL_ERASING);
 
        /* Shift to get first page */
        page = (int)(instr->addr >> chip->page_shift);
@@ -4155,10 +4215,10 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
        pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
 
        /* Select the NAND device */
-       chip->select_chip(chip, chipnr);
+       nand_select_target(chip, chipnr);
 
        /* Check, if it is write protected */
-       if (nand_check_wp(mtd)) {
+       if (nand_check_wp(chip)) {
                pr_debug("%s: device is write protected!\n",
                                __func__);
                ret = -EIO;
@@ -4170,7 +4230,7 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
 
        while (len) {
                /* Check if we have a bad block, we do not erase bad blocks! */
-               if (nand_block_checkbad(mtd, ((loff_t) page) <<
+               if (nand_block_checkbad(chip, ((loff_t) page) <<
                                        chip->page_shift, allowbbt)) {
                        pr_warn("%s: attempt to erase a bad block at page 0x%08x\n",
                                    __func__, page);
@@ -4209,8 +4269,8 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
                /* Check, if we cross a chip boundary */
                if (len && !(page & chip->pagemask)) {
                        chipnr++;
-                       chip->select_chip(chip, -1);
-                       chip->select_chip(chip, chipnr);
+                       nand_deselect_target(chip);
+                       nand_select_target(chip, chipnr);
                }
        }
 
@@ -4218,8 +4278,8 @@ int nand_erase_nand(struct nand_chip *chip, struct erase_info *instr,
 erase_exit:
 
        /* Deselect and wake up anyone waiting on the device */
-       chip->select_chip(chip, -1);
-       nand_release_device(mtd);
+       nand_deselect_target(chip);
+       nand_release_device(chip);
 
        /* Return more or less happy */
        return ret;
@@ -4233,12 +4293,14 @@ erase_exit:
  */
 static void nand_sync(struct mtd_info *mtd)
 {
+       struct nand_chip *chip = mtd_to_nand(mtd);
+
        pr_debug("%s: called\n", __func__);
 
        /* Grab the lock and see if the device is available */
-       nand_get_device(mtd, FL_SYNCING);
+       nand_get_device(chip, FL_SYNCING);
        /* Release it and go back */
-       nand_release_device(mtd);
+       nand_release_device(chip);
 }
 
 /**
@@ -4253,13 +4315,13 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
        int ret;
 
        /* Select the NAND device */
-       nand_get_device(mtd, FL_READING);
-       chip->select_chip(chip, chipnr);
+       nand_get_device(chip, FL_READING);
+       nand_select_target(chip, chipnr);
 
-       ret = nand_block_checkbad(mtd, offs, 0);
+       ret = nand_block_checkbad(chip, offs, 0);
 
-       chip->select_chip(chip, -1);
-       nand_release_device(mtd);
+       nand_deselect_target(chip);
+       nand_release_device(chip);
 
        return ret;
 }
@@ -4281,7 +4343,7 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
                return ret;
        }
 
-       return nand_block_markbad_lowlevel(mtd, ofs);
+       return nand_block_markbad_lowlevel(mtd_to_nand(mtd), ofs);
 }
 
 /**
@@ -4326,7 +4388,7 @@ static int nand_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len)
  */
 static int nand_suspend(struct mtd_info *mtd)
 {
-       return nand_get_device(mtd, FL_PM_SUSPENDED);
+       return nand_get_device(mtd_to_nand(mtd), FL_PM_SUSPENDED);
 }
 
 /**
@@ -4338,7 +4400,7 @@ static void nand_resume(struct mtd_info *mtd)
        struct nand_chip *chip = mtd_to_nand(mtd);
 
        if (chip->state == FL_PM_SUSPENDED)
-               nand_release_device(mtd);
+               nand_release_device(chip);
        else
                pr_err("%s called for a chip which is not in suspended state\n",
                        __func__);
@@ -4351,19 +4413,20 @@ static void nand_resume(struct mtd_info *mtd)
  */
 static void nand_shutdown(struct mtd_info *mtd)
 {
-       nand_get_device(mtd, FL_PM_SUSPENDED);
+       nand_get_device(mtd_to_nand(mtd), FL_PM_SUSPENDED);
 }
 
 /* Set default functions */
 static void nand_set_defaults(struct nand_chip *chip)
 {
-       nand_legacy_set_defaults(chip);
-
+       /* If no controller is provided, use the dummy, legacy one. */
        if (!chip->controller) {
-               chip->controller = &chip->dummy_controller;
+               chip->controller = &chip->legacy.dummy_controller;
                nand_controller_init(chip->controller);
        }
 
+       nand_legacy_set_defaults(chip);
+
        if (!chip->buf_align)
                chip->buf_align = 1;
 }
@@ -4627,7 +4690,7 @@ static int nand_detect(struct nand_chip *chip, struct nand_flash_dev *type)
                return ret;
 
        /* Select the device */
-       chip->select_chip(chip, 0);
+       nand_select_target(chip, 0);
 
        /* Send the command for reading device ID */
        ret = nand_readid_op(chip, 0, id_data, 2);
@@ -4952,6 +5015,9 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
        unsigned int i;
        int ret;
 
+       /* Assume all dies are deselected when we enter nand_scan_ident(). */
+       chip->cur_cs = -1;
+
        /* Enforce the right timings for reset/detection */
        onfi_fill_data_interface(chip, NAND_SDR_IFACE, 0);
 
@@ -4962,31 +5028,32 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
        if (!mtd->name && mtd->dev.parent)
                mtd->name = dev_name(mtd->dev.parent);
 
-       if (chip->exec_op && !chip->select_chip) {
-               pr_err("->select_chip() is mandatory when implementing ->exec_op()\n");
-               return -EINVAL;
-       }
+       /*
+        * Start with chips->numchips = maxchips to let nand_select_target() do
+        * its job. chip->numchips will be adjusted after.
+        */
+       chip->numchips = maxchips;
+
+       /* Set the default functions */
+       nand_set_defaults(chip);
 
        ret = nand_legacy_check_hooks(chip);
        if (ret)
                return ret;
 
-       /* Set the default functions */
-       nand_set_defaults(chip);
-
        /* Read the flash type */
        ret = nand_detect(chip, table);
        if (ret) {
                if (!(chip->options & NAND_SCAN_SILENT_NODEV))
                        pr_warn("No NAND device found\n");
-               chip->select_chip(chip, -1);
+               nand_deselect_target(chip);
                return ret;
        }
 
        nand_maf_id = chip->id.data[0];
        nand_dev_id = chip->id.data[1];
 
-       chip->select_chip(chip, -1);
+       nand_deselect_target(chip);
 
        /* Check for a chip array */
        for (i = 1; i < maxchips; i++) {
@@ -4995,15 +5062,15 @@ static int nand_scan_ident(struct nand_chip *chip, unsigned int maxchips,
                /* See comment in nand_get_flash_type for reset */
                nand_reset(chip, i);
 
-               chip->select_chip(chip, i);
+               nand_select_target(chip, i);
                /* Send the command for reading device ID */
                nand_readid_op(chip, 0, id, sizeof(id));
                /* Read manufacturer and device IDs */
                if (nand_maf_id != id[0] || nand_dev_id != id[1]) {
-                       chip->select_chip(chip, -1);
+                       nand_deselect_target(chip);
                        break;
                }
-               chip->select_chip(chip, -1);
+               nand_deselect_target(chip);
        }
        if (i > 1)
                pr_info("%d chips detected\n", i);
@@ -5021,9 +5088,9 @@ static void nand_scan_ident_cleanup(struct nand_chip *chip)
        kfree(chip->parameters.onfi);
 }
 
-static int nand_set_ecc_soft_ops(struct mtd_info *mtd)
+static int nand_set_ecc_soft_ops(struct nand_chip *chip)
 {
-       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct mtd_info *mtd = nand_to_mtd(chip);
        struct nand_ecc_ctrl *ecc = &chip->ecc;
 
        if (WARN_ON(ecc->mode != NAND_ECC_SOFT))
@@ -5379,9 +5446,9 @@ EXPORT_SYMBOL_GPL(nand_ecc_choose_conf);
  * Requirement (2) ensures we can correct even when all bitflips are clumped
  * in the same sector.
  */
-static bool nand_ecc_strength_good(struct mtd_info *mtd)
+static bool nand_ecc_strength_good(struct nand_chip *chip)
 {
-       struct nand_chip *chip = mtd_to_nand(mtd);
+       struct mtd_info *mtd = nand_to_mtd(chip);
        struct nand_ecc_ctrl *ecc = &chip->ecc;
        int corr, ds_corr;
 
@@ -5429,9 +5496,9 @@ static int nand_scan_tail(struct nand_chip *chip)
         * to explictly select the relevant die when interacting with the NAND
         * chip.
         */
-       chip->select_chip(chip, 0);
+       nand_select_target(chip, 0);
        ret = nand_manufacturer_init(chip);
-       chip->select_chip(chip, -1);
+       nand_deselect_target(chip);
        if (ret)
                goto err_free_buf;
 
@@ -5546,7 +5613,7 @@ static int nand_scan_tail(struct nand_chip *chip)
                ecc->algo = NAND_ECC_HAMMING;
 
        case NAND_ECC_SOFT:
-               ret = nand_set_ecc_soft_ops(mtd);
+               ret = nand_set_ecc_soft_ops(chip);
                if (ret) {
                        ret = -EINVAL;
                        goto err_nand_manuf_cleanup;
@@ -5631,7 +5698,7 @@ static int nand_scan_tail(struct nand_chip *chip)
        mtd->oobavail = ret;
 
        /* ECC sanity check: warn if it's too weak */
-       if (!nand_ecc_strength_good(mtd))
+       if (!nand_ecc_strength_good(chip))
                pr_warn("WARNING: %s: the ECC used on your system is too weak compared to the one required by the NAND chip\n",
                        mtd->name);
 
index 98a8268..1b722fe 100644 (file)
@@ -77,8 +77,6 @@
 #define BBT_ENTRY_MASK         0x03
 #define BBT_ENTRY_SHIFT                2
 
-static int nand_update_bbt(struct mtd_info *mtd, loff_t offs);
-
 static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block)
 {
        uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT];
@@ -160,7 +158,7 @@ static u32 add_marker_len(struct nand_bbt_descr *td)
 
 /**
  * read_bbt - [GENERIC] Read the bad block table starting from page
- * @mtd: MTD device structure
+ * @chip: NAND chip object
  * @buf: temporary buffer
  * @page: the starting page
  * @num: the number of bbt descriptors to read
@@ -169,11 +167,11 @@ static u32 add_marker_len(struct nand_bbt_descr *td)
  *
  * Read the bad block table starting from page.
  */
-static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
-               struct nand_bbt_descr *td, int offs)
+static int read_bbt(struct nand_chip *this, uint8_t *buf, int page, int num,
+                   struct nand_bbt_descr *td, int offs)
 {
+       struct mtd_info *mtd = nand_to_mtd(this);
        int res, ret = 0, i, j, act = 0;
-       struct nand_chip *this = mtd_to_nand(mtd);
        size_t retlen, len, totlen;
        loff_t from;
        int bits = td->options & NAND_BBT_NRBITS_MSK;
@@ -253,7 +251,7 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
 
 /**
  * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page
- * @mtd: MTD device structure
+ * @this: NAND chip object
  * @buf: temporary buffer
  * @td: descriptor for the bad block table
  * @chip: read the table for a specific chip, -1 read all chips; applies only if
@@ -262,16 +260,17 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
  * Read the bad block table for all chips starting at a given page. We assume
  * that the bbt bits are in consecutive order.
  */
-static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
+static int read_abs_bbt(struct nand_chip *this, uint8_t *buf,
+                       struct nand_bbt_descr *td, int chip)
 {
-       struct nand_chip *this = mtd_to_nand(mtd);
+       struct mtd_info *mtd = nand_to_mtd(this);
        int res = 0, i;
 
        if (td->options & NAND_BBT_PERCHIP) {
                int offs = 0;
                for (i = 0; i < this->numchips; i++) {
                        if (chip == -1 || chip == i)
-                               res = read_bbt(mtd, buf, td->pages[i],
+                               res = read_bbt(this, buf, td->pages[i],
                                        this->chipsize >> this->bbt_erase_shift,
                                        td, offs);
                        if (res)
@@ -279,7 +278,7 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
                        offs += this->chipsize >> this->bbt_erase_shift;
                }
        } else {
-               res = read_bbt(mtd, buf, td->pages[0],
+               res = read_bbt(this, buf, td->pages[0],
                                mtd->size >> this->bbt_erase_shift, td, 0);
                if (res)
                        return res;
@@ -288,9 +287,10 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
 }
 
 /* BBT marker is in the first page, no OOB */
-static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
-                        struct nand_bbt_descr *td)
+static int scan_read_data(struct nand_chip *this, uint8_t *buf, loff_t offs,
+                         struct nand_bbt_descr *td)
 {
+       struct mtd_info *mtd = nand_to_mtd(this);
        size_t retlen;
        size_t len;
 
@@ -303,7 +303,7 @@ static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
 
 /**
  * scan_read_oob - [GENERIC] Scan data+OOB region to buffer
- * @mtd: MTD device structure
+ * @this: NAND chip object
  * @buf: temporary buffer
  * @offs: offset at which to scan
  * @len: length of data region to read
@@ -312,9 +312,10 @@ static int scan_read_data(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
  * page,OOB,page,OOB,... in buf. Completes transfer and returns the "strongest"
  * ECC condition (error or bitflip). May quit on the first (non-ECC) error.
  */
-static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+static int scan_read_oob(struct nand_chip *this, uint8_t *buf, loff_t offs,
                         size_t len)
 {
+       struct mtd_info *mtd = nand_to_mtd(this);
        struct mtd_oob_ops ops;
        int res, ret = 0;
 
@@ -342,19 +343,20 @@ static int scan_read_oob(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
        return ret;
 }
 
-static int scan_read(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
-                        size_t len, struct nand_bbt_descr *td)
+static int scan_read(struct nand_chip *this, uint8_t *buf, loff_t offs,
+                    size_t len, struct nand_bbt_descr *td)
 {
        if (td->options & NAND_BBT_NO_OOB)
-               return scan_read_data(mtd, buf, offs, td);
+               return scan_read_data(this, buf, offs, td);
        else
-               return scan_read_oob(mtd, buf, offs, len);
+               return scan_read_oob(this, buf, offs, len);
 }
 
 /* Scan write data with oob to flash */
-static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
+static int scan_write_bbt(struct nand_chip *this, loff_t offs, size_t len,
                          uint8_t *buf, uint8_t *oob)
 {
+       struct mtd_info *mtd = nand_to_mtd(this);
        struct mtd_oob_ops ops;
 
        ops.mode = MTD_OPS_PLACE_OOB;
@@ -367,8 +369,9 @@ static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
        return mtd_write_oob(mtd, offs, &ops);
 }
 
-static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)
+static u32 bbt_get_ver_offs(struct nand_chip *this, struct nand_bbt_descr *td)
 {
+       struct mtd_info *mtd = nand_to_mtd(this);
        u32 ver_offs = td->veroffs;
 
        if (!(td->options & NAND_BBT_NO_OOB))
@@ -378,7 +381,7 @@ static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)
 
 /**
  * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
- * @mtd: MTD device structure
+ * @this: NAND chip object
  * @buf: temporary buffer
  * @td: descriptor for the bad block table
  * @md:        descriptor for the bad block table mirror
@@ -386,34 +389,35 @@ static u32 bbt_get_ver_offs(struct mtd_info *mtd, struct nand_bbt_descr *td)
  * Read the bad block table(s) for all chips starting at a given page. We
  * assume that the bbt bits are in consecutive order.
  */
-static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
+static void read_abs_bbts(struct nand_chip *this, uint8_t *buf,
                          struct nand_bbt_descr *td, struct nand_bbt_descr *md)
 {
-       struct nand_chip *this = mtd_to_nand(mtd);
+       struct mtd_info *mtd = nand_to_mtd(this);
 
        /* Read the primary version, if available */
        if (td->options & NAND_BBT_VERSION) {
-               scan_read(mtd, buf, (loff_t)td->pages[0] << this->page_shift,
-                             mtd->writesize, td);
-               td->version[0] = buf[bbt_get_ver_offs(mtd, td)];
+               scan_read(this, buf, (loff_t)td->pages[0] << this->page_shift,
+                         mtd->writesize, td);
+               td->version[0] = buf[bbt_get_ver_offs(this, td)];
                pr_info("Bad block table at page %d, version 0x%02X\n",
                         td->pages[0], td->version[0]);
        }
 
        /* Read the mirror version, if available */
        if (md && (md->options & NAND_BBT_VERSION)) {
-               scan_read(mtd, buf, (loff_t)md->pages[0] << this->page_shift,
-                             mtd->writesize, md);
-               md->version[0] = buf[bbt_get_ver_offs(mtd, md)];
+               scan_read(this, buf, (loff_t)md->pages[0] << this->page_shift,
+                         mtd->writesize, md);
+               md->version[0] = buf[bbt_get_ver_offs(this, md)];
                pr_info("Bad block table at page %d, version 0x%02X\n",
                         md->pages[0], md->version[0]);
        }
 }
 
 /* Scan a given block partially */
-static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
+static int scan_block_fast(struct nand_chip *this, struct nand_bbt_descr *bd,
                           loff_t offs, uint8_t *buf, int numpages)
 {
+       struct mtd_info *mtd = nand_to_mtd(this);
        struct mtd_oob_ops ops;
        int j, ret;
 
@@ -443,7 +447,7 @@ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
 
 /**
  * create_bbt - [GENERIC] Create a bad block table by scanning the device
- * @mtd: MTD device structure
+ * @this: NAND chip object
  * @buf: temporary buffer
  * @bd: descriptor for the good/bad block search pattern
  * @chip: create the table for a specific chip, -1 read all chips; applies only
@@ -452,10 +456,10 @@ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
  * Create a bad block table by scanning the device for the given good/bad block
  * identify pattern.
  */
-static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
-       struct nand_bbt_descr *bd, int chip)
+static int create_bbt(struct nand_chip *this, uint8_t *buf,
+                     struct nand_bbt_descr *bd, int chip)
 {
-       struct nand_chip *this = mtd_to_nand(mtd);
+       struct mtd_info *mtd = nand_to_mtd(this);
        int i, numblocks, numpages;
        int startblock;
        loff_t from;
@@ -491,7 +495,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
 
                BUG_ON(bd->options & NAND_BBT_NO_OOB);
 
-               ret = scan_block_fast(mtd, bd, from, buf, numpages);
+               ret = scan_block_fast(this, bd, from, buf, numpages);
                if (ret < 0)
                        return ret;
 
@@ -509,7 +513,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
 
 /**
  * search_bbt - [GENERIC] scan the device for a specific bad block table
- * @mtd: MTD device structure
+ * @this: NAND chip object
  * @buf: temporary buffer
  * @td: descriptor for the bad block table
  *
@@ -522,9 +526,10 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
  *
  * The bbt ident pattern resides in the oob area of the first page in a block.
  */
-static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
+static int search_bbt(struct nand_chip *this, uint8_t *buf,
+                     struct nand_bbt_descr *td)
 {
-       struct nand_chip *this = mtd_to_nand(mtd);
+       struct mtd_info *mtd = nand_to_mtd(this);
        int i, chips;
        int startblock, block, dir;
        int scanlen = mtd->writesize + mtd->oobsize;
@@ -561,11 +566,11 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
                        loff_t offs = (loff_t)actblock << this->bbt_erase_shift;
 
                        /* Read first page */
-                       scan_read(mtd, buf, offs, mtd->writesize, td);
+                       scan_read(this, buf, offs, mtd->writesize, td);
                        if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
                                td->pages[i] = actblock << blocktopage;
                                if (td->options & NAND_BBT_VERSION) {
-                                       offs = bbt_get_ver_offs(mtd, td);
+                                       offs = bbt_get_ver_offs(this, td);
                                        td->version[i] = buf[offs];
                                }
                                break;
@@ -586,23 +591,23 @@ static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
 
 /**
  * search_read_bbts - [GENERIC] scan the device for bad block table(s)
- * @mtd: MTD device structure
+ * @this: NAND chip object
  * @buf: temporary buffer
  * @td: descriptor for the bad block table
  * @md: descriptor for the bad block table mirror
  *
  * Search and read the bad block table(s).
  */
-static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf,
+static void search_read_bbts(struct nand_chip *this, uint8_t *buf,
                             struct nand_bbt_descr *td,
                             struct nand_bbt_descr *md)
 {
        /* Search the primary table */
-       search_bbt(mtd, buf, td);
+       search_bbt(this, buf, td);
 
        /* Search the mirror table */
        if (md)
-               search_bbt(mtd, buf, md);
+               search_bbt(this, buf, md);
 }
 
 /**
@@ -700,7 +705,7 @@ static void mark_bbt_block_bad(struct nand_chip *this,
 
 /**
  * write_bbt - [GENERIC] (Re)write the bad block table
- * @mtd: MTD device structure
+ * @this: NAND chip object
  * @buf: temporary buffer
  * @td: descriptor for the bad block table
  * @md: descriptor for the bad block table mirror
@@ -708,11 +713,11 @@ static void mark_bbt_block_bad(struct nand_chip *this,
  *
  * (Re)write the bad block table.
  */
-static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
+static int write_bbt(struct nand_chip *this, uint8_t *buf,
                     struct nand_bbt_descr *td, struct nand_bbt_descr *md,
                     int chipsel)
 {
-       struct nand_chip *this = mtd_to_nand(mtd);
+       struct mtd_info *mtd = nand_to_mtd(this);
        struct erase_info einfo;
        int i, res, chip = 0;
        int bits, page, offs, numblocks, sft, sftmsk;
@@ -862,9 +867,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
                        continue;
                }
 
-               res = scan_write_bbt(mtd, to, len, buf,
-                               td->options & NAND_BBT_NO_OOB ? NULL :
-                               &buf[len]);
+               res = scan_write_bbt(this, to, len, buf,
+                                    td->options & NAND_BBT_NO_OOB ?
+                                    NULL : &buf[len]);
                if (res < 0) {
                        pr_warn("nand_bbt: error while writing BBT block %d\n",
                                res);
@@ -887,22 +892,21 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
 
 /**
  * nand_memory_bbt - [GENERIC] create a memory based bad block table
- * @mtd: MTD device structure
+ * @this: NAND chip object
  * @bd: descriptor for the good/bad block search pattern
  *
  * The function creates a memory based bbt by scanning the device for
  * manufacturer / software marked good / bad blocks.
  */
-static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+static inline int nand_memory_bbt(struct nand_chip *this,
+                                 struct nand_bbt_descr *bd)
 {
-       struct nand_chip *this = mtd_to_nand(mtd);
-
-       return create_bbt(mtd, this->data_buf, bd, -1);
+       return create_bbt(this, this->data_buf, bd, -1);
 }
 
 /**
  * check_create - [GENERIC] create and write bbt(s) if necessary
- * @mtd: MTD device structure
+ * @this: the NAND device
  * @buf: temporary buffer
  * @bd: descriptor for the good/bad block search pattern
  *
@@ -911,10 +915,10 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b
  * for the chip/device. Update is necessary if one of the tables is missing or
  * the version nr. of one table is less than the other.
  */
-static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
+static int check_create(struct nand_chip *this, uint8_t *buf,
+                       struct nand_bbt_descr *bd)
 {
        int i, chips, writeops, create, chipsel, res, res2;
-       struct nand_chip *this = mtd_to_nand(mtd);
        struct nand_bbt_descr *td = this->bbt_td;
        struct nand_bbt_descr *md = this->bbt_md;
        struct nand_bbt_descr *rd, *rd2;
@@ -971,7 +975,7 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
 
                        /* Create the table in memory by scanning the chip(s) */
                        if (!(this->bbt_options & NAND_BBT_CREATE_EMPTY))
-                               create_bbt(mtd, buf, bd, chipsel);
+                               create_bbt(this, buf, bd, chipsel);
 
                        td->version[i] = 1;
                        if (md)
@@ -980,7 +984,7 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
 
                /* Read back first? */
                if (rd) {
-                       res = read_abs_bbt(mtd, buf, rd, chipsel);
+                       res = read_abs_bbt(this, buf, rd, chipsel);
                        if (mtd_is_eccerr(res)) {
                                /* Mark table as invalid */
                                rd->pages[i] = -1;
@@ -991,7 +995,7 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
                }
                /* If they weren't versioned, read both */
                if (rd2) {
-                       res2 = read_abs_bbt(mtd, buf, rd2, chipsel);
+                       res2 = read_abs_bbt(this, buf, rd2, chipsel);
                        if (mtd_is_eccerr(res2)) {
                                /* Mark table as invalid */
                                rd2->pages[i] = -1;
@@ -1013,14 +1017,14 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
 
                /* Write the bad block table to the device? */
                if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
-                       res = write_bbt(mtd, buf, td, md, chipsel);
+                       res = write_bbt(this, buf, td, md, chipsel);
                        if (res < 0)
                                return res;
                }
 
                /* Write the mirror bad block table to the device? */
                if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
-                       res = write_bbt(mtd, buf, md, td, chipsel);
+                       res = write_bbt(this, buf, md, td, chipsel);
                        if (res < 0)
                                return res;
                }
@@ -1028,17 +1032,72 @@ static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
        return 0;
 }
 
+/**
+ * nand_update_bbt - update bad block table(s)
+ * @this: the NAND device
+ * @offs: the offset of the newly marked block
+ *
+ * The function updates the bad block table(s).
+ */
+static int nand_update_bbt(struct nand_chip *this, loff_t offs)
+{
+       struct mtd_info *mtd = nand_to_mtd(this);
+       int len, res = 0;
+       int chip, chipsel;
+       uint8_t *buf;
+       struct nand_bbt_descr *td = this->bbt_td;
+       struct nand_bbt_descr *md = this->bbt_md;
+
+       if (!this->bbt || !td)
+               return -EINVAL;
+
+       /* Allocate a temporary buffer for one eraseblock incl. oob */
+       len = (1 << this->bbt_erase_shift);
+       len += (len >> this->page_shift) * mtd->oobsize;
+       buf = kmalloc(len, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       /* Do we have a bbt per chip? */
+       if (td->options & NAND_BBT_PERCHIP) {
+               chip = (int)(offs >> this->chip_shift);
+               chipsel = chip;
+       } else {
+               chip = 0;
+               chipsel = -1;
+       }
+
+       td->version[chip]++;
+       if (md)
+               md->version[chip]++;
+
+       /* Write the bad block table to the device? */
+       if (td->options & NAND_BBT_WRITE) {
+               res = write_bbt(this, buf, td, md, chipsel);
+               if (res < 0)
+                       goto out;
+       }
+       /* Write the mirror bad block table to the device? */
+       if (md && (md->options & NAND_BBT_WRITE)) {
+               res = write_bbt(this, buf, md, td, chipsel);
+       }
+
+ out:
+       kfree(buf);
+       return res;
+}
+
 /**
  * mark_bbt_regions - [GENERIC] mark the bad block table regions
- * @mtd: MTD device structure
+ * @this: the NAND device
  * @td: bad block table descriptor
  *
  * The bad block table regions are marked as "bad" to prevent accidental
  * erasures / writes. The regions are identified by the mark 0x02.
  */
-static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
+static void mark_bbt_region(struct nand_chip *this, struct nand_bbt_descr *td)
 {
-       struct nand_chip *this = mtd_to_nand(mtd);
+       struct mtd_info *mtd = nand_to_mtd(this);
        int i, j, chips, block, nrblocks, update;
        uint8_t oldval;
 
@@ -1061,7 +1120,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
                        bbt_mark_entry(this, block, BBT_BLOCK_RESERVED);
                        if ((oldval != BBT_BLOCK_RESERVED) &&
                                        td->reserved_block_code)
-                               nand_update_bbt(mtd, (loff_t)block <<
+                               nand_update_bbt(this, (loff_t)block <<
                                                this->bbt_erase_shift);
                        continue;
                }
@@ -1083,22 +1142,22 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
                 * bbts.  This should only happen once.
                 */
                if (update && td->reserved_block_code)
-                       nand_update_bbt(mtd, (loff_t)(block - 1) <<
+                       nand_update_bbt(this, (loff_t)(block - 1) <<
                                        this->bbt_erase_shift);
        }
 }
 
 /**
  * verify_bbt_descr - verify the bad block description
- * @mtd: MTD device structure
+ * @this: the NAND device
  * @bd: the table to verify
  *
  * This functions performs a few sanity checks on the bad block description
  * table.
  */
-static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+static void verify_bbt_descr(struct nand_chip *this, struct nand_bbt_descr *bd)
 {
-       struct nand_chip *this = mtd_to_nand(mtd);
+       struct mtd_info *mtd = nand_to_mtd(this);
        u32 pattern_len;
        u32 bits;
        u32 table_size;
@@ -1138,7 +1197,7 @@ static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
 
 /**
  * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
- * @mtd: MTD device structure
+ * @this: the NAND device
  * @bd: descriptor for the good/bad block search pattern
  *
  * The function checks, if a bad block table(s) is/are already available. If
@@ -1148,9 +1207,9 @@ static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd)
  * The bad block table memory is allocated here. It must be freed by calling
  * the nand_free_bbt function.
  */
-static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+static int nand_scan_bbt(struct nand_chip *this, struct nand_bbt_descr *bd)
 {
-       struct nand_chip *this = mtd_to_nand(mtd);
+       struct mtd_info *mtd = nand_to_mtd(this);
        int len, res;
        uint8_t *buf;
        struct nand_bbt_descr *td = this->bbt_td;
@@ -1170,14 +1229,14 @@ static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
         * memory based bad block table.
         */
        if (!td) {
-               if ((res = nand_memory_bbt(mtd, bd))) {
+               if ((res = nand_memory_bbt(this, bd))) {
                        pr_err("nand_bbt: can't scan flash and build the RAM-based BBT\n");
                        goto err;
                }
                return 0;
        }
-       verify_bbt_descr(mtd, td);
-       verify_bbt_descr(mtd, md);
+       verify_bbt_descr(this, td);
+       verify_bbt_descr(this, md);
 
        /* Allocate a temporary buffer for one eraseblock incl. oob */
        len = (1 << this->bbt_erase_shift);
@@ -1190,20 +1249,20 @@ static int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
 
        /* Is the bbt at a given page? */
        if (td->options & NAND_BBT_ABSPAGE) {
-               read_abs_bbts(mtd, buf, td, md);
+               read_abs_bbts(this, buf, td, md);
        } else {
                /* Search the bad block table using a pattern in oob */
-               search_read_bbts(mtd, buf, td, md);
+               search_read_bbts(this, buf, td, md);
        }
 
-       res = check_create(mtd, buf, bd);
+       res = check_create(this, buf, bd);
        if (res)
                goto err;
 
        /* Prevent the bbt regions from erasing / writing */
-       mark_bbt_region(mtd, td);
+       mark_bbt_region(this, td);
        if (md)
-               mark_bbt_region(mtd, md);
+               mark_bbt_region(this, md);
 
        vfree(buf);
        return 0;
@@ -1214,61 +1273,6 @@ err:
        return res;
 }
 
-/**
- * nand_update_bbt - update bad block table(s)
- * @mtd: MTD device structure
- * @offs: the offset of the newly marked block
- *
- * The function updates the bad block table(s).
- */
-static int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
-{
-       struct nand_chip *this = mtd_to_nand(mtd);
-       int len, res = 0;
-       int chip, chipsel;
-       uint8_t *buf;
-       struct nand_bbt_descr *td = this->bbt_td;
-       struct nand_bbt_descr *md = this->bbt_md;
-
-       if (!this->bbt || !td)
-               return -EINVAL;
-
-       /* Allocate a temporary buffer for one eraseblock incl. oob */
-       len = (1 << this->bbt_erase_shift);
-       len += (len >> this->page_shift) * mtd->oobsize;
-       buf = kmalloc(len, GFP_KERNEL);
-       if (!buf)
-               return -ENOMEM;
-
-       /* Do we have a bbt per chip? */
-       if (td->options & NAND_BBT_PERCHIP) {
-               chip = (int)(offs >> this->chip_shift);
-               chipsel = chip;
-       } else {
-               chip = 0;
-               chipsel = -1;
-       }
-
-       td->version[chip]++;
-       if (md)
-               md->version[chip]++;
-
-       /* Write the bad block table to the device? */
-       if (td->options & NAND_BBT_WRITE) {
-               res = write_bbt(mtd, buf, td, md, chipsel);
-               if (res < 0)
-                       goto out;
-       }
-       /* Write the mirror bad block table to the device? */
-       if (md && (md->options & NAND_BBT_WRITE)) {
-               res = write_bbt(mtd, buf, md, td, chipsel);
-       }
-
- out:
-       kfree(buf);
-       return res;
-}
-
 /*
  * Define some generic bad / good block scan pattern which are used
  * while scanning a device for factory marked good / bad blocks.
@@ -1382,7 +1386,7 @@ int nand_create_bbt(struct nand_chip *this)
                        return ret;
        }
 
-       return nand_scan_bbt(nand_to_mtd(this), this->badblock_pattern);
+       return nand_scan_bbt(this, this->badblock_pattern);
 }
 EXPORT_SYMBOL(nand_create_bbt);
 
@@ -1433,7 +1437,6 @@ int nand_isbad_bbt(struct nand_chip *this, loff_t offs, int allowbbt)
  */
 int nand_markbad_bbt(struct nand_chip *this, loff_t offs)
 {
-       struct mtd_info *mtd = nand_to_mtd(this);
        int block, ret = 0;
 
        block = (int)(offs >> this->bbt_erase_shift);
@@ -1443,7 +1446,7 @@ int nand_markbad_bbt(struct nand_chip *this, loff_t offs)
 
        /* Update flash-based bad block table */
        if (this->bbt_options & NAND_BBT_USE_FLASH)
-               ret = nand_update_bbt(mtd, offs);
+               ret = nand_update_bbt(this, offs);
 
        return ret;
 }
index ac1b5c1..343f477 100644 (file)
@@ -80,11 +80,11 @@ static bool hynix_nand_has_valid_jedecid(struct nand_chip *chip)
 
 static int hynix_nand_cmd_op(struct nand_chip *chip, u8 cmd)
 {
-       if (chip->exec_op) {
+       if (nand_has_exec_op(chip)) {
                struct nand_op_instr instrs[] = {
                        NAND_OP_CMD(cmd, 0),
                };
-               struct nand_operation op = NAND_OPERATION(instrs);
+               struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
 
                return nand_exec_op(chip, &op);
        }
@@ -98,12 +98,12 @@ static int hynix_nand_reg_write_op(struct nand_chip *chip, u8 addr, u8 val)
 {
        u16 column = ((u16)addr << 8) | addr;
 
-       if (chip->exec_op) {
+       if (nand_has_exec_op(chip)) {
                struct nand_op_instr instrs[] = {
                        NAND_OP_ADDR(1, &addr, 0),
                        NAND_OP_8BIT_DATA_OUT(1, &val, 0),
                };
-               struct nand_operation op = NAND_OPERATION(instrs);
+               struct nand_operation op = NAND_OPERATION(chip->cur_cs, instrs);
 
                return nand_exec_op(chip, &op);
        }
index 5c26492..38b5dc2 100644 (file)
@@ -107,6 +107,8 @@ int nand_jedec_detect(struct nand_chip *chip)
                pr_warn("Invalid codeword size\n");
        }
 
+       ret = 1;
+
 free_jedec_param_page:
        kfree(p);
        return ret;
index c5ddc86..4357594 100644 (file)
@@ -165,15 +165,14 @@ static void nand_read_buf16(struct nand_chip *chip, uint8_t *buf, int len)
 
 /**
  * panic_nand_wait_ready - [GENERIC] Wait for the ready pin after commands.
- * @mtd: MTD device structure
+ * @chip: NAND chip object
  * @timeo: Timeout
  *
  * Helper function for nand_wait_ready used when needing to wait in interrupt
  * context.
  */
-static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo)
+static void panic_nand_wait_ready(struct nand_chip *chip, unsigned long timeo)
 {
-       struct nand_chip *chip = mtd_to_nand(mtd);
        int i;
 
        /* Wait for the device to get ready */
@@ -193,11 +192,10 @@ static void panic_nand_wait_ready(struct mtd_info *mtd, unsigned long timeo)
  */
 void nand_wait_ready(struct nand_chip *chip)
 {
-       struct mtd_info *mtd = nand_to_mtd(chip);
        unsigned long timeo = 400;
 
        if (in_interrupt() || oops_in_progress)
-               return panic_nand_wait_ready(mtd, timeo);
+               return panic_nand_wait_ready(chip, timeo);
 
        /* Wait until command is processed or timeout occurs */
        timeo = jiffies + msecs_to_jiffies(timeo);
@@ -214,14 +212,13 @@ EXPORT_SYMBOL_GPL(nand_wait_ready);
 
 /**
  * nand_wait_status_ready - [GENERIC] Wait for the ready status after commands.
- * @mtd: MTD device structure
+ * @chip: NAND chip object
  * @timeo: Timeout in ms
  *
  * Wait for status ready (i.e. command done) or timeout.
  */
-static void nand_wait_status_ready(struct mtd_info *mtd, unsigned long timeo)
+static void nand_wait_status_ready(struct nand_chip *chip, unsigned long timeo)
 {
-       register struct nand_chip *chip = mtd_to_nand(mtd);
        int ret;
 
        timeo = jiffies + msecs_to_jiffies(timeo);
@@ -321,7 +318,7 @@ static void nand_command(struct nand_chip *chip, unsigned int command,
                chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE,
                                      NAND_NCE | NAND_CTRL_CHANGE);
                /* EZ-NAND can take upto 250ms as per ONFi v4.0 */
-               nand_wait_status_ready(mtd, 250);
+               nand_wait_status_ready(chip, 250);
                return;
 
                /* This applies to read commands */
@@ -367,7 +364,7 @@ static void nand_ccs_delay(struct nand_chip *chip)
         * Wait tCCS_min if it is correctly defined, otherwise wait 500ns
         * (which should be safe for all NANDs).
         */
-       if (chip->setup_data_interface)
+       if (nand_has_setup_data_iface(chip))
                ndelay(chip->data_interface.timings.sdr.tCCS_min / 1000);
        else
                ndelay(500);
@@ -458,7 +455,7 @@ static void nand_command_lp(struct nand_chip *chip, unsigned int command,
                chip->legacy.cmd_ctrl(chip, NAND_CMD_NONE,
                                      NAND_NCE | NAND_CTRL_CHANGE);
                /* EZ-NAND can take upto 250ms as per ONFi v4.0 */
-               nand_wait_status_ready(mtd, 250);
+               nand_wait_status_ready(chip, 250);
                return;
 
        case NAND_CMD_RNDOUT:
@@ -525,7 +522,6 @@ EXPORT_SYMBOL(nand_get_set_features_notsupp);
 
 /**
  * nand_wait - [DEFAULT] wait until the command is done
- * @mtd: MTD device structure
  * @chip: NAND chip structure
  *
  * Wait for command done. This applies to erase and program only.
@@ -581,7 +577,7 @@ void nand_legacy_set_defaults(struct nand_chip *chip)
 {
        unsigned int busw = chip->options & NAND_BUSWIDTH_16;
 
-       if (chip->exec_op)
+       if (nand_has_exec_op(chip))
                return;
 
        /* check for proper chip_delay setup, set 20us if not */
@@ -589,15 +585,15 @@ void nand_legacy_set_defaults(struct nand_chip *chip)
                chip->legacy.chip_delay = 20;
 
        /* check, if a user supplied command function given */
-       if (!chip->legacy.cmdfunc && !chip->exec_op)
+       if (!chip->legacy.cmdfunc)
                chip->legacy.cmdfunc = nand_command;
 
        /* check, if a user supplied wait function given */
        if (chip->legacy.waitfunc == NULL)
                chip->legacy.waitfunc = nand_wait;
 
-       if (!chip->select_chip)
-               chip->select_chip = nand_select_chip;
+       if (!chip->legacy.select_chip)
+               chip->legacy.select_chip = nand_select_chip;
 
        /* If called twice, pointers that depend on busw may need to be reset */
        if (!chip->legacy.read_byte || chip->legacy.read_byte == nand_read_byte)
@@ -625,14 +621,15 @@ int nand_legacy_check_hooks(struct nand_chip *chip)
         * ->legacy.cmdfunc() is legacy and will only be used if ->exec_op() is
         * not populated.
         */
-       if (chip->exec_op)
+       if (nand_has_exec_op(chip))
                return 0;
 
        /*
         * Default functions assigned for ->legacy.cmdfunc() and
-        * ->select_chip() both expect ->legacy.cmd_ctrl() to be populated.
+        * ->legacy.select_chip() both expect ->legacy.cmd_ctrl() to be
+        *  populated.
         */
-       if ((!chip->legacy.cmdfunc || !chip->select_chip) &&
+       if ((!chip->legacy.cmdfunc || !chip->legacy.select_chip) &&
            !chip->legacy.cmd_ctrl) {
                pr_err("->legacy.cmd_ctrl() should be provided\n");
                return -EINVAL;
index 358dcc9..47d8cda 100644 (file)
@@ -33,6 +33,13 @@ static void macronix_nand_fix_broken_get_timings(struct nand_chip *chip)
                "MX30LF4G18AC",
                "MX30LF4G28AC",
                "MX60LF8G18AC",
+               "MX30UF1G18AC",
+               "MX30UF1G16AC",
+               "MX30UF2G18AC",
+               "MX30UF2G16AC",
+               "MX30UF4G18AC",
+               "MX30UF4G16AC",
+               "MX30UF4G28AC",
        };
 
        if (!chip->parameters.supports_set_get_features)
index c452819..933d1a6 100644 (file)
@@ -443,7 +443,7 @@ static unsigned long total_wear = 0;
 /* MTD structure for NAND controller */
 static struct mtd_info *nsmtd;
 
-static int nandsim_debugfs_show(struct seq_file *m, void *private)
+static int nandsim_show(struct seq_file *m, void *private)
 {
        unsigned long wmin = -1, wmax = 0, avg;
        unsigned long deciles[10], decile_max[10], tot = 0;
@@ -494,18 +494,7 @@ static int nandsim_debugfs_show(struct seq_file *m, void *private)
 
        return 0;
 }
-
-static int nandsim_debugfs_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, nandsim_debugfs_show, inode->i_private);
-}
-
-static const struct file_operations dfs_fops = {
-       .open           = nandsim_debugfs_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(nandsim);
 
 /**
  * nandsim_debugfs_create - initialize debugfs
@@ -531,7 +520,7 @@ static int nandsim_debugfs_create(struct nandsim *dev)
        }
 
        dent = debugfs_create_file("nandsim_wear_report", S_IRUSR,
-                                  root, dev, &dfs_fops);
+                                  root, dev, &nandsim_fops);
        if (IS_ERR_OR_NULL(dent)) {
                NS_ERR("cannot create \"nandsim_wear_report\" debugfs entry\n");
                return -1;
@@ -2304,7 +2293,7 @@ static int __init ns_init_module(void)
        if ((retval = parse_gravepages()) != 0)
                goto error;
 
-       chip->dummy_controller.ops = &ns_controller_ops;
+       chip->legacy.dummy_controller.ops = &ns_controller_ops;
        retval = nand_scan(chip, 1);
        if (retval) {
                NS_ERR("Could not scan NAND Simulator device\n");
index d49a7a1..9857e0e 100644 (file)
@@ -146,7 +146,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc,
        chip->legacy.IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA;
        chip->legacy.cmd_ctrl = ndfc_hwcontrol;
        chip->legacy.dev_ready = ndfc_ready;
-       chip->select_chip = ndfc_select_chip;
+       chip->legacy.select_chip = ndfc_select_chip;
        chip->legacy.chip_delay = 50;
        chip->controller = &ndfc->ndfc_control;
        chip->legacy.read_buf = ndfc_read_buf;
index 886d05c..68e8b9f 100644 (file)
@@ -1944,7 +1944,7 @@ static int omap_nand_attach_chip(struct nand_chip *chip)
        case NAND_OMAP_PREFETCH_DMA:
                dma_cap_zero(mask);
                dma_cap_set(DMA_SLAVE, mask);
-               info->dma = dma_request_chan(dev, "rxtx");
+               info->dma = dma_request_chan(dev->parent, "rxtx");
 
                if (IS_ERR(info->dma)) {
                        dev_err(dev, "DMA engine request failed\n");
index 86c536d..a994b76 100644 (file)
@@ -63,7 +63,7 @@ static int plat_nand_probe(struct platform_device *pdev)
        data->chip.legacy.IO_ADDR_W = data->io_base;
        data->chip.legacy.cmd_ctrl = pdata->ctrl.cmd_ctrl;
        data->chip.legacy.dev_ready = pdata->ctrl.dev_ready;
-       data->chip.select_chip = pdata->ctrl.select_chip;
+       data->chip.legacy.select_chip = pdata->ctrl.select_chip;
        data->chip.legacy.write_buf = pdata->ctrl.write_buf;
        data->chip.legacy.read_buf = pdata->ctrl.read_buf;
        data->chip.legacy.chip_delay = pdata->chip.chip_delay;
index 699d3cf..46c62a3 100644 (file)
@@ -2804,7 +2804,7 @@ static int qcom_nand_host_init_and_register(struct qcom_nand_controller *nandc,
        mtd->dev.parent = dev;
 
        chip->legacy.cmdfunc    = qcom_nandc_command;
-       chip->select_chip       = qcom_nandc_select_chip;
+       chip->legacy.select_chip        = qcom_nandc_select_chip;
        chip->legacy.read_byte  = qcom_nandc_read_byte;
        chip->legacy.read_buf   = qcom_nandc_read_buf;
        chip->legacy.write_buf  = qcom_nandc_write_buf;
index 39be65b..c01422d 100644 (file)
@@ -151,8 +151,9 @@ static void r852_dma_done(struct r852_device *dev, int error)
        dev->dma_stage = 0;
 
        if (dev->phys_dma_addr && dev->phys_dma_addr != dev->phys_bounce_buffer)
-               pci_unmap_single(dev->pci_dev, dev->phys_dma_addr, R852_DMA_LEN,
-                       dev->dma_dir ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
+               dma_unmap_single(&dev->pci_dev->dev, dev->phys_dma_addr,
+                       R852_DMA_LEN,
+                       dev->dma_dir ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
 }
 
 /*
@@ -197,11 +198,10 @@ static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read)
                bounce = 1;
 
        if (!bounce) {
-               dev->phys_dma_addr = pci_map_single(dev->pci_dev, (void *)buf,
+               dev->phys_dma_addr = dma_map_single(&dev->pci_dev->dev, buf,
                        R852_DMA_LEN,
-                       (do_read ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE));
-
-               if (pci_dma_mapping_error(dev->pci_dev, dev->phys_dma_addr))
+                       do_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+               if (dma_mapping_error(&dev->pci_dev->dev, dev->phys_dma_addr))
                        bounce = 1;
        }
 
@@ -835,7 +835,7 @@ static int  r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
 
        pci_set_master(pci_dev);
 
-       error = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32));
+       error = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32));
        if (error)
                goto error2;
 
@@ -885,8 +885,8 @@ static int  r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
        dev->pci_dev = pci_dev;
        pci_set_drvdata(pci_dev, dev);
 
-       dev->bounce_buffer = pci_alloc_consistent(pci_dev, R852_DMA_LEN,
-               &dev->phys_bounce_buffer);
+       dev->bounce_buffer = dma_alloc_coherent(&pci_dev->dev, R852_DMA_LEN,
+               &dev->phys_bounce_buffer, GFP_KERNEL);
 
        if (!dev->bounce_buffer)
                goto error6;
@@ -946,8 +946,8 @@ error9:
 error8:
        pci_iounmap(pci_dev, dev->mmio);
 error7:
-       pci_free_consistent(pci_dev, R852_DMA_LEN,
-               dev->bounce_buffer, dev->phys_bounce_buffer);
+       dma_free_coherent(&pci_dev->dev, R852_DMA_LEN, dev->bounce_buffer,
+                         dev->phys_bounce_buffer);
 error6:
        kfree(dev);
 error5:
@@ -980,8 +980,8 @@ static void r852_remove(struct pci_dev *pci_dev)
        /* Cleanup */
        kfree(dev->tmp_buffer);
        pci_iounmap(pci_dev, dev->mmio);
-       pci_free_consistent(pci_dev, R852_DMA_LEN,
-               dev->bounce_buffer, dev->phys_bounce_buffer);
+       dma_free_coherent(&pci_dev->dev, R852_DMA_LEN, dev->bounce_buffer,
+                         dev->phys_bounce_buffer);
 
        kfree(dev->chip);
        kfree(dev);
@@ -1045,9 +1045,9 @@ static int r852_resume(struct device *device)
        /* Otherwise, initialize the card */
        if (dev->card_registered) {
                r852_engine_enable(dev);
-               dev->chip->select_chip(dev->chip, 0);
+               nand_select_target(dev->chip, 0);
                nand_reset_op(dev->chip);
-               dev->chip->select_chip(dev->chip, -1);
+               nand_deselect_target(dev->chip);
        }
 
        /* Program card detection IRQ */
index d2e42e9..adc7a19 100644 (file)
@@ -866,7 +866,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
 
        chip->legacy.write_buf    = s3c2410_nand_write_buf;
        chip->legacy.read_buf     = s3c2410_nand_read_buf;
-       chip->select_chip  = s3c2410_nand_select_chip;
+       chip->legacy.select_chip  = s3c2410_nand_select_chip;
        chip->legacy.chip_delay   = 50;
        nand_set_controller_data(chip, nmtd);
        chip->options      = set->options;
@@ -876,8 +876,8 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
         * let's keep behavior unchanged for legacy boards booting via pdata and
         * auto-detect timings only when booting with a device tree.
         */
-       if (np)
-               chip->setup_data_interface = s3c2410_nand_setup_data_interface;
+       if (!np)
+               chip->options |= NAND_KEEP_TIMINGS;
 
        switch (info->cpu_type) {
        case TYPE_S3C2410:
@@ -1011,6 +1011,7 @@ static int s3c2410_nand_attach_chip(struct nand_chip *chip)
 
 static const struct nand_controller_ops s3c24xx_nand_controller_ops = {
        .attach_chip = s3c2410_nand_attach_chip,
+       .setup_data_interface = s3c2410_nand_setup_data_interface,
 };
 
 static const struct of_device_id s3c24xx_nand_dt_ids[] = {
index 4d20d03..cf6b1be 100644 (file)
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * SuperH FLCTL nand controller
  *
@@ -5,20 +6,6 @@
  * Copyright (c) 2008 Atom Create Engineering Co., Ltd.
  *
  * Based on fsl_elbc_nand.c, Copyright (c) 2006-2007 Freescale Semiconductor
- *
- * 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; version 2 of the License.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
  */
 
 #include <linux/module.h>
@@ -1183,7 +1170,7 @@ static int flctl_probe(struct platform_device *pdev)
        nand->legacy.read_byte = flctl_read_byte;
        nand->legacy.write_buf = flctl_write_buf;
        nand->legacy.read_buf = flctl_read_buf;
-       nand->select_chip = flctl_select_chip;
+       nand->legacy.select_chip = flctl_select_chip;
        nand->legacy.cmdfunc = flctl_cmdfunc;
        nand->legacy.set_features = nand_get_set_features_notsupp;
        nand->legacy.get_features = nand_get_set_features_notsupp;
@@ -1196,7 +1183,7 @@ static int flctl_probe(struct platform_device *pdev)
 
        flctl_setup_dma(flctl);
 
-       nand->dummy_controller.ops = &flctl_nand_controller_ops;
+       nand->legacy.dummy_controller.ops = &flctl_nand_controller_ops;
        ret = nand_scan(nand, 1);
        if (ret)
                goto err_chip;
@@ -1236,7 +1223,7 @@ static struct platform_driver flctl_driver = {
 
 module_platform_driver_probe(flctl_driver, flctl_probe);
 
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Yoshihiro Shimoda");
 MODULE_DESCRIPTION("SuperH FLCTL driver");
 MODULE_ALIAS("platform:sh_flctl");
index 6f063ef..409d036 100644 (file)
@@ -194,7 +194,7 @@ int sm_register_device(struct mtd_info *mtd, int smartmedia)
        chip->options |= NAND_SKIP_BBTSCAN;
 
        /* Scan for card properties */
-       chip->dummy_controller.ops = &sm_controller_ops;
+       chip->legacy.dummy_controller.ops = &sm_controller_ops;
        flash_ids = smartmedia ? nand_smartmedia_flash_ids : nand_xd_flash_ids;
        ret = nand_scan_with_ids(chip, 1, flash_ids);
        if (ret)
index 51b1a54..e828ee5 100644 (file)
@@ -1393,7 +1393,7 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct nand_chip *chip,
        sunxi_nfc_randomizer_enable(mtd);
 
        writel((NAND_CMD_RNDIN << 8) | NAND_CMD_PAGEPROG,
-              nfc->regs + NFC_REG_RCMD_SET);
+              nfc->regs + NFC_REG_WCMD_SET);
 
        dma_async_issue_pending(nfc->dmac);
 
@@ -1847,6 +1847,7 @@ static int sunxi_nand_attach_chip(struct nand_chip *nand)
 
 static const struct nand_controller_ops sunxi_nand_controller_ops = {
        .attach_chip = sunxi_nand_attach_chip,
+       .setup_data_interface = sunxi_nfc_setup_data_interface,
 };
 
 static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
@@ -1922,12 +1923,11 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
         */
        nand->ecc.mode = NAND_ECC_HW;
        nand_set_flash_node(nand, np);
-       nand->select_chip = sunxi_nfc_select_chip;
+       nand->legacy.select_chip = sunxi_nfc_select_chip;
        nand->legacy.cmd_ctrl = sunxi_nfc_cmd_ctrl;
        nand->legacy.read_buf = sunxi_nfc_read_buf;
        nand->legacy.write_buf = sunxi_nfc_write_buf;
        nand->legacy.read_byte = sunxi_nfc_read_byte;
-       nand->setup_data_interface = sunxi_nfc_setup_data_interface;
 
        mtd = nand_to_mtd(nand);
        mtd->dev.parent = dev;
index 8818f89..cb3beda 100644 (file)
@@ -530,6 +530,7 @@ static int tango_attach_chip(struct nand_chip *chip)
 
 static const struct nand_controller_ops tango_controller_ops = {
        .attach_chip = tango_attach_chip,
+       .setup_data_interface = tango_set_timings,
 };
 
 static int chip_init(struct device *dev, struct device_node *np)
@@ -567,10 +568,9 @@ static int chip_init(struct device *dev, struct device_node *np)
        chip->legacy.read_byte = tango_read_byte;
        chip->legacy.write_buf = tango_write_buf;
        chip->legacy.read_buf = tango_read_buf;
-       chip->select_chip = tango_select_chip;
+       chip->legacy.select_chip = tango_select_chip;
        chip->legacy.cmd_ctrl = tango_cmd_ctrl;
        chip->legacy.dev_ready = tango_dev_ready;
-       chip->setup_data_interface = tango_set_timings;
        chip->options = NAND_USE_BOUNCE_BUFFER |
                        NAND_NO_SUBPAGE_WRITE |
                        NAND_WAIT_TCCS;
index 9767e29..13be32c 100644 (file)
@@ -454,29 +454,24 @@ static const struct nand_op_parser tegra_nand_op_parser = NAND_OP_PARSER(
                NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, 4)),
        );
 
+static void tegra_nand_select_target(struct nand_chip *chip,
+                                    unsigned int die_nr)
+{
+       struct tegra_nand_chip *nand = to_tegra_chip(chip);
+       struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller);
+
+       ctrl->cur_cs = nand->cs[die_nr];
+}
+
 static int tegra_nand_exec_op(struct nand_chip *chip,
                              const struct nand_operation *op,
                              bool check_only)
 {
+       tegra_nand_select_target(chip, op->cs);
        return nand_op_parser_exec_op(chip, &tegra_nand_op_parser, op,
                                      check_only);
 }
 
-static void tegra_nand_select_chip(struct nand_chip *chip, int die_nr)
-{
-       struct tegra_nand_chip *nand = to_tegra_chip(chip);
-       struct tegra_nand_controller *ctrl = to_tegra_ctrl(chip->controller);
-
-       WARN_ON(die_nr >= (int)ARRAY_SIZE(nand->cs));
-
-       if (die_nr < 0 || die_nr > 0) {
-               ctrl->cur_cs = -1;
-               return;
-       }
-
-       ctrl->cur_cs = nand->cs[die_nr];
-}
-
 static void tegra_nand_hw_ecc(struct tegra_nand_controller *ctrl,
                              struct nand_chip *chip, bool enable)
 {
@@ -503,6 +498,8 @@ static int tegra_nand_page_xfer(struct mtd_info *mtd, struct nand_chip *chip,
        u32 addr1, cmd, dma_ctrl;
        int ret;
 
+       tegra_nand_select_target(chip, chip->cur_cs);
+
        if (read) {
                writel_relaxed(NAND_CMD_READ0, ctrl->regs + CMD_REG1);
                writel_relaxed(NAND_CMD_READSTART, ctrl->regs + CMD_REG2);
@@ -1053,6 +1050,8 @@ static int tegra_nand_attach_chip(struct nand_chip *chip)
 
 static const struct nand_controller_ops tegra_nand_controller_ops = {
        .attach_chip = &tegra_nand_attach_chip,
+       .exec_op = tegra_nand_exec_op,
+       .setup_data_interface = tegra_nand_setup_data_interface,
 };
 
 static int tegra_nand_chips_init(struct device *dev,
@@ -1115,9 +1114,6 @@ static int tegra_nand_chips_init(struct device *dev,
                mtd->name = "tegra_nand";
 
        chip->options = NAND_NO_SUBPAGE_WRITE | NAND_USE_BOUNCE_BUFFER;
-       chip->exec_op = tegra_nand_exec_op;
-       chip->select_chip = tegra_nand_select_chip;
-       chip->setup_data_interface = tegra_nand_setup_data_interface;
 
        ret = nand_scan(chip, 1);
        if (ret)
index 9814fd4..a662ca1 100644 (file)
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright 2009-2015 Freescale Semiconductor, Inc. and others
  *
  *
  * Based on original driver mpc5121_nfc.c.
  *
- * This 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.
- *
  * Limitations:
  * - Untested on MPC5125 and M54418.
  * - DMA and pipelining not used.
@@ -152,6 +148,7 @@ enum vf610_nfc_variant {
 };
 
 struct vf610_nfc {
+       struct nand_controller base;
        struct nand_chip chip;
        struct device *dev;
        void __iomem *regs;
@@ -168,11 +165,6 @@ struct vf610_nfc {
        u32 ecc_mode;
 };
 
-static inline struct vf610_nfc *mtd_to_nfc(struct mtd_info *mtd)
-{
-       return container_of(mtd_to_nand(mtd), struct vf610_nfc, chip);
-}
-
 static inline struct vf610_nfc *chip_to_nfc(struct nand_chip *chip)
 {
        return container_of(chip, struct vf610_nfc, chip);
@@ -316,8 +308,7 @@ static void vf610_nfc_done(struct vf610_nfc *nfc)
 
 static irqreturn_t vf610_nfc_irq(int irq, void *data)
 {
-       struct mtd_info *mtd = data;
-       struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+       struct vf610_nfc *nfc = data;
 
        vf610_nfc_clear(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT);
        complete(&nfc->cmd_done);
@@ -487,40 +478,40 @@ static const struct nand_op_parser vf610_nfc_op_parser = NAND_OP_PARSER(
                NAND_OP_PARSER_PAT_DATA_IN_ELEM(true, PAGE_2K + OOB_MAX)),
        );
 
-static int vf610_nfc_exec_op(struct nand_chip *chip,
-                            const struct nand_operation *op,
-                            bool check_only)
-{
-       return nand_op_parser_exec_op(chip, &vf610_nfc_op_parser, op,
-                                     check_only);
-}
-
 /*
  * This function supports Vybrid only (MPC5125 would have full RB and four CS)
  */
-static void vf610_nfc_select_chip(struct nand_chip *chip, int cs)
+static void vf610_nfc_select_target(struct nand_chip *chip, unsigned int cs)
 {
-       struct vf610_nfc *nfc = mtd_to_nfc(nand_to_mtd(chip));
-       u32 tmp = vf610_nfc_read(nfc, NFC_ROW_ADDR);
+       struct vf610_nfc *nfc = chip_to_nfc(chip);
+       u32 tmp;
 
        /* Vybrid only (MPC5125 would have full RB and four CS) */
        if (nfc->variant != NFC_VFC610)
                return;
 
+       tmp = vf610_nfc_read(nfc, NFC_ROW_ADDR);
        tmp &= ~(ROW_ADDR_CHIP_SEL_RB_MASK | ROW_ADDR_CHIP_SEL_MASK);
-
-       if (cs >= 0) {
-               tmp |= 1 << ROW_ADDR_CHIP_SEL_RB_SHIFT;
-               tmp |= BIT(cs) << ROW_ADDR_CHIP_SEL_SHIFT;
-       }
+       tmp |= 1 << ROW_ADDR_CHIP_SEL_RB_SHIFT;
+       tmp |= BIT(cs) << ROW_ADDR_CHIP_SEL_SHIFT;
 
        vf610_nfc_write(nfc, NFC_ROW_ADDR, tmp);
 }
 
-static inline int vf610_nfc_correct_data(struct mtd_info *mtd, uint8_t *dat,
+static int vf610_nfc_exec_op(struct nand_chip *chip,
+                            const struct nand_operation *op,
+                            bool check_only)
+{
+       vf610_nfc_select_target(chip, op->cs);
+       return nand_op_parser_exec_op(chip, &vf610_nfc_op_parser, op,
+                                     check_only);
+}
+
+static inline int vf610_nfc_correct_data(struct nand_chip *chip, uint8_t *dat,
                                         uint8_t *oob, int page)
 {
-       struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+       struct vf610_nfc *nfc = chip_to_nfc(chip);
+       struct mtd_info *mtd = nand_to_mtd(chip);
        u32 ecc_status_off = NFC_MAIN_AREA(0) + ECC_SRAM_ADDR + ECC_STATUS;
        u8 ecc_status;
        u8 ecc_count;
@@ -560,12 +551,14 @@ static void vf610_nfc_fill_row(struct nand_chip *chip, int page, u32 *code,
 static int vf610_nfc_read_page(struct nand_chip *chip, uint8_t *buf,
                               int oob_required, int page)
 {
+       struct vf610_nfc *nfc = chip_to_nfc(chip);
        struct mtd_info *mtd = nand_to_mtd(chip);
-       struct vf610_nfc *nfc = mtd_to_nfc(mtd);
        int trfr_sz = mtd->writesize + mtd->oobsize;
        u32 row = 0, cmd1 = 0, cmd2 = 0, code = 0;
        int stat;
 
+       vf610_nfc_select_target(chip, chip->cur_cs);
+
        cmd2 |= NAND_CMD_READ0 << CMD_BYTE1_SHIFT;
        code |= COMMAND_CMD_BYTE1 | COMMAND_CAR_BYTE1 | COMMAND_CAR_BYTE2;
 
@@ -592,7 +585,7 @@ static int vf610_nfc_read_page(struct nand_chip *chip, uint8_t *buf,
                                                   mtd->writesize,
                                       mtd->oobsize, false);
 
-       stat = vf610_nfc_correct_data(mtd, buf, chip->oob_poi, page);
+       stat = vf610_nfc_correct_data(chip, buf, chip->oob_poi, page);
 
        if (stat < 0) {
                mtd->ecc_stats.failed++;
@@ -606,13 +599,15 @@ static int vf610_nfc_read_page(struct nand_chip *chip, uint8_t *buf,
 static int vf610_nfc_write_page(struct nand_chip *chip, const uint8_t *buf,
                                int oob_required, int page)
 {
+       struct vf610_nfc *nfc = chip_to_nfc(chip);
        struct mtd_info *mtd = nand_to_mtd(chip);
-       struct vf610_nfc *nfc = mtd_to_nfc(mtd);
        int trfr_sz = mtd->writesize + mtd->oobsize;
        u32 row = 0, cmd1 = 0, cmd2 = 0, code = 0;
        u8 status;
        int ret;
 
+       vf610_nfc_select_target(chip, chip->cur_cs);
+
        cmd2 |= NAND_CMD_SEQIN << CMD_BYTE1_SHIFT;
        code |= COMMAND_CMD_BYTE1 | COMMAND_CAR_BYTE1 | COMMAND_CAR_BYTE2;
 
@@ -648,8 +643,7 @@ static int vf610_nfc_write_page(struct nand_chip *chip, const uint8_t *buf,
 static int vf610_nfc_read_page_raw(struct nand_chip *chip, u8 *buf,
                                   int oob_required, int page)
 {
-       struct mtd_info *mtd = nand_to_mtd(chip);
-       struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+       struct vf610_nfc *nfc = chip_to_nfc(chip);
        int ret;
 
        nfc->data_access = true;
@@ -662,8 +656,8 @@ static int vf610_nfc_read_page_raw(struct nand_chip *chip, u8 *buf,
 static int vf610_nfc_write_page_raw(struct nand_chip *chip, const u8 *buf,
                                    int oob_required, int page)
 {
+       struct vf610_nfc *nfc = chip_to_nfc(chip);
        struct mtd_info *mtd = nand_to_mtd(chip);
-       struct vf610_nfc *nfc = mtd_to_nfc(mtd);
        int ret;
 
        nfc->data_access = true;
@@ -681,7 +675,7 @@ static int vf610_nfc_write_page_raw(struct nand_chip *chip, const u8 *buf,
 
 static int vf610_nfc_read_oob(struct nand_chip *chip, int page)
 {
-       struct vf610_nfc *nfc = mtd_to_nfc(nand_to_mtd(chip));
+       struct vf610_nfc *nfc = chip_to_nfc(chip);
        int ret;
 
        nfc->data_access = true;
@@ -694,7 +688,7 @@ static int vf610_nfc_read_oob(struct nand_chip *chip, int page)
 static int vf610_nfc_write_oob(struct nand_chip *chip, int page)
 {
        struct mtd_info *mtd = nand_to_mtd(chip);
-       struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+       struct vf610_nfc *nfc = chip_to_nfc(chip);
        int ret;
 
        nfc->data_access = true;
@@ -751,7 +745,7 @@ static void vf610_nfc_init_controller(struct vf610_nfc *nfc)
 static int vf610_nfc_attach_chip(struct nand_chip *chip)
 {
        struct mtd_info *mtd = nand_to_mtd(chip);
-       struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+       struct vf610_nfc *nfc = chip_to_nfc(chip);
 
        vf610_nfc_init_controller(nfc);
 
@@ -809,6 +803,8 @@ static int vf610_nfc_attach_chip(struct nand_chip *chip)
 
 static const struct nand_controller_ops vf610_nfc_controller_ops = {
        .attach_chip = vf610_nfc_attach_chip,
+       .exec_op = vf610_nfc_exec_op,
+
 };
 
 static int vf610_nfc_probe(struct platform_device *pdev)
@@ -876,14 +872,11 @@ static int vf610_nfc_probe(struct platform_device *pdev)
                goto err_disable_clk;
        }
 
-       chip->exec_op = vf610_nfc_exec_op;
-       chip->select_chip = vf610_nfc_select_chip;
-
        chip->options |= NAND_NO_SUBPAGE_WRITE;
 
        init_completion(&nfc->cmd_done);
 
-       err = devm_request_irq(nfc->dev, irq, vf610_nfc_irq, 0, DRV_NAME, mtd);
+       err = devm_request_irq(nfc->dev, irq, vf610_nfc_irq, 0, DRV_NAME, nfc);
        if (err) {
                dev_err(nfc->dev, "Error requesting IRQ!\n");
                goto err_disable_clk;
@@ -891,13 +884,16 @@ static int vf610_nfc_probe(struct platform_device *pdev)
 
        vf610_nfc_preinit_controller(nfc);
 
+       nand_controller_init(&nfc->base);
+       nfc->base.ops = &vf610_nfc_controller_ops;
+       chip->controller = &nfc->base;
+
        /* Scan the NAND chip */
-       chip->dummy_controller.ops = &vf610_nfc_controller_ops;
        err = nand_scan(chip, 1);
        if (err)
                goto err_disable_clk;
 
-       platform_set_drvdata(pdev, mtd);
+       platform_set_drvdata(pdev, nfc);
 
        /* Register device in MTD */
        err = mtd_device_register(mtd, NULL, 0);
@@ -914,10 +910,9 @@ err_disable_clk:
 
 static int vf610_nfc_remove(struct platform_device *pdev)
 {
-       struct mtd_info *mtd = platform_get_drvdata(pdev);
-       struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+       struct vf610_nfc *nfc = platform_get_drvdata(pdev);
 
-       nand_release(mtd_to_nand(mtd));
+       nand_release(&nfc->chip);
        clk_disable_unprepare(nfc->clk);
        return 0;
 }
@@ -925,8 +920,7 @@ static int vf610_nfc_remove(struct platform_device *pdev)
 #ifdef CONFIG_PM_SLEEP
 static int vf610_nfc_suspend(struct device *dev)
 {
-       struct mtd_info *mtd = dev_get_drvdata(dev);
-       struct vf610_nfc *nfc = mtd_to_nfc(mtd);
+       struct vf610_nfc *nfc = dev_get_drvdata(dev);
 
        clk_disable_unprepare(nfc->clk);
        return 0;
@@ -934,11 +928,9 @@ static int vf610_nfc_suspend(struct device *dev)
 
 static int vf610_nfc_resume(struct device *dev)
 {
+       struct vf610_nfc *nfc = dev_get_drvdata(dev);
        int err;
 
-       struct mtd_info *mtd = dev_get_drvdata(dev);
-       struct vf610_nfc *nfc = mtd_to_nfc(mtd);
-
        err = clk_prepare_enable(nfc->clk);
        if (err)
                return err;
index a234a5c..4cb7810 100644 (file)
@@ -176,7 +176,7 @@ static int xway_nand_probe(struct platform_device *pdev)
 
        data->chip.legacy.cmd_ctrl = xway_cmd_ctrl;
        data->chip.legacy.dev_ready = xway_dev_ready;
-       data->chip.select_chip = xway_select_chip;
+       data->chip.legacy.select_chip = xway_select_chip;
        data->chip.legacy.write_buf = xway_write_buf;
        data->chip.legacy.read_buf = xway_read_buf;
        data->chip.legacy.read_byte = xway_read_byte;
index b74e074..7531250 100644 (file)
@@ -1,3 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0
-spinand-objs := core.o macronix.o micron.o winbond.o
+spinand-objs := core.o gigadevice.o macronix.o micron.o toshiba.o winbond.o
 obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
index 30f8364..479c2f2 100644 (file)
@@ -764,8 +764,10 @@ static const struct nand_ops spinand_ops = {
 };
 
 static const struct spinand_manufacturer *spinand_manufacturers[] = {
+       &gigadevice_spinand_manufacturer,
        &macronix_spinand_manufacturer,
        &micron_spinand_manufacturer,
+       &toshiba_spinand_manufacturer,
        &winbond_spinand_manufacturer,
 };
 
diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c
new file mode 100644 (file)
index 0000000..e4141c2
--- /dev/null
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author:
+ *     Chuanhong Guo <gch981213@gmail.com>
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mtd/spinand.h>
+
+#define SPINAND_MFR_GIGADEVICE                 0xC8
+#define GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS     (1 << 4)
+#define GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS       (3 << 4)
+
+static SPINAND_OP_VARIANTS(read_cache_variants,
+               SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
+
+static SPINAND_OP_VARIANTS(write_cache_variants,
+               SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
+               SPINAND_PROG_LOAD(true, 0, NULL, 0));
+
+static SPINAND_OP_VARIANTS(update_cache_variants,
+               SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
+               SPINAND_PROG_LOAD(false, 0, NULL, 0));
+
+static int gd5fxgq4xa_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                 struct mtd_oob_region *region)
+{
+       if (section > 3)
+               return -ERANGE;
+
+       region->offset = (16 * section) + 8;
+       region->length = 8;
+
+       return 0;
+}
+
+static int gd5fxgq4xa_ooblayout_free(struct mtd_info *mtd, int section,
+                                  struct mtd_oob_region *region)
+{
+       if (section > 3)
+               return -ERANGE;
+
+       if (section) {
+               region->offset = 16 * section;
+               region->length = 8;
+       } else {
+               /* section 0 has one byte reserved for bad block mark */
+               region->offset = 1;
+               region->length = 7;
+       }
+       return 0;
+}
+
+static int gd5fxgq4xa_ecc_get_status(struct spinand_device *spinand,
+                                        u8 status)
+{
+       switch (status & STATUS_ECC_MASK) {
+       case STATUS_ECC_NO_BITFLIPS:
+               return 0;
+
+       case GD5FXGQ4XA_STATUS_ECC_1_7_BITFLIPS:
+               /* 1-7 bits are flipped. return the maximum. */
+               return 7;
+
+       case GD5FXGQ4XA_STATUS_ECC_8_BITFLIPS:
+               return 8;
+
+       case STATUS_ECC_UNCOR_ERROR:
+               return -EBADMSG;
+
+       default:
+               break;
+       }
+
+       return -EINVAL;
+}
+
+static const struct mtd_ooblayout_ops gd5fxgq4xa_ooblayout = {
+       .ecc = gd5fxgq4xa_ooblayout_ecc,
+       .free = gd5fxgq4xa_ooblayout_free,
+};
+
+static const struct spinand_info gigadevice_spinand_table[] = {
+       SPINAND_INFO("GD5F1GQ4xA", 0xF1,
+                    NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    0,
+                    SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
+                                    gd5fxgq4xa_ecc_get_status)),
+       SPINAND_INFO("GD5F2GQ4xA", 0xF2,
+                    NAND_MEMORG(1, 2048, 64, 64, 2048, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    0,
+                    SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
+                                    gd5fxgq4xa_ecc_get_status)),
+       SPINAND_INFO("GD5F4GQ4xA", 0xF4,
+                    NAND_MEMORG(1, 2048, 64, 64, 4096, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    0,
+                    SPINAND_ECCINFO(&gd5fxgq4xa_ooblayout,
+                                    gd5fxgq4xa_ecc_get_status)),
+};
+
+static int gigadevice_spinand_detect(struct spinand_device *spinand)
+{
+       u8 *id = spinand->id.data;
+       int ret;
+
+       /*
+        * For GD NANDs, There is an address byte needed to shift in before IDs
+        * are read out, so the first byte in raw_id is dummy.
+        */
+       if (id[1] != SPINAND_MFR_GIGADEVICE)
+               return 0;
+
+       ret = spinand_match_and_init(spinand, gigadevice_spinand_table,
+                                    ARRAY_SIZE(gigadevice_spinand_table),
+                                    id[2]);
+       if (ret)
+               return ret;
+
+       return 1;
+}
+
+static const struct spinand_manufacturer_ops gigadevice_spinand_manuf_ops = {
+       .detect = gigadevice_spinand_detect,
+};
+
+const struct spinand_manufacturer gigadevice_spinand_manufacturer = {
+       .id = SPINAND_MFR_GIGADEVICE,
+       .name = "GigaDevice",
+       .ops = &gigadevice_spinand_manuf_ops,
+};
diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c
new file mode 100644 (file)
index 0000000..0812655
--- /dev/null
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 exceet electronics GmbH
+ * Copyright (c) 2018 Kontron Electronics GmbH
+ *
+ * Author: Frieder Schrempf <frieder.schrempf@kontron.de>
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mtd/spinand.h>
+
+#define SPINAND_MFR_TOSHIBA            0x98
+#define TOSH_STATUS_ECC_HAS_BITFLIPS_T (3 << 4)
+
+static SPINAND_OP_VARIANTS(read_cache_variants,
+               SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
+               SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
+
+static SPINAND_OP_VARIANTS(write_cache_variants,
+               SPINAND_PROG_LOAD(true, 0, NULL, 0));
+
+static SPINAND_OP_VARIANTS(update_cache_variants,
+               SPINAND_PROG_LOAD(false, 0, NULL, 0));
+
+static int tc58cvg2s0h_ooblayout_ecc(struct mtd_info *mtd, int section,
+                                    struct mtd_oob_region *region)
+{
+       if (section > 7)
+               return -ERANGE;
+
+       region->offset = 128 + 16 * section;
+       region->length = 16;
+
+       return 0;
+}
+
+static int tc58cvg2s0h_ooblayout_free(struct mtd_info *mtd, int section,
+                                     struct mtd_oob_region *region)
+{
+       if (section > 0)
+               return -ERANGE;
+
+       /* 2 bytes reserved for BBM */
+       region->offset = 2;
+       region->length = 126;
+
+       return 0;
+}
+
+static const struct mtd_ooblayout_ops tc58cvg2s0h_ooblayout = {
+       .ecc = tc58cvg2s0h_ooblayout_ecc,
+       .free = tc58cvg2s0h_ooblayout_free,
+};
+
+static int tc58cvg2s0h_ecc_get_status(struct spinand_device *spinand,
+                                     u8 status)
+{
+       struct nand_device *nand = spinand_to_nand(spinand);
+       u8 mbf = 0;
+       struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf);
+
+       switch (status & STATUS_ECC_MASK) {
+       case STATUS_ECC_NO_BITFLIPS:
+               return 0;
+
+       case STATUS_ECC_UNCOR_ERROR:
+               return -EBADMSG;
+
+       case STATUS_ECC_HAS_BITFLIPS:
+       case TOSH_STATUS_ECC_HAS_BITFLIPS_T:
+               /*
+                * Let's try to retrieve the real maximum number of bitflips
+                * in order to avoid forcing the wear-leveling layer to move
+                * data around if it's not necessary.
+                */
+               if (spi_mem_exec_op(spinand->spimem, &op))
+                       return nand->eccreq.strength;
+
+               mbf >>= 4;
+
+               if (WARN_ON(mbf > nand->eccreq.strength || !mbf))
+                       return nand->eccreq.strength;
+
+               return mbf;
+
+       default:
+               break;
+       }
+
+       return -EINVAL;
+}
+
+static const struct spinand_info toshiba_spinand_table[] = {
+       SPINAND_INFO("TC58CVG2S0H", 0xCD,
+                    NAND_MEMORG(1, 4096, 256, 64, 2048, 1, 1, 1),
+                    NAND_ECCREQ(8, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    SPINAND_HAS_QE_BIT,
+                    SPINAND_ECCINFO(&tc58cvg2s0h_ooblayout,
+                                    tc58cvg2s0h_ecc_get_status)),
+};
+
+static int toshiba_spinand_detect(struct spinand_device *spinand)
+{
+       u8 *id = spinand->id.data;
+       int ret;
+
+       /*
+        * Toshiba SPI NAND read ID needs a dummy byte,
+        * so the first byte in id is garbage.
+        */
+       if (id[1] != SPINAND_MFR_TOSHIBA)
+               return 0;
+
+       ret = spinand_match_and_init(spinand, toshiba_spinand_table,
+                                    ARRAY_SIZE(toshiba_spinand_table),
+                                    id[2]);
+       if (ret)
+               return ret;
+
+       return 1;
+}
+
+static const struct spinand_manufacturer_ops toshiba_spinand_manuf_ops = {
+       .detect = toshiba_spinand_detect,
+};
+
+const struct spinand_manufacturer toshiba_spinand_manufacturer = {
+       .id = SPINAND_MFR_TOSHIBA,
+       .name = "Toshiba",
+       .ops = &toshiba_spinand_manuf_ops,
+};
index 67baa1b..5d94458 100644 (file)
@@ -84,6 +84,14 @@ static const struct spinand_info winbond_spinand_table[] = {
                     0,
                     SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL),
                     SPINAND_SELECT_TARGET(w25m02gv_select_target)),
+       SPINAND_INFO("W25N01GV", 0xAA,
+                    NAND_MEMORG(1, 2048, 64, 64, 1024, 1, 1, 1),
+                    NAND_ECCREQ(1, 512),
+                    SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+                                             &write_cache_variants,
+                                             &update_cache_variants),
+                    0,
+                    SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)),
 };
 
 /**
index 91b7fb3..334aa5b 100644 (file)
@@ -346,25 +346,26 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block)
                goto fail;
        }
 
-               /* increase and write Wear-Leveling info */
-               nb_erases = le32_to_cpu(uci.WearInfo);
-               nb_erases++;
-
-               /* wrap (almost impossible with current flash) or free block */
-               if (nb_erases == 0)
-                       nb_erases = 1;
-
-               /* check the "freeness" of Erase Unit before updating metadata
-                * FixMe:  is this check really necessary ? since we have check the
-                *         return code after the erase operation. */
-               if (check_free_sectors(nftl, instr->addr, nftl->EraseSize, 1) != 0)
-                       goto fail;
-
-               uci.WearInfo = le32_to_cpu(nb_erases);
-               if (nftl_write_oob(mtd, block * nftl->EraseSize + SECTORSIZE +
-                                  8, 8, &retlen, (char *)&uci) < 0)
-                       goto fail;
-               return 0;
+       /* increase and write Wear-Leveling info */
+       nb_erases = le32_to_cpu(uci.WearInfo);
+       nb_erases++;
+
+       /* wrap (almost impossible with current flash) or free block */
+       if (nb_erases == 0)
+               nb_erases = 1;
+
+       /* check the "freeness" of Erase Unit before updating metadata
+        * FixMe:  is this check really necessary ? since we have check the
+        *         return code after the erase operation.
+        */
+       if (check_free_sectors(nftl, instr->addr, nftl->EraseSize, 1) != 0)
+               goto fail;
+
+       uci.WearInfo = le32_to_cpu(nb_erases);
+       if (nftl_write_oob(mtd, block * nftl->EraseSize + SECTORSIZE +
+                          8, 8, &retlen, (char *)&uci) < 0)
+               goto fail;
+       return 0;
 fail:
        /* could not format, update the bad block table (caller is responsible
           for setting the ReplUnitTable to BLOCK_RESERVED on failure) */
index ee5ab99..fccf195 100644 (file)
@@ -14,3 +14,53 @@ config MTD_SHARPSL_PARTS
          This provides the read-only FTL logic necessary to read the partition
          table from the NAND flash of Sharp SL Series (Zaurus) and the MTD
          partition parser using this code.
+
+config MTD_REDBOOT_PARTS
+       tristate "RedBoot partition table parsing"
+       help
+         RedBoot is a ROM monitor and bootloader which deals with multiple
+         'images' in flash devices by putting a table one of the erase
+         blocks on the device, similar to a partition table, which gives
+         the offsets, lengths and names of all the images stored in the
+         flash.
+
+         If you need code which can detect and parse this table, and register
+         MTD 'partitions' corresponding to each image in the table, enable
+         this option.
+
+         You will still need the parsing functions to be called by the driver
+         for your particular device. It won't happen automatically. The
+         SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for
+         example.
+
+if MTD_REDBOOT_PARTS
+
+config MTD_REDBOOT_DIRECTORY_BLOCK
+       int "Location of RedBoot partition table"
+       default "-1"
+       help
+         This option is the Linux counterpart to the
+         CYGNUM_REDBOOT_FIS_DIRECTORY_BLOCK RedBoot compile time
+         option.
+
+         The option specifies which Flash sectors holds the RedBoot
+         partition table.  A zero or positive value gives an absolute
+         erase block number. A negative value specifies a number of
+         sectors before the end of the device.
+
+         For example "2" means block number 2, "-1" means the last
+         block and "-2" means the penultimate block.
+
+config MTD_REDBOOT_PARTS_UNALLOCATED
+       bool "Include unallocated flash regions"
+       help
+         If you need to register each unallocated flash region as a MTD
+         'partition', enable this option.
+
+config MTD_REDBOOT_PARTS_READONLY
+       bool "Force read-only for RedBoot system images"
+       help
+         If you need to force read-only for 'RedBoot', 'RedBoot Config' and
+         'FIS directory' images, enable this option.
+
+endif # MTD_REDBOOT_PARTS
index 5b1bcc3..d8418bf 100644 (file)
@@ -1,2 +1,3 @@
 obj-$(CONFIG_MTD_PARSER_TRX)           += parser_trx.o
 obj-$(CONFIG_MTD_SHARPSL_PARTS)                += sharpslpart.o
+obj-$(CONFIG_MTD_REDBOOT_PARTS)                += redboot.o
diff --git a/drivers/mtd/parsers/redboot.c b/drivers/mtd/parsers/redboot.c
new file mode 100644 (file)
index 0000000..957538d
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * Parse RedBoot-style Flash Image System (FIS) tables and
+ * produce a Linux partition array to match.
+ *
+ * Copyright © 2001      Red Hat UK Limited
+ * Copyright © 2001-2010 David Woodhouse <dwmw2@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/of.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/module.h>
+
+struct fis_image_desc {
+    unsigned char name[16];      // Null terminated name
+    uint32_t     flash_base;    // Address within FLASH of image
+    uint32_t     mem_base;      // Address in memory where it executes
+    uint32_t     size;          // Length of image
+    uint32_t     entry_point;   // Execution entry point
+    uint32_t     data_length;   // Length of actual data
+    unsigned char _pad[256-(16+7*sizeof(uint32_t))];
+    uint32_t     desc_cksum;    // Checksum over image descriptor
+    uint32_t     file_cksum;    // Checksum over image data
+};
+
+struct fis_list {
+       struct fis_image_desc *img;
+       struct fis_list *next;
+};
+
+static int directory = CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK;
+module_param(directory, int, 0);
+
+static inline int redboot_checksum(struct fis_image_desc *img)
+{
+       /* RedBoot doesn't actually write the desc_cksum field yet AFAICT */
+       return 1;
+}
+
+static void parse_redboot_of(struct mtd_info *master)
+{
+       struct device_node *np;
+       u32 dirblock;
+       int ret;
+
+       np = mtd_get_of_node(master);
+       if (!np)
+               return;
+
+       ret = of_property_read_u32(np, "fis-index-block", &dirblock);
+       if (ret)
+               return;
+
+       /*
+        * Assign the block found in the device tree to the local
+        * directory block pointer.
+        */
+       directory = dirblock;
+}
+
+static int parse_redboot_partitions(struct mtd_info *master,
+                                   const struct mtd_partition **pparts,
+                                   struct mtd_part_parser_data *data)
+{
+       int nrparts = 0;
+       struct fis_image_desc *buf;
+       struct mtd_partition *parts;
+       struct fis_list *fl = NULL, *tmp_fl;
+       int ret, i;
+       size_t retlen;
+       char *names;
+       char *nullname;
+       int namelen = 0;
+       int nulllen = 0;
+       int numslots;
+       unsigned long offset;
+#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
+       static char nullstring[] = "unallocated";
+#endif
+
+       parse_redboot_of(master);
+
+       if ( directory < 0 ) {
+               offset = master->size + directory * master->erasesize;
+               while (mtd_block_isbad(master, offset)) {
+                       if (!offset) {
+                       nogood:
+                               printk(KERN_NOTICE "Failed to find a non-bad block to check for RedBoot partition table\n");
+                               return -EIO;
+                       }
+                       offset -= master->erasesize;
+               }
+       } else {
+               offset = directory * master->erasesize;
+               while (mtd_block_isbad(master, offset)) {
+                       offset += master->erasesize;
+                       if (offset == master->size)
+                               goto nogood;
+               }
+       }
+       buf = vmalloc(master->erasesize);
+
+       if (!buf)
+               return -ENOMEM;
+
+       printk(KERN_NOTICE "Searching for RedBoot partition table in %s at offset 0x%lx\n",
+              master->name, offset);
+
+       ret = mtd_read(master, offset, master->erasesize, &retlen,
+                      (void *)buf);
+
+       if (ret)
+               goto out;
+
+       if (retlen != master->erasesize) {
+               ret = -EIO;
+               goto out;
+       }
+
+       numslots = (master->erasesize / sizeof(struct fis_image_desc));
+       for (i = 0; i < numslots; i++) {
+               if (!memcmp(buf[i].name, "FIS directory", 14)) {
+                       /* This is apparently the FIS directory entry for the
+                        * FIS directory itself.  The FIS directory size is
+                        * one erase block; if the buf[i].size field is
+                        * swab32(erasesize) then we know we are looking at
+                        * a byte swapped FIS directory - swap all the entries!
+                        * (NOTE: this is 'size' not 'data_length'; size is
+                        * the full size of the entry.)
+                        */
+
+                       /* RedBoot can combine the FIS directory and
+                          config partitions into a single eraseblock;
+                          we assume wrong-endian if either the swapped
+                          'size' matches the eraseblock size precisely,
+                          or if the swapped size actually fits in an
+                          eraseblock while the unswapped size doesn't. */
+                       if (swab32(buf[i].size) == master->erasesize ||
+                           (buf[i].size > master->erasesize
+                            && swab32(buf[i].size) < master->erasesize)) {
+                               int j;
+                               /* Update numslots based on actual FIS directory size */
+                               numslots = swab32(buf[i].size) / sizeof (struct fis_image_desc);
+                               for (j = 0; j < numslots; ++j) {
+
+                                       /* A single 0xff denotes a deleted entry.
+                                        * Two of them in a row is the end of the table.
+                                        */
+                                       if (buf[j].name[0] == 0xff) {
+                                               if (buf[j].name[1] == 0xff) {
+                                                       break;
+                                               } else {
+                                                       continue;
+                                               }
+                                       }
+
+                                       /* The unsigned long fields were written with the
+                                        * wrong byte sex, name and pad have no byte sex.
+                                        */
+                                       swab32s(&buf[j].flash_base);
+                                       swab32s(&buf[j].mem_base);
+                                       swab32s(&buf[j].size);
+                                       swab32s(&buf[j].entry_point);
+                                       swab32s(&buf[j].data_length);
+                                       swab32s(&buf[j].desc_cksum);
+                                       swab32s(&buf[j].file_cksum);
+                               }
+                       } else if (buf[i].size < master->erasesize) {
+                               /* Update numslots based on actual FIS directory size */
+                               numslots = buf[i].size / sizeof(struct fis_image_desc);
+                       }
+                       break;
+               }
+       }
+       if (i == numslots) {
+               /* Didn't find it */
+               printk(KERN_NOTICE "No RedBoot partition table detected in %s\n",
+                      master->name);
+               ret = 0;
+               goto out;
+       }
+
+       for (i = 0; i < numslots; i++) {
+               struct fis_list *new_fl, **prev;
+
+               if (buf[i].name[0] == 0xff) {
+                       if (buf[i].name[1] == 0xff) {
+                               break;
+                       } else {
+                               continue;
+                       }
+               }
+               if (!redboot_checksum(&buf[i]))
+                       break;
+
+               new_fl = kmalloc(sizeof(struct fis_list), GFP_KERNEL);
+               namelen += strlen(buf[i].name)+1;
+               if (!new_fl) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               new_fl->img = &buf[i];
+               if (data && data->origin)
+                       buf[i].flash_base -= data->origin;
+               else
+                       buf[i].flash_base &= master->size-1;
+
+               /* I'm sure the JFFS2 code has done me permanent damage.
+                * I now think the following is _normal_
+                */
+               prev = &fl;
+               while(*prev && (*prev)->img->flash_base < new_fl->img->flash_base)
+                       prev = &(*prev)->next;
+               new_fl->next = *prev;
+               *prev = new_fl;
+
+               nrparts++;
+       }
+#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
+       if (fl->img->flash_base) {
+               nrparts++;
+               nulllen = sizeof(nullstring);
+       }
+
+       for (tmp_fl = fl; tmp_fl->next; tmp_fl = tmp_fl->next) {
+               if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize <= tmp_fl->next->img->flash_base) {
+                       nrparts++;
+                       nulllen = sizeof(nullstring);
+               }
+       }
+#endif
+       parts = kzalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL);
+
+       if (!parts) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       nullname = (char *)&parts[nrparts];
+#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
+       if (nulllen > 0) {
+               strcpy(nullname, nullstring);
+       }
+#endif
+       names = nullname + nulllen;
+
+       i=0;
+
+#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
+       if (fl->img->flash_base) {
+              parts[0].name = nullname;
+              parts[0].size = fl->img->flash_base;
+              parts[0].offset = 0;
+               i++;
+       }
+#endif
+       for ( ; i<nrparts; i++) {
+               parts[i].size = fl->img->size;
+               parts[i].offset = fl->img->flash_base;
+               parts[i].name = names;
+
+               strcpy(names, fl->img->name);
+#ifdef CONFIG_MTD_REDBOOT_PARTS_READONLY
+               if (!memcmp(names, "RedBoot", 8) ||
+                               !memcmp(names, "RedBoot config", 15) ||
+                               !memcmp(names, "FIS directory", 14)) {
+                       parts[i].mask_flags = MTD_WRITEABLE;
+               }
+#endif
+               names += strlen(names)+1;
+
+#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
+               if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) {
+                       i++;
+                       parts[i].offset = parts[i-1].size + parts[i-1].offset;
+                       parts[i].size = fl->next->img->flash_base - parts[i].offset;
+                       parts[i].name = nullname;
+               }
+#endif
+               tmp_fl = fl;
+               fl = fl->next;
+               kfree(tmp_fl);
+       }
+       ret = nrparts;
+       *pparts = parts;
+ out:
+       while (fl) {
+               struct fis_list *old = fl;
+               fl = fl->next;
+               kfree(old);
+       }
+       vfree(buf);
+       return ret;
+}
+
+static const struct of_device_id mtd_parser_redboot_of_match_table[] = {
+       { .compatible = "redboot-fis" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, mtd_parser_redboot_of_match_table);
+
+static struct mtd_part_parser redboot_parser = {
+       .parse_fn = parse_redboot_partitions,
+       .name = "RedBoot",
+       .of_match_table = mtd_parser_redboot_of_match_table,
+};
+module_mtd_part_parser(redboot_parser);
+
+/* mtd parsers will request the module by parser name */
+MODULE_ALIAS("RedBoot");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_DESCRIPTION("Parsing code for RedBoot Flash Image System (FIS) tables");
diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c
deleted file mode 100644 (file)
index 7623ac5..0000000
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
- * Parse RedBoot-style Flash Image System (FIS) tables and
- * produce a Linux partition array to match.
- *
- * Copyright © 2001      Red Hat UK Limited
- * Copyright © 2001-2010 David Woodhouse <dwmw2@infradead.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/vmalloc.h>
-
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/module.h>
-
-struct fis_image_desc {
-    unsigned char name[16];      // Null terminated name
-    uint32_t     flash_base;    // Address within FLASH of image
-    uint32_t     mem_base;      // Address in memory where it executes
-    uint32_t     size;          // Length of image
-    uint32_t     entry_point;   // Execution entry point
-    uint32_t     data_length;   // Length of actual data
-    unsigned char _pad[256-(16+7*sizeof(uint32_t))];
-    uint32_t     desc_cksum;    // Checksum over image descriptor
-    uint32_t     file_cksum;    // Checksum over image data
-};
-
-struct fis_list {
-       struct fis_image_desc *img;
-       struct fis_list *next;
-};
-
-static int directory = CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK;
-module_param(directory, int, 0);
-
-static inline int redboot_checksum(struct fis_image_desc *img)
-{
-       /* RedBoot doesn't actually write the desc_cksum field yet AFAICT */
-       return 1;
-}
-
-static int parse_redboot_partitions(struct mtd_info *master,
-                                   const struct mtd_partition **pparts,
-                                   struct mtd_part_parser_data *data)
-{
-       int nrparts = 0;
-       struct fis_image_desc *buf;
-       struct mtd_partition *parts;
-       struct fis_list *fl = NULL, *tmp_fl;
-       int ret, i;
-       size_t retlen;
-       char *names;
-       char *nullname;
-       int namelen = 0;
-       int nulllen = 0;
-       int numslots;
-       unsigned long offset;
-#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
-       static char nullstring[] = "unallocated";
-#endif
-
-       if ( directory < 0 ) {
-               offset = master->size + directory * master->erasesize;
-               while (mtd_block_isbad(master, offset)) {
-                       if (!offset) {
-                       nogood:
-                               printk(KERN_NOTICE "Failed to find a non-bad block to check for RedBoot partition table\n");
-                               return -EIO;
-                       }
-                       offset -= master->erasesize;
-               }
-       } else {
-               offset = directory * master->erasesize;
-               while (mtd_block_isbad(master, offset)) {
-                       offset += master->erasesize;
-                       if (offset == master->size)
-                               goto nogood;
-               }
-       }
-       buf = vmalloc(master->erasesize);
-
-       if (!buf)
-               return -ENOMEM;
-
-       printk(KERN_NOTICE "Searching for RedBoot partition table in %s at offset 0x%lx\n",
-              master->name, offset);
-
-       ret = mtd_read(master, offset, master->erasesize, &retlen,
-                      (void *)buf);
-
-       if (ret)
-               goto out;
-
-       if (retlen != master->erasesize) {
-               ret = -EIO;
-               goto out;
-       }
-
-       numslots = (master->erasesize / sizeof(struct fis_image_desc));
-       for (i = 0; i < numslots; i++) {
-               if (!memcmp(buf[i].name, "FIS directory", 14)) {
-                       /* This is apparently the FIS directory entry for the
-                        * FIS directory itself.  The FIS directory size is
-                        * one erase block; if the buf[i].size field is
-                        * swab32(erasesize) then we know we are looking at
-                        * a byte swapped FIS directory - swap all the entries!
-                        * (NOTE: this is 'size' not 'data_length'; size is
-                        * the full size of the entry.)
-                        */
-
-                       /* RedBoot can combine the FIS directory and
-                          config partitions into a single eraseblock;
-                          we assume wrong-endian if either the swapped
-                          'size' matches the eraseblock size precisely,
-                          or if the swapped size actually fits in an
-                          eraseblock while the unswapped size doesn't. */
-                       if (swab32(buf[i].size) == master->erasesize ||
-                           (buf[i].size > master->erasesize
-                            && swab32(buf[i].size) < master->erasesize)) {
-                               int j;
-                               /* Update numslots based on actual FIS directory size */
-                               numslots = swab32(buf[i].size) / sizeof (struct fis_image_desc);
-                               for (j = 0; j < numslots; ++j) {
-
-                                       /* A single 0xff denotes a deleted entry.
-                                        * Two of them in a row is the end of the table.
-                                        */
-                                       if (buf[j].name[0] == 0xff) {
-                                               if (buf[j].name[1] == 0xff) {
-                                                       break;
-                                               } else {
-                                                       continue;
-                                               }
-                                       }
-
-                                       /* The unsigned long fields were written with the
-                                        * wrong byte sex, name and pad have no byte sex.
-                                        */
-                                       swab32s(&buf[j].flash_base);
-                                       swab32s(&buf[j].mem_base);
-                                       swab32s(&buf[j].size);
-                                       swab32s(&buf[j].entry_point);
-                                       swab32s(&buf[j].data_length);
-                                       swab32s(&buf[j].desc_cksum);
-                                       swab32s(&buf[j].file_cksum);
-                               }
-                       } else if (buf[i].size < master->erasesize) {
-                               /* Update numslots based on actual FIS directory size */
-                               numslots = buf[i].size / sizeof(struct fis_image_desc);
-                       }
-                       break;
-               }
-       }
-       if (i == numslots) {
-               /* Didn't find it */
-               printk(KERN_NOTICE "No RedBoot partition table detected in %s\n",
-                      master->name);
-               ret = 0;
-               goto out;
-       }
-
-       for (i = 0; i < numslots; i++) {
-               struct fis_list *new_fl, **prev;
-
-               if (buf[i].name[0] == 0xff) {
-                       if (buf[i].name[1] == 0xff) {
-                               break;
-                       } else {
-                               continue;
-                       }
-               }
-               if (!redboot_checksum(&buf[i]))
-                       break;
-
-               new_fl = kmalloc(sizeof(struct fis_list), GFP_KERNEL);
-               namelen += strlen(buf[i].name)+1;
-               if (!new_fl) {
-                       ret = -ENOMEM;
-                       goto out;
-               }
-               new_fl->img = &buf[i];
-               if (data && data->origin)
-                       buf[i].flash_base -= data->origin;
-               else
-                       buf[i].flash_base &= master->size-1;
-
-               /* I'm sure the JFFS2 code has done me permanent damage.
-                * I now think the following is _normal_
-                */
-               prev = &fl;
-               while(*prev && (*prev)->img->flash_base < new_fl->img->flash_base)
-                       prev = &(*prev)->next;
-               new_fl->next = *prev;
-               *prev = new_fl;
-
-               nrparts++;
-       }
-#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
-       if (fl->img->flash_base) {
-               nrparts++;
-               nulllen = sizeof(nullstring);
-       }
-
-       for (tmp_fl = fl; tmp_fl->next; tmp_fl = tmp_fl->next) {
-               if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize <= tmp_fl->next->img->flash_base) {
-                       nrparts++;
-                       nulllen = sizeof(nullstring);
-               }
-       }
-#endif
-       parts = kzalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL);
-
-       if (!parts) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       nullname = (char *)&parts[nrparts];
-#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
-       if (nulllen > 0) {
-               strcpy(nullname, nullstring);
-       }
-#endif
-       names = nullname + nulllen;
-
-       i=0;
-
-#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
-       if (fl->img->flash_base) {
-              parts[0].name = nullname;
-              parts[0].size = fl->img->flash_base;
-              parts[0].offset = 0;
-               i++;
-       }
-#endif
-       for ( ; i<nrparts; i++) {
-               parts[i].size = fl->img->size;
-               parts[i].offset = fl->img->flash_base;
-               parts[i].name = names;
-
-               strcpy(names, fl->img->name);
-#ifdef CONFIG_MTD_REDBOOT_PARTS_READONLY
-               if (!memcmp(names, "RedBoot", 8) ||
-                               !memcmp(names, "RedBoot config", 15) ||
-                               !memcmp(names, "FIS directory", 14)) {
-                       parts[i].mask_flags = MTD_WRITEABLE;
-               }
-#endif
-               names += strlen(names)+1;
-
-#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
-               if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) {
-                       i++;
-                       parts[i].offset = parts[i-1].size + parts[i-1].offset;
-                       parts[i].size = fl->next->img->flash_base - parts[i].offset;
-                       parts[i].name = nullname;
-               }
-#endif
-               tmp_fl = fl;
-               fl = fl->next;
-               kfree(tmp_fl);
-       }
-       ret = nrparts;
-       *pparts = parts;
- out:
-       while (fl) {
-               struct fis_list *old = fl;
-               fl = fl->next;
-               kfree(old);
-       }
-       vfree(buf);
-       return ret;
-}
-
-static struct mtd_part_parser redboot_parser = {
-       .parse_fn = parse_redboot_partitions,
-       .name = "RedBoot",
-};
-module_mtd_part_parser(redboot_parser);
-
-/* mtd parsers will request the module by parser name */
-MODULE_ALIAS("RedBoot");
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
-MODULE_DESCRIPTION("Parsing code for RedBoot Flash Image System (FIS) tables");
index 7c01553..e4f6088 100644 (file)
@@ -78,8 +78,6 @@ source "drivers/staging/goldfish/Kconfig"
 
 source "drivers/staging/netlogic/Kconfig"
 
-source "drivers/staging/mt29f_spinand/Kconfig"
-
 source "drivers/staging/gs_fpgaboot/Kconfig"
 
 source "drivers/staging/unisys/Kconfig"
index a79b3fe..5868631 100644 (file)
@@ -29,7 +29,6 @@ obj-$(CONFIG_STAGING_BOARD)   += board/
 obj-$(CONFIG_LTE_GDM724X)      += gdm724x/
 obj-$(CONFIG_FIREWIRE_SERIAL)  += fwserial/
 obj-$(CONFIG_GOLDFISH)         += goldfish/
-obj-$(CONFIG_MTD_SPINAND_MT29F)        += mt29f_spinand/
 obj-$(CONFIG_GS_FPGABOOT)      += gs_fpgaboot/
 obj-$(CONFIG_UNISYSSPAR)       += unisys/
 obj-$(CONFIG_COMMON_CLK_XLNX_CLKWZRD)  += clocking-wizard/
diff --git a/drivers/staging/mt29f_spinand/Kconfig b/drivers/staging/mt29f_spinand/Kconfig
deleted file mode 100644 (file)
index f3f9cb3..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-config MTD_SPINAND_MT29F
-       tristate "SPINAND Device Support for Micron"
-       depends on MTD_NAND && SPI
-       help
-         This enables support for accessing Micron SPI NAND flash
-         devices.
-         If you have Micron SPI NAND chip say yes.
-
-         If unsure, say no here.
-
-config MTD_SPINAND_ONDIEECC
-       bool "Use SPINAND internal ECC"
-       depends on MTD_SPINAND_MT29F
-       help
-         Internal ECC.
-         Enables Hardware ECC support for Micron SPI NAND.
diff --git a/drivers/staging/mt29f_spinand/Makefile b/drivers/staging/mt29f_spinand/Makefile
deleted file mode 100644 (file)
index e47af0f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand.o
diff --git a/drivers/staging/mt29f_spinand/TODO b/drivers/staging/mt29f_spinand/TODO
deleted file mode 100644 (file)
index a2209b7..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-TODO:
-       - Tested on XLP platform, needs to be tested on other platforms.
-       - Checkpatch.pl cleanups
-       - Sparce fixes.
-       - Clean up coding style to meet kernel standard.
-
-Please send patches
-To:
-Kamlakant Patel <kamlakant.patel@broadcom.com>
-Cc:
-Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-Mona Anonuevo <manonuevo@micron.com>
-linux-mtd@lists.infradead.org
diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.c b/drivers/staging/mt29f_spinand/mt29f_spinand.c
deleted file mode 100644 (file)
index def8a1f..0000000
+++ /dev/null
@@ -1,980 +0,0 @@
-/*
- * Copyright (c) 2003-2013 Broadcom Corporation
- *
- * Copyright (c) 2009-2010 Micron Technology, Inc.
- *
- * 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/module.h>
-#include <linux/delay.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/mtd/rawnand.h>
-#include <linux/spi/spi.h>
-
-#include "mt29f_spinand.h"
-
-#define BUFSIZE (10 * 64 * 2048)
-#define CACHE_BUF 2112
-/*
- * OOB area specification layout:  Total 32 available free bytes.
- */
-
-static inline struct spinand_state *mtd_to_state(struct mtd_info *mtd)
-{
-       struct nand_chip *chip = mtd_to_nand(mtd);
-       struct spinand_info *info = nand_get_controller_data(chip);
-       struct spinand_state *state = info->priv;
-
-       return state;
-}
-
-#ifdef CONFIG_MTD_SPINAND_ONDIEECC
-static int enable_hw_ecc;
-static int enable_read_hw_ecc;
-
-static int spinand_ooblayout_64_ecc(struct mtd_info *mtd, int section,
-                                   struct mtd_oob_region *oobregion)
-{
-       if (section > 3)
-               return -ERANGE;
-
-       oobregion->offset = (section * 16) + 1;
-       oobregion->length = 6;
-
-       return 0;
-}
-
-static int spinand_ooblayout_64_free(struct mtd_info *mtd, int section,
-                                    struct mtd_oob_region *oobregion)
-{
-       if (section > 3)
-               return -ERANGE;
-
-       oobregion->offset = (section * 16) + 8;
-       oobregion->length = 8;
-
-       return 0;
-}
-
-static const struct mtd_ooblayout_ops spinand_oob_64_ops = {
-       .ecc = spinand_ooblayout_64_ecc,
-       .free = spinand_ooblayout_64_free,
-};
-#endif
-
-/**
- * spinand_cmd - process a command to send to the SPI Nand
- * Description:
- *    Set up the command buffer to send to the SPI controller.
- *    The command buffer has to initialized to 0.
- */
-
-static int spinand_cmd(struct spi_device *spi, struct spinand_cmd *cmd)
-{
-       struct spi_message message;
-       struct spi_transfer x[4];
-       u8 dummy = 0xff;
-
-       spi_message_init(&message);
-       memset(x, 0, sizeof(x));
-
-       x[0].len = 1;
-       x[0].tx_buf = &cmd->cmd;
-       spi_message_add_tail(&x[0], &message);
-
-       if (cmd->n_addr) {
-               x[1].len = cmd->n_addr;
-               x[1].tx_buf = cmd->addr;
-               spi_message_add_tail(&x[1], &message);
-       }
-
-       if (cmd->n_dummy) {
-               x[2].len = cmd->n_dummy;
-               x[2].tx_buf = &dummy;
-               spi_message_add_tail(&x[2], &message);
-       }
-
-       if (cmd->n_tx) {
-               x[3].len = cmd->n_tx;
-               x[3].tx_buf = cmd->tx_buf;
-               spi_message_add_tail(&x[3], &message);
-       }
-
-       if (cmd->n_rx) {
-               x[3].len = cmd->n_rx;
-               x[3].rx_buf = cmd->rx_buf;
-               spi_message_add_tail(&x[3], &message);
-       }
-
-       return spi_sync(spi, &message);
-}
-
-/**
- * spinand_read_id - Read SPI Nand ID
- * Description:
- *    read two ID bytes from the SPI Nand device
- */
-static int spinand_read_id(struct spi_device *spi_nand, u8 *id)
-{
-       int retval;
-       u8 nand_id[3];
-       struct spinand_cmd cmd = {0};
-
-       cmd.cmd = CMD_READ_ID;
-       cmd.n_rx = 3;
-       cmd.rx_buf = &nand_id[0];
-
-       retval = spinand_cmd(spi_nand, &cmd);
-       if (retval < 0) {
-               dev_err(&spi_nand->dev, "error %d reading id\n", retval);
-               return retval;
-       }
-       id[0] = nand_id[1];
-       id[1] = nand_id[2];
-       return retval;
-}
-
-/**
- * spinand_read_status - send command 0xf to the SPI Nand status register
- * Description:
- *    After read, write, or erase, the Nand device is expected to set the
- *    busy status.
- *    This function is to allow reading the status of the command: read,
- *    write, and erase.
- *    Once the status turns to be ready, the other status bits also are
- *    valid status bits.
- */
-static int spinand_read_status(struct spi_device *spi_nand, u8 *status)
-{
-       struct spinand_cmd cmd = {0};
-       int ret;
-
-       cmd.cmd = CMD_READ_REG;
-       cmd.n_addr = 1;
-       cmd.addr[0] = REG_STATUS;
-       cmd.n_rx = 1;
-       cmd.rx_buf = status;
-
-       ret = spinand_cmd(spi_nand, &cmd);
-       if (ret < 0)
-               dev_err(&spi_nand->dev, "err: %d read status register\n", ret);
-
-       return ret;
-}
-
-#define MAX_WAIT_JIFFIES  (40 * HZ)
-static int wait_till_ready(struct spi_device *spi_nand)
-{
-       unsigned long deadline;
-       int retval;
-       u8 stat = 0;
-
-       deadline = jiffies + MAX_WAIT_JIFFIES;
-       do {
-               retval = spinand_read_status(spi_nand, &stat);
-               if (retval < 0)
-                       return -1;
-               if (!(stat & 0x1))
-                       break;
-
-               cond_resched();
-       } while (!time_after_eq(jiffies, deadline));
-
-       if ((stat & 0x1) == 0)
-               return 0;
-
-       return -1;
-}
-
-/**
- * spinand_get_otp - send command 0xf to read the SPI Nand OTP register
- * Description:
- *   There is one bit( bit 0x10 ) to set or to clear the internal ECC.
- *   Enable chip internal ECC, set the bit to 1
- *   Disable chip internal ECC, clear the bit to 0
- */
-static int spinand_get_otp(struct spi_device *spi_nand, u8 *otp)
-{
-       struct spinand_cmd cmd = {0};
-       int retval;
-
-       cmd.cmd = CMD_READ_REG;
-       cmd.n_addr = 1;
-       cmd.addr[0] = REG_OTP;
-       cmd.n_rx = 1;
-       cmd.rx_buf = otp;
-
-       retval = spinand_cmd(spi_nand, &cmd);
-       if (retval < 0)
-               dev_err(&spi_nand->dev, "error %d get otp\n", retval);
-       return retval;
-}
-
-/**
- * spinand_set_otp - send command 0x1f to write the SPI Nand OTP register
- * Description:
- *   There is one bit( bit 0x10 ) to set or to clear the internal ECC.
- *   Enable chip internal ECC, set the bit to 1
- *   Disable chip internal ECC, clear the bit to 0
- */
-static int spinand_set_otp(struct spi_device *spi_nand, u8 *otp)
-{
-       int retval;
-       struct spinand_cmd cmd = {0};
-
-       cmd.cmd = CMD_WRITE_REG;
-       cmd.n_addr = 1;
-       cmd.addr[0] = REG_OTP;
-       cmd.n_tx = 1;
-       cmd.tx_buf = otp;
-
-       retval = spinand_cmd(spi_nand, &cmd);
-       if (retval < 0)
-               dev_err(&spi_nand->dev, "error %d set otp\n", retval);
-
-       return retval;
-}
-
-#ifdef CONFIG_MTD_SPINAND_ONDIEECC
-/**
- * spinand_enable_ecc - send command 0x1f to write the SPI Nand OTP register
- * Description:
- *   There is one bit( bit 0x10 ) to set or to clear the internal ECC.
- *   Enable chip internal ECC, set the bit to 1
- *   Disable chip internal ECC, clear the bit to 0
- */
-static int spinand_enable_ecc(struct spi_device *spi_nand)
-{
-       int retval;
-       u8 otp = 0;
-
-       retval = spinand_get_otp(spi_nand, &otp);
-       if (retval < 0)
-               return retval;
-
-       if ((otp & OTP_ECC_MASK) == OTP_ECC_MASK)
-               return 0;
-       otp |= OTP_ECC_MASK;
-       retval = spinand_set_otp(spi_nand, &otp);
-       if (retval < 0)
-               return retval;
-       return spinand_get_otp(spi_nand, &otp);
-}
-#endif
-
-static int spinand_disable_ecc(struct spi_device *spi_nand)
-{
-       int retval;
-       u8 otp = 0;
-
-       retval = spinand_get_otp(spi_nand, &otp);
-       if (retval < 0)
-               return retval;
-
-       if ((otp & OTP_ECC_MASK) == OTP_ECC_MASK) {
-               otp &= ~OTP_ECC_MASK;
-               retval = spinand_set_otp(spi_nand, &otp);
-               if (retval < 0)
-                       return retval;
-               return spinand_get_otp(spi_nand, &otp);
-       }
-       return 0;
-}
-
-/**
- * spinand_write_enable - send command 0x06 to enable write or erase the
- * Nand cells
- * Description:
- *   Before write and erase the Nand cells, the write enable has to be set.
- *   After the write or erase, the write enable bit is automatically
- *   cleared (status register bit 2)
- *   Set the bit 2 of the status register has the same effect
- */
-static int spinand_write_enable(struct spi_device *spi_nand)
-{
-       struct spinand_cmd cmd = {0};
-
-       cmd.cmd = CMD_WR_ENABLE;
-       return spinand_cmd(spi_nand, &cmd);
-}
-
-static int spinand_read_page_to_cache(struct spi_device *spi_nand, u16 page_id)
-{
-       struct spinand_cmd cmd = {0};
-       u16 row;
-
-       row = page_id;
-       cmd.cmd = CMD_READ;
-       cmd.n_addr = 3;
-       cmd.addr[0] = (u8)((row & 0xff0000) >> 16);
-       cmd.addr[1] = (u8)((row & 0xff00) >> 8);
-       cmd.addr[2] = (u8)(row & 0x00ff);
-
-       return spinand_cmd(spi_nand, &cmd);
-}
-
-/**
- * spinand_read_from_cache - send command 0x03 to read out the data from the
- * cache register (2112 bytes max)
- * Description:
- *   The read can specify 1 to 2112 bytes of data read at the corresponding
- *   locations.
- *   No tRd delay.
- */
-static int spinand_read_from_cache(struct spi_device *spi_nand, u16 page_id,
-                                  u16 byte_id, u16 len, u8 *rbuf)
-{
-       struct spinand_cmd cmd = {0};
-       u16 column;
-
-       column = byte_id;
-       cmd.cmd = CMD_READ_RDM;
-       cmd.n_addr = 3;
-       cmd.addr[0] = (u8)((column & 0xff00) >> 8);
-       cmd.addr[0] |= (u8)(((page_id >> 6) & 0x1) << 4);
-       cmd.addr[1] = (u8)(column & 0x00ff);
-       cmd.addr[2] = (u8)(0xff);
-       cmd.n_dummy = 0;
-       cmd.n_rx = len;
-       cmd.rx_buf = rbuf;
-
-       return spinand_cmd(spi_nand, &cmd);
-}
-
-/**
- * spinand_read_page - read a page
- * @page_id: the physical page number
- * @offset:  the location from 0 to 2111
- * @len:     number of bytes to read
- * @rbuf:    read buffer to hold @len bytes
- *
- * Description:
- *   The read includes two commands to the Nand - 0x13 and 0x03 commands
- *   Poll to read status to wait for tRD time.
- */
-static int spinand_read_page(struct spi_device *spi_nand, u16 page_id,
-                            u16 offset, u16 len, u8 *rbuf)
-{
-       int ret;
-       u8 status = 0;
-
-#ifdef CONFIG_MTD_SPINAND_ONDIEECC
-       if (enable_read_hw_ecc) {
-               if (spinand_enable_ecc(spi_nand) < 0)
-                       dev_err(&spi_nand->dev, "enable HW ECC failed!");
-       }
-#endif
-       ret = spinand_read_page_to_cache(spi_nand, page_id);
-       if (ret < 0)
-               return ret;
-
-       if (wait_till_ready(spi_nand))
-               dev_err(&spi_nand->dev, "WAIT timedout!!!\n");
-
-       while (1) {
-               ret = spinand_read_status(spi_nand, &status);
-               if (ret < 0) {
-                       dev_err(&spi_nand->dev,
-                               "err %d read status register\n", ret);
-                       return ret;
-               }
-
-               if ((status & STATUS_OIP_MASK) == STATUS_READY) {
-                       if ((status & STATUS_ECC_MASK) == STATUS_ECC_ERROR) {
-                               dev_err(&spi_nand->dev, "ecc error, page=%d\n",
-                                       page_id);
-                               return 0;
-                       }
-                       break;
-               }
-       }
-
-       ret = spinand_read_from_cache(spi_nand, page_id, offset, len, rbuf);
-       if (ret < 0) {
-               dev_err(&spi_nand->dev, "read from cache failed!!\n");
-               return ret;
-       }
-
-#ifdef CONFIG_MTD_SPINAND_ONDIEECC
-       if (enable_read_hw_ecc) {
-               ret = spinand_disable_ecc(spi_nand);
-               if (ret < 0) {
-                       dev_err(&spi_nand->dev, "disable ecc failed!!\n");
-                       return ret;
-               }
-               enable_read_hw_ecc = 0;
-       }
-#endif
-       return ret;
-}
-
-/**
- * spinand_program_data_to_cache - write a page to cache
- * @byte_id: the location to write to the cache
- * @len:     number of bytes to write
- * @wbuf:    write buffer holding @len bytes
- *
- * Description:
- *   The write command used here is 0x84--indicating that the cache is
- *   not cleared first.
- *   Since it is writing the data to cache, there is no tPROG time.
- */
-static int spinand_program_data_to_cache(struct spi_device *spi_nand,
-                                        u16 page_id, u16 byte_id,
-                                        u16 len, u8 *wbuf)
-{
-       struct spinand_cmd cmd = {0};
-       u16 column;
-
-       column = byte_id;
-       cmd.cmd = CMD_PROG_PAGE_CLRCACHE;
-       cmd.n_addr = 2;
-       cmd.addr[0] = (u8)((column & 0xff00) >> 8);
-       cmd.addr[0] |= (u8)(((page_id >> 6) & 0x1) << 4);
-       cmd.addr[1] = (u8)(column & 0x00ff);
-       cmd.n_tx = len;
-       cmd.tx_buf = wbuf;
-
-       return spinand_cmd(spi_nand, &cmd);
-}
-
-/**
- * spinand_program_execute - write a page from cache to the Nand array
- * @page_id: the physical page location to write the page.
- *
- * Description:
- *   The write command used here is 0x10--indicating the cache is writing to
- *   the Nand array.
- *   Need to wait for tPROG time to finish the transaction.
- */
-static int spinand_program_execute(struct spi_device *spi_nand, u16 page_id)
-{
-       struct spinand_cmd cmd = {0};
-       u16 row;
-
-       row = page_id;
-       cmd.cmd = CMD_PROG_PAGE_EXC;
-       cmd.n_addr = 3;
-       cmd.addr[0] = (u8)((row & 0xff0000) >> 16);
-       cmd.addr[1] = (u8)((row & 0xff00) >> 8);
-       cmd.addr[2] = (u8)(row & 0x00ff);
-
-       return spinand_cmd(spi_nand, &cmd);
-}
-
-/**
- * spinand_program_page - write a page
- * @page_id: the physical page location to write the page.
- * @offset:  the location from the cache starting from 0 to 2111
- * @len:     the number of bytes to write
- * @buf:     the buffer holding @len bytes
- *
- * Description:
- *   The commands used here are 0x06, 0x84, and 0x10--indicating that
- *   the write enable is first sent, the write cache command, and the
- *   write execute command.
- *   Poll to wait for the tPROG time to finish the transaction.
- */
-static int spinand_program_page(struct spi_device *spi_nand,
-                               u16 page_id, u16 offset, u16 len, u8 *buf)
-{
-       int retval;
-       u8 status = 0;
-       u8 *wbuf;
-#ifdef CONFIG_MTD_SPINAND_ONDIEECC
-       unsigned int i, j;
-
-       wbuf = devm_kzalloc(&spi_nand->dev, CACHE_BUF, GFP_KERNEL);
-       if (!wbuf)
-               return -ENOMEM;
-
-       enable_read_hw_ecc = 1;
-       retval = spinand_read_page(spi_nand, page_id, 0, CACHE_BUF, wbuf);
-       if (retval < 0) {
-               dev_err(&spi_nand->dev, "ecc error on read page!!!\n");
-               return retval;
-       }
-
-       for (i = offset, j = 0; i < len; i++, j++)
-               wbuf[i] &= buf[j];
-
-       if (enable_hw_ecc) {
-               retval = spinand_enable_ecc(spi_nand);
-               if (retval < 0) {
-                       dev_err(&spi_nand->dev, "enable ecc failed!!\n");
-                       return retval;
-               }
-       }
-#else
-       wbuf = buf;
-#endif
-       retval = spinand_write_enable(spi_nand);
-       if (retval < 0) {
-               dev_err(&spi_nand->dev, "write enable failed!!\n");
-               return retval;
-       }
-       if (wait_till_ready(spi_nand))
-               dev_err(&spi_nand->dev, "wait timedout!!!\n");
-
-       retval = spinand_program_data_to_cache(spi_nand, page_id,
-                                              offset, len, wbuf);
-       if (retval < 0)
-               return retval;
-       retval = spinand_program_execute(spi_nand, page_id);
-       if (retval < 0)
-               return retval;
-       while (1) {
-               retval = spinand_read_status(spi_nand, &status);
-               if (retval < 0) {
-                       dev_err(&spi_nand->dev,
-                               "error %d reading status register\n", retval);
-                       return retval;
-               }
-
-               if ((status & STATUS_OIP_MASK) == STATUS_READY) {
-                       if ((status & STATUS_P_FAIL_MASK) == STATUS_P_FAIL) {
-                               dev_err(&spi_nand->dev,
-                                       "program error, page %d\n", page_id);
-                               return -1;
-                       }
-                       break;
-               }
-       }
-#ifdef CONFIG_MTD_SPINAND_ONDIEECC
-       if (enable_hw_ecc) {
-               retval = spinand_disable_ecc(spi_nand);
-               if (retval < 0) {
-                       dev_err(&spi_nand->dev, "disable ecc failed!!\n");
-                       return retval;
-               }
-               enable_hw_ecc = 0;
-       }
-#endif
-
-       return 0;
-}
-
-/**
- * spinand_erase_block_erase - erase a page
- * @block_id: the physical block location to erase.
- *
- * Description:
- *   The command used here is 0xd8--indicating an erase command to erase
- *   one block--64 pages
- *   Need to wait for tERS.
- */
-static int spinand_erase_block_erase(struct spi_device *spi_nand, u16 block_id)
-{
-       struct spinand_cmd cmd = {0};
-       u16 row;
-
-       row = block_id;
-       cmd.cmd = CMD_ERASE_BLK;
-       cmd.n_addr = 3;
-       cmd.addr[0] = (u8)((row & 0xff0000) >> 16);
-       cmd.addr[1] = (u8)((row & 0xff00) >> 8);
-       cmd.addr[2] = (u8)(row & 0x00ff);
-
-       return spinand_cmd(spi_nand, &cmd);
-}
-
-/**
- * spinand_erase_block - erase a page
- * @block_id: the physical block location to erase.
- *
- * Description:
- *   The commands used here are 0x06 and 0xd8--indicating an erase
- *   command to erase one block--64 pages
- *   It will first to enable the write enable bit (0x06 command),
- *   and then send the 0xd8 erase command
- *   Poll to wait for the tERS time to complete the tranaction.
- */
-static int spinand_erase_block(struct spi_device *spi_nand, u16 block_id)
-{
-       int retval;
-       u8 status = 0;
-
-       retval = spinand_write_enable(spi_nand);
-       if (wait_till_ready(spi_nand))
-               dev_err(&spi_nand->dev, "wait timedout!!!\n");
-
-       retval = spinand_erase_block_erase(spi_nand, block_id);
-       while (1) {
-               retval = spinand_read_status(spi_nand, &status);
-               if (retval < 0) {
-                       dev_err(&spi_nand->dev,
-                               "error %d reading status register\n", retval);
-                       return retval;
-               }
-
-               if ((status & STATUS_OIP_MASK) == STATUS_READY) {
-                       if ((status & STATUS_E_FAIL_MASK) == STATUS_E_FAIL) {
-                               dev_err(&spi_nand->dev,
-                                       "erase error, block %d\n", block_id);
-                               return -1;
-                       }
-                       break;
-               }
-       }
-       return 0;
-}
-
-#ifdef CONFIG_MTD_SPINAND_ONDIEECC
-static int spinand_write_page_hwecc(struct nand_chip *chip,
-                                   const u8 *buf, int oob_required,
-                                   int page)
-{
-       const u8 *p = buf;
-       int eccsize = chip->ecc.size;
-       int eccsteps = chip->ecc.steps;
-
-       enable_hw_ecc = 1;
-       return nand_prog_page_op(chip, page, 0, p, eccsize * eccsteps);
-}
-
-static int spinand_read_page_hwecc(struct nand_chip *chip, u8 *buf,
-                                  int oob_required, int page)
-{
-       int retval;
-       u8 status;
-       u8 *p = buf;
-       int eccsize = chip->ecc.size;
-       int eccsteps = chip->ecc.steps;
-       struct mtd_info *mtd = nand_to_mtd(chip);
-       struct spinand_info *info = nand_get_controller_data(chip);
-
-       enable_read_hw_ecc = 1;
-
-       nand_read_page_op(chip, page, 0, p, eccsize * eccsteps);
-       if (oob_required)
-               chip->legacy.read_buf(chip, chip->oob_poi, mtd->oobsize);
-
-       while (1) {
-               retval = spinand_read_status(info->spi, &status);
-               if (retval < 0) {
-                       dev_err(&mtd->dev,
-                               "error %d reading status register\n", retval);
-                       return retval;
-               }
-
-               if ((status & STATUS_OIP_MASK) == STATUS_READY) {
-                       if ((status & STATUS_ECC_MASK) == STATUS_ECC_ERROR) {
-                               pr_info("spinand: ECC error\n");
-                               mtd->ecc_stats.failed++;
-                       } else if ((status & STATUS_ECC_MASK) ==
-                                       STATUS_ECC_1BIT_CORRECTED)
-                               mtd->ecc_stats.corrected++;
-                       break;
-               }
-       }
-       return 0;
-}
-#endif
-
-static void spinand_select_chip(struct nand_chip *chip, int dev)
-{
-}
-
-static u8 spinand_read_byte(struct nand_chip *chip)
-{
-       struct spinand_state *state = mtd_to_state(nand_to_mtd(chip));
-       u8 data;
-
-       data = state->buf[state->buf_ptr];
-       state->buf_ptr++;
-       return data;
-}
-
-static int spinand_wait(struct nand_chip *chip)
-{
-       struct mtd_info *mtd = nand_to_mtd(chip);
-       struct spinand_info *info = nand_get_controller_data(chip);
-
-       unsigned long timeo = jiffies;
-       int retval, state = chip->state;
-       u8 status;
-
-       if (state == FL_ERASING)
-               timeo += (HZ * 400) / 1000;
-       else
-               timeo += (HZ * 20) / 1000;
-
-       while (time_before(jiffies, timeo)) {
-               retval = spinand_read_status(info->spi, &status);
-               if (retval < 0) {
-                       dev_err(&mtd->dev,
-                               "error %d reading status register\n", retval);
-                       return retval;
-               }
-
-               if ((status & STATUS_OIP_MASK) == STATUS_READY)
-                       return 0;
-
-               cond_resched();
-       }
-       return 0;
-}
-
-static void spinand_write_buf(struct nand_chip *chip, const u8 *buf, int len)
-{
-       struct spinand_state *state = mtd_to_state(nand_to_mtd(chip));
-
-       memcpy(state->buf + state->buf_ptr, buf, len);
-       state->buf_ptr += len;
-}
-
-static void spinand_read_buf(struct nand_chip *chip, u8 *buf, int len)
-{
-       struct spinand_state *state = mtd_to_state(nand_to_mtd(chip));
-
-       memcpy(buf, state->buf + state->buf_ptr, len);
-       state->buf_ptr += len;
-}
-
-/*
- * spinand_reset- send RESET command "0xff" to the Nand device.
- */
-static void spinand_reset(struct spi_device *spi_nand)
-{
-       struct spinand_cmd cmd = {0};
-
-       cmd.cmd = CMD_RESET;
-
-       if (spinand_cmd(spi_nand, &cmd) < 0)
-               pr_info("spinand reset failed!\n");
-
-       /* elapse 1ms before issuing any other command */
-       usleep_range(1000, 2000);
-
-       if (wait_till_ready(spi_nand))
-               dev_err(&spi_nand->dev, "wait timedout!\n");
-}
-
-static void spinand_cmdfunc(struct nand_chip *chip, unsigned int command,
-                           int column, int page)
-{
-       struct mtd_info *mtd = nand_to_mtd(chip);
-       struct spinand_info *info = nand_get_controller_data(chip);
-       struct spinand_state *state = info->priv;
-
-       switch (command) {
-       /*
-        * READ0 - read in first  0x800 bytes
-        */
-       case NAND_CMD_READ1:
-       case NAND_CMD_READ0:
-               state->buf_ptr = 0;
-               spinand_read_page(info->spi, page, 0x0, 0x840, state->buf);
-               break;
-       /* READOOB reads only the OOB because no ECC is performed. */
-       case NAND_CMD_READOOB:
-               state->buf_ptr = 0;
-               spinand_read_page(info->spi, page, 0x800, 0x40, state->buf);
-               break;
-       case NAND_CMD_RNDOUT:
-               state->buf_ptr = column;
-               break;
-       case NAND_CMD_READID:
-               state->buf_ptr = 0;
-               spinand_read_id(info->spi, state->buf);
-               break;
-       case NAND_CMD_PARAM:
-               state->buf_ptr = 0;
-               break;
-       /* ERASE1 stores the block and page address */
-       case NAND_CMD_ERASE1:
-               spinand_erase_block(info->spi, page);
-               break;
-       /* ERASE2 uses the block and page address from ERASE1 */
-       case NAND_CMD_ERASE2:
-               break;
-       /* SEQIN sets up the addr buffer and all registers except the length */
-       case NAND_CMD_SEQIN:
-               state->col = column;
-               state->row = page;
-               state->buf_ptr = 0;
-               break;
-       /* PAGEPROG reuses all of the setup from SEQIN and adds the length */
-       case NAND_CMD_PAGEPROG:
-               spinand_program_page(info->spi, state->row, state->col,
-                                    state->buf_ptr, state->buf);
-               break;
-       case NAND_CMD_STATUS:
-               spinand_get_otp(info->spi, state->buf);
-               if (!(state->buf[0] & 0x80))
-                       state->buf[0] = 0x80;
-               state->buf_ptr = 0;
-               break;
-       /* RESET command */
-       case NAND_CMD_RESET:
-               if (wait_till_ready(info->spi))
-                       dev_err(&info->spi->dev, "WAIT timedout!!!\n");
-               /* a minimum of 250us must elapse before issuing RESET cmd*/
-               usleep_range(250, 1000);
-               spinand_reset(info->spi);
-               break;
-       default:
-               dev_err(&mtd->dev, "Unknown CMD: 0x%x\n", command);
-       }
-}
-
-/**
- * spinand_lock_block - send write register 0x1f command to the Nand device
- *
- * Description:
- *    After power up, all the Nand blocks are locked.  This function allows
- *    one to unlock the blocks, and so it can be written or erased.
- */
-static int spinand_lock_block(struct spi_device *spi_nand, u8 lock)
-{
-       struct spinand_cmd cmd = {0};
-       int ret;
-       u8 otp = 0;
-
-       ret = spinand_get_otp(spi_nand, &otp);
-
-       cmd.cmd = CMD_WRITE_REG;
-       cmd.n_addr = 1;
-       cmd.addr[0] = REG_BLOCK_LOCK;
-       cmd.n_tx = 1;
-       cmd.tx_buf = &lock;
-
-       ret = spinand_cmd(spi_nand, &cmd);
-       if (ret < 0)
-               dev_err(&spi_nand->dev, "error %d lock block\n", ret);
-
-       return ret;
-}
-
-/**
- * spinand_probe - [spinand Interface]
- * @spi_nand: registered device driver.
- *
- * Description:
- *   Set up the device driver parameters to make the device available.
- */
-static int spinand_probe(struct spi_device *spi_nand)
-{
-       struct mtd_info *mtd;
-       struct nand_chip *chip;
-       struct spinand_info *info;
-       struct spinand_state *state;
-
-       info  = devm_kzalloc(&spi_nand->dev, sizeof(struct spinand_info),
-                            GFP_KERNEL);
-       if (!info)
-               return -ENOMEM;
-
-       info->spi = spi_nand;
-
-       spinand_lock_block(spi_nand, BL_ALL_UNLOCKED);
-
-       state = devm_kzalloc(&spi_nand->dev, sizeof(struct spinand_state),
-                            GFP_KERNEL);
-       if (!state)
-               return -ENOMEM;
-
-       info->priv      = state;
-       state->buf_ptr  = 0;
-       state->buf      = devm_kzalloc(&spi_nand->dev, BUFSIZE, GFP_KERNEL);
-       if (!state->buf)
-               return -ENOMEM;
-
-       chip = devm_kzalloc(&spi_nand->dev, sizeof(struct nand_chip),
-                           GFP_KERNEL);
-       if (!chip)
-               return -ENOMEM;
-
-#ifdef CONFIG_MTD_SPINAND_ONDIEECC
-       chip->ecc.mode  = NAND_ECC_HW;
-       chip->ecc.size  = 0x200;
-       chip->ecc.bytes = 0x6;
-       chip->ecc.steps = 0x4;
-
-       chip->ecc.strength = 1;
-       chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
-       chip->ecc.read_page = spinand_read_page_hwecc;
-       chip->ecc.write_page = spinand_write_page_hwecc;
-#else
-       chip->ecc.mode  = NAND_ECC_SOFT;
-       chip->ecc.algo  = NAND_ECC_HAMMING;
-       if (spinand_disable_ecc(spi_nand) < 0)
-               dev_info(&spi_nand->dev, "%s: disable ecc failed!\n",
-                        __func__);
-#endif
-
-       nand_set_flash_node(chip, spi_nand->dev.of_node);
-       nand_set_controller_data(chip, info);
-       chip->legacy.read_buf   = spinand_read_buf;
-       chip->legacy.write_buf  = spinand_write_buf;
-       chip->legacy.read_byte  = spinand_read_byte;
-       chip->legacy.cmdfunc    = spinand_cmdfunc;
-       chip->legacy.waitfunc   = spinand_wait;
-       chip->options   |= NAND_CACHEPRG;
-       chip->select_chip = spinand_select_chip;
-       chip->legacy.set_features = nand_get_set_features_notsupp;
-       chip->legacy.get_features = nand_get_set_features_notsupp;
-
-       mtd = nand_to_mtd(chip);
-
-       dev_set_drvdata(&spi_nand->dev, mtd);
-
-       mtd->dev.parent = &spi_nand->dev;
-       mtd->oobsize = 64;
-#ifdef CONFIG_MTD_SPINAND_ONDIEECC
-       mtd_set_ooblayout(mtd, &spinand_oob_64_ops);
-#endif
-
-       if (nand_scan(chip, 1))
-               return -ENXIO;
-
-       return mtd_device_register(mtd, NULL, 0);
-}
-
-/**
- * spinand_remove - remove the device driver
- * @spi: the spi device.
- *
- * Description:
- *   Remove the device driver parameters and free up allocated memories.
- */
-static int spinand_remove(struct spi_device *spi)
-{
-       mtd_device_unregister(dev_get_drvdata(&spi->dev));
-
-       return 0;
-}
-
-static const struct of_device_id spinand_dt[] = {
-       { .compatible = "spinand,mt29f", },
-       {}
-};
-MODULE_DEVICE_TABLE(of, spinand_dt);
-
-/*
- * Device name structure description
- */
-static struct spi_driver spinand_driver = {
-       .driver = {
-               .name           = "mt29f",
-               .of_match_table = spinand_dt,
-       },
-       .probe          = spinand_probe,
-       .remove         = spinand_remove,
-};
-
-module_spi_driver(spinand_driver);
-
-MODULE_DESCRIPTION("SPI NAND driver for Micron");
-MODULE_AUTHOR("Henry Pan <hspan@micron.com>, Kamlakant Patel <kamlakant.patel@broadcom.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/mt29f_spinand/mt29f_spinand.h b/drivers/staging/mt29f_spinand/mt29f_spinand.h
deleted file mode 100644 (file)
index 457dc7f..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*-
- * Copyright 2013 Broadcom Corporation
- *
- * Copyright (c) 2009-2010 Micron Technology, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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.
- *
- * Henry Pan <hspan@micron.com>
- *
- * based on nand.h
- */
-#ifndef __LINUX_MTD_SPI_NAND_H
-#define __LINUX_MTD_SPI_NAND_H
-
-#include <linux/wait.h>
-#include <linux/spinlock.h>
-#include <linux/mtd/mtd.h>
-
-/* cmd */
-#define CMD_READ                       0x13
-#define CMD_READ_RDM                   0x03
-#define CMD_PROG_PAGE_CLRCACHE         0x02
-#define CMD_PROG_PAGE                  0x84
-#define CMD_PROG_PAGE_EXC              0x10
-#define CMD_ERASE_BLK                  0xd8
-#define CMD_WR_ENABLE                  0x06
-#define CMD_WR_DISABLE                 0x04
-#define CMD_READ_ID                    0x9f
-#define CMD_RESET                      0xff
-#define CMD_READ_REG                   0x0f
-#define CMD_WRITE_REG                  0x1f
-
-/* feature/ status reg */
-#define REG_BLOCK_LOCK                 0xa0
-#define REG_OTP                                0xb0
-#define REG_STATUS                     0xc0/* timing */
-
-/* status */
-#define STATUS_OIP_MASK                        0x01
-#define STATUS_READY                   0
-#define STATUS_BUSY                    BIT(0)
-
-#define STATUS_E_FAIL_MASK             0x04
-#define STATUS_E_FAIL                  BIT(2)
-
-#define STATUS_P_FAIL_MASK             0x08
-#define STATUS_P_FAIL                  BIT(3)
-
-#define STATUS_ECC_MASK                        0x30
-#define STATUS_ECC_1BIT_CORRECTED      BIT(4)
-#define STATUS_ECC_ERROR               BIT(5)
-#define STATUS_ECC_RESERVED            (BIT(5) | BIT(4))
-
-/*ECC enable defines*/
-#define OTP_ECC_MASK                   0x10
-#define OTP_ECC_OFF                    0
-#define OTP_ECC_ON                     1
-
-#define ECC_DISABLED
-#define ECC_IN_NAND
-#define ECC_SOFT
-
-/* block lock */
-#define BL_ALL_LOCKED      0x38
-#define BL_1_2_LOCKED      0x30
-#define BL_1_4_LOCKED      0x28
-#define BL_1_8_LOCKED      0x20
-#define BL_1_16_LOCKED     0x18
-#define BL_1_32_LOCKED     0x10
-#define BL_1_64_LOCKED     0x08
-#define BL_ALL_UNLOCKED    0
-
-struct spinand_info {
-       struct spi_device *spi;
-       void *priv;
-};
-
-struct spinand_state {
-       u32     col;
-       u32     row;
-       int             buf_ptr;
-       u8              *buf;
-};
-
-struct spinand_cmd {
-       u8              cmd;
-       u32             n_addr;         /* Number of address */
-       u8              addr[3];        /* Reg Offset */
-       u32             n_dummy;        /* Dummy use */
-       u32             n_tx;           /* Number of tx bytes */
-       u8              *tx_buf;        /* Tx buf */
-       u32             n_rx;           /* Number of rx bytes */
-       u8              *rx_buf;        /* Rx buf */
-};
-
-int spinand_mtd(struct mtd_info *mtd);
-void spinand_mtd_release(struct mtd_info *mtd);
-
-#endif /* __LINUX_MTD_SPI_NAND_H */
index 902a7dd..bb6ae38 100644 (file)
@@ -101,7 +101,8 @@ static int jffs2_sync_fs(struct super_block *sb, int wait)
        struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
 
 #ifdef CONFIG_JFFS2_FS_WRITEBUFFER
-       cancel_delayed_work_sync(&c->wbuf_dwork);
+       if (jffs2_is_writebuffered(c))
+               cancel_delayed_work_sync(&c->wbuf_dwork);
 #endif
 
        mutex_lock(&c->alloc_sem);
index cd0be91..ba8fa90 100644 (file)
@@ -207,6 +207,7 @@ struct mtd_debug_info {
 struct mtd_info {
        u_char type;
        uint32_t flags;
+       uint32_t orig_flags; /* Flags as before running mtd checks */
        uint64_t size;   // Total size of the MTD
 
        /* "Major" erase size for the device. Naïve users may take this
@@ -386,7 +387,7 @@ static inline struct device_node *mtd_get_of_node(struct mtd_info *mtd)
        return dev_of_node(&mtd->dev);
 }
 
-static inline int mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops)
+static inline u32 mtd_oobavail(struct mtd_info *mtd, struct mtd_oob_ops *ops)
 {
        return ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize;
 }
index e10b126..33e240a 100644 (file)
@@ -203,9 +203,12 @@ enum nand_ecc_algo {
  */
 #define NAND_IS_BOOT_MEDIUM    0x00400000
 
-/* Options set by nand scan */
-/* Nand scan has allocated controller struct */
-#define NAND_CONTROLLER_ALLOC  0x80000000
+/*
+ * Do not try to tweak the timings at runtime. This is needed when the
+ * controller initializes the timings on itself or when it relies on
+ * configuration done by the bootloader.
+ */
+#define NAND_KEEP_TIMINGS      0x00800000
 
 /* Cell info constants */
 #define NAND_CI_CHIPNR_MSK     0x03
@@ -244,49 +247,6 @@ struct nand_id {
        int len;
 };
 
-/**
- * struct nand_controller_ops - Controller operations
- *
- * @attach_chip: this method is called after the NAND detection phase after
- *              flash ID and MTD fields such as erase size, page size and OOB
- *              size have been set up. ECC requirements are available if
- *              provided by the NAND chip or device tree. Typically used to
- *              choose the appropriate ECC configuration and allocate
- *              associated resources.
- *              This hook is optional.
- * @detach_chip: free all resources allocated/claimed in
- *              nand_controller_ops->attach_chip().
- *              This hook is optional.
- */
-struct nand_controller_ops {
-       int (*attach_chip)(struct nand_chip *chip);
-       void (*detach_chip)(struct nand_chip *chip);
-};
-
-/**
- * struct nand_controller - Structure used to describe a NAND controller
- *
- * @lock:               protection lock
- * @active:            the mtd device which holds the controller currently
- * @wq:                        wait queue to sleep on if a NAND operation is in
- *                     progress used instead of the per chip wait queue
- *                     when a hw controller is available.
- * @ops:               NAND controller operations.
- */
-struct nand_controller {
-       spinlock_t lock;
-       struct nand_chip *active;
-       wait_queue_head_t wq;
-       const struct nand_controller_ops *ops;
-};
-
-static inline void nand_controller_init(struct nand_controller *nfc)
-{
-       nfc->active = NULL;
-       spin_lock_init(&nfc->lock);
-       init_waitqueue_head(&nfc->wq);
-}
-
 /**
  * struct nand_ecc_step_info - ECC step information of ECC engine
  * @stepsize: data bytes per ECC step
@@ -879,18 +839,21 @@ struct nand_op_parser {
 
 /**
  * struct nand_operation - NAND operation descriptor
+ * @cs: the CS line to select for this NAND operation
  * @instrs: array of instructions to execute
  * @ninstrs: length of the @instrs array
  *
  * The actual operation structure that will be passed to chip->exec_op().
  */
 struct nand_operation {
+       unsigned int cs;
        const struct nand_op_instr *instrs;
        unsigned int ninstrs;
 };
 
-#define NAND_OPERATION(_instrs)                                        \
+#define NAND_OPERATION(_cs, _instrs)                           \
        {                                                       \
+               .cs = _cs,                                      \
                .instrs = _instrs,                              \
                .ninstrs = ARRAY_SIZE(_instrs),                 \
        }
@@ -898,11 +861,68 @@ struct nand_operation {
 int nand_op_parser_exec_op(struct nand_chip *chip,
                           const struct nand_op_parser *parser,
                           const struct nand_operation *op, bool check_only);
+/**
+ * struct nand_controller_ops - Controller operations
+ *
+ * @attach_chip: this method is called after the NAND detection phase after
+ *              flash ID and MTD fields such as erase size, page size and OOB
+ *              size have been set up. ECC requirements are available if
+ *              provided by the NAND chip or device tree. Typically used to
+ *              choose the appropriate ECC configuration and allocate
+ *              associated resources.
+ *              This hook is optional.
+ * @detach_chip: free all resources allocated/claimed in
+ *              nand_controller_ops->attach_chip().
+ *              This hook is optional.
+ * @exec_op:    controller specific method to execute NAND operations.
+ *              This method replaces chip->legacy.cmdfunc(),
+ *              chip->legacy.{read,write}_{buf,byte,word}(),
+ *              chip->legacy.dev_ready() and chip->legacy.waifunc().
+ * @setup_data_interface: setup the data interface and timing. If
+ *                       chipnr is set to %NAND_DATA_IFACE_CHECK_ONLY this
+ *                       means the configuration should not be applied but
+ *                       only checked.
+ *                       This hook is optional.
+ */
+struct nand_controller_ops {
+       int (*attach_chip)(struct nand_chip *chip);
+       void (*detach_chip)(struct nand_chip *chip);
+       int (*exec_op)(struct nand_chip *chip,
+                      const struct nand_operation *op,
+                      bool check_only);
+       int (*setup_data_interface)(struct nand_chip *chip, int chipnr,
+                                   const struct nand_data_interface *conf);
+};
+
+/**
+ * struct nand_controller - Structure used to describe a NAND controller
+ *
+ * @lock:               protection lock
+ * @active:            the mtd device which holds the controller currently
+ * @wq:                        wait queue to sleep on if a NAND operation is in
+ *                     progress used instead of the per chip wait queue
+ *                     when a hw controller is available.
+ * @ops:               NAND controller operations.
+ */
+struct nand_controller {
+       spinlock_t lock;
+       struct nand_chip *active;
+       wait_queue_head_t wq;
+       const struct nand_controller_ops *ops;
+};
+
+static inline void nand_controller_init(struct nand_controller *nfc)
+{
+       nfc->active = NULL;
+       spin_lock_init(&nfc->lock);
+       init_waitqueue_head(&nfc->wq);
+}
 
 /**
  * struct nand_legacy - NAND chip legacy fields/hooks
  * @IO_ADDR_R: address to read the 8 I/O lines of the flash device
  * @IO_ADDR_W: address to write the 8 I/O lines of the flash device
+ * @select_chip: select/deselect a specific target/die
  * @read_byte: read one byte from the chip
  * @write_byte: write a single byte to the chip on the low 8 I/O lines
  * @write_buf: write data from the buffer to the chip
@@ -921,6 +941,8 @@ int nand_op_parser_exec_op(struct nand_chip *chip,
  * @get_features: get the NAND chip features
  * @chip_delay: chip dependent delay for transferring data from array to read
  *             regs (tR).
+ * @dummy_controller: dummy controller implementation for drivers that can
+ *                   only control a single chip
  *
  * If you look at this structure you're already wrong. These fields/hooks are
  * all deprecated.
@@ -928,6 +950,7 @@ int nand_op_parser_exec_op(struct nand_chip *chip,
 struct nand_legacy {
        void __iomem *IO_ADDR_R;
        void __iomem *IO_ADDR_W;
+       void (*select_chip)(struct nand_chip *chip, int cs);
        u8 (*read_byte)(struct nand_chip *chip);
        void (*write_byte)(struct nand_chip *chip, u8 byte);
        void (*write_buf)(struct nand_chip *chip, const u8 *buf, int len);
@@ -945,6 +968,7 @@ struct nand_legacy {
        int (*get_features)(struct nand_chip *chip, int feature_addr,
                            u8 *subfeature_para);
        int chip_delay;
+       struct nand_controller dummy_controller;
 };
 
 /**
@@ -955,17 +979,10 @@ struct nand_legacy {
  *                     you're modifying an existing driver that is using those
  *                     fields/hooks, you should consider reworking the driver
  *                     avoid using them.
- * @select_chip:       [REPLACEABLE] select chip nr
- * @exec_op:           controller specific method to execute NAND operations.
- *                     This method replaces ->cmdfunc(),
- *                     ->legacy.{read,write}_{buf,byte,word}(),
- *                     ->legacy.dev_ready() and ->waifunc().
  * @setup_read_retry:  [FLASHSPECIFIC] flash (vendor) specific function for
  *                     setting the read-retry mode. Mostly needed for MLC NAND.
  * @ecc:               [BOARDSPECIFIC] ECC control structure
  * @buf_align:         minimum buffer alignment required by a platform
- * @dummy_controller:  dummy controller implementation for drivers that can
- *                     only control a single chip
  * @state:             [INTERN] the current state of the NAND device
  * @oob_poi:           "poison value buffer," used for laying out OOB data
  *                     before writing
@@ -1012,11 +1029,11 @@ struct nand_legacy {
  *                     this nand device will encounter their life times.
  * @blocks_per_die:    [INTERN] The number of PEBs in a die
  * @data_interface:    [INTERN] NAND interface timing information
+ * @cur_cs:            currently selected target. -1 means no target selected,
+ *                     otherwise we should always have cur_cs >= 0 &&
+ *                     cur_cs < numchips. NAND Controller drivers should not
+ *                     modify this value, but they're allowed to read it.
  * @read_retries:      [INTERN] the number of read retry modes supported
- * @setup_data_interface: [OPTIONAL] setup the data interface and timing. If
- *                       chipnr is set to %NAND_DATA_IFACE_CHECK_ONLY this
- *                       means the configuration should not be applied but
- *                       only checked.
  * @bbt:               [INTERN] bad block table pointer
  * @bbt_td:            [REPLACEABLE] bad block table descriptor for flash
  *                     lookup.
@@ -1037,13 +1054,7 @@ struct nand_chip {
 
        struct nand_legacy legacy;
 
-       void (*select_chip)(struct nand_chip *chip, int cs);
-       int (*exec_op)(struct nand_chip *chip,
-                      const struct nand_operation *op,
-                      bool check_only);
        int (*setup_read_retry)(struct nand_chip *chip, int retry_mode);
-       int (*setup_data_interface)(struct nand_chip *chip, int chipnr,
-                                   const struct nand_data_interface *conf);
 
        unsigned int options;
        unsigned int bbt_options;
@@ -1073,6 +1084,8 @@ struct nand_chip {
 
        struct nand_data_interface data_interface;
 
+       int cur_cs;
+
        int read_retries;
 
        flstate_t state;
@@ -1082,7 +1095,6 @@ struct nand_chip {
 
        struct nand_ecc_ctrl ecc;
        unsigned long buf_align;
-       struct nand_controller dummy_controller;
 
        uint8_t *bbt;
        struct nand_bbt_descr *bbt_td;
@@ -1098,15 +1110,6 @@ struct nand_chip {
        } manufacturer;
 };
 
-static inline int nand_exec_op(struct nand_chip *chip,
-                              const struct nand_operation *op)
-{
-       if (!chip->exec_op)
-               return -ENOTSUPP;
-
-       return chip->exec_op(chip, op, false);
-}
-
 extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops;
 extern const struct mtd_ooblayout_ops nand_ooblayout_lp_ops;
 
@@ -1345,5 +1348,12 @@ void nand_release(struct nand_chip *chip);
  * instruction and have no physical pin to check it.
  */
 int nand_soft_waitrdy(struct nand_chip *chip, unsigned long timeout_ms);
+struct gpio_desc;
+int nand_gpio_waitrdy(struct nand_chip *chip, struct gpio_desc *gpiod,
+                     unsigned long timeout_ms);
+
+/* Select/deselect a NAND target. */
+void nand_select_target(struct nand_chip *chip, unsigned int cs);
+void nand_deselect_target(struct nand_chip *chip);
 
 #endif /* __LINUX_MTD_RAWNAND_H */
index c759d40..78fc2d4 100644 (file)
@@ -1,20 +1,8 @@
-/*
+/* SPDX-License-Identifier: GPL-2.0
+ *
  * SuperH FLCTL nand controller
  *
  * Copyright © 2008 Renesas Solutions Corp.
- *
- * 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; version 2 of the License.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
 #ifndef __SH_FLCTL_H__
index 088ff96..b92e2aa 100644 (file)
@@ -194,8 +194,10 @@ struct spinand_manufacturer {
 };
 
 /* SPI NAND manufacturers */
+extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
 extern const struct spinand_manufacturer macronix_spinand_manufacturer;
 extern const struct spinand_manufacturer micron_spinand_manufacturer;
+extern const struct spinand_manufacturer toshiba_spinand_manufacturer;
 extern const struct spinand_manufacturer winbond_spinand_manufacturer;
 
 /**