Merge git://git.infradead.org/mtd-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 23 Sep 2009 17:07:49 +0000 (10:07 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 23 Sep 2009 17:07:49 +0000 (10:07 -0700)
* git://git.infradead.org/mtd-2.6: (58 commits)
  mtd: jedec_probe: add PSD4256G6V id
  mtd: OneNand support for Nomadik 8815 SoC (on NHK8815 board)
  mtd: nand: driver for Nomadik 8815 SoC (on NHK8815 board)
  m25p80: Add Spansion S25FL129P serial flashes
  jffs2: Use SLAB_HWCACHE_ALIGN for jffs2_raw_{dirent,inode} slabs
  mtd: sh_flctl: register sh_flctl using platform_driver_probe()
  mtd: nand: txx9ndfmc: transfer 512 byte at a time if possible
  mtd: nand: fix tmio_nand ecc correction
  mtd: nand: add __nand_correct_data helper function
  mtd: cfi_cmdset_0002: add 0xFF intolerance for M29W128G
  mtd: inftl: fix fold chain block number
  mtd: jedec: fix compilation problem with I28F640C3B definition
  mtd: nand: fix ECC Correction bug for SMC ordering for NDFC driver
  mtd: ofpart: Check availability of reg property instead of name property
  driver/Makefile: Initialize "mtd" and "spi" before "net"
  mtd: omap: adding DMA mode support in nand prefetch/post-write
  mtd: omap: add support for nand prefetch-read and post-write
  mtd: add nand support for w90p910 (v2)
  mtd: maps: add mtd-ram support to physmap_of
  mtd: pxa3xx_nand: add single-bit error corrections reporting
  ...

64 files changed:
Documentation/powerpc/dts-bindings/mtd-physmap.txt
arch/arm/configs/nhk8815_defconfig
arch/arm/mach-nomadik/board-nhk8815.c
arch/arm/mach-nomadik/include/mach/fsmc.h [new file with mode: 0644]
arch/arm/mach-nomadik/include/mach/nand.h [new file with mode: 0644]
arch/arm/mach-omap2/board-apollon.c
arch/arm/mach-omap2/gpmc.c
arch/arm/plat-omap/include/mach/gpmc.h
drivers/Makefile
drivers/mtd/Kconfig
drivers/mtd/afs.c
drivers/mtd/chips/cfi_cmdset_0002.c
drivers/mtd/chips/cfi_util.c [changed mode: 0644->0755]
drivers/mtd/chips/jedec_probe.c
drivers/mtd/devices/Kconfig
drivers/mtd/devices/Makefile
drivers/mtd/devices/lart.c
drivers/mtd/devices/m25p80.c
drivers/mtd/devices/mtd_dataflash.c
drivers/mtd/devices/phram.c
drivers/mtd/devices/slram.c
drivers/mtd/devices/sst25l.c [new file with mode: 0644]
drivers/mtd/inftlcore.c [changed mode: 0644->0755]
drivers/mtd/maps/Kconfig
drivers/mtd/maps/Makefile
drivers/mtd/maps/gpio-addr-flash.c [new file with mode: 0644]
drivers/mtd/maps/physmap_of.c
drivers/mtd/maps/plat-ram.c
drivers/mtd/maps/pmcmsp-flash.c
drivers/mtd/maps/uclinux.c
drivers/mtd/mtdblock.c
drivers/mtd/mtdconcat.c
drivers/mtd/mtdcore.c
drivers/mtd/mtdpart.c
drivers/mtd/nand/Kconfig
drivers/mtd/nand/Makefile
drivers/mtd/nand/atmel_nand.c
drivers/mtd/nand/cafe_nand.c
drivers/mtd/nand/davinci_nand.c
drivers/mtd/nand/fsl_elbc_nand.c
drivers/mtd/nand/mxc_nand.c
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/nand_ecc.c
drivers/mtd/nand/ndfc.c
drivers/mtd/nand/nomadik_nand.c [new file with mode: 0644]
drivers/mtd/nand/omap2.c
drivers/mtd/nand/orion_nand.c
drivers/mtd/nand/pxa3xx_nand.c
drivers/mtd/nand/sh_flctl.c
drivers/mtd/nand/tmio_nand.c
drivers/mtd/nand/txx9ndfmc.c
drivers/mtd/nand/w90p910_nand.c [new file with mode: 0644]
drivers/mtd/ofpart.c
drivers/mtd/onenand/Kconfig
drivers/mtd/onenand/generic.c
drivers/mtd/onenand/onenand_base.c
drivers/mtd/tests/mtd_oobtest.c
drivers/mtd/tests/mtd_pagetest.c
fs/jffs2/background.c
fs/jffs2/malloc.c
include/linux/mtd/nand.h
include/linux/mtd/nand_ecc.h
include/linux/mtd/onenand.h
include/linux/mtd/onenand_regs.h

index 667c9bde8699a3eb8433a223579529d443378aaa..80152cb567d91941f9b91e79cb456dc6a16a9833 100644 (file)
@@ -1,18 +1,19 @@
-CFI or JEDEC memory-mapped NOR flash
+CFI or JEDEC memory-mapped NOR flash, MTD-RAM (NVRAM...)
 
 Flash chips (Memory Technology Devices) are often used for solid state
 file systems on embedded devices.
 
- - compatible : should contain the specific model of flash chip(s)
-   used, if known, followed by either "cfi-flash" or "jedec-flash"
- - reg : Address range(s) of the flash chip(s)
+ - compatible : should contain the specific model of mtd chip(s)
+   used, if known, followed by either "cfi-flash", "jedec-flash"
+   or "mtd-ram".
+ - reg : Address range(s) of the mtd chip(s)
    It's possible to (optionally) define multiple "reg" tuples so that
-   non-identical NOR chips can be described in one flash node.
- - bank-width : Width (in bytes) of the flash bank.  Equal to the
+   non-identical chips can be described in one node.
+ - bank-width : Width (in bytes) of the bank.  Equal to the
    device width times the number of interleaved chips.
- - device-width : (optional) Width of a single flash chip.  If
+ - device-width : (optional) Width of a single mtd chip.  If
    omitted, assumed to be equal to 'bank-width'.
- - #address-cells, #size-cells : Must be present if the flash has
+ - #address-cells, #size-cells : Must be present if the device has
    sub-nodes representing partitions (see below).  In this case
    both #address-cells and #size-cells must be equal to 1.
 
@@ -22,24 +23,24 @@ are defined:
  - vendor-id : Contains the flash chip's vendor id (1 byte).
  - device-id : Contains the flash chip's device id (1 byte).
 
-In addition to the information on the flash bank itself, the
+In addition to the information on the mtd bank itself, the
 device tree may optionally contain additional information
-describing partitions of the flash address space.  This can be
+describing partitions of the address space.  This can be
 used on platforms which have strong conventions about which
-portions of the flash are used for what purposes, but which don't
+portions of a flash are used for what purposes, but which don't
 use an on-flash partition table such as RedBoot.
 
-Each partition is represented as a sub-node of the flash device.
+Each partition is represented as a sub-node of the mtd device.
 Each node's name represents the name of the corresponding
-partition of the flash device.
+partition of the mtd device.
 
 Flash partitions
- - reg : The partition's offset and size within the flash bank.
- - label : (optional) The label / name for this flash partition.
+ - reg : The partition's offset and size within the mtd bank.
+ - label : (optional) The label / name for this partition.
    If omitted, the label is taken from the node name (excluding
    the unit address).
  - read-only : (optional) This parameter, if present, is a hint to
-   Linux that this flash partition should only be mounted
+   Linux that this partition should only be mounted
    read-only.  This is usually used for flash partitions
    containing early-boot firmware images or data which should not
    be clobbered.
@@ -78,3 +79,12 @@ Here an example with multiple "reg" tuples:
                        reg = <0 0x04000000>;
                };
        };
+
+An example using SRAM:
+
+       sram@2,0 {
+               compatible = "samsung,k6f1616u6a", "mtd-ram";
+               reg = <2 0 0x00200000>;
+               bank-width = <2>;
+       };
+
index 9bb45b932f048959a1cf5452ed4c38e0d8fe4f6e..600cb270f2bfee936c42448826c37b4678e02156 100644 (file)
@@ -498,7 +498,7 @@ CONFIG_MTD_CFI_I2=y
 # CONFIG_MTD_DOC2001PLUS is not set
 CONFIG_MTD_NAND=y
 CONFIG_MTD_NAND_VERIFY_WRITE=y
-# CONFIG_MTD_NAND_ECC_SMC is not set
+CONFIG_MTD_NAND_ECC_SMC=y
 # CONFIG_MTD_NAND_MUSEUM_IDS is not set
 # CONFIG_MTD_NAND_GPIO is not set
 CONFIG_MTD_NAND_IDS=y
index 79bdea943eb4bcc1299f2fd68a887c1d01f111fd..6bfd537d5afbeba3f4518c5381f3711cac5d9311 100644 (file)
 #include <linux/amba/bus.h>
 #include <linux/interrupt.h>
 #include <linux/gpio.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/io.h>
+#include <asm/sizes.h>
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/irq.h>
+#include <asm/mach/flash.h>
 #include <mach/setup.h>
+#include <mach/nand.h>
+#include <mach/fsmc.h>
 #include "clock.h"
 
+/* These adresses span 16MB, so use three individual pages */
+static struct resource nhk8815_nand_resources[] = {
+       {
+               .name = "nand_addr",
+               .start = NAND_IO_ADDR,
+               .end = NAND_IO_ADDR + 0xfff,
+               .flags = IORESOURCE_MEM,
+       }, {
+               .name = "nand_cmd",
+               .start = NAND_IO_CMD,
+               .end = NAND_IO_CMD + 0xfff,
+               .flags = IORESOURCE_MEM,
+       }, {
+               .name = "nand_data",
+               .start = NAND_IO_DATA,
+               .end = NAND_IO_DATA + 0xfff,
+               .flags = IORESOURCE_MEM,
+       }
+};
+
+static int nhk8815_nand_init(void)
+{
+       /* FSMC setup for nand chip select (8-bit nand in 8815NHK) */
+       writel(0x0000000E, FSMC_PCR(0));
+       writel(0x000D0A00, FSMC_PMEM(0));
+       writel(0x00100A00, FSMC_PATT(0));
+
+       /* enable access to the chip select area */
+       writel(readl(FSMC_PCR(0)) | 0x04, FSMC_PCR(0));
+
+       return 0;
+}
+
+/*
+ * These partitions are the same as those used in the 2.6.20 release
+ * shipped by the vendor; the first two partitions are mandated
+ * by the boot ROM, and the bootloader area is somehow oversized...
+ */
+static struct mtd_partition nhk8815_partitions[] = {
+       {
+               .name   = "X-Loader(NAND)",
+               .offset = 0,
+               .size   = SZ_256K,
+       }, {
+               .name   = "MemInit(NAND)",
+               .offset = MTDPART_OFS_APPEND,
+               .size   = SZ_256K,
+       }, {
+               .name   = "BootLoader(NAND)",
+               .offset = MTDPART_OFS_APPEND,
+               .size   = SZ_2M,
+       }, {
+               .name   = "Kernel zImage(NAND)",
+               .offset = MTDPART_OFS_APPEND,
+               .size   = 3 * SZ_1M,
+       }, {
+               .name   = "Root Filesystem(NAND)",
+               .offset = MTDPART_OFS_APPEND,
+               .size   = 22 * SZ_1M,
+       }, {
+               .name   = "User Filesystem(NAND)",
+               .offset = MTDPART_OFS_APPEND,
+               .size   = MTDPART_SIZ_FULL,
+       }
+};
+
+static struct nomadik_nand_platform_data nhk8815_nand_data = {
+       .parts          = nhk8815_partitions,
+       .nparts         = ARRAY_SIZE(nhk8815_partitions),
+       .options        = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING \
+                       | NAND_NO_READRDY | NAND_NO_AUTOINCR,
+       .init           = nhk8815_nand_init,
+};
+
+static struct platform_device nhk8815_nand_device = {
+       .name           = "nomadik_nand",
+       .dev            = {
+               .platform_data = &nhk8815_nand_data,
+       },
+       .resource       = nhk8815_nand_resources,
+       .num_resources  = ARRAY_SIZE(nhk8815_nand_resources),
+};
+
+/* These are the partitions for the OneNand device, different from above */
+static struct mtd_partition nhk8815_onenand_partitions[] = {
+       {
+               .name   = "X-Loader(OneNAND)",
+               .offset = 0,
+               .size   = SZ_256K,
+       }, {
+               .name   = "MemInit(OneNAND)",
+               .offset = MTDPART_OFS_APPEND,
+               .size   = SZ_256K,
+       }, {
+               .name   = "BootLoader(OneNAND)",
+               .offset = MTDPART_OFS_APPEND,
+               .size   = SZ_2M-SZ_256K,
+       }, {
+               .name   = "SysImage(OneNAND)",
+               .offset = MTDPART_OFS_APPEND,
+               .size   = 4 * SZ_1M,
+       }, {
+               .name   = "Root Filesystem(OneNAND)",
+               .offset = MTDPART_OFS_APPEND,
+               .size   = 22 * SZ_1M,
+       }, {
+               .name   = "User Filesystem(OneNAND)",
+               .offset = MTDPART_OFS_APPEND,
+               .size   = MTDPART_SIZ_FULL,
+       }
+};
+
+static struct flash_platform_data nhk8815_onenand_data = {
+       .parts          = nhk8815_onenand_partitions,
+       .nr_parts       = ARRAY_SIZE(nhk8815_onenand_partitions),
+};
+
+static struct resource nhk8815_onenand_resource[] = {
+       {
+               .start          = 0x30000000,
+               .end            = 0x30000000 + SZ_128K - 1,
+               .flags          = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device nhk8815_onenand_device = {
+       .name           = "onenand",
+       .id             = -1,
+       .dev            = {
+               .platform_data  = &nhk8815_onenand_data,
+       },
+       .resource       = nhk8815_onenand_resource,
+       .num_resources  = ARRAY_SIZE(nhk8815_onenand_resource),
+};
+
+static void __init nhk8815_onenand_init(void)
+{
+#ifdef CONFIG_ONENAND
+       /* Set up SMCS0 for OneNand */
+       writel(0x000030db, FSMC_BCR0);
+       writel(0x02100551, FSMC_BTR0);
+#endif
+}
+
 #define __MEM_4K_RESOURCE(x) \
        .res = {.start = (x), .end = (x) + SZ_4K - 1, .flags = IORESOURCE_MEM}
 
@@ -81,6 +233,8 @@ static int __init nhk8815_eth_init(void)
 device_initcall(nhk8815_eth_init);
 
 static struct platform_device *nhk8815_platform_devices[] __initdata = {
+       &nhk8815_nand_device,
+       &nhk8815_onenand_device,
        &nhk8815_eth_device,
        /* will add more devices */
 };
@@ -90,6 +244,7 @@ static void __init nhk8815_platform_init(void)
        int i;
 
        cpu8815_platform_init();
+       nhk8815_onenand_init();
        platform_add_devices(nhk8815_platform_devices,
                             ARRAY_SIZE(nhk8815_platform_devices));
 
diff --git a/arch/arm/mach-nomadik/include/mach/fsmc.h b/arch/arm/mach-nomadik/include/mach/fsmc.h
new file mode 100644 (file)
index 0000000..8c2c051
--- /dev/null
@@ -0,0 +1,29 @@
+
+/* Definitions for the Nomadik FSMC "Flexible Static Memory controller" */
+
+#ifndef __ASM_ARCH_FSMC_H
+#define __ASM_ARCH_FSMC_H
+
+#include <mach/hardware.h>
+/*
+ * Register list
+ */
+
+/* bus control reg. and bus timing reg. for CS0..CS3 */
+#define FSMC_BCR(x)     (NOMADIK_FSMC_VA + (x << 3))
+#define FSMC_BTR(x)     (NOMADIK_FSMC_VA + (x << 3) + 0x04)
+
+/* PC-card and NAND:
+ * PCR = control register
+ * PMEM = memory timing
+ * PATT = attribute timing
+ * PIO = I/O timing
+ * PECCR = ECC result
+ */
+#define FSMC_PCR(x)     (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x00)
+#define FSMC_PMEM(x)    (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x08)
+#define FSMC_PATT(x)    (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x0c)
+#define FSMC_PIO(x)     (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x10)
+#define FSMC_PECCR(x)   (NOMADIK_FSMC_VA + ((2 + x) << 5) + 0x14)
+
+#endif /* __ASM_ARCH_FSMC_H */
diff --git a/arch/arm/mach-nomadik/include/mach/nand.h b/arch/arm/mach-nomadik/include/mach/nand.h
new file mode 100644 (file)
index 0000000..c3c8254
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef __ASM_ARCH_NAND_H
+#define __ASM_ARCH_NAND_H
+
+struct nomadik_nand_platform_data {
+       struct mtd_partition *parts;
+       int nparts;
+       int options;
+       int (*init) (void);
+       int (*exit) (void);
+};
+
+#define NAND_IO_DATA   0x40000000
+#define NAND_IO_CMD    0x40800000
+#define NAND_IO_ADDR   0x41000000
+
+#endif                         /* __ASM_ARCH_NAND_H */
index 7a2b54c7291acb6b910d3dfed15f54811b8c7df5..a1132288c7015bbcaf51b0764968f9a29a72b449 100644 (file)
@@ -87,7 +87,7 @@ static struct mtd_partition apollon_partitions[] = {
        },
 };
 
-static struct flash_platform_data apollon_flash_data = {
+static struct onenand_platform_data apollon_flash_data = {
        .parts          = apollon_partitions,
        .nr_parts       = ARRAY_SIZE(apollon_partitions),
 };
@@ -99,7 +99,7 @@ static struct resource apollon_flash_resource[] = {
 };
 
 static struct platform_device apollon_onenand_device = {
-       .name           = "onenand",
+       .name           = "onenand-flash",
        .id             = -1,
        .dev            = {
                .platform_data  = &apollon_flash_data,
index f91934b2b092dc7ca199f853de520637baa32d08..15876828db23b05abd24c4ab36eac7e6c9040a7d 100644 (file)
 #define GPMC_CHUNK_SHIFT       24              /* 16 MB */
 #define GPMC_SECTION_SHIFT     28              /* 128 MB */
 
+#define PREFETCH_FIFOTHRESHOLD (0x40 << 8)
+#define CS_NUM_SHIFT           24
+#define ENABLE_PREFETCH                (0x1 << 7)
+#define DMA_MPU_MODE           2
+
 static struct resource gpmc_mem_root;
 static struct resource gpmc_cs_mem[GPMC_CS_NUM];
 static DEFINE_SPINLOCK(gpmc_mem_lock);
@@ -386,6 +391,63 @@ void gpmc_cs_free(int cs)
 }
 EXPORT_SYMBOL(gpmc_cs_free);
 
+/**
+ * gpmc_prefetch_enable - configures and starts prefetch transfer
+ * @cs: nand cs (chip select) number
+ * @dma_mode: dma mode enable (1) or disable (0)
+ * @u32_count: number of bytes to be transferred
+ * @is_write: prefetch read(0) or write post(1) mode
+ */
+int gpmc_prefetch_enable(int cs, int dma_mode,
+                               unsigned int u32_count, int is_write)
+{
+       uint32_t prefetch_config1;
+
+       if (!(gpmc_read_reg(GPMC_PREFETCH_CONTROL))) {
+               /* Set the amount of bytes to be prefetched */
+               gpmc_write_reg(GPMC_PREFETCH_CONFIG2, u32_count);
+
+               /* Set dma/mpu mode, the prefetch read / post write and
+                * enable the engine. Set which cs is has requested for.
+                */
+               prefetch_config1 = ((cs << CS_NUM_SHIFT) |
+                                       PREFETCH_FIFOTHRESHOLD |
+                                       ENABLE_PREFETCH |
+                                       (dma_mode << DMA_MPU_MODE) |
+                                       (0x1 & is_write));
+               gpmc_write_reg(GPMC_PREFETCH_CONFIG1, prefetch_config1);
+       } else {
+               return -EBUSY;
+       }
+       /*  Start the prefetch engine */
+       gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x1);
+
+       return 0;
+}
+EXPORT_SYMBOL(gpmc_prefetch_enable);
+
+/**
+ * gpmc_prefetch_reset - disables and stops the prefetch engine
+ */
+void gpmc_prefetch_reset(void)
+{
+       /* Stop the PFPW engine */
+       gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x0);
+
+       /* Reset/disable the PFPW engine */
+       gpmc_write_reg(GPMC_PREFETCH_CONFIG1, 0x0);
+}
+EXPORT_SYMBOL(gpmc_prefetch_reset);
+
+/**
+ * gpmc_prefetch_status - reads prefetch status of engine
+ */
+int  gpmc_prefetch_status(void)
+{
+       return gpmc_read_reg(GPMC_PREFETCH_STATUS);
+}
+EXPORT_SYMBOL(gpmc_prefetch_status);
+
 static void __init gpmc_mem_init(void)
 {
        int cs;
@@ -452,6 +514,5 @@ void __init gpmc_init(void)
        l &= 0x03 << 3;
        l |= (0x02 << 3) | (1 << 0);
        gpmc_write_reg(GPMC_SYSCONFIG, l);
-
        gpmc_mem_init();
 }
index 921b16532ff55c4e28301cb5bcce975de546a70b..9c99cda77ba6383c7f3bcd74a9986117de23fcca 100644 (file)
@@ -103,6 +103,10 @@ extern int gpmc_cs_request(int cs, unsigned long size, unsigned long *base);
 extern void gpmc_cs_free(int cs);
 extern int gpmc_cs_set_reserved(int cs, int reserved);
 extern int gpmc_cs_reserved(int cs);
+extern int gpmc_prefetch_enable(int cs, int dma_mode,
+                                       unsigned int u32_count, int is_write);
+extern void gpmc_prefetch_reset(void);
+extern int gpmc_prefetch_status(void);
 extern void __init gpmc_init(void);
 
 #endif
index ccfa259fa848743d1cd8c8e3d107696044944a63..6ee53c7a57a1c5162265156bced43d6364c73237 100644 (file)
@@ -43,6 +43,8 @@ obj-y                         += macintosh/
 obj-$(CONFIG_IDE)              += ide/
 obj-$(CONFIG_SCSI)             += scsi/
 obj-$(CONFIG_ATA)              += ata/
+obj-$(CONFIG_MTD)              += mtd/
+obj-$(CONFIG_SPI)              += spi/
 obj-y                          += net/
 obj-$(CONFIG_ATM)              += atm/
 obj-$(CONFIG_FUSION)           += message/
@@ -51,8 +53,6 @@ obj-y                         += ieee1394/
 obj-$(CONFIG_UIO)              += uio/
 obj-y                          += cdrom/
 obj-y                          += auxdisplay/
-obj-$(CONFIG_MTD)              += mtd/
-obj-$(CONFIG_SPI)              += spi/
 obj-$(CONFIG_PCCARD)           += pcmcia/
 obj-$(CONFIG_DIO)              += dio/
 obj-$(CONFIG_SBUS)             += sbus/
index b8e35a0b4d72df589446f1ad026f35644d285b22..e4ec3659759a91acc38cedefb0160c3c0640c5e1 100644 (file)
@@ -25,6 +25,14 @@ config MTD_DEBUG_VERBOSE
        help
          Determines the verbosity level of the MTD debugging messages.
 
+config MTD_TESTS
+       tristate "MTD tests support"
+       depends on m
+       help
+         This option includes various MTD tests into compilation. The tests
+         should normally be compiled as kernel modules. The modules perform
+         various checks and verifications when loaded.
+
 config MTD_CONCAT
        tristate "MTD concatenating support"
        help
@@ -45,14 +53,6 @@ config MTD_PARTITIONS
          devices. Partitioning on NFTL 'devices' is a different - that's the
          'normal' form of partitioning used on a block device.
 
-config MTD_TESTS
-       tristate "MTD tests support"
-       depends on m
-       help
-         This option includes various MTD tests into compilation. The tests
-         should normally be compiled as kernel modules. The modules perform
-         various checks and verifications when loaded.
-
 config MTD_REDBOOT_PARTS
        tristate "RedBoot partition table parsing"
        depends on MTD_PARTITIONS
index d072ca5be689a5ee8f1a426e3d1103762fb75283..cec7ab98b2a9d9bed2496a335881469b12b0b0b0 100644 (file)
@@ -239,7 +239,7 @@ static int parse_afs_partitions(struct mtd_info *mtd,
                parts[idx].offset       = img_ptr;
                parts[idx].mask_flags   = 0;
 
-               printk("  mtd%d: at 0x%08x, %5dKB, %8u, %s\n",
+               printk("  mtd%d: at 0x%08x, %5lluKiB, %8u, %s\n",
                        idx, img_ptr, parts[idx].size / 1024,
                        iis.imageNumber, str);
 
index 61ea833e09086e631024f298575678ff3f123ea3..94bb61e19047764ae06dcf91408946219869fa06 100644 (file)
@@ -282,16 +282,6 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param)
        }
 }
 
-static void fixup_M29W128G_write_buffer(struct mtd_info *mtd, void *param)
-{
-       struct map_info *map = mtd->priv;
-       struct cfi_private *cfi = map->fldrv_priv;
-       if (cfi->cfiq->BufWriteTimeoutTyp) {
-               pr_warning("Don't use write buffer on ST flash M29W128G\n");
-               cfi->cfiq->BufWriteTimeoutTyp = 0;
-       }
-}
-
 static struct cfi_fixup cfi_fixup_table[] = {
        { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL },
 #ifdef AMD_BOOTLOC_BUG
@@ -308,7 +298,6 @@ static struct cfi_fixup cfi_fixup_table[] = {
        { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, },
        { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, },
        { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, },
-       { CFI_MFR_ST,  0x227E, fixup_M29W128G_write_buffer, NULL, },
 #if !FORCE_WORD_WRITE
        { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, },
 #endif
old mode 100644 (file)
new mode 100755 (executable)
index 34d40e2..c5a84fd
@@ -81,6 +81,10 @@ void __xipram cfi_qry_mode_off(uint32_t base, struct map_info *map,
 {
        cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
        cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
+       /* M29W128G flashes require an additional reset command
+          when exit qry mode */
+       if ((cfi->mfr == CFI_MFR_ST) && (cfi->id == 0x227E || cfi->id == 0x7E))
+               cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
 }
 EXPORT_SYMBOL_GPL(cfi_qry_mode_off);
 
index ccc4cfc7e4b5efb415d6887216c137d863ae1cad..736a3be265f2b3da75975dc7af2614b68c58a8ee 100644 (file)
 #define I28F320B3B     0x8897
 #define I28F640B3T     0x8898
 #define I28F640B3B     0x8899
+#define I28F640C3B     0x88CD
+#define I28F160F3T     0x88F3
+#define I28F160F3B     0x88F4
+#define I28F160C3T     0x88C2
+#define I28F160C3B     0x88C3
 #define I82802AB       0x00ad
 #define I82802AC       0x00ac
 
 #define M50LPW080       0x002F
 #define M50FLW080A     0x0080
 #define M50FLW080B     0x0081
+#define PSD4256G6V     0x00e9
 
 /* SST */
 #define SST29EE020     0x0010
@@ -201,6 +207,7 @@ enum uaddr {
        MTD_UADDR_0x0555_0x02AA,
        MTD_UADDR_0x0555_0x0AAA,
        MTD_UADDR_0x5555_0x2AAA,
+       MTD_UADDR_0x0AAA_0x0554,
        MTD_UADDR_0x0AAA_0x0555,
        MTD_UADDR_0xAAAA_0x5555,
        MTD_UADDR_DONT_CARE,            /* Requires an arbitrary address */
@@ -245,6 +252,11 @@ static const struct unlock_addr  unlock_addrs[] = {
                .addr2 = 0x2aaa
        },
 
+       [MTD_UADDR_0x0AAA_0x0554] = {
+               .addr1 = 0x0AAA,
+               .addr2 = 0x0554
+       },
+
        [MTD_UADDR_0x0AAA_0x0555] = {
                .addr1 = 0x0AAA,
                .addr2 = 0x0555
@@ -1101,6 +1113,19 @@ static const struct amd_flash_info jedec_table[] = {
                        ERASEINFO(0x10000, 127),
                        ERASEINFO(0x02000, 8),
                }
+       }, {
+               .mfr_id         = MANUFACTURER_INTEL,
+               .dev_id         = I28F640C3B,
+               .name           = "Intel 28F640C3B",
+               .devtypes       = CFI_DEVICETYPE_X16,
+               .uaddr          = MTD_UADDR_UNNECESSARY,
+               .dev_size       = SIZE_8MiB,
+               .cmd_set        = P_ID_INTEL_STD,
+               .nr_regions     = 2,
+               .regions        = {
+                       ERASEINFO(0x02000, 8),
+                       ERASEINFO(0x10000, 127),
+               }
        }, {
                .mfr_id         = MANUFACTURER_INTEL,
                .dev_id         = I82802AB,
@@ -1156,8 +1181,8 @@ static const struct amd_flash_info jedec_table[] = {
                .mfr_id         = MANUFACTURER_NEC,
                .dev_id         = UPD29F064115,
                .name           = "NEC uPD29F064115",
-               .devtypes       = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8,
-               .uaddr          = MTD_UADDR_0x0555_0x02AA,      /* ???? */
+               .devtypes       = CFI_DEVICETYPE_X16,
+               .uaddr          = MTD_UADDR_0xAAAA_0x5555,
                .dev_size       = SIZE_8MiB,
                .cmd_set        = P_ID_AMD_STD,
                .nr_regions     = 3,
@@ -1725,6 +1750,18 @@ static const struct amd_flash_info jedec_table[] = {
                        ERASEINFO(0x10000,13),
                        ERASEINFO(0x1000,16),
                }
+       }, {
+               .mfr_id         = 0xff00 | MANUFACTURER_ST,
+               .dev_id         = 0xff00 | PSD4256G6V,
+               .name           = "ST PSD4256G6V",
+               .devtypes       = CFI_DEVICETYPE_X16,
+               .uaddr          = MTD_UADDR_0x0AAA_0x0554,
+               .dev_size       = SIZE_1MiB,
+               .cmd_set        = P_ID_AMD_STD,
+               .nr_regions     = 1,
+               .regions        = {
+                       ERASEINFO(0x10000,16),
+               }
        }, {
                .mfr_id         = MANUFACTURER_TOSHIBA,
                .dev_id         = TC58FVT160,
index 325fab92a62ccec543e013e701667b8013d85649..c222514bb70de1527f1f0c28c5b0bd03fc30fea6 100644 (file)
@@ -104,6 +104,16 @@ config M25PXX_USE_FAST_READ
        help
          This option enables FAST_READ access supported by ST M25Pxx.
 
+config MTD_SST25L
+       tristate "Support SST25L (non JEDEC) SPI Flash chips"
+       depends on SPI_MASTER
+       help
+         This enables access to the non JEDEC SST25L SPI flash chips, used
+         for program and data storage.
+
+         Set up your spi devices with the right board-specific platform data,
+         if you want to specify device partitioning.
+
 config MTD_SLRAM
        tristate "Uncached system RAM"
        help
index 0993d5cf3923f3a4895a6579aa18759bb76a3c72..ab5c9b92ac82ae39f24914a10830691e818b81f4 100644 (file)
@@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_LART)                += lart.o
 obj-$(CONFIG_MTD_BLOCK2MTD)    += block2mtd.o
 obj-$(CONFIG_MTD_DATAFLASH)    += mtd_dataflash.o
 obj-$(CONFIG_MTD_M25P80)       += m25p80.o
+obj-$(CONFIG_MTD_SST25L)       += sst25l.o
index 578de1c67bfe047a78b43c95845fdc6d5aae43e3..f4359fe7150f253ed641361a5a8809e845abef06 100644 (file)
@@ -393,7 +393,8 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr)
        * erase range is aligned with the erase size which is in
        * effect here.
        */
-   if (instr->addr & (mtd->eraseregions[i].erasesize - 1)) return (-EINVAL);
+   if (i < 0 || (instr->addr & (mtd->eraseregions[i].erasesize - 1)))
+      return -EINVAL;
 
    /* Remember the erase region we start on */
    first = i;
@@ -409,7 +410,8 @@ static int flash_erase (struct mtd_info *mtd,struct erase_info *instr)
    i--;
 
    /* is the end aligned on a block boundary? */
-   if ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1)) return (-EINVAL);
+   if (i < 0 || ((instr->addr + instr->len) & (mtd->eraseregions[i].erasesize - 1)))
+      return -EINVAL;
 
    addr = instr->addr;
    len = instr->len;
index eb495d83064fd86d3f4945956a587dc1decf69a3..379c316f329eb0d054718a12ed1c9111845d68db 100644 (file)
 #define        OPCODE_SE               0xd8    /* Sector erase (usually 64KiB) */
 #define        OPCODE_RDID             0x9f    /* Read JEDEC ID */
 
+/* Used for SST flashes only. */
+#define        OPCODE_BP               0x02    /* Byte program */
+#define        OPCODE_WRDI             0x04    /* Write disable */
+#define        OPCODE_AAI_WP           0xad    /* Auto address increment word program */
+
 /* Status Register bits. */
 #define        SR_WIP                  1       /* Write in progress */
 #define        SR_WEL                  2       /* Write enable latch */
@@ -132,6 +137,15 @@ static inline int write_enable(struct m25p *flash)
        return spi_write_then_read(flash->spi, &code, 1, NULL, 0);
 }
 
+/*
+ * Send write disble instruction to the chip.
+ */
+static inline int write_disable(struct m25p *flash)
+{
+       u8      code = OPCODE_WRDI;
+
+       return spi_write_then_read(flash->spi, &code, 1, NULL, 0);
+}
 
 /*
  * Service routine to read status register until ready, or timeout occurs.
@@ -454,6 +468,111 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
        return 0;
 }
 
+static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
+               size_t *retlen, const u_char *buf)
+{
+       struct m25p *flash = mtd_to_m25p(mtd);
+       struct spi_transfer t[2];
+       struct spi_message m;
+       size_t actual;
+       int cmd_sz, ret;
+
+       if (retlen)
+               *retlen = 0;
+
+       /* sanity checks */
+       if (!len)
+               return 0;
+
+       if (to + len > flash->mtd.size)
+               return -EINVAL;
+
+       spi_message_init(&m);
+       memset(t, 0, (sizeof t));
+
+       t[0].tx_buf = flash->command;
+       t[0].len = CMD_SIZE;
+       spi_message_add_tail(&t[0], &m);
+
+       t[1].tx_buf = buf;
+       spi_message_add_tail(&t[1], &m);
+
+       mutex_lock(&flash->lock);
+
+       /* Wait until finished previous write command. */
+       ret = wait_till_ready(flash);
+       if (ret)
+               goto time_out;
+
+       write_enable(flash);
+
+       actual = to % 2;
+       /* Start write from odd address. */
+       if (actual) {
+               flash->command[0] = OPCODE_BP;
+               flash->command[1] = to >> 16;
+               flash->command[2] = to >> 8;
+               flash->command[3] = to;
+
+               /* write one byte. */
+               t[1].len = 1;
+               spi_sync(flash->spi, &m);
+               ret = wait_till_ready(flash);
+               if (ret)
+                       goto time_out;
+               *retlen += m.actual_length - CMD_SIZE;
+       }
+       to += actual;
+
+       flash->command[0] = OPCODE_AAI_WP;
+       flash->command[1] = to >> 16;
+       flash->command[2] = to >> 8;
+       flash->command[3] = to;
+
+       /* Write out most of the data here. */
+       cmd_sz = CMD_SIZE;
+       for (; actual < len - 1; actual += 2) {
+               t[0].len = cmd_sz;
+               /* write two bytes. */
+               t[1].len = 2;
+               t[1].tx_buf = buf + actual;
+
+               spi_sync(flash->spi, &m);
+               ret = wait_till_ready(flash);
+               if (ret)
+                       goto time_out;
+               *retlen += m.actual_length - cmd_sz;
+               cmd_sz = 1;
+               to += 2;
+       }
+       write_disable(flash);
+       ret = wait_till_ready(flash);
+       if (ret)
+               goto time_out;
+
+       /* Write out trailing byte if it exists. */
+       if (actual != len) {
+               write_enable(flash);
+               flash->command[0] = OPCODE_BP;
+               flash->command[1] = to >> 16;
+               flash->command[2] = to >> 8;
+               flash->command[3] = to;
+               t[0].len = CMD_SIZE;
+               t[1].len = 1;
+               t[1].tx_buf = buf + actual;
+
+               spi_sync(flash->spi, &m);
+               ret = wait_till_ready(flash);
+               if (ret)
+                       goto time_out;
+               *retlen += m.actual_length - CMD_SIZE;
+               write_disable(flash);
+       }
+
+time_out:
+       mutex_unlock(&flash->lock);
+       return ret;
+}
 
 /****************************************************************************/
 
@@ -501,7 +620,10 @@ static struct flash_info __devinitdata m25p_data [] = {
        { "at26df321",  0x1f4701, 0, 64 * 1024, 64, SECT_4K, },
 
        /* Macronix */
+       { "mx25l3205d", 0xc22016, 0, 64 * 1024, 64, },
+       { "mx25l6405d", 0xc22017, 0, 64 * 1024, 128, },
        { "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, },
+       { "mx25l12855e", 0xc22618, 0, 64 * 1024, 256, },
 
        /* Spansion -- single (large) sector size only, at least
         * for the chips listed here (without boot sectors).
@@ -511,14 +633,20 @@ static struct flash_info __devinitdata m25p_data [] = {
        { "s25sl016a", 0x010214, 0, 64 * 1024, 32, },
        { "s25sl032a", 0x010215, 0, 64 * 1024, 64, },
        { "s25sl064a", 0x010216, 0, 64 * 1024, 128, },
-        { "s25sl12800", 0x012018, 0x0300, 256 * 1024, 64, },
+       { "s25sl12800", 0x012018, 0x0300, 256 * 1024, 64, },
        { "s25sl12801", 0x012018, 0x0301, 64 * 1024, 256, },
+       { "s25fl129p0", 0x012018, 0x4d00, 256 * 1024, 64, },
+       { "s25fl129p1", 0x012018, 0x4d01, 64 * 1024, 256, },
 
        /* SST -- large erase sizes are "overlays", "sectors" are 4K */
        { "sst25vf040b", 0xbf258d, 0, 64 * 1024, 8, SECT_4K, },
        { "sst25vf080b", 0xbf258e, 0, 64 * 1024, 16, SECT_4K, },
        { "sst25vf016b", 0xbf2541, 0, 64 * 1024, 32, SECT_4K, },
        { "sst25vf032b", 0xbf254a, 0, 64 * 1024, 64, SECT_4K, },
+       { "sst25wf512",  0xbf2501, 0, 64 * 1024, 1, SECT_4K, },
+       { "sst25wf010",  0xbf2502, 0, 64 * 1024, 2, SECT_4K, },
+       { "sst25wf020",  0xbf2503, 0, 64 * 1024, 4, SECT_4K, },
+       { "sst25wf040",  0xbf2504, 0, 64 * 1024, 8, SECT_4K, },
 
        /* ST Microelectronics -- newer production may have feature updates */
        { "m25p05",  0x202010,  0, 32 * 1024, 2, },
@@ -667,7 +795,12 @@ static int __devinit m25p_probe(struct spi_device *spi)
        flash->mtd.size = info->sector_size * info->n_sectors;
        flash->mtd.erase = m25p80_erase;
        flash->mtd.read = m25p80_read;
-       flash->mtd.write = m25p80_write;
+
+       /* sst flash chips use AAI word program */
+       if (info->jedec_id >> 16 == 0xbf)
+               flash->mtd.write = sst_write;
+       else
+               flash->mtd.write = m25p80_write;
 
        /* prefer "small sector" erase if possible */
        if (info->flags & SECT_4K) {
index 211c27acd01e585529b3bc10496757f6b3fb6d2c..93e3627be74c76a37117f13883e74d0bd1607f11 100644 (file)
@@ -401,7 +401,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
                (void) dataflash_waitready(priv->spi);
 
 
-#ifdef CONFIG_MTD_DATAFLASH_VERIFY_WRITE
+#ifdef CONFIG_MTD_DATAFLASH_WRITE_VERIFY
 
                /* (3) Compare to Buffer1 */
                addr = pageaddr << priv->page_offset;
@@ -430,7 +430,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
                } else
                        status = 0;
 
-#endif /* CONFIG_MTD_DATAFLASH_VERIFY_WRITE */
+#endif /* CONFIG_MTD_DATAFLASH_WRITE_VERIFY */
 
                remaining = remaining - writelen;
                pageaddr++;
index 088fbb7595b5adbb5fee1e240ecd605b4a53b51e..1696bbecaa7e0e86b31a2e7126b9bf77faf4d596 100644 (file)
@@ -14,6 +14,9 @@
  * Example:
  *     phram=swap,64Mi,128Mi phram=test,900Mi,1Mi
  */
+
+#define pr_fmt(fmt) "phram: " fmt
+
 #include <asm/io.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -23,8 +26,6 @@
 #include <linux/slab.h>
 #include <linux/mtd/mtd.h>
 
-#define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args)
-
 struct phram_mtd_list {
        struct mtd_info mtd;
        struct list_head list;
@@ -132,7 +133,7 @@ static int register_device(char *name, unsigned long start, unsigned long len)
        ret = -EIO;
        new->mtd.priv = ioremap(start, len);
        if (!new->mtd.priv) {
-               ERROR("ioremap failed\n");
+               pr_err("ioremap failed\n");
                goto out1;
        }
 
@@ -152,7 +153,7 @@ static int register_device(char *name, unsigned long start, unsigned long len)
 
        ret = -EAGAIN;
        if (add_mtd_device(&new->mtd)) {
-               ERROR("Failed to register new device\n");
+               pr_err("Failed to register new device\n");
                goto out2;
        }
 
@@ -227,8 +228,8 @@ static inline void kill_final_newline(char *str)
 
 
 #define parse_err(fmt, args...) do {   \
-       ERROR(fmt , ## args);   \
-       return 0;               \
+       pr_err(fmt , ## args);  \
+       return 1;               \
 } while (0)
 
 static int phram_setup(const char *val, struct kernel_param *kp)
@@ -256,12 +257,8 @@ static int phram_setup(const char *val, struct kernel_param *kp)
                parse_err("not enough arguments\n");
 
        ret = parse_name(&name, token[0]);
-       if (ret == -ENOMEM)
-               parse_err("out of memory\n");
-       if (ret == -ENOSPC)
-               parse_err("name too long\n");
        if (ret)
-               return 0;
+               return ret;
 
        ret = parse_num32(&start, token[1]);
        if (ret) {
@@ -275,9 +272,11 @@ static int phram_setup(const char *val, struct kernel_param *kp)
                parse_err("illegal device length\n");
        }
 
-       register_device(name, start, len);
+       ret = register_device(name, start, len);
+       if (!ret)
+               pr_info("%s device: %#x at %#x\n", name, len, start);
 
-       return 0;
+       return ret;
 }
 
 module_param_call(phram, phram_setup, NULL, NULL, 000);
index 7d846e9173da4a967e169c711fc64393d7d3c933..3aa05cd18ea195878141aabe1669def78bc60dc6 100644 (file)
@@ -341,7 +341,7 @@ static int __init init_slram(void)
 #else
        int count;
 
-       for (count = 0; (map[count]) && (count < SLRAM_MAX_DEVICES_PARAMS);
+       for (count = 0; count < SLRAM_MAX_DEVICES_PARAMS && map[count];
                        count++) {
        }
 
diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c
new file mode 100644 (file)
index 0000000..c2baf33
--- /dev/null
@@ -0,0 +1,512 @@
+/*
+ * sst25l.c
+ *
+ * Driver for SST25L SPI Flash chips
+ *
+ * Copyright Â© 2009 Bluewater Systems Ltd
+ * Author: Andre Renaud <andre@bluewatersys.com>
+ * Author: Ryan Mallon <ryan@bluewatersys.com>
+ *
+ * Based on m25p80.c
+ *
+ * This code is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/flash.h>
+
+/* Erases can take up to 3 seconds! */
+#define MAX_READY_WAIT_JIFFIES msecs_to_jiffies(3000)
+
+#define SST25L_CMD_WRSR                0x01    /* Write status register */
+#define SST25L_CMD_WRDI                0x04    /* Write disable */
+#define SST25L_CMD_RDSR                0x05    /* Read status register */
+#define SST25L_CMD_WREN                0x06    /* Write enable */
+#define SST25L_CMD_READ                0x03    /* High speed read */
+
+#define SST25L_CMD_EWSR                0x50    /* Enable write status register */
+#define SST25L_CMD_SECTOR_ERASE        0x20    /* Erase sector */
+#define SST25L_CMD_READ_ID     0x90    /* Read device ID */
+#define SST25L_CMD_AAI_PROGRAM 0xaf    /* Auto address increment */
+
+#define SST25L_STATUS_BUSY     (1 << 0)        /* Chip is busy */
+#define SST25L_STATUS_WREN     (1 << 1)        /* Write enabled */
+#define SST25L_STATUS_BP0      (1 << 2)        /* Block protection 0 */
+#define SST25L_STATUS_BP1      (1 << 3)        /* Block protection 1 */
+
+struct sst25l_flash {
+       struct spi_device       *spi;
+       struct mutex            lock;
+       struct mtd_info         mtd;
+
+       int                     partitioned;
+};
+
+struct flash_info {
+       const char              *name;
+       uint16_t                device_id;
+       unsigned                page_size;
+       unsigned                nr_pages;
+       unsigned                erase_size;
+};
+
+#define to_sst25l_flash(x) container_of(x, struct sst25l_flash, mtd)
+
+static struct flash_info __initdata sst25l_flash_info[] = {
+       {"sst25lf020a", 0xbf43, 256, 1024, 4096},
+       {"sst25lf040a", 0xbf44, 256, 2048, 4096},
+};
+
+static int sst25l_status(struct sst25l_flash *flash, int *status)
+{
+       unsigned char command, response;
+       int err;
+
+       command = SST25L_CMD_RDSR;
+       err = spi_write_then_read(flash->spi, &command, 1, &response, 1);
+       if (err < 0)
+               return err;
+
+       *status = response;
+       return 0;
+}
+
+static int sst25l_write_enable(struct sst25l_flash *flash, int enable)
+{
+       unsigned char command[2];
+       int status, err;
+
+       command[0] = enable ? SST25L_CMD_WREN : SST25L_CMD_WRDI;
+       err = spi_write(flash->spi, command, 1);
+       if (err)
+               return err;
+
+       command[0] = SST25L_CMD_EWSR;
+       err = spi_write(flash->spi, command, 1);
+       if (err)
+               return err;
+
+       command[0] = SST25L_CMD_WRSR;
+       command[1] = enable ? 0 : SST25L_STATUS_BP0 | SST25L_STATUS_BP1;
+       err = spi_write(flash->spi, command, 2);
+       if (err)
+               return err;
+
+       if (enable) {
+               err = sst25l_status(flash, &status);
+               if (err)
+                       return err;
+               if (!(status & SST25L_STATUS_WREN))
+                       return -EROFS;
+       }
+
+       return 0;
+}
+
+static int sst25l_wait_till_ready(struct sst25l_flash *flash)
+{
+       unsigned long deadline;
+       int status, err;
+
+       deadline = jiffies + MAX_READY_WAIT_JIFFIES;
+       do {
+               err = sst25l_status(flash, &status);
+               if (err)
+                       return err;
+               if (!(status & SST25L_STATUS_BUSY))
+                       return 0;
+
+               cond_resched();
+       } while (!time_after_eq(jiffies, deadline));
+
+       return -ETIMEDOUT;
+}
+
+static int sst25l_erase_sector(struct sst25l_flash *flash, uint32_t offset)
+{
+       unsigned char command[4];
+       int err;
+
+       err = sst25l_write_enable(flash, 1);
+       if (err)
+               return err;
+
+       command[0] = SST25L_CMD_SECTOR_ERASE;
+       command[1] = offset >> 16;
+       command[2] = offset >> 8;
+       command[3] = offset;
+       err = spi_write(flash->spi, command, 4);
+       if (err)
+               return err;
+
+       err = sst25l_wait_till_ready(flash);
+       if (err)
+               return err;
+
+       return sst25l_write_enable(flash, 0);
+}
+
+static int sst25l_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+       struct sst25l_flash *flash = to_sst25l_flash(mtd);
+       uint32_t addr, end;
+       int err;
+
+       /* Sanity checks */
+       if (instr->addr + instr->len > flash->mtd.size)
+               return -EINVAL;
+
+       if ((uint32_t)instr->len % mtd->erasesize)
+               return -EINVAL;
+
+       if ((uint32_t)instr->addr % mtd->erasesize)
+               return -EINVAL;
+
+       addr = instr->addr;
+       end = addr + instr->len;
+
+       mutex_lock(&flash->lock);
+
+       err = sst25l_wait_till_ready(flash);
+       if (err) {
+               mutex_unlock(&flash->lock);
+               return err;
+       }
+
+       while (addr < end) {
+               err = sst25l_erase_sector(flash, addr);
+               if (err) {
+                       mutex_unlock(&flash->lock);
+                       instr->state = MTD_ERASE_FAILED;
+                       dev_err(&flash->spi->dev, "Erase failed\n");
+                       return err;
+               }
+
+               addr += mtd->erasesize;
+       }
+
+       mutex_unlock(&flash->lock);
+
+       instr->state = MTD_ERASE_DONE;
+       mtd_erase_callback(instr);
+       return 0;
+}
+
+static int sst25l_read(struct mtd_info *mtd, loff_t from, size_t len,
+                      size_t *retlen, unsigned char *buf)
+{
+       struct sst25l_flash *flash = to_sst25l_flash(mtd);
+       struct spi_transfer transfer[2];
+       struct spi_message message;
+       unsigned char command[4];
+       int ret;
+
+       /* Sanity checking */
+       if (len == 0)
+               return 0;
+
+       if (from + len > flash->mtd.size)
+               return -EINVAL;
+
+       if (retlen)
+               *retlen = 0;
+
+       spi_message_init(&message);
+       memset(&transfer, 0, sizeof(transfer));
+
+       command[0] = SST25L_CMD_READ;
+       command[1] = from >> 16;
+       command[2] = from >> 8;
+       command[3] = from;
+
+       transfer[0].tx_buf = command;
+       transfer[0].len = sizeof(command);
+       spi_message_add_tail(&transfer[0], &message);
+
+       transfer[1].rx_buf = buf;
+       transfer[1].len = len;
+       spi_message_add_tail(&transfer[1], &message);
+
+       mutex_lock(&flash->lock);
+
+       /* Wait for previous write/erase to complete */
+       ret = sst25l_wait_till_ready(flash);
+       if (ret) {
+               mutex_unlock(&flash->lock);
+               return ret;
+       }
+
+       spi_sync(flash->spi, &message);
+
+       if (retlen && message.actual_length > sizeof(command))
+               *retlen += message.actual_length - sizeof(command);
+
+       mutex_unlock(&flash->lock);
+       return 0;
+}
+
+static int sst25l_write(struct mtd_info *mtd, loff_t to, size_t len,
+                       size_t *retlen, const unsigned char *buf)
+{
+       struct sst25l_flash *flash = to_sst25l_flash(mtd);
+       int i, j, ret, bytes, copied = 0;
+       unsigned char command[5];
+
+       /* Sanity checks */
+       if (!len)
+               return 0;
+
+       if (to + len > flash->mtd.size)
+               return -EINVAL;
+
+       if ((uint32_t)to % mtd->writesize)
+               return -EINVAL;
+
+       mutex_lock(&flash->lock);
+
+       ret = sst25l_write_enable(flash, 1);
+       if (ret)
+               goto out;
+
+       for (i = 0; i < len; i += mtd->writesize) {
+               ret = sst25l_wait_till_ready(flash);
+               if (ret)
+                       goto out;
+
+               /* Write the first byte of the page */
+               command[0] = SST25L_CMD_AAI_PROGRAM;
+               command[1] = (to + i) >> 16;
+               command[2] = (to + i) >> 8;
+               command[3] = (to + i);
+               command[4] = buf[i];
+               ret = spi_write(flash->spi, command, 5);
+               if (ret < 0)
+                       goto out;
+               copied++;
+
+               /*
+                * Write the remaining bytes using auto address
+                * increment mode
+                */
+               bytes = min_t(uint32_t, mtd->writesize, len - i);
+               for (j = 1; j < bytes; j++, copied++) {
+                       ret = sst25l_wait_till_ready(flash);
+                       if (ret)
+                               goto out;
+
+                       command[1] = buf[i + j];
+                       ret = spi_write(flash->spi, command, 2);
+                       if (ret)
+                               goto out;
+               }
+       }
+
+out:
+       ret = sst25l_write_enable(flash, 0);
+
+       if (retlen)
+               *retlen = copied;
+
+       mutex_unlock(&flash->lock);
+       return ret;
+}
+
+static struct flash_info *__init sst25l_match_device(struct spi_device *spi)
+{
+       struct flash_info *flash_info = NULL;
+       unsigned char command[4], response;
+       int i, err;
+       uint16_t id;
+
+       command[0] = SST25L_CMD_READ_ID;
+       command[1] = 0;
+       command[2] = 0;
+       command[3] = 0;
+       err = spi_write_then_read(spi, command, sizeof(command), &response, 1);
+       if (err < 0) {
+               dev_err(&spi->dev, "error reading device id msb\n");
+               return NULL;
+       }
+
+       id = response << 8;
+
+       command[0] = SST25L_CMD_READ_ID;
+       command[1] = 0;
+       command[2] = 0;
+       command[3] = 1;
+       err = spi_write_then_read(spi, command, sizeof(command), &response, 1);
+       if (err < 0) {
+               dev_err(&spi->dev, "error reading device id lsb\n");
+               return NULL;
+       }
+
+       id |= response;
+
+       for (i = 0; i < ARRAY_SIZE(sst25l_flash_info); i++)
+               if (sst25l_flash_info[i].device_id == id)
+                       flash_info = &sst25l_flash_info[i];
+
+       if (!flash_info)
+               dev_err(&spi->dev, "unknown id %.4x\n", id);
+
+       return flash_info;
+}
+
+static int __init sst25l_probe(struct spi_device *spi)
+{
+       struct flash_info *flash_info;
+       struct sst25l_flash *flash;
+       struct flash_platform_data *data;
+       int ret, i;
+
+       flash_info = sst25l_match_device(spi);
+       if (!flash_info)
+               return -ENODEV;
+
+       flash = kzalloc(sizeof(struct sst25l_flash), GFP_KERNEL);
+       if (!flash)
+               return -ENOMEM;
+
+       flash->spi = spi;
+       mutex_init(&flash->lock);
+       dev_set_drvdata(&spi->dev, flash);
+
+       data = spi->dev.platform_data;
+       if (data && data->name)
+               flash->mtd.name = data->name;
+       else
+               flash->mtd.name = dev_name(&spi->dev);
+
+       flash->mtd.type         = MTD_NORFLASH;
+       flash->mtd.flags        = MTD_CAP_NORFLASH;
+       flash->mtd.erasesize    = flash_info->erase_size;
+       flash->mtd.writesize    = flash_info->page_size;
+       flash->mtd.size         = flash_info->page_size * flash_info->nr_pages;
+       flash->mtd.erase        = sst25l_erase;
+       flash->mtd.read         = sst25l_read;
+       flash->mtd.write        = sst25l_write;
+
+       dev_info(&spi->dev, "%s (%lld KiB)\n", flash_info->name,
+                (long long)flash->mtd.size >> 10);
+
+       DEBUG(MTD_DEBUG_LEVEL2,
+             "mtd .name = %s, .size = 0x%llx (%lldMiB) "
+             ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
+             flash->mtd.name,
+             (long long)flash->mtd.size, (long long)(flash->mtd.size >> 20),
+             flash->mtd.erasesize, flash->mtd.erasesize / 1024,
+             flash->mtd.numeraseregions);
+
+       if (flash->mtd.numeraseregions)
+               for (i = 0; i < flash->mtd.numeraseregions; i++)
+                       DEBUG(MTD_DEBUG_LEVEL2,
+                             "mtd.eraseregions[%d] = { .offset = 0x%llx, "
+                             ".erasesize = 0x%.8x (%uKiB), "
+                             ".numblocks = %d }\n",
+                             i, (long long)flash->mtd.eraseregions[i].offset,
+                             flash->mtd.eraseregions[i].erasesize,
+                             flash->mtd.eraseregions[i].erasesize / 1024,
+                             flash->mtd.eraseregions[i].numblocks);
+
+       if (mtd_has_partitions()) {
+               struct mtd_partition *parts = NULL;
+               int nr_parts = 0;
+
+               if (mtd_has_cmdlinepart()) {
+                       static const char *part_probes[] =
+                               {"cmdlinepart", NULL};
+
+                       nr_parts = parse_mtd_partitions(&flash->mtd,
+                                                       part_probes,
+                                                       &parts, 0);
+               }
+
+               if (nr_parts <= 0 && data && data->parts) {
+                       parts = data->parts;
+                       nr_parts = data->nr_parts;
+               }
+
+               if (nr_parts > 0) {
+                       for (i = 0; i < nr_parts; i++) {
+                               DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = "
+                                     "{.name = %s, .offset = 0x%llx, "
+                                     ".size = 0x%llx (%lldKiB) }\n",
+                                     i, parts[i].name,
+                                     (long long)parts[i].offset,
+                                     (long long)parts[i].size,
+                                     (long long)(parts[i].size >> 10));
+                       }
+
+                       flash->partitioned = 1;
+                       return add_mtd_partitions(&flash->mtd,
+                                                 parts, nr_parts);
+               }
+
+       } else if (data->nr_parts) {
+               dev_warn(&spi->dev, "ignoring %d default partitions on %s\n",
+                        data->nr_parts, data->name);
+       }
+
+       ret = add_mtd_device(&flash->mtd);
+       if (ret == 1) {
+               kfree(flash);
+               dev_set_drvdata(&spi->dev, NULL);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static int __exit sst25l_remove(struct spi_device *spi)
+{
+       struct sst25l_flash *flash = dev_get_drvdata(&spi->dev);
+       int ret;
+
+       if (mtd_has_partitions() && flash->partitioned)
+               ret = del_mtd_partitions(&flash->mtd);
+       else
+               ret = del_mtd_device(&flash->mtd);
+       if (ret == 0)
+               kfree(flash);
+       return ret;
+}
+
+static struct spi_driver sst25l_driver = {
+       .driver = {
+               .name   = "sst25l",
+               .bus    = &spi_bus_type,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = sst25l_probe,
+       .remove         = __exit_p(sst25l_remove),
+};
+
+static int __init sst25l_init(void)
+{
+       return spi_register_driver(&sst25l_driver);
+}
+
+static void __exit sst25l_exit(void)
+{
+       spi_unregister_driver(&sst25l_driver);
+}
+
+module_init(sst25l_init);
+module_exit(sst25l_exit);
+
+MODULE_DESCRIPTION("MTD SPI driver for SST25L Flash chips");
+MODULE_AUTHOR("Andre Renaud <andre@bluewatersys.com>, "
+             "Ryan Mallon <ryan@bluewatersys.com>");
+MODULE_LICENSE("GPL");
old mode 100644 (file)
new mode 100755 (executable)
index d8cf29c..8aca552
@@ -550,7 +550,7 @@ hitused:
                         * waiting to be picked up. We're going to have to fold
                         * a chain to make room.
                         */
-                       thisEUN = INFTL_makefreeblock(inftl, BLOCK_NIL);
+                       thisEUN = INFTL_makefreeblock(inftl, block);
 
                        /*
                         * Hopefully we free something, lets try again.
index 7a58bd5522fd736a83584acaf9a9472b4a57e876..3a9a960644b69464c45236803db90b0e832cdf17 100644 (file)
@@ -484,9 +484,19 @@ config MTD_BFIN_ASYNC
 
          If compiled as a module, it will be called bfin-async-flash.
 
+config MTD_GPIO_ADDR
+       tristate "GPIO-assisted Flash Chip Support"
+       depends on MTD_COMPLEX_MAPPINGS
+       select MTD_PARTITIONS
+       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_PARTITIONS && MTD_RAM && !MMU
+       depends on MTD_PARTITIONS && MTD_RAM=y && !MMU
        help
          Map driver to support image based filesystems for uClinux.
 
index 5beb0662d724b7097d3bb7fd5c62169f715d6a3e..1d5cf8636723104f942a073ef72275c74b0ddaec 100644 (file)
@@ -58,5 +58,4 @@ obj-$(CONFIG_MTD_PLATRAM)     += plat-ram.o
 obj-$(CONFIG_MTD_OMAP_NOR)     += omap_nor.o
 obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
 obj-$(CONFIG_MTD_BFIN_ASYNC)   += bfin-async-flash.o
-obj-$(CONFIG_MTD_RBTX4939)     += rbtx4939-flash.o
-obj-$(CONFIG_MTD_VMU)          += vmu-flash.o
+obj-$(CONFIG_MTD_GPIO_ADDR)    += gpio-addr-flash.o
diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c
new file mode 100644 (file)
index 0000000..44ef9a4
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * 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/init.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/types.h>
+
+#include <asm/gpio.h>
+#include <asm/io.h>
+
+#define pr_devinit(fmt, args...) ({ static const __devinitconst char __fmt[] = fmt; printk(__fmt, ## args); })
+
+#define DRIVER_NAME "gpio-addr-flash"
+#define PFX DRIVER_NAME ": "
+
+/**
+ * struct async_state - keep GPIO flash state
+ *     @mtd:         MTD state for this mapping
+ *     @map:         MTD map state for this flash
+ *     @gpio_count:  number of GPIOs used to address
+ *     @gpio_addrs:  array of GPIOs to twiddle
+ *     @gpio_values: cached GPIO values
+ *     @win_size:    dedicated memory size (if no GPIOs)
+ */
+struct async_state {
+       struct mtd_info *mtd;
+       struct map_info map;
+       size_t gpio_count;
+       unsigned *gpio_addrs;
+       int *gpio_values;
+       unsigned long win_size;
+};
+#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).  We rely on the GPIO framework to treat non-zero value as high so
+ * that we don't have to normalize the bits.
+ */
+static void gf_set_gpios(struct async_state *state, unsigned long ofs)
+{
+       size_t i = 0;
+       int value;
+       ofs /= state->win_size;
+       do {
+               value = ofs & (1 << i);
+               if (state->gpio_values[i] != value) {
+                       gpio_set_value(state->gpio_addrs[i], value);
+                       state->gpio_values[i] = value;
+               }
+       } while (++i < state->gpio_count);
+}
+
+/**
+ * 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 % state->win_size));
+       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
+ *
+ * We rely on the MTD layer to chunk up copies such that a single request here
+ * will not cross a window size.  This allows us to only wiggle the GPIOs once
+ * before falling back to a normal memcpy.  Reading the higher layer code shows
+ * that this is indeed the case, but add a BUG_ON() to future proof.
+ */
+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);
+
+       gf_set_gpios(state, from);
+
+       /* BUG if operation crosses the win_size */
+       BUG_ON(!((from + len) % state->win_size <= (from + len)));
+
+       /* operation does not cross the win_size, so one shot it */
+       memcpy_fromio(to, map->virt + (from % state->win_size), 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 % state->win_size));
+}
+
+/**
+ * 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);
+
+       gf_set_gpios(state, to);
+
+       /* BUG if operation crosses the win_size */
+       BUG_ON(!((to + len) % state->win_size <= (to + len)));
+
+       /* operation does not cross the win_size, so one shot it */
+       memcpy_toio(map->virt + (to % state->win_size), from, len);
+}
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL };
+#endif
+
+/**
+ * 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 = { ... };
+ * unsigned flash_gpios[] = { GPIO_XX, GPIO_XX, ... };
+ * struct resource flash_resource[] = {
+ *     {
+ *             .name  = "cfi_probe",
+ *             .start = 0x20000000,
+ *             .end   = 0x201fffff,
+ *             .flags = IORESOURCE_MEM,
+ *     }, {
+ *             .start = (unsigned long)flash_gpios,
+ *             .end   = ARRAY_SIZE(flash_gpios),
+ *             .flags = IORESOURCE_IRQ,
+ *     }
+ * };
+ * 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 __devinit gpio_flash_probe(struct platform_device *pdev)
+{
+       int ret;
+       size_t i, arr_size;
+       struct physmap_flash_data *pdata;
+       struct resource *memory;
+       struct resource *gpios;
+       struct async_state *state;
+
+       pdata = pdev->dev.platform_data;
+       memory = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       gpios = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+
+       if (!memory || !gpios || !gpios->end)
+               return -EINVAL;
+
+       arr_size = sizeof(int) * gpios->end;
+       state = kzalloc(sizeof(*state) + arr_size, GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+
+       state->gpio_count     = gpios->end;
+       state->gpio_addrs     = (void *)gpios->start;
+       state->gpio_values    = (void *)(state + 1);
+       state->win_size       = memory->end - memory->start + 1;
+       memset(state->gpio_values, 0xff, arr_size);
+
+       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       = state->win_size * (1 << state->gpio_count);
+       state->map.virt       = (void __iomem *)memory->start;
+       state->map.phys       = NO_XIP;
+       state->map.map_priv_1 = (unsigned long)state;
+
+       platform_set_drvdata(pdev, state);
+
+       i = 0;
+       do {
+               if (gpio_request(state->gpio_addrs[i], DRIVER_NAME)) {
+                       pr_devinit(KERN_ERR PFX "failed to request gpio %d\n",
+                               state->gpio_addrs[i]);
+                       while (i--)
+                               gpio_free(state->gpio_addrs[i]);
+                       kfree(state);
+                       return -EBUSY;
+               }
+               gpio_direction_output(state->gpio_addrs[i], 0);
+       } while (++i < state->gpio_count);
+
+       pr_devinit(KERN_NOTICE PFX "probing %d-bit flash bus\n",
+               state->map.bankwidth * 8);
+       state->mtd = do_map_probe(memory->name, &state->map);
+       if (!state->mtd) {
+               for (i = 0; i < state->gpio_count; ++i)
+                       gpio_free(state->gpio_addrs[i]);
+               kfree(state);
+               return -ENXIO;
+       }
+
+#ifdef CONFIG_MTD_PARTITIONS
+       ret = parse_mtd_partitions(state->mtd, part_probe_types, &pdata->parts, 0);
+       if (ret > 0) {
+               pr_devinit(KERN_NOTICE PFX "Using commandline partition definition\n");
+               add_mtd_partitions(state->mtd, pdata->parts, ret);
+               kfree(pdata->parts);
+
+       } else if (pdata->nr_parts) {
+               pr_devinit(KERN_NOTICE PFX "Using board partition definition\n");
+               add_mtd_partitions(state->mtd, pdata->parts, pdata->nr_parts);
+
+       } else
+#endif
+       {
+               pr_devinit(KERN_NOTICE PFX "no partition info available, registering whole flash at once\n");
+               add_mtd_device(state->mtd);
+       }
+
+       return 0;
+}
+
+static int __devexit gpio_flash_remove(struct platform_device *pdev)
+{
+       struct async_state *state = platform_get_drvdata(pdev);
+       size_t i = 0;
+       do {
+               gpio_free(state->gpio_addrs[i]);
+       } while (++i < state->gpio_count);
+#ifdef CONFIG_MTD_PARTITIONS
+       del_mtd_partitions(state->mtd);
+#endif
+       map_destroy(state->mtd);
+       kfree(state);
+       return 0;
+}
+
+static struct platform_driver gpio_flash_driver = {
+       .probe          = gpio_flash_probe,
+       .remove         = __devexit_p(gpio_flash_remove),
+       .driver         = {
+               .name   = DRIVER_NAME,
+       },
+};
+
+static int __init gpio_flash_init(void)
+{
+       return platform_driver_register(&gpio_flash_driver);
+}
+module_init(gpio_flash_init);
+
+static void __exit gpio_flash_exit(void)
+{
+       platform_driver_unregister(&gpio_flash_driver);
+}
+module_exit(gpio_flash_exit);
+
+MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
+MODULE_DESCRIPTION("MTD map driver for flashes addressed physically and with gpios");
+MODULE_LICENSE("GPL");
index 39d357b2eb47fdb24a0b7889cee4adeb46ee3171..61e4eb48bb2d5a37dfad765e3399e3b22d6270f9 100644 (file)
@@ -190,6 +190,7 @@ static int __devinit of_flash_probe(struct of_device *dev,
        const u32 *p;
        int reg_tuple_size;
        struct mtd_info **mtd_list = NULL;
+       resource_size_t res_size;
 
        reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32);
 
@@ -204,7 +205,7 @@ static int __devinit of_flash_probe(struct of_device *dev,
                dev_err(&dev->dev, "Malformed reg property on %s\n",
                                dev->node->full_name);
                err = -EINVAL;
-               goto err_out;
+               goto err_flash_remove;
        }
        count /= reg_tuple_size;
 
@@ -212,14 +213,14 @@ static int __devinit of_flash_probe(struct of_device *dev,
        info = kzalloc(sizeof(struct of_flash) +
                       sizeof(struct of_flash_list) * count, GFP_KERNEL);
        if (!info)
-               goto err_out;
-
-       mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL);
-       if (!info)
-               goto err_out;
+               goto err_flash_remove;
 
        dev_set_drvdata(&dev->dev, info);
 
+       mtd_list = kzalloc(sizeof(struct mtd_info) * count, 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)) {
@@ -233,8 +234,8 @@ static int __devinit of_flash_probe(struct of_device *dev,
                        (unsigned long long)res.end);
 
                err = -EBUSY;
-               info->list[i].res = request_mem_region(res.start, res.end -
-                                                      res.start + 1,
+               res_size = resource_size(&res);
+               info->list[i].res = request_mem_region(res.start, res_size,
                                                       dev_name(&dev->dev));
                if (!info->list[i].res)
                        goto err_out;
@@ -249,7 +250,7 @@ static int __devinit of_flash_probe(struct of_device *dev,
 
                info->list[i].map.name = dev_name(&dev->dev);
                info->list[i].map.phys = res.start;
-               info->list[i].map.size = res.end - res.start + 1;
+               info->list[i].map.size = res_size;
                info->list[i].map.bankwidth = *width;
 
                err = -ENOMEM;
@@ -338,6 +339,7 @@ static int __devinit of_flash_probe(struct of_device *dev,
 
 err_out:
        kfree(mtd_list);
+err_flash_remove:
        of_flash_remove(dev);
 
        return err;
@@ -359,6 +361,10 @@ static struct of_device_id of_flash_match[] = {
                .compatible     = "jedec-flash",
                .data           = (void *)"jedec_probe",
        },
+       {
+               .compatible     = "mtd-ram",
+               .data           = (void *)"map_ram",
+       },
        {
                .type           = "rom",
                .compatible     = "direct-mapped"
index 49c9ece764770e6f8cfddb9d46fd26b94d22bb47..dafb91944e7027db4103ab8d5a51329ac6a69660 100644 (file)
@@ -175,7 +175,7 @@ static int platram_probe(struct platform_device *pdev)
        /* setup map parameters */
 
        info->map.phys = res->start;
-       info->map.size = (res->end - res->start) + 1;
+       info->map.size = resource_size(res);
        info->map.name = pdata->mapname != NULL ?
                        (char *)pdata->mapname : (char *)pdev->name;
        info->map.bankwidth = pdata->bankwidth;
index 4768bd5459d631e4b8a6ee76eb30c03843274b50..c8fd8da4bc8780407b7b5e07a633da68c1909572 100644 (file)
@@ -50,7 +50,7 @@ static int fcnt;
 
 static int __init init_msp_flash(void)
 {
-       int i, j;
+       int i, j, ret = -ENOMEM;
        int offset, coff;
        char *env;
        int pcnt;
@@ -75,14 +75,16 @@ static int __init init_msp_flash(void)
        printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt);
 
        msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL);
+       if (!msp_flash)
+               return -ENOMEM;
+
        msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL);
+       if (!msp_parts)
+               goto free_msp_flash;
+
        msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL);
-       if (!msp_flash || !msp_parts || !msp_maps) {
-               kfree(msp_maps);
-               kfree(msp_parts);
-               kfree(msp_flash);
-               return -ENOMEM;
-       }
+       if (!msp_maps)
+               goto free_msp_parts;
 
        /* loop over the flash devices, initializing each */
        for (i = 0; i < fcnt; i++) {
@@ -100,13 +102,18 @@ static int __init init_msp_flash(void)
 
                msp_parts[i] = kcalloc(pcnt, sizeof(struct mtd_partition),
                                       GFP_KERNEL);
+               if (!msp_parts[i])
+                       goto cleanup_loop;
 
                /* now initialize the devices proper */
                flash_name[5] = '0' + i;
                env = prom_getenv(flash_name);
 
-               if (sscanf(env, "%x:%x", &addr, &size) < 2)
-                       return -ENXIO;
+               if (sscanf(env, "%x:%x", &addr, &size) < 2) {
+                       ret = -ENXIO;
+                       kfree(msp_parts[i]);
+                       goto cleanup_loop;
+               }
                addr = CPHYSADDR(addr);
 
                printk(KERN_NOTICE
@@ -122,13 +129,23 @@ static int __init init_msp_flash(void)
                 */
                if (size > CONFIG_MSP_FLASH_MAP_LIMIT)
                        size = CONFIG_MSP_FLASH_MAP_LIMIT;
+
                msp_maps[i].virt = ioremap(addr, size);
+               if (msp_maps[i].virt == NULL) {
+                       ret = -ENXIO;
+                       kfree(msp_parts[i]);
+                       goto cleanup_loop;
+               }
+
                msp_maps[i].bankwidth = 1;
-               msp_maps[i].name = strncpy(kmalloc(7, GFP_KERNEL),
-                                       flash_name, 7);
+               msp_maps[i].name = kmalloc(7, GFP_KERNEL);
+               if (!msp_maps[i].name) {
+                       iounmap(msp_maps[i].virt);
+                       kfree(msp_parts[i]);
+                       goto cleanup_loop;
+               }
 
-               if (msp_maps[i].virt == NULL)
-                       return -ENXIO;
+               msp_maps[i].name = strncpy(msp_maps[i].name, flash_name, 7);
 
                for (j = 0; j < pcnt; j++) {
                        part_name[5] = '0' + i;
@@ -136,8 +153,14 @@ static int __init init_msp_flash(void)
 
                        env = prom_getenv(part_name);
 
-                       if (sscanf(env, "%x:%x:%n", &offset, &size, &coff) < 2)
-                               return -ENXIO;
+                       if (sscanf(env, "%x:%x:%n", &offset, &size,
+                                               &coff) < 2) {
+                               ret = -ENXIO;
+                               kfree(msp_maps[i].name);
+                               iounmap(msp_maps[i].virt);
+                               kfree(msp_parts[i]);
+                               goto cleanup_loop;
+                       }
 
                        msp_parts[i][j].size = size;
                        msp_parts[i][j].offset = offset;
@@ -152,18 +175,37 @@ static int __init init_msp_flash(void)
                        add_mtd_partitions(msp_flash[i], msp_parts[i], pcnt);
                } else {
                        printk(KERN_ERR "map probe failed for flash\n");
-                       return -ENXIO;
+                       ret = -ENXIO;
+                       kfree(msp_maps[i].name);
+                       iounmap(msp_maps[i].virt);
+                       kfree(msp_parts[i]);
+                       goto cleanup_loop;
                }
        }
 
        return 0;
+
+cleanup_loop:
+       while (i--) {
+               del_mtd_partitions(msp_flash[i]);
+               map_destroy(msp_flash[i]);
+               kfree(msp_maps[i].name);
+               iounmap(msp_maps[i].virt);
+               kfree(msp_parts[i]);
+       }
+       kfree(msp_maps);
+free_msp_parts:
+       kfree(msp_parts);
+free_msp_flash:
+       kfree(msp_flash);
+       return ret;
 }
 
 static void __exit cleanup_msp_flash(void)
 {
        int i;
 
-       for (i = 0; i < sizeof(msp_flash) / sizeof(struct mtd_info **); i++) {
+       for (i = 0; i < fcnt; i++) {
                del_mtd_partitions(msp_flash[i]);
                map_destroy(msp_flash[i]);
                iounmap((void *)msp_maps[i].virt);
index d4314fb8821219c78279599a8113efab68f76b4f..35009294b435f33a62c0dbb4496ffe5c8b189174 100644 (file)
@@ -89,7 +89,11 @@ static int __init uclinux_mtd_init(void)
        mtd->priv = mapp;
 
        uclinux_ram_mtdinfo = mtd;
+#ifdef CONFIG_MTD_PARTITIONS
        add_mtd_partitions(mtd, uclinux_romfs, NUM_PARTITIONS);
+#else
+       add_mtd_device(mtd);
+#endif
 
        return(0);
 }
@@ -99,7 +103,11 @@ static int __init uclinux_mtd_init(void)
 static void __exit uclinux_mtd_cleanup(void)
 {
        if (uclinux_ram_mtdinfo) {
+#ifdef CONFIG_MTD_PARTITIONS
                del_mtd_partitions(uclinux_ram_mtdinfo);
+#else
+               del_mtd_device(uclinux_ram_mtdinfo);
+#endif
                map_destroy(uclinux_ram_mtdinfo);
                uclinux_ram_mtdinfo = NULL;
        }
index 2d70295a5fa350ae7e541948bfc9623b634b18fa..9f41b1a853c1f051e447f84fea5ec472ab986754 100644 (file)
@@ -84,7 +84,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos,
        remove_wait_queue(&wait_q, &wait);
 
        /*
-        * Next, writhe data to flash.
+        * Next, write the data to flash.
         */
 
        ret = mtd->write(mtd, pos, len, &retlen, buf);
index 792b547786b8fdf6c4122f28240b1458c0d80f66..db6de74082ad647d0540b0904e35dacec9c707c9 100644 (file)
@@ -427,7 +427,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
                 * to-be-erased area begins. Verify that the starting
                 * offset is aligned to this region's erase size:
                 */
-               if (instr->addr & (erase_regions[i].erasesize - 1))
+               if (i < 0 || instr->addr & (erase_regions[i].erasesize - 1))
                        return -EINVAL;
 
                /*
@@ -440,8 +440,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
                /*
                 * check if the ending offset is aligned to this region's erase size
                 */
-               if ((instr->addr + instr->len) & (erase_regions[i].erasesize -
-                                                 1))
+               if (i < 0 || ((instr->addr + instr->len) &
+                                       (erase_regions[i].erasesize - 1)))
                        return -EINVAL;
        }
 
index 69007a6eff50c3014c10677af1aea8f84a9fde81..467a4f177bfb269d684029954fb1731a011a12ce 100644 (file)
@@ -213,11 +213,11 @@ static struct attribute *mtd_attrs[] = {
        NULL,
 };
 
-struct attribute_group mtd_group = {
+static struct attribute_group mtd_group = {
        .attrs          = mtd_attrs,
 };
 
-const struct attribute_group *mtd_groups[] = {
+static const struct attribute_group *mtd_groups[] = {
        &mtd_group,
        NULL,
 };
index 742504ea96f5de11cfb377a8c82126675958fe62..b8043a9ba32d43329080b3664a8ae8678f7c7693 100644 (file)
@@ -453,7 +453,8 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
                for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
                        ;
                /* The loop searched for the region _behind_ the first one */
-               i--;
+               if (i > 0)
+                       i--;
 
                /* Pick biggest erasesize */
                for (; i < max && regions[i].offset < end; i++) {
index ce96c091f01be39b23b7b255b84779bfe58e8f32..2fda0b6152463087afd896f66db2703c9bec5920 100644 (file)
@@ -80,6 +80,23 @@ config MTD_NAND_OMAP2
        help
           Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms.
 
+config MTD_NAND_OMAP_PREFETCH
+       bool "GPMC prefetch support for NAND Flash device"
+       depends on MTD_NAND && MTD_NAND_OMAP2
+       default y
+       help
+        The NAND device can be accessed for Read/Write using GPMC PREFETCH engine
+        to improve the performance.
+
+config MTD_NAND_OMAP_PREFETCH_DMA
+       depends on MTD_NAND_OMAP_PREFETCH
+       bool "DMA mode"
+       default n
+       help
+        The GPMC PREFETCH engine can be configured eigther in MPU interrupt mode
+        or in DMA interrupt mode.
+        Say y for DMA mode or MPU mode will be used
+
 config MTD_NAND_TS7250
        tristate "NAND Flash device on TS-7250 board"
        depends on MACH_TS72XX
@@ -426,6 +443,12 @@ config MTD_NAND_MXC
          This enables the driver for the NAND flash controller on the
          MXC processors.
 
+config MTD_NAND_NOMADIK
+       tristate "ST Nomadik 8815 NAND support"
+       depends on ARCH_NOMADIK
+       help
+         Driver for the NAND flash controller on the Nomadik, with ECC.
+
 config MTD_NAND_SH_FLCTL
        tristate "Support for NAND on Renesas SuperH FLCTL"
        depends on MTD_NAND && SUPERH && CPU_SUBTYPE_SH7723
@@ -452,4 +475,11 @@ config MTD_NAND_SOCRATES
        help
          Enables support for NAND Flash chips wired onto Socrates board.
 
+config MTD_NAND_W90P910
+       tristate "Support for NAND on w90p910 evaluation board."
+       depends on ARCH_W90X900 && MTD_PARTITIONS
+       help
+         This enables the driver for the NAND Flash on evaluation board based
+         on w90p910.
+
 endif # MTD_NAND
index f3a786b3cff377b182a15ac41ad88546ca4fa940..6950d3dabf104a1815fa5699183633d36b4ae955 100644 (file)
@@ -40,5 +40,7 @@ obj-$(CONFIG_MTD_NAND_SH_FLCTL)               += sh_flctl.o
 obj-$(CONFIG_MTD_NAND_MXC)             += mxc_nand.o
 obj-$(CONFIG_MTD_NAND_SOCRATES)                += socrates_nand.o
 obj-$(CONFIG_MTD_NAND_TXX9NDFMC)       += txx9ndfmc.o
+obj-$(CONFIG_MTD_NAND_W90P910)         += w90p910_nand.o
+obj-$(CONFIG_MTD_NAND_NOMADIK)         += nomadik_nand.o
 
 nand-objs := nand_base.o nand_bbt.o
index 20c828ba94052daae1e7a79ac4b4edb8f6e51104..f8e9975c86e562ca471601ca5f129ee9f402e7cc 100644 (file)
@@ -218,7 +218,7 @@ static int atmel_nand_calculate(struct mtd_info *mtd,
  * buf:        buffer to store read data
  */
 static int atmel_nand_read_page(struct mtd_info *mtd,
-               struct nand_chip *chip, uint8_t *buf)
+               struct nand_chip *chip, uint8_t *buf, int page)
 {
        int eccsize = chip->ecc.size;
        int eccbytes = chip->ecc.bytes;
index 1b4690bdfdb3ef8a623ba6c18af3ca225d41eb2c..c828d9ac7bd71a7709fac3ea16bd1e9419e062f6 100644 (file)
@@ -381,7 +381,7 @@ static int cafe_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
  * we need a special oob layout and handling.
  */
 static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
-                              uint8_t *buf)
+                              uint8_t *buf, int page)
 {
        struct cafe_priv *cafe = mtd->priv;
 
index 0fad6487e6f4bcb062334ae082126d73ad41cd59..f13f5b9afaf744f0ef6e234bf45115c01a83b219 100644 (file)
@@ -348,6 +348,12 @@ compare:
        if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3]))
                return 0;
 
+       /*
+        * Clear any previous address calculation by doing a dummy read of an
+        * error address register.
+        */
+       davinci_nand_readl(info, NAND_ERR_ADD1_OFFSET);
+
        /* Start address calculation, and wait for it to complete.
         * We _could_ start reading more data while this is working,
         * to speed up the overall page read.
@@ -359,8 +365,10 @@ compare:
 
                switch ((fsr >> 8) & 0x0f) {
                case 0:         /* no error, should not happen */
+                       davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET);
                        return 0;
                case 1:         /* five or more errors detected */
+                       davinci_nand_readl(info, NAND_ERR_ERRVAL1_OFFSET);
                        return -EIO;
                case 2:         /* error addresses computed */
                case 3:
@@ -500,6 +508,26 @@ static struct nand_ecclayout hwecc4_small __initconst = {
        },
 };
 
+/* An ECC layout for using 4-bit ECC with large-page (2048bytes) flash,
+ * storing ten ECC bytes plus the manufacturer's bad block marker byte,
+ * and not overlapping the default BBT markers.
+ */
+static struct nand_ecclayout hwecc4_2048 __initconst = {
+       .eccbytes = 40,
+       .eccpos = {
+               /* at the end of spare sector */
+               24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
+               34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
+               44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+               54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+               },
+       .oobfree = {
+               /* 2 bytes at offset 0 hold manufacturer badblock markers */
+               {.offset = 2, .length = 22, },
+               /* 5 bytes at offset 8 hold BBT markers */
+               /* 8 bytes at offset 16 hold JFFS2 clean markers */
+       },
+};
 
 static int __init nand_davinci_probe(struct platform_device *pdev)
 {
@@ -690,15 +718,20 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
                                info->mtd.oobsize - 16;
                        goto syndrome_done;
                }
+               if (chunks == 4) {
+                       info->ecclayout = hwecc4_2048;
+                       info->chip.ecc.mode = NAND_ECC_HW_OOB_FIRST;
+                       goto syndrome_done;
+               }
 
-               /* For large page chips we'll be wanting to use a
-                * not-yet-implemented mode that reads OOB data
-                * before reading the body of the page, to avoid
-                * the "infix OOB" model of NAND_ECC_HW_SYNDROME
-                * (and preserve manufacturer badblock markings).
+               /* 4KiB page chips are not yet supported. The eccpos from
+                * nand_ecclayout cannot hold 80 bytes and change to eccpos[]
+                * breaks userspace ioctl interface with mtd-utils. Once we
+                * resolve this issue, NAND_ECC_HW_OOB_FIRST mode can be used
+                * for the 4KiB page chips.
                 */
                dev_warn(&pdev->dev, "no 4-bit ECC support yet "
-                               "for large page NAND\n");
+                               "for 4KiB-page NAND\n");
                ret = -EIO;
                goto err_scan;
 
index 1f6eb2578717e479904efad7387164e18d702547..ddd37d2554ede02c08044642d4f30c2d3307ee57 100644 (file)
@@ -739,7 +739,8 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
 
 static int fsl_elbc_read_page(struct mtd_info *mtd,
                               struct nand_chip *chip,
-                              uint8_t *buf)
+                             uint8_t *buf,
+                             int page)
 {
        fsl_elbc_read_buf(mtd, buf, mtd->writesize);
        fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
index 76beea40d2cf2d35d6f0f5b32f3b45f404a5704b..65b26d5a5c0d391cf72559f6d6f38eadb7b089a1 100644 (file)
@@ -857,6 +857,17 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
        }
 }
 
+/* Define some generic bad / good block scan pattern which are used
+ * while scanning a device for factory marked good / bad blocks. */
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr smallpage_memorybased = {
+       .options = NAND_BBT_SCAN2NDPAGE,
+       .offs = 5,
+       .len = 1,
+       .pattern = scan_ff_pattern
+};
+
 static int __init mxcnd_probe(struct platform_device *pdev)
 {
        struct nand_chip *this;
@@ -973,7 +984,10 @@ static int __init mxcnd_probe(struct platform_device *pdev)
                goto escan;
        }
 
-       host->pagesize_2k = (mtd->writesize == 2048) ? 1 : 0;
+       if (mtd->writesize == 2048) {
+               host->pagesize_2k = 1;
+               this->badblock_pattern = &smallpage_memorybased;
+       }
 
        if (this->ecc.mode == NAND_ECC_HW) {
                switch (mtd->oobsize) {
index 8c21b89d2d0c2a7ea79eccf0d9c840771c68a58a..22113865438b83eab930c9aa839a127c6a5c8de3 100644 (file)
@@ -688,8 +688,7 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state)
  retry:
        spin_lock(lock);
 
-       /* Hardware controller shared among independend devices */
-       /* Hardware controller shared among independend devices */
+       /* Hardware controller shared among independent devices */
        if (!chip->controller->active)
                chip->controller->active = chip;
 
@@ -766,7 +765,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
  * Not for syndrome calculating ecc controllers, which use a special oob layout
  */
 static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
-                             uint8_t *buf)
+                             uint8_t *buf, int page)
 {
        chip->read_buf(mtd, buf, mtd->writesize);
        chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
@@ -782,7 +781,7 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
  * We need a special oob layout and handling even when OOB isn't used.
  */
 static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
-                             uint8_t *buf)
+                             uint8_t *buf, int page)
 {
        int eccsize = chip->ecc.size;
        int eccbytes = chip->ecc.bytes;
@@ -821,7 +820,7 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *c
  * @buf:       buffer to store read data
  */
 static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
-                               uint8_t *buf)
+                               uint8_t *buf, int page)
 {
        int i, eccsize = chip->ecc.size;
        int eccbytes = chip->ecc.bytes;
@@ -831,7 +830,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
        uint8_t *ecc_code = chip->buffers->ecccode;
        uint32_t *eccpos = chip->ecc.layout->eccpos;
 
-       chip->ecc.read_page_raw(mtd, chip, buf);
+       chip->ecc.read_page_raw(mtd, chip, buf, page);
 
        for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
                chip->ecc.calculate(mtd, p, &ecc_calc[i]);
@@ -944,7 +943,7 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint3
  * Not for syndrome calculating ecc controllers which need a special oob layout
  */
 static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
-                               uint8_t *buf)
+                               uint8_t *buf, int page)
 {
        int i, eccsize = chip->ecc.size;
        int eccbytes = chip->ecc.bytes;
@@ -979,6 +978,54 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
        return 0;
 }
 
+/**
+ * nand_read_page_hwecc_oob_first - [REPLACABLE] hw ecc, read oob first
+ * @mtd:       mtd info structure
+ * @chip:      nand chip info structure
+ * @buf:       buffer to store read data
+ *
+ * Hardware ECC for large page chips, require OOB to be read first.
+ * For this ECC mode, the write_page method is re-used from ECC_HW.
+ * These methods read/write ECC from the OOB area, unlike the
+ * ECC_HW_SYNDROME support with multiple ECC steps, follows the
+ * "infix ECC" scheme and reads/writes ECC from the data area, by
+ * overwriting the NAND manufacturer bad block markings.
+ */
+static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
+       struct nand_chip *chip, uint8_t *buf, int page)
+{
+       int i, eccsize = chip->ecc.size;
+       int eccbytes = chip->ecc.bytes;
+       int eccsteps = chip->ecc.steps;
+       uint8_t *p = buf;
+       uint8_t *ecc_code = chip->buffers->ecccode;
+       uint32_t *eccpos = chip->ecc.layout->eccpos;
+       uint8_t *ecc_calc = chip->buffers->ecccalc;
+
+       /* Read the OOB area first */
+       chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+       chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+       chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+       for (i = 0; i < chip->ecc.total; i++)
+               ecc_code[i] = chip->oob_poi[eccpos[i]];
+
+       for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+               int stat;
+
+               chip->ecc.hwctl(mtd, NAND_ECC_READ);
+               chip->read_buf(mtd, p, eccsize);
+               chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+               stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL);
+               if (stat < 0)
+                       mtd->ecc_stats.failed++;
+               else
+                       mtd->ecc_stats.corrected += stat;
+       }
+       return 0;
+}
+
 /**
  * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read
  * @mtd:       mtd info structure
@@ -989,7 +1036,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
  * we need a special oob layout and handling.
  */
 static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
-                                  uint8_t *buf)
+                                  uint8_t *buf, int page)
 {
        int i, eccsize = chip->ecc.size;
        int eccbytes = chip->ecc.bytes;
@@ -1131,11 +1178,13 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
 
                        /* Now read the page into the buffer */
                        if (unlikely(ops->mode == MTD_OOB_RAW))
-                               ret = chip->ecc.read_page_raw(mtd, chip, bufpoi);
+                               ret = chip->ecc.read_page_raw(mtd, chip,
+                                                             bufpoi, page);
                        else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
                                ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi);
                        else
-                               ret = chip->ecc.read_page(mtd, chip, bufpoi);
+                               ret = chip->ecc.read_page(mtd, chip, bufpoi,
+                                                         page);
                        if (ret < 0)
                                break;
 
@@ -1413,8 +1462,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
        int len;
        uint8_t *buf = ops->oobbuf;
 
-       DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n",
-             (unsigned long long)from, readlen);
+       DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08Lx, len = %i\n",
+                       __func__, (unsigned long long)from, readlen);
 
        if (ops->mode == MTD_OOB_AUTO)
                len = chip->ecc.layout->oobavail;
@@ -1422,8 +1471,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
                len = mtd->oobsize;
 
        if (unlikely(ops->ooboffs >= len)) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
-                       "Attempt to start read outside oob\n");
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start read "
+                                       "outside oob\n", __func__);
                return -EINVAL;
        }
 
@@ -1431,8 +1480,8 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
        if (unlikely(from >= mtd->size ||
                     ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) -
                                        (from >> chip->page_shift)) * len)) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
-                       "Attempt read beyond end of device\n");
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read beyond end "
+                                       "of device\n", __func__);
                return -EINVAL;
        }
 
@@ -1506,8 +1555,8 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
 
        /* Do not allow reads past end of device */
        if (ops->datbuf && (from + ops->len) > mtd->size) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
-                     "Attempt read beyond end of device\n");
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt read "
+                               "beyond end of device\n", __func__);
                return -EINVAL;
        }
 
@@ -1816,8 +1865,8 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
 
        /* reject writes, which are not page aligned */
        if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
-               printk(KERN_NOTICE "nand_write: "
-                      "Attempt to write not page aligned data\n");
+               printk(KERN_NOTICE "%s: Attempt to write not "
+                               "page aligned data\n", __func__);
                return -EINVAL;
        }
 
@@ -1944,8 +1993,8 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
        int chipnr, page, status, len;
        struct nand_chip *chip = mtd->priv;
 
-       DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n",
-             (unsigned int)to, (int)ops->ooblen);
+       DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n",
+                        __func__, (unsigned int)to, (int)ops->ooblen);
 
        if (ops->mode == MTD_OOB_AUTO)
                len = chip->ecc.layout->oobavail;
@@ -1954,14 +2003,14 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
 
        /* Do not allow write past end of page */
        if ((ops->ooboffs + ops->ooblen) > len) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
-                     "Attempt to write past end of page\n");
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to write "
+                               "past end of page\n", __func__);
                return -EINVAL;
        }
 
        if (unlikely(ops->ooboffs >= len)) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_do_write_oob: "
-                       "Attempt to start write outside oob\n");
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt to start "
+                               "write outside oob\n", __func__);
                return -EINVAL;
        }
 
@@ -1970,8 +2019,8 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
                     ops->ooboffs + ops->ooblen >
                        ((mtd->size >> chip->page_shift) -
                         (to >> chip->page_shift)) * len)) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_do_write_oob: "
-                       "Attempt write beyond end of device\n");
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond "
+                               "end of device\n", __func__);
                return -EINVAL;
        }
 
@@ -2026,8 +2075,8 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
 
        /* Do not allow writes past end of device */
        if (ops->datbuf && (to + ops->len) > mtd->size) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
-                     "Attempt write beyond end of device\n");
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: Attempt write beyond "
+                               "end of device\n", __func__);
                return -EINVAL;
        }
 
@@ -2117,26 +2166,27 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
        unsigned int bbt_masked_page = 0xffffffff;
        loff_t len;
 
-       DEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%012llx, len = %llu\n",
-             (unsigned long long)instr->addr, (unsigned long long)instr->len);
+       DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n",
+                               __func__, (unsigned long long)instr->addr,
+                               (unsigned long long)instr->len);
 
        /* Start address must align on block boundary */
        if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__);
                return -EINVAL;
        }
 
        /* Length must align on block boundary */
        if (instr->len & ((1 << chip->phys_erase_shift) - 1)) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
-                     "Length not block aligned\n");
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n",
+                                       __func__);
                return -EINVAL;
        }
 
        /* Do not allow erase past end of device */
        if ((instr->len + instr->addr) > mtd->size) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
-                     "Erase past end of device\n");
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: Erase past end of device\n",
+                                       __func__);
                return -EINVAL;
        }
 
@@ -2157,8 +2207,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
 
        /* Check, if it is write protected */
        if (nand_check_wp(mtd)) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
-                     "Device is write protected!!!\n");
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n",
+                                       __func__);
                instr->state = MTD_ERASE_FAILED;
                goto erase_exit;
        }
@@ -2183,8 +2233,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
                 */
                if (nand_block_checkbad(mtd, ((loff_t) page) <<
                                        chip->page_shift, 0, allowbbt)) {
-                       printk(KERN_WARNING "nand_erase: attempt to erase a "
-                              "bad block at page 0x%08x\n", page);
+                       printk(KERN_WARNING "%s: attempt to erase a bad block "
+                                       "at page 0x%08x\n", __func__, page);
                        instr->state = MTD_ERASE_FAILED;
                        goto erase_exit;
                }
@@ -2211,8 +2261,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
 
                /* See if block erase succeeded */
                if (status & NAND_STATUS_FAIL) {
-                       DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
-                             "Failed erase, page 0x%08x\n", page);
+                       DEBUG(MTD_DEBUG_LEVEL0, "%s: Failed erase, "
+                                       "page 0x%08x\n", __func__, page);
                        instr->state = MTD_ERASE_FAILED;
                        instr->fail_addr =
                                ((loff_t)page << chip->page_shift);
@@ -2272,9 +2322,9 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
                if (!rewrite_bbt[chipnr])
                        continue;
                /* update the BBT for chip */
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt "
-                     "(%d:0x%0llx 0x%0x)\n", chipnr, rewrite_bbt[chipnr],
-                     chip->bbt_td->pages[chipnr]);
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: nand_update_bbt "
+                       "(%d:0x%0llx 0x%0x)\n", __func__, chipnr,
+                       rewrite_bbt[chipnr], chip->bbt_td->pages[chipnr]);
                nand_update_bbt(mtd, rewrite_bbt[chipnr]);
        }
 
@@ -2292,7 +2342,7 @@ static void nand_sync(struct mtd_info *mtd)
 {
        struct nand_chip *chip = mtd->priv;
 
-       DEBUG(MTD_DEBUG_LEVEL3, "nand_sync: called\n");
+       DEBUG(MTD_DEBUG_LEVEL3, "%s: called\n", __func__);
 
        /* Grab the lock and see if the device is available */
        nand_get_device(chip, mtd, FL_SYNCING);
@@ -2356,8 +2406,8 @@ static void nand_resume(struct mtd_info *mtd)
        if (chip->state == FL_PM_SUSPENDED)
                nand_release_device(mtd);
        else
-               printk(KERN_ERR "nand_resume() called for a chip which is not "
-                      "in suspended state\n");
+               printk(KERN_ERR "%s called for a chip which is not "
+                      "in suspended state\n", __func__);
 }
 
 /*
@@ -2671,6 +2721,17 @@ int nand_scan_tail(struct mtd_info *mtd)
         */
 
        switch (chip->ecc.mode) {
+       case NAND_ECC_HW_OOB_FIRST:
+               /* Similar to NAND_ECC_HW, but a separate read_page handle */
+               if (!chip->ecc.calculate || !chip->ecc.correct ||
+                    !chip->ecc.hwctl) {
+                       printk(KERN_WARNING "No ECC functions supplied; "
+                              "Hardware ECC not possible\n");
+                       BUG();
+               }
+               if (!chip->ecc.read_page)
+                       chip->ecc.read_page = nand_read_page_hwecc_oob_first;
+
        case NAND_ECC_HW:
                /* Use standard hwecc read page function ? */
                if (!chip->ecc.read_page)
@@ -2693,7 +2754,7 @@ int nand_scan_tail(struct mtd_info *mtd)
                     chip->ecc.read_page == nand_read_page_hwecc ||
                     !chip->ecc.write_page ||
                     chip->ecc.write_page == nand_write_page_hwecc)) {
-                       printk(KERN_WARNING "No ECC functions supplied, "
+                       printk(KERN_WARNING "No ECC functions supplied; "
                               "Hardware ECC not possible\n");
                        BUG();
                }
@@ -2728,7 +2789,8 @@ int nand_scan_tail(struct mtd_info *mtd)
                chip->ecc.write_page_raw = nand_write_page_raw;
                chip->ecc.read_oob = nand_read_oob_std;
                chip->ecc.write_oob = nand_write_oob_std;
-               chip->ecc.size = 256;
+               if (!chip->ecc.size)
+                       chip->ecc.size = 256;
                chip->ecc.bytes = 3;
                break;
 
@@ -2858,7 +2920,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
 
        /* Many callers got this wrong, so check for it for a while... */
        if (!mtd->owner && caller_is_module()) {
-               printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n");
+               printk(KERN_CRIT "%s called with NULL mtd->owner!\n",
+                               __func__);
                BUG();
        }
 
index c0cb87d6d16e27ec697c77e719e6723eae9ce6de..db7ae9d6a29656d3f1826e64f175e3989ec3ad05 100644 (file)
@@ -417,22 +417,22 @@ int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf,
 EXPORT_SYMBOL(nand_calculate_ecc);
 
 /**
- * nand_correct_data - [NAND Interface] Detect and correct bit error(s)
- * @mtd:       MTD block structure
+ * __nand_correct_data - [NAND Interface] Detect and correct bit error(s)
  * @buf:       raw data read from the chip
  * @read_ecc:  ECC from the chip
  * @calc_ecc:  the ECC calculated from raw data
+ * @eccsize:   data bytes per ecc step (256 or 512)
  *
- * Detect and correct a 1 bit error for 256/512 byte block
+ * Detect and correct a 1 bit error for eccsize byte block
  */
-int nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
-                     unsigned char *read_ecc, unsigned char *calc_ecc)
+int __nand_correct_data(unsigned char *buf,
+                       unsigned char *read_ecc, unsigned char *calc_ecc,
+                       unsigned int eccsize)
 {
        unsigned char b0, b1, b2, bit_addr;
        unsigned int byte_addr;
        /* 256 or 512 bytes/ecc  */
-       const uint32_t eccsize_mult =
-                       (((struct nand_chip *)mtd->priv)->ecc.size) >> 8;
+       const uint32_t eccsize_mult = eccsize >> 8;
 
        /*
         * b0 to b2 indicate which bit is faulty (if any)
@@ -495,6 +495,23 @@ int nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
        printk(KERN_ERR "uncorrectable error : ");
        return -1;
 }
+EXPORT_SYMBOL(__nand_correct_data);
+
+/**
+ * nand_correct_data - [NAND Interface] Detect and correct bit error(s)
+ * @mtd:       MTD block structure
+ * @buf:       raw data read from the chip
+ * @read_ecc:  ECC from the chip
+ * @calc_ecc:  the ECC calculated from raw data
+ *
+ * Detect and correct a 1 bit error for 256/512 byte block
+ */
+int nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
+                     unsigned char *read_ecc, unsigned char *calc_ecc)
+{
+       return __nand_correct_data(buf, read_ecc, calc_ecc,
+                                  ((struct nand_chip *)mtd->priv)->ecc.size);
+}
 EXPORT_SYMBOL(nand_correct_data);
 
 MODULE_LICENSE("GPL");
index 89bf85af642cb630b641a73ff966dd73aa60811a..40b5658bdbe6f39ae574fb8b41257c64c17c14b5 100644 (file)
@@ -102,8 +102,8 @@ static int ndfc_calculate_ecc(struct mtd_info *mtd,
        wmb();
        ecc = in_be32(ndfc->ndfcbase + NDFC_ECC);
        /* The NDFC uses Smart Media (SMC) bytes order */
-       ecc_code[0] = p[2];
-       ecc_code[1] = p[1];
+       ecc_code[0] = p[1];
+       ecc_code[1] = p[2];
        ecc_code[2] = p[3];
 
        return 0;
diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c
new file mode 100644 (file)
index 0000000..7c302d5
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ *  drivers/mtd/nand/nomadik_nand.c
+ *
+ *  Overview:
+ *     Driver for on-board NAND flash on Nomadik Platforms
+ *
+ * Copyright Â© 2007 STMicroelectronics Pvt. Ltd.
+ * Author: Sachin Verma <sachin.verma@st.com>
+ *
+ * Copyright Â© 2009 Alessandro Rubini
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/partitions.h>
+#include <linux/io.h>
+#include <mach/nand.h>
+#include <mach/fsmc.h>
+
+#include <mtd/mtd-abi.h>
+
+struct nomadik_nand_host {
+       struct mtd_info         mtd;
+       struct nand_chip        nand;
+       void __iomem *data_va;
+       void __iomem *cmd_va;
+       void __iomem *addr_va;
+       struct nand_bbt_descr *bbt_desc;
+};
+
+static struct nand_ecclayout nomadik_ecc_layout = {
+       .eccbytes = 3 * 4,
+       .eccpos = { /* each subpage has 16 bytes: pos 2,3,4 hosts ECC */
+               0x02, 0x03, 0x04,
+               0x12, 0x13, 0x14,
+               0x22, 0x23, 0x24,
+               0x32, 0x33, 0x34},
+       /* let's keep bytes 5,6,7 for us, just in case we change ECC algo */
+       .oobfree = { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} },
+};
+
+static void nomadik_ecc_control(struct mtd_info *mtd, int mode)
+{
+       /* No need to enable hw ecc, it's on by default */
+}
+
+static void nomadik_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+       struct nand_chip *nand = mtd->priv;
+       struct nomadik_nand_host *host = nand->priv;
+
+       if (cmd == NAND_CMD_NONE)
+               return;
+
+       if (ctrl & NAND_CLE)
+               writeb(cmd, host->cmd_va);
+       else
+               writeb(cmd, host->addr_va);
+}
+
+static int nomadik_nand_probe(struct platform_device *pdev)
+{
+       struct nomadik_nand_platform_data *pdata = pdev->dev.platform_data;
+       struct nomadik_nand_host *host;
+       struct mtd_info *mtd;
+       struct nand_chip *nand;
+       struct resource *res;
+       int ret = 0;
+
+       /* Allocate memory for the device structure (and zero it) */
+       host = kzalloc(sizeof(struct nomadik_nand_host), GFP_KERNEL);
+       if (!host) {
+               dev_err(&pdev->dev, "Failed to allocate device structure.\n");
+               return -ENOMEM;
+       }
+
+       /* Call the client's init function, if any */
+       if (pdata->init)
+               ret = pdata->init();
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Init function failed\n");
+               goto err;
+       }
+
+       /* ioremap three regions */
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr");
+       if (!res) {
+               ret = -EIO;
+               goto err_unmap;
+       }
+       host->addr_va = ioremap(res->start, res->end - res->start + 1);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
+       if (!res) {
+               ret = -EIO;
+               goto err_unmap;
+       }
+       host->data_va = ioremap(res->start, res->end - res->start + 1);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd");
+       if (!res) {
+               ret = -EIO;
+               goto err_unmap;
+       }
+       host->cmd_va = ioremap(res->start, res->end - res->start + 1);
+
+       if (!host->addr_va || !host->data_va || !host->cmd_va) {
+               ret = -ENOMEM;
+               goto err_unmap;
+       }
+
+       /* Link all private pointers */
+       mtd = &host->mtd;
+       nand = &host->nand;
+       mtd->priv = nand;
+       nand->priv = host;
+
+       host->mtd.owner = THIS_MODULE;
+       nand->IO_ADDR_R = host->data_va;
+       nand->IO_ADDR_W = host->data_va;
+       nand->cmd_ctrl = nomadik_cmd_ctrl;
+
+       /*
+        * This stanza declares ECC_HW but uses soft routines. It's because
+        * HW claims to make the calculation but not the correction. However,
+        * I haven't managed to get the desired data out of it until now.
+        */
+       nand->ecc.mode = NAND_ECC_SOFT;
+       nand->ecc.layout = &nomadik_ecc_layout;
+       nand->ecc.hwctl = nomadik_ecc_control;
+       nand->ecc.size = 512;
+       nand->ecc.bytes = 3;
+
+       nand->options = pdata->options;
+
+       /*
+        * Scan to find existance of the device
+        */
+       if (nand_scan(&host->mtd, 1)) {
+               ret = -ENXIO;
+               goto err_unmap;
+       }
+
+#ifdef CONFIG_MTD_PARTITIONS
+       add_mtd_partitions(&host->mtd, pdata->parts, pdata->nparts);
+#else
+       pr_info("Registering %s as whole device\n", mtd->name);
+       add_mtd_device(mtd);
+#endif
+
+       platform_set_drvdata(pdev, host);
+       return 0;
+
+ err_unmap:
+       if (host->cmd_va)
+               iounmap(host->cmd_va);
+       if (host->data_va)
+               iounmap(host->data_va);
+       if (host->addr_va)
+               iounmap(host->addr_va);
+ err:
+       kfree(host);
+       return ret;
+}
+
+/*
+ * Clean up routine
+ */
+static int nomadik_nand_remove(struct platform_device *pdev)
+{
+       struct nomadik_nand_host *host = platform_get_drvdata(pdev);
+       struct nomadik_nand_platform_data *pdata = pdev->dev.platform_data;
+
+       if (pdata->exit)
+               pdata->exit();
+
+       if (host) {
+               iounmap(host->cmd_va);
+               iounmap(host->data_va);
+               iounmap(host->addr_va);
+               kfree(host);
+       }
+       return 0;
+}
+
+static int nomadik_nand_suspend(struct device *dev)
+{
+       struct nomadik_nand_host *host = dev_get_drvdata(dev);
+       int ret = 0;
+       if (host)
+               ret = host->mtd.suspend(&host->mtd);
+       return ret;
+}
+
+static int nomadik_nand_resume(struct device *dev)
+{
+       struct nomadik_nand_host *host = dev_get_drvdata(dev);
+       if (host)
+               host->mtd.resume(&host->mtd);
+       return 0;
+}
+
+static struct dev_pm_ops nomadik_nand_pm_ops = {
+       .suspend = nomadik_nand_suspend,
+       .resume = nomadik_nand_resume,
+};
+
+static struct platform_driver nomadik_nand_driver = {
+       .probe = nomadik_nand_probe,
+       .remove = nomadik_nand_remove,
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "nomadik_nand",
+               .pm = &nomadik_nand_pm_ops,
+       },
+};
+
+static int __init nand_nomadik_init(void)
+{
+       pr_info("Nomadik NAND driver\n");
+       return platform_driver_register(&nomadik_nand_driver);
+}
+
+static void __exit nand_nomadik_exit(void)
+{
+       platform_driver_unregister(&nomadik_nand_driver);
+}
+
+module_init(nand_nomadik_init);
+module_exit(nand_nomadik_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("ST Microelectronics (sachin.verma@st.com)");
+MODULE_DESCRIPTION("NAND driver for Nomadik Platform");
index ebd07e95b8140354ae8c51061ef7cb9b7945d153..090ab87086b5245ed522ed940658c2db0f825503 100644 (file)
@@ -18,8 +18,7 @@
 #include <linux/mtd/partitions.h>
 #include <linux/io.h>
 
-#include <asm/dma.h>
-
+#include <mach/dma.h>
 #include <mach/gpmc.h>
 #include <mach/nand.h>
 
 static const char *part_probes[] = { "cmdlinepart", NULL };
 #endif
 
+#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH
+static int use_prefetch = 1;
+
+/* "modprobe ... use_prefetch=0" etc */
+module_param(use_prefetch, bool, 0);
+MODULE_PARM_DESC(use_prefetch, "enable/disable use of PREFETCH");
+
+#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA
+static int use_dma = 1;
+
+/* "modprobe ... use_dma=0" etc */
+module_param(use_dma, bool, 0);
+MODULE_PARM_DESC(use_dma, "enable/disable use of DMA");
+#else
+const int use_dma;
+#endif
+#else
+const int use_prefetch;
+const int use_dma;
+#endif
+
 struct omap_nand_info {
        struct nand_hw_control          controller;
        struct omap_nand_platform_data  *pdata;
@@ -124,6 +144,9 @@ struct omap_nand_info {
        unsigned long                   phys_base;
        void __iomem                    *gpmc_cs_baseaddr;
        void __iomem                    *gpmc_baseaddr;
+       void __iomem                    *nand_pref_fifo_add;
+       struct completion               comp;
+       int                             dma_ch;
 };
 
 /**
@@ -188,6 +211,38 @@ static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
                __raw_writeb(cmd, info->nand.IO_ADDR_W);
 }
 
+/**
+ * omap_read_buf8 - read data from NAND controller into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void omap_read_buf8(struct mtd_info *mtd, u_char *buf, int len)
+{
+       struct nand_chip *nand = mtd->priv;
+
+       ioread8_rep(nand->IO_ADDR_R, buf, len);
+}
+
+/**
+ * omap_write_buf8 - write buffer to NAND controller
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len)
+{
+       struct omap_nand_info *info = container_of(mtd,
+                                               struct omap_nand_info, mtd);
+       u_char *p = (u_char *)buf;
+
+       while (len--) {
+               iowrite8(*p++, info->nand.IO_ADDR_W);
+               while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr +
+                                               GPMC_STATUS) & GPMC_BUF_FULL));
+       }
+}
+
 /**
  * omap_read_buf16 - read data from NAND controller into buffer
  * @mtd: MTD device structure
@@ -198,7 +253,7 @@ static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
 {
        struct nand_chip *nand = mtd->priv;
 
-       __raw_readsw(nand->IO_ADDR_R, buf, len / 2);
+       ioread16_rep(nand->IO_ADDR_R, buf, len / 2);
 }
 
 /**
@@ -217,13 +272,242 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
        len >>= 1;
 
        while (len--) {
-               writew(*p++, info->nand.IO_ADDR_W);
+               iowrite16(*p++, info->nand.IO_ADDR_W);
 
                while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr +
                                                GPMC_STATUS) & GPMC_BUF_FULL))
                        ;
        }
 }
+
+/**
+ * omap_read_buf_pref - read data from NAND controller into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len)
+{
+       struct omap_nand_info *info = container_of(mtd,
+                                               struct omap_nand_info, mtd);
+       uint32_t pfpw_status = 0, r_count = 0;
+       int ret = 0;
+       u32 *p = (u32 *)buf;
+
+       /* take care of subpage reads */
+       for (; len % 4 != 0; ) {
+               *buf++ = __raw_readb(info->nand.IO_ADDR_R);
+               len--;
+       }
+       p = (u32 *) buf;
+
+       /* configure and start prefetch transfer */
+       ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x0);
+       if (ret) {
+               /* PFPW engine is busy, use cpu copy method */
+               if (info->nand.options & NAND_BUSWIDTH_16)
+                       omap_read_buf16(mtd, buf, len);
+               else
+                       omap_read_buf8(mtd, buf, len);
+       } else {
+               do {
+                       pfpw_status = gpmc_prefetch_status();
+                       r_count = ((pfpw_status >> 24) & 0x7F) >> 2;
+                       ioread32_rep(info->nand_pref_fifo_add, p, r_count);
+                       p += r_count;
+                       len -= r_count << 2;
+               } while (len);
+
+               /* disable and stop the PFPW engine */
+               gpmc_prefetch_reset();
+       }
+}
+
+/**
+ * omap_write_buf_pref - write buffer to NAND controller
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void omap_write_buf_pref(struct mtd_info *mtd,
+                                       const u_char *buf, int len)
+{
+       struct omap_nand_info *info = container_of(mtd,
+                                               struct omap_nand_info, mtd);
+       uint32_t pfpw_status = 0, w_count = 0;
+       int i = 0, ret = 0;
+       u16 *p = (u16 *) buf;
+
+       /* take care of subpage writes */
+       if (len % 2 != 0) {
+               writeb(*buf, info->nand.IO_ADDR_R);
+               p = (u16 *)(buf + 1);
+               len--;
+       }
+
+       /*  configure and start prefetch transfer */
+       ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x1);
+       if (ret) {
+               /* PFPW engine is busy, use cpu copy method */
+               if (info->nand.options & NAND_BUSWIDTH_16)
+                       omap_write_buf16(mtd, buf, len);
+               else
+                       omap_write_buf8(mtd, buf, len);
+       } else {
+               pfpw_status = gpmc_prefetch_status();
+               while (pfpw_status & 0x3FFF) {
+                       w_count = ((pfpw_status >> 24) & 0x7F) >> 1;
+                       for (i = 0; (i < w_count) && len; i++, len -= 2)
+                               iowrite16(*p++, info->nand_pref_fifo_add);
+                       pfpw_status = gpmc_prefetch_status();
+               }
+
+               /* disable and stop the PFPW engine */
+               gpmc_prefetch_reset();
+       }
+}
+
+#ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA
+/*
+ * omap_nand_dma_cb: callback on the completion of dma transfer
+ * @lch: logical channel
+ * @ch_satuts: channel status
+ * @data: pointer to completion data structure
+ */
+static void omap_nand_dma_cb(int lch, u16 ch_status, void *data)
+{
+       complete((struct completion *) data);
+}
+
+/*
+ * omap_nand_dma_transfer: configer and start dma transfer
+ * @mtd: MTD device structure
+ * @addr: virtual address in RAM of source/destination
+ * @len: number of data bytes to be transferred
+ * @is_write: flag for read/write operation
+ */
+static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
+                                       unsigned int len, int is_write)
+{
+       struct omap_nand_info *info = container_of(mtd,
+                                       struct omap_nand_info, mtd);
+       uint32_t prefetch_status = 0;
+       enum dma_data_direction dir = is_write ? DMA_TO_DEVICE :
+                                                       DMA_FROM_DEVICE;
+       dma_addr_t dma_addr;
+       int ret;
+
+       /* The fifo depth is 64 bytes. We have a sync at each frame and frame
+        * length is 64 bytes.
+        */
+       int buf_len = len >> 6;
+
+       if (addr >= high_memory) {
+               struct page *p1;
+
+               if (((size_t)addr & PAGE_MASK) !=
+                       ((size_t)(addr + len - 1) & PAGE_MASK))
+                       goto out_copy;
+               p1 = vmalloc_to_page(addr);
+               if (!p1)
+                       goto out_copy;
+               addr = page_address(p1) + ((size_t)addr & ~PAGE_MASK);
+       }
+
+       dma_addr = dma_map_single(&info->pdev->dev, addr, len, dir);
+       if (dma_mapping_error(&info->pdev->dev, dma_addr)) {
+               dev_err(&info->pdev->dev,
+                       "Couldn't DMA map a %d byte buffer\n", len);
+               goto out_copy;
+       }
+
+       if (is_write) {
+           omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+                                               info->phys_base, 0, 0);
+           omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+                                                       dma_addr, 0, 0);
+           omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32,
+                                       0x10, buf_len, OMAP_DMA_SYNC_FRAME,
+                                       OMAP24XX_DMA_GPMC, OMAP_DMA_DST_SYNC);
+       } else {
+           omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
+                                               info->phys_base, 0, 0);
+           omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
+                                                       dma_addr, 0, 0);
+           omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32,
+                                       0x10, buf_len, OMAP_DMA_SYNC_FRAME,
+                                       OMAP24XX_DMA_GPMC, OMAP_DMA_SRC_SYNC);
+       }
+       /*  configure and start prefetch transfer */
+       ret = gpmc_prefetch_enable(info->gpmc_cs, 0x1, len, is_write);
+       if (ret)
+               /* PFPW engine is busy, use cpu copy methode */
+               goto out_copy;
+
+       init_completion(&info->comp);
+
+       omap_start_dma(info->dma_ch);
+
+       /* setup and start DMA using dma_addr */
+       wait_for_completion(&info->comp);
+
+       while (0x3fff & (prefetch_status = gpmc_prefetch_status()))
+               ;
+       /* disable and stop the PFPW engine */
+       gpmc_prefetch_reset();
+
+       dma_unmap_single(&info->pdev->dev, dma_addr, len, dir);
+       return 0;
+
+out_copy:
+       if (info->nand.options & NAND_BUSWIDTH_16)
+               is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len)
+                       : omap_write_buf16(mtd, (u_char *) addr, len);
+       else
+               is_write == 0 ? omap_read_buf8(mtd, (u_char *) addr, len)
+                       : omap_write_buf8(mtd, (u_char *) addr, len);
+       return 0;
+}
+#else
+static void omap_nand_dma_cb(int lch, u16 ch_status, void *data) {}
+static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr,
+                                       unsigned int len, int is_write)
+{
+       return 0;
+}
+#endif
+
+/**
+ * omap_read_buf_dma_pref - read data from NAND controller into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void omap_read_buf_dma_pref(struct mtd_info *mtd, u_char *buf, int len)
+{
+       if (len <= mtd->oobsize)
+               omap_read_buf_pref(mtd, buf, len);
+       else
+               /* start transfer in DMA mode */
+               omap_nand_dma_transfer(mtd, buf, len, 0x0);
+}
+
+/**
+ * omap_write_buf_dma_pref - write buffer to NAND controller
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void omap_write_buf_dma_pref(struct mtd_info *mtd,
+                                       const u_char *buf, int len)
+{
+       if (len <= mtd->oobsize)
+               omap_write_buf_pref(mtd, buf, len);
+       else
+               /* start transfer in DMA mode */
+               omap_nand_dma_transfer(mtd, buf, len, 0x1);
+}
+
 /**
  * omap_verify_buf - Verify chip data against buffer
  * @mtd: MTD device structure
@@ -658,17 +942,12 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
                err = -ENOMEM;
                goto out_release_mem_region;
        }
+
        info->nand.controller = &info->controller;
 
        info->nand.IO_ADDR_W = info->nand.IO_ADDR_R;
        info->nand.cmd_ctrl  = omap_hwcontrol;
 
-       /* REVISIT:  only supports 16-bit NAND flash */
-
-       info->nand.read_buf   = omap_read_buf16;
-       info->nand.write_buf  = omap_write_buf16;
-       info->nand.verify_buf = omap_verify_buf;
-
        /*
         * If RDY/BSY line is connected to OMAP then use the omap ready
         * funcrtion and the generic nand_wait function which reads the status
@@ -689,6 +968,40 @@ static int __devinit omap_nand_probe(struct platform_device *pdev)
                                                                == 0x1000)
                info->nand.options  |= NAND_BUSWIDTH_16;
 
+       if (use_prefetch) {
+               /* copy the virtual address of nand base for fifo access */
+               info->nand_pref_fifo_add = info->nand.IO_ADDR_R;
+
+               info->nand.read_buf   = omap_read_buf_pref;
+               info->nand.write_buf  = omap_write_buf_pref;
+               if (use_dma) {
+                       err = omap_request_dma(OMAP24XX_DMA_GPMC, "NAND",
+                               omap_nand_dma_cb, &info->comp, &info->dma_ch);
+                       if (err < 0) {
+                               info->dma_ch = -1;
+                               printk(KERN_WARNING "DMA request failed."
+                                       " Non-dma data transfer mode\n");
+                       } else {
+                               omap_set_dma_dest_burst_mode(info->dma_ch,
+                                               OMAP_DMA_DATA_BURST_16);
+                               omap_set_dma_src_burst_mode(info->dma_ch,
+                                               OMAP_DMA_DATA_BURST_16);
+
+                               info->nand.read_buf   = omap_read_buf_dma_pref;
+                               info->nand.write_buf  = omap_write_buf_dma_pref;
+                       }
+               }
+       } else {
+               if (info->nand.options & NAND_BUSWIDTH_16) {
+                       info->nand.read_buf   = omap_read_buf16;
+                       info->nand.write_buf  = omap_write_buf16;
+               } else {
+                       info->nand.read_buf   = omap_read_buf8;
+                       info->nand.write_buf  = omap_write_buf8;
+               }
+       }
+       info->nand.verify_buf = omap_verify_buf;
+
 #ifdef CONFIG_MTD_NAND_OMAP_HWECC
        info->nand.ecc.bytes            = 3;
        info->nand.ecc.size             = 512;
@@ -744,9 +1057,12 @@ static int omap_nand_remove(struct platform_device *pdev)
        struct omap_nand_info *info = mtd->priv;
 
        platform_set_drvdata(pdev, NULL);
+       if (use_dma)
+               omap_free_dma(info->dma_ch);
+
        /* Release NAND device, its internal structures and partitions */
        nand_release(&info->mtd);
-       iounmap(info->nand.IO_ADDR_R);
+       iounmap(info->nand_pref_fifo_add);
        kfree(&info->mtd);
        return 0;
 }
@@ -763,6 +1079,15 @@ static struct platform_driver omap_nand_driver = {
 static int __init omap_nand_init(void)
 {
        printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME);
+
+       /* This check is required if driver is being
+        * loaded run time as a module
+        */
+       if ((1 == use_dma) && (0 == use_prefetch)) {
+               printk(KERN_INFO"Wrong parameters: 'use_dma' can not be 1 "
+                               "without use_prefetch'. Prefetch will not be"
+                               " used in either mode (mpu or dma)\n");
+       }
        return platform_driver_register(&omap_nand_driver);
 }
 
index 0d9d4bc9c762fe5caeab5770419f14dfe2c81403..f59c07427af3eda9c71dd18cb4244200375f5216 100644 (file)
@@ -171,7 +171,6 @@ static int __devexit orion_nand_remove(struct platform_device *pdev)
 }
 
 static struct platform_driver orion_nand_driver = {
-       .probe          = orion_nand_probe,
        .remove         = __devexit_p(orion_nand_remove),
        .driver         = {
                .name   = "orion_nand",
@@ -181,7 +180,7 @@ static struct platform_driver orion_nand_driver = {
 
 static int __init orion_nand_init(void)
 {
-       return platform_driver_register(&orion_nand_driver);
+       return platform_driver_probe(&orion_nand_driver, orion_nand_probe);
 }
 
 static void __exit orion_nand_exit(void)
index 30a8ce6d3e69bcdc1f0429daafacd06115baf06e..6ea520ae2410a9113690641670d488321ac3da23 100644 (file)
@@ -102,6 +102,7 @@ enum {
        ERR_SENDCMD     = -2,
        ERR_DBERR       = -3,
        ERR_BBERR       = -4,
+       ERR_SBERR       = -5,
 };
 
 enum {
@@ -564,11 +565,13 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
 
        status = nand_readl(info, NDSR);
 
-       if (status & (NDSR_RDDREQ | NDSR_DBERR)) {
+       if (status & (NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR)) {
                if (status & NDSR_DBERR)
                        info->retcode = ERR_DBERR;
+               else if (status & NDSR_SBERR)
+                       info->retcode = ERR_SBERR;
 
-               disable_int(info, NDSR_RDDREQ | NDSR_DBERR);
+               disable_int(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR);
 
                if (info->use_dma) {
                        info->state = STATE_DMA_READING;
@@ -670,7 +673,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
                if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr))
                        break;
 
-               pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR);
+               pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR);
 
                /* We only are OOB, so if the data has error, does not matter */
                if (info->retcode == ERR_DBERR)
@@ -687,7 +690,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
                if (prepare_read_prog_cmd(info, cmdset->read1, column, page_addr))
                        break;
 
-               pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR);
+               pxa3xx_nand_do_cmd(info, NDSR_RDDREQ | NDSR_DBERR | NDSR_SBERR);
 
                if (info->retcode == ERR_DBERR) {
                        /* for blank page (all 0xff), HW will calculate its ECC as
@@ -861,8 +864,12 @@ static int pxa3xx_nand_ecc_correct(struct mtd_info *mtd,
         * consider it as a ecc error which will tell the caller the
         * read fail We have distinguish all the errors, but the
         * nand_read_ecc only check this function return value
+        *
+        * Corrected (single-bit) errors must also be noted.
         */
-       if (info->retcode != ERR_NONE)
+       if (info->retcode == ERR_SBERR)
+               return 1;
+       else if (info->retcode != ERR_NONE)
                return -1;
 
        return 0;
index 2bc896623e2d7e21ea5d76977ce3417958293c6f..02bef21f2e4b987099a8f1f8d856c7364cbf9a58 100644 (file)
@@ -329,7 +329,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va
 }
 
 static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
-                               uint8_t *buf)
+                               uint8_t *buf, int page)
 {
        int i, eccsize = chip->ecc.size;
        int eccbytes = chip->ecc.bytes;
@@ -857,7 +857,6 @@ static int __exit flctl_remove(struct platform_device *pdev)
 }
 
 static struct platform_driver flctl_driver = {
-       .probe          = flctl_probe,
        .remove         = flctl_remove,
        .driver = {
                .name   = "sh_flctl",
@@ -867,7 +866,7 @@ static struct platform_driver flctl_driver = {
 
 static int __init flctl_nand_init(void)
 {
-       return platform_driver_register(&flctl_driver);
+       return platform_driver_probe(&flctl_driver, flctl_probe);
 }
 
 static void __exit flctl_nand_cleanup(void)
index daa6a4c3b8cecc885a32a328a5cc71cc93907b73..92c73344a669eebbd56f2b450161260d07872409 100644 (file)
@@ -301,6 +301,21 @@ static int tmio_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
        return 0;
 }
 
+static int tmio_nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
+               unsigned char *read_ecc, unsigned char *calc_ecc)
+{
+       int r0, r1;
+
+       /* assume ecc.size = 512 and ecc.bytes = 6 */
+       r0 = __nand_correct_data(buf, read_ecc, calc_ecc, 256);
+       if (r0 < 0)
+               return r0;
+       r1 = __nand_correct_data(buf + 256, read_ecc + 3, calc_ecc + 3, 256);
+       if (r1 < 0)
+               return r1;
+       return r0 + r1;
+}
+
 static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio)
 {
        struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data;
@@ -424,7 +439,7 @@ static int tmio_probe(struct platform_device *dev)
        nand_chip->ecc.bytes = 6;
        nand_chip->ecc.hwctl = tmio_nand_enable_hwecc;
        nand_chip->ecc.calculate = tmio_nand_calculate_ecc;
-       nand_chip->ecc.correct = nand_correct_data;
+       nand_chip->ecc.correct = tmio_nand_correct_data;
 
        if (data)
                nand_chip->badblock_pattern = data->badblock_pattern;
index 488088eff2cae01ab5b8db8dab1e904d7307da8f..73af8324d0d0b4e540195063c9b311dcaf875b3b 100644 (file)
@@ -189,18 +189,43 @@ static int txx9ndfmc_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
                                   uint8_t *ecc_code)
 {
        struct platform_device *dev = mtd_to_platdev(mtd);
+       struct nand_chip *chip = mtd->priv;
+       int eccbytes;
        u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);
 
        mcr &= ~TXX9_NDFMCR_ECC_ALL;
        txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR);
        txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_READ, TXX9_NDFMCR);
-       ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR);
-       ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR);
-       ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR);
+       for (eccbytes = chip->ecc.bytes; eccbytes > 0; eccbytes -= 3) {
+               ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR);
+               ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR);
+               ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR);
+               ecc_code += 3;
+       }
        txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR);
        return 0;
 }
 
+static int txx9ndfmc_correct_data(struct mtd_info *mtd, unsigned char *buf,
+               unsigned char *read_ecc, unsigned char *calc_ecc)
+{
+       struct nand_chip *chip = mtd->priv;
+       int eccsize;
+       int corrected = 0;
+       int stat;
+
+       for (eccsize = chip->ecc.size; eccsize > 0; eccsize -= 256) {
+               stat = __nand_correct_data(buf, read_ecc, calc_ecc, 256);
+               if (stat < 0)
+                       return stat;
+               corrected += stat;
+               buf += 256;
+               read_ecc += 3;
+               calc_ecc += 3;
+       }
+       return corrected;
+}
+
 static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
 {
        struct platform_device *dev = mtd_to_platdev(mtd);
@@ -244,6 +269,22 @@ static void txx9ndfmc_initialize(struct platform_device *dev)
 #define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \
        DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000)
 
+static int txx9ndfmc_nand_scan(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = mtd->priv;
+       int ret;
+
+       ret = nand_scan_ident(mtd, 1);
+       if (!ret) {
+               if (mtd->writesize >= 512) {
+                       chip->ecc.size = mtd->writesize;
+                       chip->ecc.bytes = 3 * (mtd->writesize / 256);
+               }
+               ret = nand_scan_tail(mtd);
+       }
+       return ret;
+}
+
 static int __init txx9ndfmc_probe(struct platform_device *dev)
 {
        struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
@@ -321,9 +362,10 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
                chip->cmd_ctrl = txx9ndfmc_cmd_ctrl;
                chip->dev_ready = txx9ndfmc_dev_ready;
                chip->ecc.calculate = txx9ndfmc_calculate_ecc;
-               chip->ecc.correct = nand_correct_data;
+               chip->ecc.correct = txx9ndfmc_correct_data;
                chip->ecc.hwctl = txx9ndfmc_enable_hwecc;
                chip->ecc.mode = NAND_ECC_HW;
+               /* txx9ndfmc_nand_scan will overwrite ecc.size and ecc.bytes */
                chip->ecc.size = 256;
                chip->ecc.bytes = 3;
                chip->chip_delay = 100;
@@ -349,7 +391,7 @@ static int __init txx9ndfmc_probe(struct platform_device *dev)
                if (plat->wide_mask & (1 << i))
                        chip->options |= NAND_BUSWIDTH_16;
 
-               if (nand_scan(mtd, 1)) {
+               if (txx9ndfmc_nand_scan(mtd)) {
                        kfree(txx9_priv->mtdname);
                        kfree(txx9_priv);
                        continue;
diff --git a/drivers/mtd/nand/w90p910_nand.c b/drivers/mtd/nand/w90p910_nand.c
new file mode 100644 (file)
index 0000000..7680e73
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * Copyright (c) 2009 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation;version 2 of the License.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+#define REG_FMICSR     0x00
+#define REG_SMCSR      0xa0
+#define REG_SMISR      0xac
+#define REG_SMCMD      0xb0
+#define REG_SMADDR     0xb4
+#define REG_SMDATA     0xb8
+
+#define RESET_FMI      0x01
+#define NAND_EN                0x08
+#define READYBUSY      (0x01 << 18)
+
+#define SWRST          0x01
+#define PSIZE          (0x01 << 3)
+#define DMARWEN                (0x03 << 1)
+#define BUSWID         (0x01 << 4)
+#define ECC4EN         (0x01 << 5)
+#define WP             (0x01 << 24)
+#define NANDCS         (0x01 << 25)
+#define ENDADDR                (0x01 << 31)
+
+#define read_data_reg(dev)             \
+       __raw_readl((dev)->reg + REG_SMDATA)
+
+#define write_data_reg(dev, val)       \
+       __raw_writel((val), (dev)->reg + REG_SMDATA)
+
+#define write_cmd_reg(dev, val)                \
+       __raw_writel((val), (dev)->reg + REG_SMCMD)
+
+#define write_addr_reg(dev, val)       \
+       __raw_writel((val), (dev)->reg + REG_SMADDR)
+
+struct w90p910_nand {
+       struct mtd_info mtd;
+       struct nand_chip chip;
+       void __iomem *reg;
+       struct clk *clk;
+       spinlock_t lock;
+};
+
+static const struct mtd_partition partitions[] = {
+       {
+        .name = "NAND FS 0",
+        .offset = 0,
+        .size = 8 * 1024 * 1024
+       },
+       {
+        .name = "NAND FS 1",
+        .offset = MTDPART_OFS_APPEND,
+        .size = MTDPART_SIZ_FULL
+       }
+};
+
+static unsigned char w90p910_nand_read_byte(struct mtd_info *mtd)
+{
+       unsigned char ret;
+       struct w90p910_nand *nand;
+
+       nand = container_of(mtd, struct w90p910_nand, mtd);
+
+       ret = (unsigned char)read_data_reg(nand);
+
+       return ret;
+}
+
+static void w90p910_nand_read_buf(struct mtd_info *mtd,
+                                               unsigned char *buf, int len)
+{
+       int i;
+       struct w90p910_nand *nand;
+
+       nand = container_of(mtd, struct w90p910_nand, mtd);
+
+       for (i = 0; i < len; i++)
+               buf[i] = (unsigned char)read_data_reg(nand);
+}
+
+static void w90p910_nand_write_buf(struct mtd_info *mtd,
+                                       const unsigned char *buf, int len)
+{
+       int i;
+       struct w90p910_nand *nand;
+
+       nand = container_of(mtd, struct w90p910_nand, mtd);
+
+       for (i = 0; i < len; i++)
+               write_data_reg(nand, buf[i]);
+}
+
+static int w90p910_verify_buf(struct mtd_info *mtd,
+                                       const unsigned char *buf, int len)
+{
+       int i;
+       struct w90p910_nand *nand;
+
+       nand = container_of(mtd, struct w90p910_nand, mtd);
+
+       for (i = 0; i < len; i++) {
+               if (buf[i] != (unsigned char)read_data_reg(nand))
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int w90p910_check_rb(struct w90p910_nand *nand)
+{
+       unsigned int val;
+       spin_lock(&nand->lock);
+       val = __raw_readl(REG_SMISR);
+       val &= READYBUSY;
+       spin_unlock(&nand->lock);
+
+       return val;
+}
+
+static int w90p910_nand_devready(struct mtd_info *mtd)
+{
+       struct w90p910_nand *nand;
+       int ready;
+
+       nand = container_of(mtd, struct w90p910_nand, mtd);
+
+       ready = (w90p910_check_rb(nand)) ? 1 : 0;
+       return ready;
+}
+
+static void w90p910_nand_command_lp(struct mtd_info *mtd,
+                       unsigned int command, int column, int page_addr)
+{
+       register struct nand_chip *chip = mtd->priv;
+       struct w90p910_nand *nand;
+
+       nand = container_of(mtd, struct w90p910_nand, mtd);
+
+       if (command == NAND_CMD_READOOB) {
+               column += mtd->writesize;
+               command = NAND_CMD_READ0;
+       }
+
+       write_cmd_reg(nand, command & 0xff);
+
+       if (column != -1 || page_addr != -1) {
+
+               if (column != -1) {
+                       if (chip->options & NAND_BUSWIDTH_16)
+                               column >>= 1;
+                       write_addr_reg(nand, column);
+                       write_addr_reg(nand, column >> 8 | ENDADDR);
+               }
+               if (page_addr != -1) {
+                       write_addr_reg(nand, page_addr);
+
+                       if (chip->chipsize > (128 << 20)) {
+                               write_addr_reg(nand, page_addr >> 8);
+                               write_addr_reg(nand, page_addr >> 16 | ENDADDR);
+                       } else {
+                               write_addr_reg(nand, page_addr >> 8 | ENDADDR);
+                       }
+               }
+       }
+
+       switch (command) {
+       case NAND_CMD_CACHEDPROG:
+       case NAND_CMD_PAGEPROG:
+       case NAND_CMD_ERASE1:
+       case NAND_CMD_ERASE2:
+       case NAND_CMD_SEQIN:
+       case NAND_CMD_RNDIN:
+       case NAND_CMD_STATUS:
+       case NAND_CMD_DEPLETE1:
+               return;
+
+       case NAND_CMD_STATUS_ERROR:
+       case NAND_CMD_STATUS_ERROR0:
+       case NAND_CMD_STATUS_ERROR1:
+       case NAND_CMD_STATUS_ERROR2:
+       case NAND_CMD_STATUS_ERROR3:
+               udelay(chip->chip_delay);
+               return;
+
+       case NAND_CMD_RESET:
+               if (chip->dev_ready)
+                       break;
+               udelay(chip->chip_delay);
+
+               write_cmd_reg(nand, NAND_CMD_STATUS);
+               write_cmd_reg(nand, command);
+
+               while (!w90p910_check_rb(nand))
+                       ;
+
+               return;
+
+       case NAND_CMD_RNDOUT:
+               write_cmd_reg(nand, NAND_CMD_RNDOUTSTART);
+               return;
+
+       case NAND_CMD_READ0:
+
+               write_cmd_reg(nand, NAND_CMD_READSTART);
+       default:
+
+               if (!chip->dev_ready) {
+                       udelay(chip->chip_delay);
+                       return;
+               }
+       }
+
+       /* Apply this short delay always to ensure that we do wait tWB in
+        * any case on any machine. */
+       ndelay(100);
+
+       while (!chip->dev_ready(mtd))
+               ;
+}
+
+
+static void w90p910_nand_enable(struct w90p910_nand *nand)
+{
+       unsigned int val;
+       spin_lock(&nand->lock);
+       __raw_writel(RESET_FMI, (nand->reg + REG_FMICSR));
+
+       val = __raw_readl(nand->reg + REG_FMICSR);
+
+       if (!(val & NAND_EN))
+               __raw_writel(val | NAND_EN, REG_FMICSR);
+
+       val = __raw_readl(nand->reg + REG_SMCSR);
+
+       val &= ~(SWRST|PSIZE|DMARWEN|BUSWID|ECC4EN|NANDCS);
+       val |= WP;
+
+       __raw_writel(val, nand->reg + REG_SMCSR);
+
+       spin_unlock(&nand->lock);
+}
+
+static int __devinit w90p910_nand_probe(struct platform_device *pdev)
+{
+       struct w90p910_nand *w90p910_nand;
+       struct nand_chip *chip;
+       int retval;
+       struct resource *res;
+
+       retval = 0;
+
+       w90p910_nand = kzalloc(sizeof(struct w90p910_nand), GFP_KERNEL);
+       if (!w90p910_nand)
+               return -ENOMEM;
+       chip = &(w90p910_nand->chip);
+
+       w90p910_nand->mtd.priv  = chip;
+       w90p910_nand->mtd.owner = THIS_MODULE;
+       spin_lock_init(&w90p910_nand->lock);
+
+       w90p910_nand->clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(w90p910_nand->clk)) {
+               retval = -ENOENT;
+               goto fail1;
+       }
+       clk_enable(w90p910_nand->clk);
+
+       chip->cmdfunc           = w90p910_nand_command_lp;
+       chip->dev_ready         = w90p910_nand_devready;
+       chip->read_byte         = w90p910_nand_read_byte;
+       chip->write_buf         = w90p910_nand_write_buf;
+       chip->read_buf          = w90p910_nand_read_buf;
+       chip->verify_buf        = w90p910_verify_buf;
+       chip->chip_delay        = 50;
+       chip->options           = 0;
+       chip->ecc.mode          = NAND_ECC_SOFT;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               retval = -ENXIO;
+               goto fail1;
+       }
+
+       if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+               retval = -EBUSY;
+               goto fail1;
+       }
+
+       w90p910_nand->reg = ioremap(res->start, resource_size(res));
+       if (!w90p910_nand->reg) {
+               retval = -ENOMEM;
+               goto fail2;
+       }
+
+       w90p910_nand_enable(w90p910_nand);
+
+       if (nand_scan(&(w90p910_nand->mtd), 1)) {
+               retval = -ENXIO;
+               goto fail3;
+       }
+
+       add_mtd_partitions(&(w90p910_nand->mtd), partitions,
+                                               ARRAY_SIZE(partitions));
+
+       platform_set_drvdata(pdev, w90p910_nand);
+
+       return retval;
+
+fail3: iounmap(w90p910_nand->reg);
+fail2: release_mem_region(res->start, resource_size(res));
+fail1: kfree(w90p910_nand);
+       return retval;
+}
+
+static int __devexit w90p910_nand_remove(struct platform_device *pdev)
+{
+       struct w90p910_nand *w90p910_nand = platform_get_drvdata(pdev);
+       struct resource *res;
+
+       iounmap(w90p910_nand->reg);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(res->start, resource_size(res));
+
+       clk_disable(w90p910_nand->clk);
+       clk_put(w90p910_nand->clk);
+
+       kfree(w90p910_nand);
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static struct platform_driver w90p910_nand_driver = {
+       .probe          = w90p910_nand_probe,
+       .remove         = __devexit_p(w90p910_nand_remove),
+       .driver         = {
+               .name   = "w90p910-fmi",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init w90p910_nand_init(void)
+{
+       return platform_driver_register(&w90p910_nand_driver);
+}
+
+static void __exit w90p910_nand_exit(void)
+{
+       platform_driver_unregister(&w90p910_nand_driver);
+}
+
+module_init(w90p910_nand_init);
+module_exit(w90p910_nand_exit);
+
+MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
+MODULE_DESCRIPTION("w90p910 nand driver!");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:w90p910-fmi");
index 3e164f0c9295c7bc779b0b0c72c798f76176c09e..62d6a78c4eeea43ae34aac65508d270b163b985d 100644 (file)
@@ -46,21 +46,12 @@ int __devinit of_mtd_parse_partitions(struct device *dev,
                const u32 *reg;
                int len;
 
-               /* check if this is a partition node */
-               partname = of_get_property(pp, "name", &len);
-               if (strcmp(partname, "partition") != 0) {
+               reg = of_get_property(pp, "reg", &len);
+               if (!reg) {
                        nr_parts--;
                        continue;
                }
 
-               reg = of_get_property(pp, "reg", &len);
-               if (!reg || (len != 2 * sizeof(u32))) {
-                       of_node_put(pp);
-                       dev_err(dev, "Invalid 'reg' on %s\n", node->full_name);
-                       kfree(*pparts);
-                       *pparts = NULL;
-                       return -EINVAL;
-               }
                (*pparts)[i].offset = reg[0];
                (*pparts)[i].size = reg[1];
 
@@ -75,6 +66,14 @@ int __devinit of_mtd_parse_partitions(struct device *dev,
                i++;
        }
 
+       if (!i) {
+               of_node_put(pp);
+               dev_err(dev, "No valid partition found on %s\n", node->full_name);
+               kfree(*pparts);
+               *pparts = NULL;
+               return -EINVAL;
+       }
+
        return nr_parts;
 }
 EXPORT_SYMBOL(of_mtd_parse_partitions);
index 79fa79e8f8de9b67a5949888d029f93b7dbebb27..a38f580c2bb3f82a680d392d418b753aeb1562c0 100644 (file)
@@ -5,6 +5,7 @@
 menuconfig MTD_ONENAND
        tristate "OneNAND Device Support"
        depends on MTD
+       select MTD_PARTITIONS
        help
          This enables support for accessing all type of OneNAND flash
          devices. For further information see
@@ -23,7 +24,6 @@ config MTD_ONENAND_VERIFY_WRITE
 
 config MTD_ONENAND_GENERIC
        tristate "OneNAND Flash device via platform device driver"
-       depends on ARM
        help
          Support for OneNAND flash via platform device driver.
 
@@ -66,7 +66,6 @@ config MTD_ONENAND_2X_PROGRAM
 
 config MTD_ONENAND_SIM
        tristate "OneNAND simulator support"
-       depends on MTD_PARTITIONS
        help
          The simulator may simulate various OneNAND flash chips for the
          OneNAND MTD layer.
index 3a496c33fb52405801d8add2191bd3df89ac435f..e78914938c5c6930cee9b9462bd33cd271bdda2d 100644 (file)
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/onenand.h>
 #include <linux/mtd/partitions.h>
-
 #include <asm/io.h>
-#include <asm/mach/flash.h>
-
-#define DRIVER_NAME    "onenand"
 
+/*
+ * Note: Driver name and platform data format have been updated!
+ *
+ * This version of the driver is named "onenand-flash" and takes struct
+ * onenand_platform_data as platform data. The old ARM-specific version
+ * with the name "onenand" used to take struct flash_platform_data.
+ */
+#define DRIVER_NAME    "onenand-flash"
 
 #ifdef CONFIG_MTD_PARTITIONS
 static const char *part_probes[] = { "cmdlinepart", NULL,  };
@@ -39,16 +43,16 @@ struct onenand_info {
 static int __devinit generic_onenand_probe(struct platform_device *pdev)
 {
        struct onenand_info *info;
-       struct flash_platform_data *pdata = pdev->dev.platform_data;
+       struct onenand_platform_data *pdata = pdev->dev.platform_data;
        struct resource *res = pdev->resource;
-       unsigned long size = res->end - res->start + 1;
+       unsigned long size = resource_size(res);
        int err;
 
        info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL);
        if (!info)
                return -ENOMEM;
 
-       if (!request_mem_region(res->start, size, pdev->dev.driver->name)) {
+       if (!request_mem_region(res->start, size, dev_name(&pdev->dev))) {
                err = -EBUSY;
                goto out_free_info;
        }
@@ -59,7 +63,7 @@ static int __devinit generic_onenand_probe(struct platform_device *pdev)
                goto out_release_mem_region;
        }
 
-       info->onenand.mmcontrol = pdata->mmcontrol;
+       info->onenand.mmcontrol = pdata ? pdata->mmcontrol : 0;
        info->onenand.irq = platform_get_irq(pdev, 0);
 
        info->mtd.name = dev_name(&pdev->dev);
@@ -75,7 +79,7 @@ static int __devinit generic_onenand_probe(struct platform_device *pdev)
        err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0);
        if (err > 0)
                add_mtd_partitions(&info->mtd, info->parts, err);
-       else if (err <= 0 && pdata->parts)
+       else if (err <= 0 && pdata && pdata->parts)
                add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts);
        else
 #endif
@@ -99,7 +103,7 @@ static int __devexit generic_onenand_remove(struct platform_device *pdev)
 {
        struct onenand_info *info = platform_get_drvdata(pdev);
        struct resource *res = pdev->resource;
-       unsigned long size = res->end - res->start + 1;
+       unsigned long size = resource_size(res);
 
        platform_set_drvdata(pdev, NULL);
 
index 6e829095ea9de8a601f58b2fcb7c4da167e2d746..ff66e4330aa71ebde90b830b0faca5e4dc6f31d0 100644 (file)
@@ -1191,7 +1191,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
                        /*
                         * Chip boundary handling in DDP
                         * Now we issued chip 1 read and pointed chip 1
-                        * bufferam so we have to point chip 0 bufferam.
+                        * bufferram so we have to point chip 0 bufferram.
                         */
                        if (ONENAND_IS_DDP(this) &&
                            unlikely(from == (this->chipsize >> 1))) {
@@ -1867,8 +1867,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
                        ONENAND_SET_NEXT_BUFFERRAM(this);
 
                /*
-                * 2 PLANE, MLC, and Flex-OneNAND doesn't support
-                * write-while-programe feature.
+                * 2 PLANE, MLC, and Flex-OneNAND do not support
+                * write-while-program feature.
                 */
                if (!ONENAND_IS_2PLANE(this) && !first) {
                        ONENAND_SET_PREV_BUFFERRAM(this);
@@ -1879,7 +1879,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
                        onenand_update_bufferram(mtd, prev, !ret && !prev_subpage);
                        if (ret) {
                                written -= prevlen;
-                               printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
+                               printk(KERN_ERR "onenand_write_ops_nolock: write failed %d\n", ret);
                                break;
                        }
 
@@ -1905,7 +1905,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
                        /* In partial page write we don't update bufferram */
                        onenand_update_bufferram(mtd, to, !ret && !subpage);
                        if (ret) {
-                               printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
+                               printk(KERN_ERR "onenand_write_ops_nolock: write failed %d\n", ret);
                                break;
                        }
 
@@ -2201,7 +2201,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
        /* Grab the lock and see if the device is available */
        onenand_get_device(mtd, FL_ERASING);
 
-       /* Loop throught the pages */
+       /* Loop through the blocks */
        instr->state = MTD_ERASING;
 
        while (len) {
@@ -2328,7 +2328,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
         if (bbm->bbt)
                 bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
 
-        /* We write two bytes, so we dont have to mess with 16 bit access */
+        /* We write two bytes, so we don't have to mess with 16-bit access */
         ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
        /* FIXME : What to do when marking SLC block in partition
         *         with MLC erasesize? For now, it is not advisable to
@@ -2557,7 +2557,7 @@ static void onenand_unlock_all(struct mtd_info *mtd)
 
 #ifdef CONFIG_MTD_ONENAND_OTP
 
-/* Interal OTP operation */
+/* Internal OTP operation */
 typedef int (*otp_op_t)(struct mtd_info *mtd, loff_t form, size_t len,
                size_t *retlen, u_char *buf);
 
@@ -2921,7 +2921,7 @@ static void onenand_check_features(struct mtd_info *mtd)
                this->options |= ONENAND_HAS_2PLANE;
 
        case ONENAND_DEVICE_DENSITY_2Gb:
-               /* 2Gb DDP don't have 2 plane */
+               /* 2Gb DDP does not have 2 plane */
                if (!ONENAND_IS_DDP(this))
                        this->options |= ONENAND_HAS_2PLANE;
                this->options |= ONENAND_HAS_UNLOCK_ALL;
@@ -3364,7 +3364,7 @@ static int onenand_probe(struct mtd_info *mtd)
        /* It's real page size */
        this->writesize = mtd->writesize;
 
-       /* REVIST: Multichip handling */
+       /* REVISIT: Multichip handling */
 
        if (FLEXONENAND(this))
                flexonenand_get_size(mtd);
index a18e8d2f255765db492c8b1797dd99c2eaf54ece..5553cd4eab2083d80824f49e95423e1e2507a705 100644 (file)
@@ -512,7 +512,7 @@ static int __init mtd_oobtest_init(void)
                goto out;
 
        addr0 = 0;
-       for (i = 0; bbt[i] && i < ebcnt; ++i)
+       for (i = 0; i < ebcnt && bbt[i]; ++i)
                addr0 += mtd->erasesize;
 
        /* Attempt to write off end of OOB */
index 9648818b9e2c3857feb9f1a2e57a159ef073b94a..103cac480feec2fbf622db0d4d65694cc611547a 100644 (file)
@@ -116,11 +116,11 @@ static int verify_eraseblock(int ebnum)
        loff_t addr = ebnum * mtd->erasesize;
 
        addr0 = 0;
-       for (i = 0; bbt[i] && i < ebcnt; ++i)
+       for (i = 0; i < ebcnt && bbt[i]; ++i)
                addr0 += mtd->erasesize;
 
        addrn = mtd->size;
-       for (i = 0; bbt[ebcnt - i - 1] && i < ebcnt; ++i)
+       for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i)
                addrn -= mtd->erasesize;
 
        set_random_data(writebuf, mtd->erasesize);
@@ -219,11 +219,11 @@ static int crosstest(void)
        memset(pp1, 0, pgsize * 4);
 
        addr0 = 0;
-       for (i = 0; bbt[i] && i < ebcnt; ++i)
+       for (i = 0; i < ebcnt && bbt[i]; ++i)
                addr0 += mtd->erasesize;
 
        addrn = mtd->size;
-       for (i = 0; bbt[ebcnt - i - 1] && i < ebcnt; ++i)
+       for (i = 0; i < ebcnt && bbt[ebcnt - i - 1]; ++i)
                addrn -= mtd->erasesize;
 
        /* Read 2nd-to-last page to pp1 */
@@ -317,7 +317,7 @@ static int erasecrosstest(void)
 
        ebnum = 0;
        addr0 = 0;
-       for (i = 0; bbt[i] && i < ebcnt; ++i) {
+       for (i = 0; i < ebcnt && bbt[i]; ++i) {
                addr0 += mtd->erasesize;
                ebnum += 1;
        }
@@ -413,7 +413,7 @@ static int erasetest(void)
 
        ebnum = 0;
        addr0 = 0;
-       for (i = 0; bbt[i] && i < ebcnt; ++i) {
+       for (i = 0; i < ebcnt && bbt[i]; ++i) {
                addr0 += mtd->erasesize;
                ebnum += 1;
        }
index e9580104b6ba477894b60c6bb3e69839c5677d2f..3ff50da94789111a4707082eefb4289c377e1a51 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/completion.h>
 #include <linux/sched.h>
 #include <linux/freezer.h>
+#include <linux/kthread.h>
 #include "nodelist.h"
 
 
@@ -31,7 +32,7 @@ void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c)
 /* This must only ever be called when no GC thread is currently running */
 int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
 {
-       pid_t pid;
+       struct task_struct *tsk;
        int ret = 0;
 
        BUG_ON(c->gc_task);
@@ -39,15 +40,16 @@ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
        init_completion(&c->gc_thread_start);
        init_completion(&c->gc_thread_exit);
 
-       pid = kernel_thread(jffs2_garbage_collect_thread, c, CLONE_FS|CLONE_FILES);
-       if (pid < 0) {
-               printk(KERN_WARNING "fork failed for JFFS2 garbage collect thread: %d\n", -pid);
+       tsk = kthread_run(jffs2_garbage_collect_thread, c, "jffs2_gcd_mtd%d", c->mtd->index);
+       if (IS_ERR(tsk)) {
+               printk(KERN_WARNING "fork failed for JFFS2 garbage collect thread: %ld\n", -PTR_ERR(tsk));
                complete(&c->gc_thread_exit);
-               ret = pid;
+               ret = PTR_ERR(tsk);
        } else {
                /* Wait for it... */
-               D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", pid));
+               D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", tsk->pid));
                wait_for_completion(&c->gc_thread_start);
+               ret = tsk->pid;
        }
 
        return ret;
@@ -71,7 +73,6 @@ static int jffs2_garbage_collect_thread(void *_c)
 {
        struct jffs2_sb_info *c = _c;
 
-       daemonize("jffs2_gcd_mtd%d", c->mtd->index);
        allow_signal(SIGKILL);
        allow_signal(SIGSTOP);
        allow_signal(SIGCONT);
@@ -107,6 +108,11 @@ static int jffs2_garbage_collect_thread(void *_c)
                 * the GC thread get there first. */
                schedule_timeout_interruptible(msecs_to_jiffies(50));
 
+               if (kthread_should_stop()) {
+                       D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread():  kthread_stop() called.\n"));
+                       goto die;
+               }
+
                /* Put_super will send a SIGKILL and then wait on the sem.
                 */
                while (signal_pending(current) || freezing(current)) {
index 9eff2bdae8a798631cb2c987215e92eaddd75538..c082868910f2b54482fcd20340c493e804ea375e 100644 (file)
@@ -39,13 +39,13 @@ int __init jffs2_create_slab_caches(void)
 
        raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent",
                                            sizeof(struct jffs2_raw_dirent),
-                                           0, 0, NULL);
+                                           0, SLAB_HWCACHE_ALIGN, NULL);
        if (!raw_dirent_slab)
                goto err;
 
        raw_inode_slab = kmem_cache_create("jffs2_raw_inode",
                                           sizeof(struct jffs2_raw_inode),
-                                          0, 0, NULL);
+                                          0, SLAB_HWCACHE_ALIGN, NULL);
        if (!raw_inode_slab)
                goto err;
 
index 4030ebada49e922cbdc8fb8e486a7a2b149d177d..7a232a9bdd6230e0db32e93b582af621c88a02ba 100644 (file)
@@ -121,6 +121,7 @@ typedef enum {
        NAND_ECC_SOFT,
        NAND_ECC_HW,
        NAND_ECC_HW_SYNDROME,
+       NAND_ECC_HW_OOB_FIRST,
 } nand_ecc_modes_t;
 
 /*
@@ -271,13 +272,13 @@ struct nand_ecc_ctrl {
                                           uint8_t *calc_ecc);
        int                     (*read_page_raw)(struct mtd_info *mtd,
                                                 struct nand_chip *chip,
-                                                uint8_t *buf);
+                                                uint8_t *buf, int page);
        void                    (*write_page_raw)(struct mtd_info *mtd,
                                                  struct nand_chip *chip,
                                                  const uint8_t *buf);
        int                     (*read_page)(struct mtd_info *mtd,
                                             struct nand_chip *chip,
-                                            uint8_t *buf);
+                                            uint8_t *buf, int page);
        int                     (*read_subpage)(struct mtd_info *mtd,
                                             struct nand_chip *chip,
                                             uint32_t offs, uint32_t len,
index 090da505425d77d105acb6133d63dfaaa7c95b7b..052ea8ca24347c83a7bfb4df0032ebec54fd9c83 100644 (file)
@@ -20,6 +20,12 @@ struct mtd_info;
  */
 int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
 
+/*
+ * Detect and correct a 1 bit error for eccsize byte block
+ */
+int __nand_correct_data(u_char *dat, u_char *read_ecc, u_char *calc_ecc,
+                       unsigned int eccsize);
+
 /*
  * Detect and correct a 1 bit error for 256 byte block
  */
index 8ed8733743818f17411b98aade04b9e4c63acef6..4e49f3350678c63ee2b1c48908498bee0cdb6ffc 100644 (file)
@@ -214,4 +214,12 @@ unsigned onenand_block(struct onenand_chip *this, loff_t addr);
 loff_t onenand_addr(struct onenand_chip *this, int block);
 int flexonenand_region(struct mtd_info *mtd, loff_t addr);
 
+struct mtd_partition;
+
+struct onenand_platform_data {
+       void            (*mmcontrol)(struct mtd_info *mtd, int sync_read);
+       struct mtd_partition *parts;
+       unsigned int    nr_parts;
+};
+
 #endif /* __LINUX_MTD_ONENAND_H */
index 86a6bbef646596c1735e509fece60d863b26d2e8..acadbf53a69f519f021e4943e07c5ac2163f4db2 100644 (file)
 #define ONENAND_ECC_2BIT               (1 << 1)
 #define ONENAND_ECC_2BIT_ALL           (0xAAAA)
 #define FLEXONENAND_UNCORRECTABLE_ERROR        (0x1010)
+#define ONENAND_ECC_3BIT               (1 << 2)
+#define ONENAND_ECC_4BIT               (1 << 3)
+#define ONENAND_ECC_4BIT_UNCORRECTABLE (0x1010)
 
 /*
  * One-Time Programmable (OTP)