Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
authorDavid Woodhouse <David.Woodhouse@intel.com>
Fri, 26 Mar 2010 14:55:59 +0000 (14:55 +0000)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Fri, 26 Mar 2010 14:55:59 +0000 (14:55 +0000)
Conflicts:
drivers/mtd/nand/sh_flctl.c

Maxim's patch to initialise sysfs attributes depends on the patch which
actually adds sysfs_attr_init().

72 files changed:
MAINTAINERS
arch/arm/mach-ep93xx/include/mach/ts72xx.h
arch/arm/mach-ep93xx/ts72xx.c
drivers/mtd/Kconfig
drivers/mtd/Makefile
drivers/mtd/chips/cfi_cmdset_0001.c
drivers/mtd/chips/cfi_cmdset_0002.c
drivers/mtd/devices/Makefile
drivers/mtd/devices/block2mtd.c
drivers/mtd/devices/pmc551.c
drivers/mtd/ftl.c
drivers/mtd/inftlcore.c
drivers/mtd/inftlmount.c
drivers/mtd/maps/bfin-async-flash.c
drivers/mtd/maps/ceiva.c
drivers/mtd/maps/ixp4xx.c
drivers/mtd/maps/physmap_of.c
drivers/mtd/maps/pismo.c
drivers/mtd/mtd_blkdevs.c
drivers/mtd/mtdblock.c
drivers/mtd/mtdblock_ro.c
drivers/mtd/mtdchar.c
drivers/mtd/mtdcore.c
drivers/mtd/mtdcore.h
drivers/mtd/mtdoops.c
drivers/mtd/mtdsuper.c
drivers/mtd/nand/Kconfig
drivers/mtd/nand/Makefile
drivers/mtd/nand/alauda.c
drivers/mtd/nand/atmel_nand.c
drivers/mtd/nand/au1550nd.c
drivers/mtd/nand/bcm_umi_nand.c
drivers/mtd/nand/cafe_nand.c
drivers/mtd/nand/davinci_nand.c
drivers/mtd/nand/fsl_elbc_nand.c
drivers/mtd/nand/fsl_upm.c
drivers/mtd/nand/gpio.c
drivers/mtd/nand/mpc5121_nfc.c [new file with mode: 0644]
drivers/mtd/nand/mxc_nand.c
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/nand_bbt.c
drivers/mtd/nand/nand_bcm_umi.h
drivers/mtd/nand/nandsim.c
drivers/mtd/nand/nomadik_nand.c
drivers/mtd/nand/nuc900_nand.c [moved from drivers/mtd/nand/w90p910_nand.c with 63% similarity]
drivers/mtd/nand/omap2.c
drivers/mtd/nand/orion_nand.c
drivers/mtd/nand/pasemi_nand.c
drivers/mtd/nand/r852.c [new file with mode: 0644]
drivers/mtd/nand/r852.h [new file with mode: 0644]
drivers/mtd/nand/s3c2410.c
drivers/mtd/nand/sh_flctl.c
drivers/mtd/nand/sm_common.c [new file with mode: 0644]
drivers/mtd/nand/sm_common.h [new file with mode: 0644]
drivers/mtd/nand/socrates_nand.c
drivers/mtd/nand/tmio_nand.c
drivers/mtd/nand/ts7250.c [deleted file]
drivers/mtd/nand/txx9ndfmc.c
drivers/mtd/nftlcore.c
drivers/mtd/onenand/omap2.c
drivers/mtd/rfd_ftl.c
drivers/mtd/sm_ftl.c [new file with mode: 0644]
drivers/mtd/sm_ftl.h [new file with mode: 0644]
drivers/mtd/ssfdc.c
fs/jffs2/fs.c
fs/jffs2/nodelist.h
include/linux/jffs2.h
include/linux/mtd/blktrans.h
include/linux/mtd/cfi.h
include/linux/mtd/mtd.h
include/linux/mtd/nand.h
lib/idr.c

index 449d444..32d7091 100644 (file)
@@ -4701,6 +4701,12 @@ S:       Maintained
 F:     Documentation/rfkill.txt
 F:     net/rfkill/
 
+RICOH SMARTMEDIA/XD DRIVER
+M:     Maxim Levitsky <maximlevitsky@gmail.com>
+S:     Maintained
+F:     drivers/mtd/nand/r822.c
+F:     drivers/mtd/nand/r822.h
+
 RISCOM8 DRIVER
 S:     Orphan
 F:     Documentation/serial/riscom8.txt
index 93107d8..0eabec6 100644 (file)
@@ -9,9 +9,6 @@
  * febff000    22000000        4K      model number register
  * febfe000    22400000        4K      options register
  * febfd000    22800000        4K      options register #2
- * febfc000    [67]0000000     4K      NAND data register
- * febfb000    [67]0400000     4K      NAND control register
- * febfa000    [67]0800000     4K      NAND busy register
  * febf9000    10800000        4K      TS-5620 RTC index register
  * febf8000    11700000        4K      TS-5620 RTC data register
  */
 #define TS72XX_OPTIONS2_TS9420_BOOT    0x02
 
 
-#define TS72XX_NAND1_DATA_PHYS_BASE    0x60000000
-#define TS72XX_NAND2_DATA_PHYS_BASE    0x70000000
-#define TS72XX_NAND_DATA_VIRT_BASE     0xfebfc000
-#define TS72XX_NAND_DATA_SIZE          0x00001000
-
-#define TS72XX_NAND1_CONTROL_PHYS_BASE 0x60400000
-#define TS72XX_NAND2_CONTROL_PHYS_BASE 0x70400000
-#define TS72XX_NAND_CONTROL_VIRT_BASE  0xfebfb000
-#define TS72XX_NAND_CONTROL_SIZE       0x00001000
-
-#define TS72XX_NAND1_BUSY_PHYS_BASE    0x60800000
-#define TS72XX_NAND2_BUSY_PHYS_BASE    0x70800000
-#define TS72XX_NAND_BUSY_VIRT_BASE     0xfebfa000
-#define TS72XX_NAND_BUSY_SIZE          0x00001000
-
-
 #define TS72XX_RTC_INDEX_VIRT_BASE     0xfebf9000
 #define TS72XX_RTC_INDEX_PHYS_BASE     0x10800000
 #define TS72XX_RTC_INDEX_SIZE          0x00001000
index fac1ec7..333d259 100644 (file)
  * your option) any later version.
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
 #include <linux/m48t86.h>
 #include <linux/mtd/physmap.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
 
 #include <mach/hardware.h>
 #include <mach/ts72xx.h>
@@ -54,92 +58,162 @@ static struct map_desc ts72xx_io_desc[] __initdata = {
        }
 };
 
-static struct map_desc ts72xx_nand_io_desc[] __initdata = {
-       {
-               .virtual        = TS72XX_NAND_DATA_VIRT_BASE,
-               .pfn            = __phys_to_pfn(TS72XX_NAND1_DATA_PHYS_BASE),
-               .length         = TS72XX_NAND_DATA_SIZE,
-               .type           = MT_DEVICE,
-       }, {
-               .virtual        = TS72XX_NAND_CONTROL_VIRT_BASE,
-               .pfn            = __phys_to_pfn(TS72XX_NAND1_CONTROL_PHYS_BASE),
-               .length         = TS72XX_NAND_CONTROL_SIZE,
-               .type           = MT_DEVICE,
-       }, {
-               .virtual        = TS72XX_NAND_BUSY_VIRT_BASE,
-               .pfn            = __phys_to_pfn(TS72XX_NAND1_BUSY_PHYS_BASE),
-               .length         = TS72XX_NAND_BUSY_SIZE,
-               .type           = MT_DEVICE,
+static void __init ts72xx_map_io(void)
+{
+       ep93xx_map_io();
+       iotable_init(ts72xx_io_desc, ARRAY_SIZE(ts72xx_io_desc));
+}
+
+
+/*************************************************************************
+ * NAND flash
+ *************************************************************************/
+#define TS72XX_NAND_CONTROL_ADDR_LINE  22      /* 0xN0400000 */
+#define TS72XX_NAND_BUSY_ADDR_LINE     23      /* 0xN0800000 */
+
+static void ts72xx_nand_hwcontrol(struct mtd_info *mtd,
+                                 int cmd, unsigned int ctrl)
+{
+       struct nand_chip *chip = mtd->priv;
+
+       if (ctrl & NAND_CTRL_CHANGE) {
+               void __iomem *addr = chip->IO_ADDR_R;
+               unsigned char bits;
+
+               addr += (1 << TS72XX_NAND_CONTROL_ADDR_LINE);
+
+               bits = __raw_readb(addr) & ~0x07;
+               bits |= (ctrl & NAND_NCE) << 2; /* bit 0 -> bit 2 */
+               bits |= (ctrl & NAND_CLE);      /* bit 1 -> bit 1 */
+               bits |= (ctrl & NAND_ALE) >> 2; /* bit 2 -> bit 0 */
+
+               __raw_writeb(bits, addr);
        }
-};
 
-static struct map_desc ts72xx_alternate_nand_io_desc[] __initdata = {
+       if (cmd != NAND_CMD_NONE)
+               __raw_writeb(cmd, chip->IO_ADDR_W);
+}
+
+static int ts72xx_nand_device_ready(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = mtd->priv;
+       void __iomem *addr = chip->IO_ADDR_R;
+
+       addr += (1 << TS72XX_NAND_BUSY_ADDR_LINE);
+
+       return !!(__raw_readb(addr) & 0x20);
+}
+
+static const char *ts72xx_nand_part_probes[] = { "cmdlinepart", NULL };
+
+#define TS72XX_BOOTROM_PART_SIZE       (SZ_16K)
+#define TS72XX_REDBOOT_PART_SIZE       (SZ_2M + SZ_1M)
+
+static struct mtd_partition ts72xx_nand_parts[] = {
        {
-               .virtual        = TS72XX_NAND_DATA_VIRT_BASE,
-               .pfn            = __phys_to_pfn(TS72XX_NAND2_DATA_PHYS_BASE),
-               .length         = TS72XX_NAND_DATA_SIZE,
-               .type           = MT_DEVICE,
+               .name           = "TS-BOOTROM",
+               .offset         = 0,
+               .size           = TS72XX_BOOTROM_PART_SIZE,
+               .mask_flags     = MTD_WRITEABLE,        /* force read-only */
        }, {
-               .virtual        = TS72XX_NAND_CONTROL_VIRT_BASE,
-               .pfn            = __phys_to_pfn(TS72XX_NAND2_CONTROL_PHYS_BASE),
-               .length         = TS72XX_NAND_CONTROL_SIZE,
-               .type           = MT_DEVICE,
+               .name           = "Linux",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = 0,                    /* filled in later */
        }, {
-               .virtual        = TS72XX_NAND_BUSY_VIRT_BASE,
-               .pfn            = __phys_to_pfn(TS72XX_NAND2_BUSY_PHYS_BASE),
-               .length         = TS72XX_NAND_BUSY_SIZE,
-               .type           = MT_DEVICE,
-       }
+               .name           = "RedBoot",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = MTDPART_SIZ_FULL,
+               .mask_flags     = MTD_WRITEABLE,        /* force read-only */
+       },
 };
 
-static void __init ts72xx_map_io(void)
+static void ts72xx_nand_set_parts(uint64_t size,
+                                 struct platform_nand_chip *chip)
 {
-       ep93xx_map_io();
-       iotable_init(ts72xx_io_desc, ARRAY_SIZE(ts72xx_io_desc));
+       /* Factory TS-72xx boards only come with 32MiB or 128MiB NAND options */
+       if (size == SZ_32M || size == SZ_128M) {
+               /* Set the "Linux" partition size */
+               ts72xx_nand_parts[1].size = size - TS72XX_REDBOOT_PART_SIZE;
 
-       /*
-        * The TS-7200 has NOR flash, the other models have NAND flash.
-        */
-       if (!board_is_ts7200()) {
-               if (is_ts9420_installed()) {
-                       iotable_init(ts72xx_alternate_nand_io_desc,
-                               ARRAY_SIZE(ts72xx_alternate_nand_io_desc));
-               } else {
-                       iotable_init(ts72xx_nand_io_desc,
-                               ARRAY_SIZE(ts72xx_nand_io_desc));
-               }
+               chip->partitions = ts72xx_nand_parts;
+               chip->nr_partitions = ARRAY_SIZE(ts72xx_nand_parts);
+       } else {
+               pr_warning("Unknown nand disk size:%lluMiB\n", size >> 20);
        }
 }
 
+static struct platform_nand_data ts72xx_nand_data = {
+       .chip = {
+               .nr_chips       = 1,
+               .chip_offset    = 0,
+               .chip_delay     = 15,
+               .part_probe_types = ts72xx_nand_part_probes,
+               .set_parts      = ts72xx_nand_set_parts,
+       },
+       .ctrl = {
+               .cmd_ctrl       = ts72xx_nand_hwcontrol,
+               .dev_ready      = ts72xx_nand_device_ready,
+       },
+};
+
+static struct resource ts72xx_nand_resource[] = {
+       {
+               .start          = 0,                    /* filled in later */
+               .end            = 0,                    /* filled in later */
+               .flags          = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device ts72xx_nand_flash = {
+       .name                   = "gen_nand",
+       .id                     = -1,
+       .dev.platform_data      = &ts72xx_nand_data,
+       .resource               = ts72xx_nand_resource,
+       .num_resources          = ARRAY_SIZE(ts72xx_nand_resource),
+};
+
+
 /*************************************************************************
  * NOR flash (TS-7200 only)
  *************************************************************************/
-static struct physmap_flash_data ts72xx_flash_data = {
+static struct physmap_flash_data ts72xx_nor_data = {
        .width          = 2,
 };
 
-static struct resource ts72xx_flash_resource = {
+static struct resource ts72xx_nor_resource = {
        .start          = EP93XX_CS6_PHYS_BASE,
        .end            = EP93XX_CS6_PHYS_BASE + SZ_16M - 1,
        .flags          = IORESOURCE_MEM,
 };
 
-static struct platform_device ts72xx_flash = {
-       .name           = "physmap-flash",
-       .id             = 0,
-       .dev            = {
-               .platform_data  = &ts72xx_flash_data,
-       },
-       .num_resources  = 1,
-       .resource       = &ts72xx_flash_resource,
+static struct platform_device ts72xx_nor_flash = {
+       .name                   = "physmap-flash",
+       .id                     = 0,
+       .dev.platform_data      = &ts72xx_nor_data,
+       .resource               = &ts72xx_nor_resource,
+       .num_resources          = 1,
 };
 
 static void __init ts72xx_register_flash(void)
 {
-       if (board_is_ts7200())
-               platform_device_register(&ts72xx_flash);
+       if (board_is_ts7200()) {
+               platform_device_register(&ts72xx_nor_flash);
+       } else {
+               resource_size_t start;
+
+               if (is_ts9420_installed())
+                       start = EP93XX_CS7_PHYS_BASE;
+               else
+                       start = EP93XX_CS6_PHYS_BASE;
+
+               ts72xx_nand_resource[0].start = start;
+               ts72xx_nand_resource[0].end = start + SZ_16M - 1;
+
+               platform_device_register(&ts72xx_nand_flash);
+       }
 }
 
+
 static unsigned char ts72xx_rtc_readbyte(unsigned long addr)
 {
        __raw_writeb(addr, TS72XX_RTC_INDEX_VIRT_BASE);
index ecf90f5..dbee14d 100644 (file)
@@ -304,6 +304,27 @@ config SSFDC
          This enables read only access to SmartMedia formatted NAND
          flash. You can mount it with FAT file system.
 
+
+config SM_FTL
+       tristate "SmartMedia/xD new translation layer"
+       depends on EXPERIMENTAL && BLOCK && MTD_NAND
+       select MTD_BLKDEVS
+       help
+         This enables new and very EXPERMENTAL support for SmartMedia/xD
+         FTL (Flash translation layer).
+         Write support isn't yet well tested, therefore this code IS likely to
+         eat your card, so please don't use it together with valuable data.
+         Use readonly driver (CONFIG_SSFDC) instead.
+
+config SM_FTL_MUSEUM
+       boolean "Additional Support for 1MiB and 2MiB SmartMedia cards"
+       depends on SM_FTL
+       select MTD_NAND_ECC_SMC
+       help
+         Very old SmartMedia cards need ECC to be calculated in the FTL.
+         Such cards are very rare, thus enabling this option is mostly useless.
+         Also this support is completely UNTESTED.
+
 config MTD_OOPS
        tristate "Log panic/oops to an MTD buffer"
        depends on MTD
index 82d1e4d..d53357b 100644 (file)
@@ -24,6 +24,7 @@ obj-$(CONFIG_NFTL)            += nftl.o
 obj-$(CONFIG_INFTL)            += inftl.o
 obj-$(CONFIG_RFD_FTL)          += rfd_ftl.o
 obj-$(CONFIG_SSFDC)            += ssfdc.o
+obj-$(CONFIG_SM_FTL)           += sm_ftl.o
 obj-$(CONFIG_MTD_OOPS)         += mtdoops.o
 
 nftl-objs              := nftlcore.o nftlmount.o
index 5fbf29e..9253043 100644 (file)
@@ -615,10 +615,8 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
        return mtd;
 
  setup_err:
-       if(mtd) {
-               kfree(mtd->eraseregions);
-               kfree(mtd);
-       }
+       kfree(mtd->eraseregions);
+       kfree(mtd);
        kfree(cfi->cmdset_priv);
        return NULL;
 }
index f3600e8..ea2a7f6 100644 (file)
 
 #define MAX_WORD_RETRIES 3
 
-#define MANUFACTURER_AMD       0x0001
-#define MANUFACTURER_ATMEL     0x001F
-#define MANUFACTURER_MACRONIX  0x00C2
-#define MANUFACTURER_SST       0x00BF
 #define SST49LF004B            0x0060
 #define SST49LF040B            0x0050
 #define SST49LF008A            0x005a
@@ -168,7 +164,7 @@ static void fixup_amd_bootblock(struct mtd_info *mtd, void* param)
                         * This reduces the risk of false detection due to
                         * the 8-bit device ID.
                         */
-                       (cfi->mfr == MANUFACTURER_MACRONIX)) {
+                       (cfi->mfr == CFI_MFR_MACRONIX)) {
                        DEBUG(MTD_DEBUG_LEVEL1,
                                "%s: Macronix MX29LV400C with bottom boot block"
                                " detected\n", map->name);
@@ -286,7 +282,7 @@ static struct cfi_fixup cfi_fixup_table[] = {
        { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL },
 #ifdef AMD_BOOTLOC_BUG
        { CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock, NULL },
-       { MANUFACTURER_MACRONIX, CFI_ID_ANY, fixup_amd_bootblock, NULL },
+       { CFI_MFR_MACRONIX, CFI_ID_ANY, fixup_amd_bootblock, NULL },
 #endif
        { CFI_MFR_AMD, 0x0050, fixup_use_secsi, NULL, },
        { CFI_MFR_AMD, 0x0053, fixup_use_secsi, NULL, },
@@ -304,9 +300,9 @@ static struct cfi_fixup cfi_fixup_table[] = {
        { 0, 0, NULL, NULL }
 };
 static struct cfi_fixup jedec_fixup_table[] = {
-       { MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, },
-       { MANUFACTURER_SST, SST49LF040B, fixup_use_fwh_lock, NULL, },
-       { MANUFACTURER_SST, SST49LF008A, fixup_use_fwh_lock, NULL, },
+       { CFI_MFR_SST, SST49LF004B, fixup_use_fwh_lock, NULL, },
+       { CFI_MFR_SST, SST49LF040B, fixup_use_fwh_lock, NULL, },
+       { CFI_MFR_SST, SST49LF008A, fixup_use_fwh_lock, NULL, },
        { 0, 0, NULL, NULL }
 };
 
@@ -494,10 +490,8 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
        return mtd;
 
  setup_err:
-       if(mtd) {
-               kfree(mtd->eraseregions);
-               kfree(mtd);
-       }
+       kfree(mtd->eraseregions);
+       kfree(mtd);
        kfree(cfi->cmdset_priv);
        kfree(cfi->cfiq);
        return NULL;
index ab5c9b9..f3226b1 100644 (file)
@@ -1,5 +1,5 @@
 #
-# linux/drivers/devices/Makefile
+# linux/drivers/mtd/devices/Makefile
 #
 
 obj-$(CONFIG_MTD_DOC2000)      += doc2000.o
index 8c295f4..4281f3e 100644 (file)
@@ -275,12 +275,10 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
 
        /* Setup the MTD structure */
        /* make the name contain the block device in */
-       name = kmalloc(sizeof("block2mtd: ") + strlen(devname) + 1,
-                       GFP_KERNEL);
+       name = kasprintf(GFP_KERNEL, "block2mtd: %s", devname);
        if (!name)
                goto devinit_err;
 
-       sprintf(name, "block2mtd: %s", devname);
        dev->mtd.name = name;
 
        dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK;
index d2fd550..fc8ea0a 100644 (file)
@@ -668,7 +668,7 @@ static int __init init_pmc551(void)
 {
        struct pci_dev *PCI_Device = NULL;
        struct mypriv *priv;
-       int count, found = 0;
+       int found = 0;
        struct mtd_info *mtd;
        u32 length = 0;
 
@@ -695,7 +695,7 @@ static int __init init_pmc551(void)
        /*
         * PCU-bus chipset probe.
         */
-       for (count = 0; count < MAX_MTD_DEVICES; count++) {
+       for (;;) {
 
                if ((PCI_Device = pci_get_device(PCI_VENDOR_ID_V3_SEMI,
                                                  PCI_DEVICE_ID_V3_SEMI_V370PDC,
index e56d6b4..62da9eb 100644 (file)
@@ -1082,7 +1082,6 @@ static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
 {
        del_mtd_blktrans_dev(dev);
        ftl_freepart((partition_t *)dev);
-       kfree(dev);
 }
 
 static struct mtd_blktrans_ops ftl_tr = {
index 8aca552..015a7fe 100644 (file)
@@ -139,7 +139,6 @@ static void inftl_remove_dev(struct mtd_blktrans_dev *dev)
 
        kfree(inftl->PUtable);
        kfree(inftl->VUtable);
-       kfree(inftl);
 }
 
 /*
index 32e82ae..8f988d7 100644 (file)
@@ -100,9 +100,10 @@ static int find_boot_record(struct INFTLrecord *inftl)
                }
 
                /* To be safer with BIOS, also use erase mark as discriminant */
-               if ((ret = inftl_read_oob(mtd, block * inftl->EraseSize +
-                                         SECTORSIZE + 8, 8, &retlen,
-                                         (char *)&h1) < 0)) {
+               ret = inftl_read_oob(mtd,
+                                    block * inftl->EraseSize + SECTORSIZE + 8,
+                                    8, &retlen,(char *)&h1);
+               if (ret < 0) {
                        printk(KERN_WARNING "INFTL: ANAND header found at "
                                "0x%x in mtd%d, but OOB data read failed "
                                "(err %d)\n", block * inftl->EraseSize,
index a7c808b..5824fd4 100644 (file)
@@ -69,7 +69,7 @@ static void switch_back(struct async_state *state)
        local_irq_restore(state->irq_flags);
 }
 
-static map_word bfin_read(struct map_info *map, unsigned long ofs)
+static map_word bfin_flash_read(struct map_info *map, unsigned long ofs)
 {
        struct async_state *state = (struct async_state *)map->map_priv_1;
        uint16_t word;
@@ -85,7 +85,7 @@ static map_word bfin_read(struct map_info *map, unsigned long ofs)
        return test;
 }
 
-static void bfin_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+static void bfin_flash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
 {
        struct async_state *state = (struct async_state *)map->map_priv_1;
 
@@ -96,7 +96,7 @@ static void bfin_copy_from(struct map_info *map, void *to, unsigned long from, s
        switch_back(state);
 }
 
-static void bfin_write(struct map_info *map, map_word d1, unsigned long ofs)
+static void bfin_flash_write(struct map_info *map, map_word d1, unsigned long ofs)
 {
        struct async_state *state = (struct async_state *)map->map_priv_1;
        uint16_t d;
@@ -111,7 +111,7 @@ static void bfin_write(struct map_info *map, map_word d1, unsigned long ofs)
        switch_back(state);
 }
 
-static void bfin_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+static void bfin_flash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
 {
        struct async_state *state = (struct async_state *)map->map_priv_1;
 
@@ -140,10 +140,10 @@ static int __devinit bfin_flash_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        state->map.name       = DRIVER_NAME;
-       state->map.read       = bfin_read;
-       state->map.copy_from  = bfin_copy_from;
-       state->map.write      = bfin_write;
-       state->map.copy_to    = bfin_copy_to;
+       state->map.read       = bfin_flash_read;
+       state->map.copy_from  = bfin_flash_copy_from;
+       state->map.write      = bfin_flash_write;
+       state->map.copy_to    = bfin_flash_copy_to;
        state->map.bankwidth  = pdata->width;
        state->map.size       = memory->end - memory->start + 1;
        state->map.virt       = (void __iomem *)memory->start;
index d41f347..c09f4f5 100644 (file)
@@ -253,7 +253,7 @@ static void __exit clps_destroy_mtd(struct clps_info *clps, struct mtd_info *mtd
 
 static int __init clps_setup_flash(void)
 {
-       int nr;
+       int nr = 0;
 
 #ifdef CONFIG_ARCH_CEIVA
        if (machine_is_ceiva()) {
index 7b05152..7513d90 100644 (file)
@@ -107,8 +107,8 @@ static void ixp4xx_copy_from(struct map_info *map, void *to,
                return;
 
        if (from & 1) {
-               *dest++ = BYTE1(flash_read16(src));
-                src++;
+               *dest++ = BYTE1(flash_read16(src-1));
+               src++;
                --len;
        }
 
index 61e4eb4..1d91333 100644 (file)
@@ -217,7 +217,7 @@ static int __devinit of_flash_probe(struct of_device *dev,
 
        dev_set_drvdata(&dev->dev, info);
 
-       mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL);
+       mtd_list = kzalloc(sizeof(*mtd_list) * count, GFP_KERNEL);
        if (!mtd_list)
                goto err_flash_remove;
 
index 30e12c8..f021018 100644 (file)
@@ -233,6 +233,7 @@ static int __devexit pismo_remove(struct i2c_client *client)
        /* FIXME: set_vpp needs saner arguments */
        pismo_setvpp_remove_fix(pismo);
 
+       i2c_set_clientdata(client, NULL);
        kfree(pismo);
 
        return 0;
@@ -271,7 +272,7 @@ static int __devinit pismo_probe(struct i2c_client *client,
        ret = pismo_eeprom_read(client, &eeprom, 0, sizeof(eeprom));
        if (ret < 0) {
                dev_err(&client->dev, "error reading EEPROM: %d\n", ret);
-               return ret;
+               goto exit_free;
        }
 
        dev_info(&client->dev, "%.15s board found\n", eeprom.board);
@@ -282,6 +283,11 @@ static int __devinit pismo_probe(struct i2c_client *client,
                                      pdata->cs_addrs[i]);
 
        return 0;
+
+ exit_free:
+       i2c_set_clientdata(client, NULL);
+       kfree(pismo);
+       return ret;
 }
 
 static const struct i2c_device_id pismo_id[] = {
index c82e09b..03e19c1 100644 (file)
@@ -14,7 +14,6 @@
 #include <linux/mtd/mtd.h>
 #include <linux/blkdev.h>
 #include <linux/blkpg.h>
-#include <linux/freezer.h>
 #include <linux/spinlock.h>
 #include <linux/hdreg.h>
 #include <linux/init.h>
 #include "mtdcore.h"
 
 static LIST_HEAD(blktrans_majors);
+static DEFINE_MUTEX(blktrans_ref_mutex);
+
+void blktrans_dev_release(struct kref *kref)
+{
+       struct mtd_blktrans_dev *dev =
+               container_of(kref, struct mtd_blktrans_dev, ref);
+
+       dev->disk->private_data = NULL;
+       blk_cleanup_queue(dev->rq);
+       put_disk(dev->disk);
+       list_del(&dev->list);
+       kfree(dev);
+}
+
+static struct mtd_blktrans_dev *blktrans_dev_get(struct gendisk *disk)
+{
+       struct mtd_blktrans_dev *dev;
+
+       mutex_lock(&blktrans_ref_mutex);
+       dev = disk->private_data;
+
+       if (!dev)
+               goto unlock;
+       kref_get(&dev->ref);
+unlock:
+       mutex_unlock(&blktrans_ref_mutex);
+       return dev;
+}
+
+void blktrans_dev_put(struct mtd_blktrans_dev *dev)
+{
+       mutex_lock(&blktrans_ref_mutex);
+       kref_put(&dev->ref, blktrans_dev_release);
+       mutex_unlock(&blktrans_ref_mutex);
+}
 
-struct mtd_blkcore_priv {
-       struct task_struct *thread;
-       struct request_queue *rq;
-       spinlock_t queue_lock;
-};
 
 static int do_blktrans_request(struct mtd_blktrans_ops *tr,
                               struct mtd_blktrans_dev *dev,
@@ -61,7 +90,6 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
                                return -EIO;
                rq_flush_dcache_pages(req);
                return 0;
-
        case WRITE:
                if (!tr->writesect)
                        return -EIO;
@@ -71,7 +99,6 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
                        if (tr->writesect(dev, block, buf))
                                return -EIO;
                return 0;
-
        default:
                printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req));
                return -EIO;
@@ -80,14 +107,13 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
 
 static int mtd_blktrans_thread(void *arg)
 {
-       struct mtd_blktrans_ops *tr = arg;
-       struct request_queue *rq = tr->blkcore_priv->rq;
+       struct mtd_blktrans_dev *dev = arg;
+       struct request_queue *rq = dev->rq;
        struct request *req = NULL;
 
        spin_lock_irq(rq->queue_lock);
 
        while (!kthread_should_stop()) {
-               struct mtd_blktrans_dev *dev;
                int res;
 
                if (!req && !(req = blk_fetch_request(rq))) {
@@ -98,13 +124,10 @@ static int mtd_blktrans_thread(void *arg)
                        continue;
                }
 
-               dev = req->rq_disk->private_data;
-               tr = dev->tr;
-
                spin_unlock_irq(rq->queue_lock);
 
                mutex_lock(&dev->lock);
-               res = do_blktrans_request(tr, dev, req);
+               res = do_blktrans_request(dev->tr, dev, req);
                mutex_unlock(&dev->lock);
 
                spin_lock_irq(rq->queue_lock);
@@ -123,81 +146,112 @@ static int mtd_blktrans_thread(void *arg)
 
 static void mtd_blktrans_request(struct request_queue *rq)
 {
-       struct mtd_blktrans_ops *tr = rq->queuedata;
-       wake_up_process(tr->blkcore_priv->thread);
-}
+       struct mtd_blktrans_dev *dev;
+       struct request *req = NULL;
+
+       dev = rq->queuedata;
 
+       if (!dev)
+               while ((req = blk_fetch_request(rq)) != NULL)
+                       __blk_end_request_all(req, -ENODEV);
+       else
+               wake_up_process(dev->thread);
+}
 
 static int blktrans_open(struct block_device *bdev, fmode_t mode)
 {
-       struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data;
-       struct mtd_blktrans_ops *tr = dev->tr;
-       int ret = -ENODEV;
-
-       if (!get_mtd_device(NULL, dev->mtd->index))
-               goto out;
-
-       if (!try_module_get(tr->owner))
-               goto out_tr;
-
-       /* FIXME: Locking. A hot pluggable device can go away
-          (del_mtd_device can be called for it) without its module
-          being unloaded. */
-       dev->mtd->usecount++;
-
-       ret = 0;
-       if (tr->open && (ret = tr->open(dev))) {
-               dev->mtd->usecount--;
-               put_mtd_device(dev->mtd);
-       out_tr:
-               module_put(tr->owner);
+       struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
+       int ret;
+
+       if (!dev)
+               return -ERESTARTSYS;
+
+       mutex_lock(&dev->lock);
+
+       if (!dev->mtd) {
+               ret = -ENXIO;
+               goto unlock;
        }
- out:
+
+       ret = !dev->open++ && dev->tr->open ? dev->tr->open(dev) : 0;
+
+       /* Take another reference on the device so it won't go away till
+               last release */
+       if (!ret)
+               kref_get(&dev->ref);
+unlock:
+       mutex_unlock(&dev->lock);
+       blktrans_dev_put(dev);
        return ret;
 }
 
 static int blktrans_release(struct gendisk *disk, fmode_t mode)
 {
-       struct mtd_blktrans_dev *dev = disk->private_data;
-       struct mtd_blktrans_ops *tr = dev->tr;
-       int ret = 0;
+       struct mtd_blktrans_dev *dev = blktrans_dev_get(disk);
+       int ret = -ENXIO;
 
-       if (tr->release)
-               ret = tr->release(dev);
+       if (!dev)
+               return ret;
 
-       if (!ret) {
-               dev->mtd->usecount--;
-               put_mtd_device(dev->mtd);
-               module_put(tr->owner);
-       }
+       mutex_lock(&dev->lock);
+
+       /* Release one reference, we sure its not the last one here*/
+       kref_put(&dev->ref, blktrans_dev_release);
 
+       if (!dev->mtd)
+               goto unlock;
+
+       ret = !--dev->open && dev->tr->release ? dev->tr->release(dev) : 0;
+unlock:
+       mutex_unlock(&dev->lock);
+       blktrans_dev_put(dev);
        return ret;
 }
 
 static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo)
 {
-       struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data;
+       struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
+       int ret = -ENXIO;
+
+       if (!dev)
+               return ret;
+
+       mutex_lock(&dev->lock);
+
+       if (!dev->mtd)
+               goto unlock;
 
-       if (dev->tr->getgeo)
-               return dev->tr->getgeo(dev, geo);
-       return -ENOTTY;
+       ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : 0;
+unlock:
+       mutex_unlock(&dev->lock);
+       blktrans_dev_put(dev);
+       return ret;
 }
 
 static int blktrans_ioctl(struct block_device *bdev, fmode_t mode,
                              unsigned int cmd, unsigned long arg)
 {
-       struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data;
-       struct mtd_blktrans_ops *tr = dev->tr;
+       struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
+       int ret = -ENXIO;
+
+       if (!dev)
+               return ret;
+
+       mutex_lock(&dev->lock);
+
+       if (!dev->mtd)
+               goto unlock;
 
        switch (cmd) {
        case BLKFLSBUF:
-               if (tr->flush)
-                       return tr->flush(dev);
-               /* The core code did the work, we had nothing to do. */
-               return 0;
+               ret = dev->tr->flush ? dev->tr->flush(dev) : 0;
        default:
-               return -ENOTTY;
+               ret = -ENOTTY;
        }
+unlock:
+       mutex_unlock(&dev->lock);
+       blktrans_dev_put(dev);
+       return ret;
 }
 
 static const struct block_device_operations mtd_blktrans_ops = {
@@ -214,12 +268,14 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
        struct mtd_blktrans_dev *d;
        int last_devnum = -1;
        struct gendisk *gd;
+       int ret;
 
        if (mutex_trylock(&mtd_table_mutex)) {
                mutex_unlock(&mtd_table_mutex);
                BUG();
        }
 
+       mutex_lock(&blktrans_ref_mutex);
        list_for_each_entry(d, &tr->devs, list) {
                if (new->devnum == -1) {
                        /* Use first free number */
@@ -231,6 +287,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
                        }
                } else if (d->devnum == new->devnum) {
                        /* Required number taken */
+                       mutex_unlock(&blktrans_ref_mutex);
                        return -EBUSY;
                } else if (d->devnum > new->devnum) {
                        /* Required number was free */
@@ -239,24 +296,38 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
                }
                last_devnum = d->devnum;
        }
+
+       ret = -EBUSY;
        if (new->devnum == -1)
                new->devnum = last_devnum+1;
 
-       if ((new->devnum << tr->part_bits) > 256) {
-               return -EBUSY;
+       /* Check that the device and any partitions will get valid
+        * minor numbers and that the disk naming code below can cope
+        * with this number. */
+       if (new->devnum > (MINORMASK >> tr->part_bits) ||
+           (tr->part_bits && new->devnum >= 27 * 26)) {
+               mutex_unlock(&blktrans_ref_mutex);
+               goto error1;
        }
 
        list_add_tail(&new->list, &tr->devs);
  added:
+       mutex_unlock(&blktrans_ref_mutex);
+
        mutex_init(&new->lock);
+       kref_init(&new->ref);
        if (!tr->writesect)
                new->readonly = 1;
 
+       /* Create gendisk */
+       ret = -ENOMEM;
        gd = alloc_disk(1 << tr->part_bits);
-       if (!gd) {
-               list_del(&new->list);
-               return -ENOMEM;
-       }
+
+       if (!gd)
+               goto error2;
+
+       new->disk = gd;
+       gd->private_data = new;
        gd->major = tr->major;
        gd->first_minor = (new->devnum) << tr->part_bits;
        gd->fops = &mtd_blktrans_ops;
@@ -274,13 +345,35 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
                snprintf(gd->disk_name, sizeof(gd->disk_name),
                         "%s%d", tr->name, new->devnum);
 
-       /* 2.5 has capacity in units of 512 bytes while still
-          having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */
        set_capacity(gd, (new->size * tr->blksize) >> 9);
 
-       gd->private_data = new;
-       new->blkcore_priv = gd;
-       gd->queue = tr->blkcore_priv->rq;
+       /* Create the request queue */
+       spin_lock_init(&new->queue_lock);
+       new->rq = blk_init_queue(mtd_blktrans_request, &new->queue_lock);
+
+       if (!new->rq)
+               goto error3;
+
+       new->rq->queuedata = new;
+       blk_queue_logical_block_size(new->rq, tr->blksize);
+
+       if (tr->discard)
+               queue_flag_set_unlocked(QUEUE_FLAG_DISCARD,
+                                       new->rq);
+
+       gd->queue = new->rq;
+
+       __get_mtd_device(new->mtd);
+       __module_get(tr->owner);
+
+       /* Create processing thread */
+       /* TODO: workqueue ? */
+       new->thread = kthread_run(mtd_blktrans_thread, new,
+                       "%s%d", tr->name, new->mtd->index);
+       if (IS_ERR(new->thread)) {
+               ret = PTR_ERR(new->thread);
+               goto error4;
+       }
        gd->driverfs_dev = &new->mtd->dev;
 
        if (new->readonly)
@@ -288,21 +381,65 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
 
        add_disk(gd);
 
+       if (new->disk_attributes) {
+               ret = sysfs_create_group(&disk_to_dev(gd)->kobj,
+                                       new->disk_attributes);
+               WARN_ON(ret);
+       }
        return 0;
+error4:
+       module_put(tr->owner);
+       __put_mtd_device(new->mtd);
+       blk_cleanup_queue(new->rq);
+error3:
+       put_disk(new->disk);
+error2:
+       list_del(&new->list);
+error1:
+       kfree(new);
+       return ret;
 }
 
 int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
 {
+       unsigned long flags;
+
        if (mutex_trylock(&mtd_table_mutex)) {
                mutex_unlock(&mtd_table_mutex);
                BUG();
        }
 
-       list_del(&old->list);
+       /* Stop new requests to arrive */
+       del_gendisk(old->disk);
+
+       if (old->disk_attributes)
+               sysfs_remove_group(&disk_to_dev(old->disk)->kobj,
+                                               old->disk_attributes);
+
+       /* Stop the thread */
+       kthread_stop(old->thread);
+
+       /* Kill current requests */
+       spin_lock_irqsave(&old->queue_lock, flags);
+       old->rq->queuedata = NULL;
+       blk_start_queue(old->rq);
+       spin_unlock_irqrestore(&old->queue_lock, flags);
+
+       /* Ask trans driver for release to the mtd device */
+       mutex_lock(&old->lock);
+       if (old->open && old->tr->release) {
+               old->tr->release(old);
+               old->open = 0;
+       }
+
+       __put_mtd_device(old->mtd);
+       module_put(old->tr->owner);
 
-       del_gendisk(old->blkcore_priv);
-       put_disk(old->blkcore_priv);
+       /* At that point, we don't touch the mtd anymore */
+       old->mtd = NULL;
 
+       mutex_unlock(&old->lock);
+       blktrans_dev_put(old);
        return 0;
 }
 
@@ -335,7 +472,8 @@ static struct mtd_notifier blktrans_notifier = {
 
 int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
 {
-       int ret, i;
+       struct mtd_info *mtd;
+       int ret;
 
        /* Register the notifier if/when the first device type is
           registered, to prevent the link/init ordering from fucking
@@ -343,9 +481,6 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
        if (!blktrans_notifier.list.next)
                register_mtd_user(&blktrans_notifier);
 
-       tr->blkcore_priv = kzalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL);
-       if (!tr->blkcore_priv)
-               return -ENOMEM;
 
        mutex_lock(&mtd_table_mutex);
 
@@ -353,49 +488,20 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
        if (ret) {
                printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
                       tr->name, tr->major, ret);
-               kfree(tr->blkcore_priv);
                mutex_unlock(&mtd_table_mutex);
                return ret;
        }
-       spin_lock_init(&tr->blkcore_priv->queue_lock);
-
-       tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
-       if (!tr->blkcore_priv->rq) {
-               unregister_blkdev(tr->major, tr->name);
-               kfree(tr->blkcore_priv);
-               mutex_unlock(&mtd_table_mutex);
-               return -ENOMEM;
-       }
-
-       tr->blkcore_priv->rq->queuedata = tr;
-       blk_queue_logical_block_size(tr->blkcore_priv->rq, tr->blksize);
-       if (tr->discard)
-               queue_flag_set_unlocked(QUEUE_FLAG_DISCARD,
-                                       tr->blkcore_priv->rq);
 
        tr->blkshift = ffs(tr->blksize) - 1;
 
-       tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
-                       "%sd", tr->name);
-       if (IS_ERR(tr->blkcore_priv->thread)) {
-               ret = PTR_ERR(tr->blkcore_priv->thread);
-               blk_cleanup_queue(tr->blkcore_priv->rq);
-               unregister_blkdev(tr->major, tr->name);
-               kfree(tr->blkcore_priv);
-               mutex_unlock(&mtd_table_mutex);
-               return ret;
-       }
-
        INIT_LIST_HEAD(&tr->devs);
        list_add(&tr->list, &blktrans_majors);
 
-       for (i=0; i<MAX_MTD_DEVICES; i++) {
-               if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT)
-                       tr->add_mtd(tr, mtd_table[i]);
-       }
+       mtd_for_each_device(mtd)
+               if (mtd->type != MTD_ABSENT)
+                       tr->add_mtd(tr, mtd);
 
        mutex_unlock(&mtd_table_mutex);
-
        return 0;
 }
 
@@ -405,22 +511,15 @@ int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
 
        mutex_lock(&mtd_table_mutex);
 
-       /* Clean up the kernel thread */
-       kthread_stop(tr->blkcore_priv->thread);
-
        /* Remove it from the list of active majors */
        list_del(&tr->list);
 
        list_for_each_entry_safe(dev, next, &tr->devs, list)
                tr->remove_dev(dev);
 
-       blk_cleanup_queue(tr->blkcore_priv->rq);
        unregister_blkdev(tr->major, tr->name);
-
        mutex_unlock(&mtd_table_mutex);
 
-       kfree(tr->blkcore_priv);
-
        BUG_ON(!list_empty(&tr->devs));
        return 0;
 }
index 9f41b1a..e6edbec 100644 (file)
 #include <linux/mutex.h>
 
 
-static struct mtdblk_dev {
-       struct mtd_info *mtd;
+struct mtdblk_dev {
+       struct mtd_blktrans_dev mbd;
        int count;
        struct mutex cache_mutex;
        unsigned char *cache_data;
        unsigned long cache_offset;
        unsigned int cache_size;
        enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;
-} *mtdblks[MAX_MTD_DEVICES];
+};
 
 static struct mutex mtdblks_lock;
 
@@ -98,7 +98,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos,
 
 static int write_cached_data (struct mtdblk_dev *mtdblk)
 {
-       struct mtd_info *mtd = mtdblk->mtd;
+       struct mtd_info *mtd = mtdblk->mbd.mtd;
        int ret;
 
        if (mtdblk->cache_state != STATE_DIRTY)
@@ -128,7 +128,7 @@ static int write_cached_data (struct mtdblk_dev *mtdblk)
 static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos,
                            int len, const char *buf)
 {
-       struct mtd_info *mtd = mtdblk->mtd;
+       struct mtd_info *mtd = mtdblk->mbd.mtd;
        unsigned int sect_size = mtdblk->cache_size;
        size_t retlen;
        int ret;
@@ -198,7 +198,7 @@ static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos,
 static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos,
                           int len, char *buf)
 {
-       struct mtd_info *mtd = mtdblk->mtd;
+       struct mtd_info *mtd = mtdblk->mbd.mtd;
        unsigned int sect_size = mtdblk->cache_size;
        size_t retlen;
        int ret;
@@ -244,16 +244,16 @@ static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos,
 static int mtdblock_readsect(struct mtd_blktrans_dev *dev,
                              unsigned long block, char *buf)
 {
-       struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
+       struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd);
        return do_cached_read(mtdblk, block<<9, 512, buf);
 }
 
 static int mtdblock_writesect(struct mtd_blktrans_dev *dev,
                              unsigned long block, char *buf)
 {
-       struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
+       struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd);
        if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) {
-               mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize);
+               mtdblk->cache_data = vmalloc(mtdblk->mbd.mtd->erasesize);
                if (!mtdblk->cache_data)
                        return -EINTR;
                /* -EINTR is not really correct, but it is the best match
@@ -266,37 +266,26 @@ static int mtdblock_writesect(struct mtd_blktrans_dev *dev,
 
 static int mtdblock_open(struct mtd_blktrans_dev *mbd)
 {
-       struct mtdblk_dev *mtdblk;
-       struct mtd_info *mtd = mbd->mtd;
-       int dev = mbd->devnum;
+       struct mtdblk_dev *mtdblk = container_of(mbd, struct mtdblk_dev, mbd);
 
        DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n");
 
        mutex_lock(&mtdblks_lock);
-       if (mtdblks[dev]) {
-               mtdblks[dev]->count++;
+       if (mtdblk->count) {
+               mtdblk->count++;
                mutex_unlock(&mtdblks_lock);
                return 0;
        }
 
        /* OK, it's not open. Create cache info for it */
-       mtdblk = kzalloc(sizeof(struct mtdblk_dev), GFP_KERNEL);
-       if (!mtdblk) {
-               mutex_unlock(&mtdblks_lock);
-               return -ENOMEM;
-       }
-
        mtdblk->count = 1;
-       mtdblk->mtd = mtd;
-
        mutex_init(&mtdblk->cache_mutex);
        mtdblk->cache_state = STATE_EMPTY;
-       if ( !(mtdblk->mtd->flags & MTD_NO_ERASE) && mtdblk->mtd->erasesize) {
-               mtdblk->cache_size = mtdblk->mtd->erasesize;
+       if (!(mbd->mtd->flags & MTD_NO_ERASE) && mbd->mtd->erasesize) {
+               mtdblk->cache_size = mbd->mtd->erasesize;
                mtdblk->cache_data = NULL;
        }
 
-       mtdblks[dev] = mtdblk;
        mutex_unlock(&mtdblks_lock);
 
        DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
@@ -306,8 +295,7 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd)
 
 static int mtdblock_release(struct mtd_blktrans_dev *mbd)
 {
-       int dev = mbd->devnum;
-       struct mtdblk_dev *mtdblk = mtdblks[dev];
+       struct mtdblk_dev *mtdblk = container_of(mbd, struct mtdblk_dev, mbd);
 
        DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n");
 
@@ -318,12 +306,10 @@ static int mtdblock_release(struct mtd_blktrans_dev *mbd)
        mutex_unlock(&mtdblk->cache_mutex);
 
        if (!--mtdblk->count) {
-               /* It was the last usage. Free the device */
-               mtdblks[dev] = NULL;
-               if (mtdblk->mtd->sync)
-                       mtdblk->mtd->sync(mtdblk->mtd);
+               /* It was the last usage. Free the cache */
+               if (mbd->mtd->sync)
+                       mbd->mtd->sync(mbd->mtd);
                vfree(mtdblk->cache_data);
-               kfree(mtdblk);
        }
 
        mutex_unlock(&mtdblks_lock);
@@ -335,40 +321,40 @@ static int mtdblock_release(struct mtd_blktrans_dev *mbd)
 
 static int mtdblock_flush(struct mtd_blktrans_dev *dev)
 {
-       struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
+       struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd);
 
        mutex_lock(&mtdblk->cache_mutex);
        write_cached_data(mtdblk);
        mutex_unlock(&mtdblk->cache_mutex);
 
-       if (mtdblk->mtd->sync)
-               mtdblk->mtd->sync(mtdblk->mtd);
+       if (dev->mtd->sync)
+               dev->mtd->sync(dev->mtd);
        return 0;
 }
 
 static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 {
-       struct mtd_blktrans_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       struct mtdblk_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 
        if (!dev)
                return;
 
-       dev->mtd = mtd;
-       dev->devnum = mtd->index;
+       dev->mbd.mtd = mtd;
+       dev->mbd.devnum = mtd->index;
 
-       dev->size = mtd->size >> 9;
-       dev->tr = tr;
+       dev->mbd.size = mtd->size >> 9;
+       dev->mbd.tr = tr;
 
        if (!(mtd->flags & MTD_WRITEABLE))
-               dev->readonly = 1;
+               dev->mbd.readonly = 1;
 
-       add_mtd_blktrans_dev(dev);
+       if (add_mtd_blktrans_dev(&dev->mbd))
+               kfree(dev);
 }
 
 static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev)
 {
        del_mtd_blktrans_dev(dev);
-       kfree(dev);
 }
 
 static struct mtd_blktrans_ops mtdblock_tr = {
index 852165f..d0d3f79 100644 (file)
@@ -43,13 +43,13 @@ static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
        dev->tr = tr;
        dev->readonly = 1;
 
-       add_mtd_blktrans_dev(dev);
+       if (add_mtd_blktrans_dev(dev))
+               kfree(dev);
 }
 
 static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev)
 {
        del_mtd_blktrans_dev(dev);
-       kfree(dev);
 }
 
 static struct mtd_blktrans_ops mtdblock_tr = {
index 5b081cb..c355491 100644 (file)
@@ -67,9 +67,6 @@ static int mtd_open(struct inode *inode, struct file *file)
 
        DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n");
 
-       if (devnum >= MAX_MTD_DEVICES)
-               return -ENODEV;
-
        /* You can't open the RO devices RW */
        if ((file->f_mode & FMODE_WRITE) && (minor & 1))
                return -EACCES;
@@ -373,7 +370,7 @@ static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd,
        if (!mtd->write_oob)
                ret = -EOPNOTSUPP;
        else
-               ret = access_ok(VERIFY_READ, ptr, length) ? 0 : EFAULT;
+               ret = access_ok(VERIFY_READ, ptr, length) ? 0 : -EFAULT;
 
        if (ret)
                return ret;
@@ -482,7 +479,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
        {
                uint32_t ur_idx;
                struct mtd_erase_region_info *kr;
-               struct region_info_user *ur = (struct region_info_user *) argp;
+               struct region_info_user __user *ur = argp;
 
                if (get_user(ur_idx, &(ur->regionindex)))
                        return -EFAULT;
@@ -958,7 +955,8 @@ static int __init init_mtdchar(void)
 {
        int status;
 
-       status = register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops);
+       status = __register_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS,
+                                  "mtd", &mtd_fops);
        if (status < 0) {
                printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
                       MTD_CHAR_MAJOR);
@@ -969,7 +967,7 @@ static int __init init_mtdchar(void)
 
 static void __exit cleanup_mtdchar(void)
 {
-       unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
+       __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
 }
 
 module_init(init_mtdchar);
index c356c0a..70a7858 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/init.h>
 #include <linux/mtd/compatmac.h>
 #include <linux/proc_fs.h>
+#include <linux/idr.h>
 
 #include <linux/mtd/mtd.h>
 #include "internal.h"
@@ -33,13 +34,18 @@ static struct class mtd_class = {
        .resume = mtd_cls_resume,
 };
 
+static DEFINE_IDR(mtd_idr);
+
 /* These are exported solely for the purpose of mtd_blkdevs.c. You
    should not use them for _anything_ else */
 DEFINE_MUTEX(mtd_table_mutex);
-struct mtd_info *mtd_table[MAX_MTD_DEVICES];
-
 EXPORT_SYMBOL_GPL(mtd_table_mutex);
-EXPORT_SYMBOL_GPL(mtd_table);
+
+struct mtd_info *__mtd_next_device(int i)
+{
+       return idr_get_next(&mtd_idr, &i);
+}
+EXPORT_SYMBOL_GPL(__mtd_next_device);
 
 static LIST_HEAD(mtd_notifiers);
 
@@ -235,13 +241,13 @@ static struct device_type mtd_devtype = {
  *     Add a device to the list of MTD devices present in the system, and
  *     notify each currently active MTD 'user' of its arrival. Returns
  *     zero on success or 1 on failure, which currently will only happen
- *     if the number of present devices exceeds MAX_MTD_DEVICES (i.e. 16)
- *     or there's a sysfs error.
+ *     if there is insufficient memory or a sysfs error.
  */
 
 int add_mtd_device(struct mtd_info *mtd)
 {
-       int i;
+       struct mtd_notifier *not;
+       int i, error;
 
        if (!mtd->backing_dev_info) {
                switch (mtd->type) {
@@ -260,70 +266,73 @@ int add_mtd_device(struct mtd_info *mtd)
        BUG_ON(mtd->writesize == 0);
        mutex_lock(&mtd_table_mutex);
 
-       for (i=0; i < MAX_MTD_DEVICES; i++)
-               if (!mtd_table[i]) {
-                       struct mtd_notifier *not;
-
-                       mtd_table[i] = mtd;
-                       mtd->index = i;
-                       mtd->usecount = 0;
-
-                       if (is_power_of_2(mtd->erasesize))
-                               mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
-                       else
-                               mtd->erasesize_shift = 0;
-
-                       if (is_power_of_2(mtd->writesize))
-                               mtd->writesize_shift = ffs(mtd->writesize) - 1;
-                       else
-                               mtd->writesize_shift = 0;
-
-                       mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
-                       mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
-
-                       /* Some chips always power up locked. Unlock them now */
-                       if ((mtd->flags & MTD_WRITEABLE)
-                           && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) {
-                               if (mtd->unlock(mtd, 0, mtd->size))
-                                       printk(KERN_WARNING
-                                              "%s: unlock failed, "
-                                              "writes may not work\n",
-                                              mtd->name);
-                       }
+       do {
+               if (!idr_pre_get(&mtd_idr, GFP_KERNEL))
+                       goto fail_locked;
+               error = idr_get_new(&mtd_idr, mtd, &i);
+       } while (error == -EAGAIN);
 
-                       /* Caller should have set dev.parent to match the
-                        * physical device.
-                        */
-                       mtd->dev.type = &mtd_devtype;
-                       mtd->dev.class = &mtd_class;
-                       mtd->dev.devt = MTD_DEVT(i);
-                       dev_set_name(&mtd->dev, "mtd%d", i);
-                       dev_set_drvdata(&mtd->dev, mtd);
-                       if (device_register(&mtd->dev) != 0) {
-                               mtd_table[i] = NULL;
-                               break;
-                       }
+       if (error)
+               goto fail_locked;
 
-                       if (MTD_DEVT(i))
-                               device_create(&mtd_class, mtd->dev.parent,
-                                               MTD_DEVT(i) + 1,
-                                               NULL, "mtd%dro", i);
-
-                       DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name);
-                       /* No need to get a refcount on the module containing
-                          the notifier, since we hold the mtd_table_mutex */
-                       list_for_each_entry(not, &mtd_notifiers, list)
-                               not->add(mtd);
-
-                       mutex_unlock(&mtd_table_mutex);
-                       /* We _know_ we aren't being removed, because
-                          our caller is still holding us here. So none
-                          of this try_ nonsense, and no bitching about it
-                          either. :) */
-                       __module_get(THIS_MODULE);
-                       return 0;
-               }
+       mtd->index = i;
+       mtd->usecount = 0;
+
+       if (is_power_of_2(mtd->erasesize))
+               mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
+       else
+               mtd->erasesize_shift = 0;
+
+       if (is_power_of_2(mtd->writesize))
+               mtd->writesize_shift = ffs(mtd->writesize) - 1;
+       else
+               mtd->writesize_shift = 0;
+
+       mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
+       mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
+
+       /* Some chips always power up locked. Unlock them now */
+       if ((mtd->flags & MTD_WRITEABLE)
+           && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) {
+               if (mtd->unlock(mtd, 0, mtd->size))
+                       printk(KERN_WARNING
+                              "%s: unlock failed, writes may not work\n",
+                              mtd->name);
+       }
+
+       /* Caller should have set dev.parent to match the
+        * physical device.
+        */
+       mtd->dev.type = &mtd_devtype;
+       mtd->dev.class = &mtd_class;
+       mtd->dev.devt = MTD_DEVT(i);
+       dev_set_name(&mtd->dev, "mtd%d", i);
+       dev_set_drvdata(&mtd->dev, mtd);
+       if (device_register(&mtd->dev) != 0)
+               goto fail_added;
+
+       if (MTD_DEVT(i))
+               device_create(&mtd_class, mtd->dev.parent,
+                             MTD_DEVT(i) + 1,
+                             NULL, "mtd%dro", i);
+
+       DEBUG(0, "mtd: Giving out device %d to %s\n", i, mtd->name);
+       /* No need to get a refcount on the module containing
+          the notifier, since we hold the mtd_table_mutex */
+       list_for_each_entry(not, &mtd_notifiers, list)
+               not->add(mtd);
 
+       mutex_unlock(&mtd_table_mutex);
+       /* We _know_ we aren't being removed, because
+          our caller is still holding us here. So none
+          of this try_ nonsense, and no bitching about it
+          either. :) */
+       __module_get(THIS_MODULE);
+       return 0;
+
+fail_added:
+       idr_remove(&mtd_idr, i);
+fail_locked:
        mutex_unlock(&mtd_table_mutex);
        return 1;
 }
@@ -341,31 +350,34 @@ int add_mtd_device(struct mtd_info *mtd)
 int del_mtd_device (struct mtd_info *mtd)
 {
        int ret;
+       struct mtd_notifier *not;
 
        mutex_lock(&mtd_table_mutex);
 
-       if (mtd_table[mtd->index] != mtd) {
+       if (idr_find(&mtd_idr, mtd->index) != mtd) {
                ret = -ENODEV;
-       } else if (mtd->usecount) {
+               goto out_error;
+       }
+
+       /* No need to get a refcount on the module containing
+               the notifier, since we hold the mtd_table_mutex */
+       list_for_each_entry(not, &mtd_notifiers, list)
+               not->remove(mtd);
+
+       if (mtd->usecount) {
                printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n",
                       mtd->index, mtd->name, mtd->usecount);
                ret = -EBUSY;
        } else {
-               struct mtd_notifier *not;
-
                device_unregister(&mtd->dev);
 
-               /* No need to get a refcount on the module containing
-                  the notifier, since we hold the mtd_table_mutex */
-               list_for_each_entry(not, &mtd_notifiers, list)
-                       not->remove(mtd);
-
-               mtd_table[mtd->index] = NULL;
+               idr_remove(&mtd_idr, mtd->index);
 
                module_put(THIS_MODULE);
                ret = 0;
        }
 
+out_error:
        mutex_unlock(&mtd_table_mutex);
        return ret;
 }
@@ -381,7 +393,7 @@ int del_mtd_device (struct mtd_info *mtd)
 
 void register_mtd_user (struct mtd_notifier *new)
 {
-       int i;
+       struct mtd_info *mtd;
 
        mutex_lock(&mtd_table_mutex);
 
@@ -389,9 +401,8 @@ void register_mtd_user (struct mtd_notifier *new)
 
        __module_get(THIS_MODULE);
 
-       for (i=0; i< MAX_MTD_DEVICES; i++)
-               if (mtd_table[i])
-                       new->add(mtd_table[i]);
+       mtd_for_each_device(mtd)
+               new->add(mtd);
 
        mutex_unlock(&mtd_table_mutex);
 }
@@ -408,15 +419,14 @@ void register_mtd_user (struct mtd_notifier *new)
 
 int unregister_mtd_user (struct mtd_notifier *old)
 {
-       int i;
+       struct mtd_info *mtd;
 
        mutex_lock(&mtd_table_mutex);
 
        module_put(THIS_MODULE);
 
-       for (i=0; i< MAX_MTD_DEVICES; i++)
-               if (mtd_table[i])
-                       old->remove(mtd_table[i]);
+       mtd_for_each_device(mtd)
+               old->remove(mtd);
 
        list_del(&old->list);
        mutex_unlock(&mtd_table_mutex);
@@ -438,42 +448,56 @@ int unregister_mtd_user (struct mtd_notifier *old)
 
 struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
 {
-       struct mtd_info *ret = NULL;
-       int i, err = -ENODEV;
+       struct mtd_info *ret = NULL, *other;
+       int err = -ENODEV;
 
        mutex_lock(&mtd_table_mutex);
 
        if (num == -1) {
-               for (i=0; i< MAX_MTD_DEVICES; i++)
-                       if (mtd_table[i] == mtd)
-                               ret = mtd_table[i];
-       } else if (num >= 0 && num < MAX_MTD_DEVICES) {
-               ret = mtd_table[num];
+               mtd_for_each_device(other) {
+                       if (other == mtd) {
+                               ret = mtd;
+                               break;
+                       }
+               }
+       } else if (num >= 0) {
+               ret = idr_find(&mtd_idr, num);
                if (mtd && mtd != ret)
                        ret = NULL;
        }
 
-       if (!ret)
-               goto out_unlock;
-
-       if (!try_module_get(ret->owner))
-               goto out_unlock;
-
-       if (ret->get_device) {
-               err = ret->get_device(ret);
-               if (err)
-                       goto out_put;
+       if (!ret) {
+               ret = ERR_PTR(err);
+               goto out;
        }
 
-       ret->usecount++;
+       err = __get_mtd_device(ret);
+       if (err)
+               ret = ERR_PTR(err);
+out:
        mutex_unlock(&mtd_table_mutex);
        return ret;
+}
 
-out_put:
-       module_put(ret->owner);
-out_unlock:
-       mutex_unlock(&mtd_table_mutex);
-       return ERR_PTR(err);
+
+int __get_mtd_device(struct mtd_info *mtd)
+{
+       int err;
+
+       if (!try_module_get(mtd->owner))
+               return -ENODEV;
+
+       if (mtd->get_device) {
+
+               err = mtd->get_device(mtd);
+
+               if (err) {
+                       module_put(mtd->owner);
+                       return err;
+               }
+       }
+       mtd->usecount++;
+       return 0;
 }
 
 /**
@@ -487,14 +511,14 @@ out_unlock:
 
 struct mtd_info *get_mtd_device_nm(const char *name)
 {
-       int i, err = -ENODEV;
-       struct mtd_info *mtd = NULL;
+       int err = -ENODEV;
+       struct mtd_info *mtd = NULL, *other;
 
        mutex_lock(&mtd_table_mutex);
 
-       for (i = 0; i < MAX_MTD_DEVICES; i++) {
-               if (mtd_table[i] && !strcmp(name, mtd_table[i]->name)) {
-                       mtd = mtd_table[i];
+       mtd_for_each_device(other) {
+               if (!strcmp(name, other->name)) {
+                       mtd = other;
                        break;
                }
        }
@@ -524,14 +548,19 @@ out_unlock:
 
 void put_mtd_device(struct mtd_info *mtd)
 {
-       int c;
-
        mutex_lock(&mtd_table_mutex);
-       c = --mtd->usecount;
+       __put_mtd_device(mtd);
+       mutex_unlock(&mtd_table_mutex);
+
+}
+
+void __put_mtd_device(struct mtd_info *mtd)
+{
+       --mtd->usecount;
+       BUG_ON(mtd->usecount < 0);
+
        if (mtd->put_device)
                mtd->put_device(mtd);
-       mutex_unlock(&mtd_table_mutex);
-       BUG_ON(c < 0);
 
        module_put(mtd->owner);
 }
@@ -569,7 +598,9 @@ EXPORT_SYMBOL_GPL(add_mtd_device);
 EXPORT_SYMBOL_GPL(del_mtd_device);
 EXPORT_SYMBOL_GPL(get_mtd_device);
 EXPORT_SYMBOL_GPL(get_mtd_device_nm);
+EXPORT_SYMBOL_GPL(__get_mtd_device);
 EXPORT_SYMBOL_GPL(put_mtd_device);
+EXPORT_SYMBOL_GPL(__put_mtd_device);
 EXPORT_SYMBOL_GPL(register_mtd_user);
 EXPORT_SYMBOL_GPL(unregister_mtd_user);
 EXPORT_SYMBOL_GPL(default_mtd_writev);
@@ -581,14 +612,9 @@ EXPORT_SYMBOL_GPL(default_mtd_writev);
 
 static struct proc_dir_entry *proc_mtd;
 
-static inline int mtd_proc_info (char *buf, int i)
+static inline int mtd_proc_info(char *buf, struct mtd_info *this)
 {
-       struct mtd_info *this = mtd_table[i];
-
-       if (!this)
-               return 0;
-
-       return sprintf(buf, "mtd%d: %8.8llx %8.8x \"%s\"\n", i,
+       return sprintf(buf, "mtd%d: %8.8llx %8.8x \"%s\"\n", this->index,
                       (unsigned long long)this->size,
                       this->erasesize, this->name);
 }
@@ -596,15 +622,15 @@ static inline int mtd_proc_info (char *buf, int i)
 static int mtd_read_proc (char *page, char **start, off_t off, int count,
                          int *eof, void *data_unused)
 {
-       int len, l, i;
+       struct mtd_info *mtd;
+       int len, l;
         off_t   begin = 0;
 
        mutex_lock(&mtd_table_mutex);
 
        len = sprintf(page, "dev:    size   erasesize  name\n");
-        for (i=0; i< MAX_MTD_DEVICES; i++) {
-
-                l = mtd_proc_info(page + len, i);
+       mtd_for_each_device(mtd) {
+               l = mtd_proc_info(page + len, mtd);
                 len += l;
                 if (len+begin > off+count)
                         goto done;
index a33251f..6a64fde 100644 (file)
@@ -8,4 +8,9 @@
    should not use them for _anything_ else */
 
 extern struct mutex mtd_table_mutex;
-extern struct mtd_info *mtd_table[MAX_MTD_DEVICES];
+extern struct mtd_info *__mtd_next_device(int i);
+
+#define mtd_for_each_device(mtd)                       \
+       for ((mtd) = __mtd_next_device(0);              \
+            (mtd) != NULL;                             \
+            (mtd) = __mtd_next_device(mtd->index + 1))
index 92e12df..328313c 100644 (file)
@@ -429,11 +429,6 @@ static int __init mtdoops_init(void)
        mtd_index = simple_strtoul(mtddev, &endp, 0);
        if (*endp == '\0')
                cxt->mtd_index = mtd_index;
-       if (cxt->mtd_index > MAX_MTD_DEVICES) {
-               printk(KERN_ERR "mtdoops: invalid mtd device number (%u) given\n",
-                               mtd_index);
-               return -EINVAL;
-       }
 
        cxt->oops_buf = vmalloc(record_size);
        if (!cxt->oops_buf) {
index af8b42e..d257052 100644 (file)
@@ -150,18 +150,12 @@ int get_sb_mtd(struct file_system_type *fs_type, int flags,
                        DEBUG(1, "MTDSB: mtd:%%s, name \"%s\"\n",
                              dev_name + 4);
 
-                       for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) {
-                               mtd = get_mtd_device(NULL, mtdnr);
-                               if (!IS_ERR(mtd)) {
-                                       if (!strcmp(mtd->name, dev_name + 4))
-                                               return get_sb_mtd_aux(
-                                                       fs_type, flags,
-                                                       dev_name, data, mtd,
-                                                       fill_super, mnt);
-
-                                       put_mtd_device(mtd);
-                               }
-                       }
+                       mtd = get_mtd_device_nm(dev_name + 4);
+                       if (!IS_ERR(mtd))
+                               return get_sb_mtd_aux(
+                                       fs_type, flags,
+                                       dev_name, data, mtd,
+                                       fill_super, mnt);
 
                        printk(KERN_NOTICE "MTD:"
                               " MTD device with name \"%s\" not found.\n",
index 42e5ea4..164bd56 100644 (file)
@@ -25,6 +25,10 @@ config MTD_NAND_ECC_SMC
          Software ECC according to the Smart Media Specification.
          The original Linux implementation had byte 0 and 1 swapped.
 
+config MTD_SM_COMMON
+       tristate
+       default n
+
 config MTD_NAND_MUSEUM_IDS
        bool "Enable chip ids for obsolete ancient NAND devices"
        depends on MTD_NAND
@@ -95,15 +99,21 @@ config MTD_NAND_OMAP_PREFETCH_DMA
         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
-       help
-         Support for NAND flash on Technologic Systems TS-7250 platform.
-
 config MTD_NAND_IDS
        tristate
 
+config MTD_NAND_RICOH
+       tristate "Ricoh xD card reader"
+       default n
+       depends on PCI
+       select MTD_SM_COMMON
+       help
+         Enable support for Ricoh R5C852 xD card reader
+         You also need to enable ether
+         NAND SSFDC (SmartMedia) read only translation layer' or new
+         expermental, readwrite
+         'SmartMedia/xD new translation layer'
+
 config MTD_NAND_AU1550
        tristate "Au1550/1200 NAND support"
        depends on SOC_AU1200 || SOC_AU1550
@@ -442,6 +452,13 @@ config MTD_NAND_FSL_UPM
          Enables support for NAND Flash chips wired onto Freescale PowerPC
          processor localbus with User-Programmable Machine support.
 
+config MTD_NAND_MPC5121_NFC
+       tristate "MPC5121 built-in NAND Flash Controller support"
+       depends on PPC_MPC512x
+       help
+         This enables the driver for the NAND flash controller on the
+         MPC5121 SoC.
+
 config MTD_NAND_MXC
        tristate "MXC NAND support"
        depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3
@@ -481,11 +498,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."
+config MTD_NAND_NUC900
+       tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards."
        depends on ARCH_W90X900 && MTD_PARTITIONS
        help
          This enables the driver for the NAND Flash on evaluation board based
-         on w90p910.
+         on w90p910 / NUC9xx.
 
 endif # MTD_NAND
index 1407bd1..5fbd1f8 100644 (file)
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_MTD_NAND)                 += nand.o nand_ecc.o
 obj-$(CONFIG_MTD_NAND_IDS)             += nand_ids.o
+obj-$(CONFIG_MTD_SM_COMMON)            += sm_common.o
 
 obj-$(CONFIG_MTD_NAND_CAFE)            += cafe_nand.o
 obj-$(CONFIG_MTD_NAND_SPIA)            += spia.o
@@ -19,7 +20,6 @@ obj-$(CONFIG_MTD_NAND_DISKONCHIP)     += diskonchip.o
 obj-$(CONFIG_MTD_NAND_H1900)           += h1910.o
 obj-$(CONFIG_MTD_NAND_RTC_FROM4)       += rtc_from4.o
 obj-$(CONFIG_MTD_NAND_SHARPSL)         += sharpsl.o
-obj-$(CONFIG_MTD_NAND_TS7250)          += ts7250.o
 obj-$(CONFIG_MTD_NAND_NANDSIM)         += nandsim.o
 obj-$(CONFIG_MTD_NAND_CS553X)          += cs553x_nand.o
 obj-$(CONFIG_MTD_NAND_NDFC)            += ndfc.o
@@ -39,8 +39,10 @@ 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_NUC900)          += nuc900_nand.o
 obj-$(CONFIG_MTD_NAND_NOMADIK)         += nomadik_nand.o
 obj-$(CONFIG_MTD_NAND_BCM_UMI)         += bcm_umi_nand.o nand_bcm_umi.o
+obj-$(CONFIG_MTD_NAND_MPC5121_NFC)     += mpc5121_nfc.o
+obj-$(CONFIG_MTD_NAND_RICOH)           += r852.o
 
 nand-objs := nand_base.o nand_bbt.o
index 2d67732..8691e04 100644 (file)
@@ -49,7 +49,7 @@
 
 #define TIMEOUT HZ
 
-static struct usb_device_id alauda_table [] = {
+static const struct usb_device_id alauda_table[] = {
        { USB_DEVICE(0x0584, 0x0008) }, /* Fujifilm DPC-R1 */
        { USB_DEVICE(0x07b4, 0x010a) }, /* Olympus MAUSB-10 */
        { }
index 524e6c9..04d3088 100644 (file)
@@ -474,7 +474,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
        }
 
        /* first scan to find the device and get the page size */
-       if (nand_scan_ident(mtd, 1)) {
+       if (nand_scan_ident(mtd, 1, NULL)) {
                res = -ENXIO;
                goto err_scan_ident;
        }
index 43d46e4..3ffe05d 100644 (file)
@@ -451,7 +451,7 @@ static int __init au1xxx_nand_init(void)
        u32 nand_phys;
 
        /* Allocate memory for MTD device structure and private data */
-       au1550_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
+       au1550_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
        if (!au1550_mtd) {
                printk("Unable to allocate NAND MTD dev structure.\n");
                return -ENOMEM;
@@ -460,10 +460,6 @@ static int __init au1xxx_nand_init(void)
        /* Get pointer to private data */
        this = (struct nand_chip *)(&au1550_mtd[1]);
 
-       /* Initialize structures */
-       memset(au1550_mtd, 0, sizeof(struct mtd_info));
-       memset(this, 0, sizeof(struct nand_chip));
-
        /* Link the private data with the MTD structure */
        au1550_mtd->priv = this;
        au1550_mtd->owner = THIS_MODULE;
@@ -544,7 +540,7 @@ static int __init au1xxx_nand_init(void)
        }
        nand_phys = (mem_staddr << 4) & 0xFFFC0000;
 
-       p_nand = (void __iomem *)ioremap(nand_phys, 0x1000);
+       p_nand = ioremap(nand_phys, 0x1000);
 
        /* make controller and MTD agree */
        if (NAND_CS == 0)
@@ -589,7 +585,7 @@ static int __init au1xxx_nand_init(void)
        return 0;
 
  outio:
-       iounmap((void *)p_nand);
+       iounmap(p_nand);
 
  outmem:
        kfree(au1550_mtd);
@@ -610,7 +606,7 @@ static void __exit au1550_cleanup(void)
        kfree(au1550_mtd);
 
        /* Unmap */
-       iounmap((void *)p_nand);
+       iounmap(p_nand);
 }
 
 module_exit(au1550_cleanup);
index 7d1cca7..7eb8674 100644 (file)
@@ -446,7 +446,7 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev)
         * layout we'll be using.
         */
 
-       err = nand_scan_ident(board_mtd, 1);
+       err = nand_scan_ident(board_mtd, 1, NULL);
        if (err) {
                printk(KERN_ERR "nand_scan failed: %d\n", err);
                iounmap(bcm_umi_io_base);
index c828d9a..01a6fe1 100644 (file)
@@ -761,7 +761,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
                cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK));
 
        /* Scan to find existence of the device */
-       if (nand_scan_ident(mtd, 2)) {
+       if (nand_scan_ident(mtd, 2, NULL)) {
                err = -ENXIO;
                goto out_irq;
        }
@@ -848,7 +848,7 @@ static void __devexit cafe_nand_remove(struct pci_dev *pdev)
        kfree(mtd);
 }
 
-static struct pci_device_id cafe_nand_tbl[] = {
+static const struct pci_device_id cafe_nand_tbl[] = {
        { PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_NAND,
          PCI_ANY_ID, PCI_ANY_ID },
        { }
index fe3eba8..45bb931 100644 (file)
@@ -566,8 +566,8 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
                goto err_nomem;
        }
 
-       vaddr = ioremap(res1->start, res1->end - res1->start);
-       base = ioremap(res2->start, res2->end - res2->start);
+       vaddr = ioremap(res1->start, resource_size(res1));
+       base = ioremap(res2->start, resource_size(res2));
        if (!vaddr || !base) {
                dev_err(&pdev->dev, "ioremap failed\n");
                ret = -EINVAL;
@@ -690,7 +690,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev)
        spin_unlock_irq(&davinci_nand_lock);
 
        /* Scan to find existence of the device(s) */
-       ret = nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1);
+       ret = nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1, NULL);
        if (ret < 0) {
                dev_dbg(&pdev->dev, "no NAND chip(s) found\n");
                goto err_scan;
index ae30fb6..3f38fb8 100644 (file)
@@ -874,7 +874,7 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
        priv->ctrl = ctrl;
        priv->dev = ctrl->dev;
 
-       priv->vbase = ioremap(res.start, res.end - res.start + 1);
+       priv->vbase = ioremap(res.start, resource_size(&res));
        if (!priv->vbase) {
                dev_err(ctrl->dev, "failed to map chip region\n");
                ret = -ENOMEM;
@@ -891,7 +891,7 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
        if (ret)
                goto err;
 
-       ret = nand_scan_ident(&priv->mtd, 1);
+       ret = nand_scan_ident(&priv->mtd, 1, NULL);
        if (ret)
                goto err;
 
index 071a60c..d721ec0 100644 (file)
@@ -302,7 +302,7 @@ static int __devinit fun_probe(struct of_device *ofdev,
                                  FSL_UPM_WAIT_WRITE_BYTE;
 
        fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start,
-                                           io_res.end - io_res.start + 1);
+                                           resource_size(&io_res));
        if (!fun->io_base) {
                ret = -ENOMEM;
                goto err2;
@@ -349,7 +349,7 @@ static int __devexit fun_remove(struct of_device *ofdev)
        return 0;
 }
 
-static struct of_device_id of_fun_match[] = {
+static const struct of_device_id of_fun_match[] = {
        { .compatible = "fsl,upm-nand" },
        {},
 };
index 8f902e7..0cde618 100644 (file)
@@ -181,11 +181,11 @@ static int __devexit gpio_nand_remove(struct platform_device *dev)
        res = platform_get_resource(dev, IORESOURCE_MEM, 1);
        iounmap(gpiomtd->io_sync);
        if (res)
-               release_mem_region(res->start, res->end - res->start + 1);
+               release_mem_region(res->start, resource_size(res));
 
        res = platform_get_resource(dev, IORESOURCE_MEM, 0);
        iounmap(gpiomtd->nand_chip.IO_ADDR_R);
-       release_mem_region(res->start, res->end - res->start + 1);
+       release_mem_region(res->start, resource_size(res));
 
        if (gpio_is_valid(gpiomtd->plat.gpio_nwp))
                gpio_set_value(gpiomtd->plat.gpio_nwp, 0);
@@ -208,14 +208,14 @@ static void __iomem *request_and_remap(struct resource *res, size_t size,
 {
        void __iomem *ptr;
 
-       if (!request_mem_region(res->start, res->end - res->start + 1, name)) {
+       if (!request_mem_region(res->start, resource_size(res), name)) {
                *err = -EBUSY;
                return NULL;
        }
 
        ptr = ioremap(res->start, size);
        if (!ptr) {
-               release_mem_region(res->start, res->end - res->start + 1);
+               release_mem_region(res->start, resource_size(res));
                *err = -ENOMEM;
        }
        return ptr;
@@ -338,10 +338,10 @@ err_nwp:
 err_nce:
        iounmap(gpiomtd->io_sync);
        if (res1)
-               release_mem_region(res1->start, res1->end - res1->start + 1);
+               release_mem_region(res1->start, resource_size(res1));
 err_sync:
        iounmap(gpiomtd->nand_chip.IO_ADDR_R);
-       release_mem_region(res0->start, res0->end - res0->start + 1);
+       release_mem_region(res0->start, resource_size(res0));
 err_map:
        kfree(gpiomtd);
        return ret;
diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c
new file mode 100644 (file)
index 0000000..d7333f4
--- /dev/null
@@ -0,0 +1,916 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc.
+ * Copyright 2009 Semihalf.
+ *
+ * Approved as OSADL project by a majority of OSADL members and funded
+ * by OSADL membership fees in 2009;  for details see www.osadl.org.
+ *
+ * Based on original driver from Freescale Semiconductor
+ * written by John Rigby <jrigby@freescale.com> on basis
+ * of drivers/mtd/nand/mxc_nand.c. Reworked and extended
+ * Piotr Ziecik <kosmo@semihalf.com>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+
+#include <asm/mpc5121.h>
+
+/* Addresses for NFC MAIN RAM BUFFER areas */
+#define NFC_MAIN_AREA(n)       ((n) *  0x200)
+
+/* Addresses for NFC SPARE BUFFER areas */
+#define NFC_SPARE_BUFFERS      8
+#define NFC_SPARE_LEN          0x40
+#define NFC_SPARE_AREA(n)      (0x1000 + ((n) * NFC_SPARE_LEN))
+
+/* MPC5121 NFC registers */
+#define NFC_BUF_ADDR           0x1E04
+#define NFC_FLASH_ADDR         0x1E06
+#define NFC_FLASH_CMD          0x1E08
+#define NFC_CONFIG             0x1E0A
+#define NFC_ECC_STATUS1                0x1E0C
+#define NFC_ECC_STATUS2                0x1E0E
+#define NFC_SPAS               0x1E10
+#define NFC_WRPROT             0x1E12
+#define NFC_NF_WRPRST          0x1E18
+#define NFC_CONFIG1            0x1E1A
+#define NFC_CONFIG2            0x1E1C
+#define NFC_UNLOCKSTART_BLK0   0x1E20
+#define NFC_UNLOCKEND_BLK0     0x1E22
+#define NFC_UNLOCKSTART_BLK1   0x1E24
+#define NFC_UNLOCKEND_BLK1     0x1E26
+#define NFC_UNLOCKSTART_BLK2   0x1E28
+#define NFC_UNLOCKEND_BLK2     0x1E2A
+#define NFC_UNLOCKSTART_BLK3   0x1E2C
+#define NFC_UNLOCKEND_BLK3     0x1E2E
+
+/* Bit Definitions: NFC_BUF_ADDR */
+#define NFC_RBA_MASK           (7 << 0)
+#define NFC_ACTIVE_CS_SHIFT    5
+#define NFC_ACTIVE_CS_MASK     (3 << NFC_ACTIVE_CS_SHIFT)
+
+/* Bit Definitions: NFC_CONFIG */
+#define NFC_BLS_UNLOCKED       (1 << 1)
+
+/* Bit Definitions: NFC_CONFIG1 */
+#define NFC_ECC_4BIT           (1 << 0)
+#define NFC_FULL_PAGE_DMA      (1 << 1)
+#define NFC_SPARE_ONLY         (1 << 2)
+#define NFC_ECC_ENABLE         (1 << 3)
+#define NFC_INT_MASK           (1 << 4)
+#define NFC_BIG_ENDIAN         (1 << 5)
+#define NFC_RESET              (1 << 6)
+#define NFC_CE                 (1 << 7)
+#define NFC_ONE_CYCLE          (1 << 8)
+#define NFC_PPB_32             (0 << 9)
+#define NFC_PPB_64             (1 << 9)
+#define NFC_PPB_128            (2 << 9)
+#define NFC_PPB_256            (3 << 9)
+#define NFC_PPB_MASK           (3 << 9)
+#define NFC_FULL_PAGE_INT      (1 << 11)
+
+/* Bit Definitions: NFC_CONFIG2 */
+#define NFC_COMMAND            (1 << 0)
+#define NFC_ADDRESS            (1 << 1)
+#define NFC_INPUT              (1 << 2)
+#define NFC_OUTPUT             (1 << 3)
+#define NFC_ID                 (1 << 4)
+#define NFC_STATUS             (1 << 5)
+#define NFC_CMD_FAIL           (1 << 15)
+#define NFC_INT                        (1 << 15)
+
+/* Bit Definitions: NFC_WRPROT */
+#define NFC_WPC_LOCK_TIGHT     (1 << 0)
+#define NFC_WPC_LOCK           (1 << 1)
+#define NFC_WPC_UNLOCK         (1 << 2)
+
+#define        DRV_NAME                "mpc5121_nfc"
+
+/* Timeouts */
+#define NFC_RESET_TIMEOUT      1000            /* 1 ms */
+#define NFC_TIMEOUT            (HZ / 10)       /* 1/10 s */
+
+struct mpc5121_nfc_prv {
+       struct mtd_info         mtd;
+       struct nand_chip        chip;
+       int                     irq;
+       void __iomem            *regs;
+       struct clk              *clk;
+       wait_queue_head_t       irq_waitq;
+       uint                    column;
+       int                     spareonly;
+       void __iomem            *csreg;
+       struct device           *dev;
+};
+
+static void mpc5121_nfc_done(struct mtd_info *mtd);
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *mpc5121_nfc_pprobes[] = { "cmdlinepart", NULL };
+#endif
+
+/* Read NFC register */
+static inline u16 nfc_read(struct mtd_info *mtd, uint reg)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct mpc5121_nfc_prv *prv = chip->priv;
+
+       return in_be16(prv->regs + reg);
+}
+
+/* Write NFC register */
+static inline void nfc_write(struct mtd_info *mtd, uint reg, u16 val)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct mpc5121_nfc_prv *prv = chip->priv;
+
+       out_be16(prv->regs + reg, val);
+}
+
+/* Set bits in NFC register */
+static inline void nfc_set(struct mtd_info *mtd, uint reg, u16 bits)
+{
+       nfc_write(mtd, reg, nfc_read(mtd, reg) | bits);
+}
+
+/* Clear bits in NFC register */
+static inline void nfc_clear(struct mtd_info *mtd, uint reg, u16 bits)
+{
+       nfc_write(mtd, reg, nfc_read(mtd, reg) & ~bits);
+}
+
+/* Invoke address cycle */
+static inline void mpc5121_nfc_send_addr(struct mtd_info *mtd, u16 addr)
+{
+       nfc_write(mtd, NFC_FLASH_ADDR, addr);
+       nfc_write(mtd, NFC_CONFIG2, NFC_ADDRESS);
+       mpc5121_nfc_done(mtd);
+}
+
+/* Invoke command cycle */
+static inline void mpc5121_nfc_send_cmd(struct mtd_info *mtd, u16 cmd)
+{
+       nfc_write(mtd, NFC_FLASH_CMD, cmd);
+       nfc_write(mtd, NFC_CONFIG2, NFC_COMMAND);
+       mpc5121_nfc_done(mtd);
+}
+
+/* Send data from NFC buffers to NAND flash */
+static inline void mpc5121_nfc_send_prog_page(struct mtd_info *mtd)
+{
+       nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
+       nfc_write(mtd, NFC_CONFIG2, NFC_INPUT);
+       mpc5121_nfc_done(mtd);
+}
+
+/* Receive data from NAND flash */
+static inline void mpc5121_nfc_send_read_page(struct mtd_info *mtd)
+{
+       nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
+       nfc_write(mtd, NFC_CONFIG2, NFC_OUTPUT);
+       mpc5121_nfc_done(mtd);
+}
+
+/* Receive ID from NAND flash */
+static inline void mpc5121_nfc_send_read_id(struct mtd_info *mtd)
+{
+       nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
+       nfc_write(mtd, NFC_CONFIG2, NFC_ID);
+       mpc5121_nfc_done(mtd);
+}
+
+/* Receive status from NAND flash */
+static inline void mpc5121_nfc_send_read_status(struct mtd_info *mtd)
+{
+       nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK);
+       nfc_write(mtd, NFC_CONFIG2, NFC_STATUS);
+       mpc5121_nfc_done(mtd);
+}
+
+/* NFC interrupt handler */
+static irqreturn_t mpc5121_nfc_irq(int irq, void *data)
+{
+       struct mtd_info *mtd = data;
+       struct nand_chip *chip = mtd->priv;
+       struct mpc5121_nfc_prv *prv = chip->priv;
+
+       nfc_set(mtd, NFC_CONFIG1, NFC_INT_MASK);
+       wake_up(&prv->irq_waitq);
+
+       return IRQ_HANDLED;
+}
+
+/* Wait for operation complete */
+static void mpc5121_nfc_done(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct mpc5121_nfc_prv *prv = chip->priv;
+       int rv;
+
+       if ((nfc_read(mtd, NFC_CONFIG2) & NFC_INT) == 0) {
+               nfc_clear(mtd, NFC_CONFIG1, NFC_INT_MASK);
+               rv = wait_event_timeout(prv->irq_waitq,
+                       (nfc_read(mtd, NFC_CONFIG2) & NFC_INT), NFC_TIMEOUT);
+
+               if (!rv)
+                       dev_warn(prv->dev,
+                               "Timeout while waiting for interrupt.\n");
+       }
+
+       nfc_clear(mtd, NFC_CONFIG2, NFC_INT);
+}
+
+/* Do address cycle(s) */
+static void mpc5121_nfc_addr_cycle(struct mtd_info *mtd, int column, int page)
+{
+       struct nand_chip *chip = mtd->priv;
+       u32 pagemask = chip->pagemask;
+
+       if (column != -1) {
+               mpc5121_nfc_send_addr(mtd, column);
+               if (mtd->writesize > 512)
+                       mpc5121_nfc_send_addr(mtd, column >> 8);
+       }
+
+       if (page != -1) {
+               do {
+                       mpc5121_nfc_send_addr(mtd, page & 0xFF);
+                       page >>= 8;
+                       pagemask >>= 8;
+               } while (pagemask);
+       }
+}
+
+/* Control chip select signals */
+static void mpc5121_nfc_select_chip(struct mtd_info *mtd, int chip)
+{
+       if (chip < 0) {
+               nfc_clear(mtd, NFC_CONFIG1, NFC_CE);
+               return;
+       }
+
+       nfc_clear(mtd, NFC_BUF_ADDR, NFC_ACTIVE_CS_MASK);
+       nfc_set(mtd, NFC_BUF_ADDR, (chip << NFC_ACTIVE_CS_SHIFT) &
+                                                       NFC_ACTIVE_CS_MASK);
+       nfc_set(mtd, NFC_CONFIG1, NFC_CE);
+}
+
+/* Init external chip select logic on ADS5121 board */
+static int ads5121_chipselect_init(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct mpc5121_nfc_prv *prv = chip->priv;
+       struct device_node *dn;
+
+       dn = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld");
+       if (dn) {
+               prv->csreg = of_iomap(dn, 0);
+               of_node_put(dn);
+               if (!prv->csreg)
+                       return -ENOMEM;
+
+               /* CPLD Register 9 controls NAND /CE Lines */
+               prv->csreg += 9;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+/* Control chips select signal on ADS5121 board */
+static void ads5121_select_chip(struct mtd_info *mtd, int chip)
+{
+       struct nand_chip *nand = mtd->priv;
+       struct mpc5121_nfc_prv *prv = nand->priv;
+       u8 v;
+
+       v = in_8(prv->csreg);
+       v |= 0x0F;
+
+       if (chip >= 0) {
+               mpc5121_nfc_select_chip(mtd, 0);
+               v &= ~(1 << chip);
+       } else
+               mpc5121_nfc_select_chip(mtd, -1);
+
+       out_8(prv->csreg, v);
+}
+
+/* Read NAND Ready/Busy signal */
+static int mpc5121_nfc_dev_ready(struct mtd_info *mtd)
+{
+       /*
+        * NFC handles ready/busy signal internally. Therefore, this function
+        * always returns status as ready.
+        */
+       return 1;
+}
+
+/* Write command to NAND flash */
+static void mpc5121_nfc_command(struct mtd_info *mtd, unsigned command,
+                                                       int column, int page)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct mpc5121_nfc_prv *prv = chip->priv;
+
+       prv->column = (column >= 0) ? column : 0;
+       prv->spareonly = 0;
+
+       switch (command) {
+       case NAND_CMD_PAGEPROG:
+               mpc5121_nfc_send_prog_page(mtd);
+               break;
+       /*
+        * NFC does not support sub-page reads and writes,
+        * so emulate them using full page transfers.
+        */
+       case NAND_CMD_READ0:
+               column = 0;
+               break;
+
+       case NAND_CMD_READ1:
+               prv->column += 256;
+               command = NAND_CMD_READ0;
+               column = 0;
+               break;
+
+       case NAND_CMD_READOOB:
+               prv->spareonly = 1;
+               command = NAND_CMD_READ0;
+               column = 0;
+               break;
+
+       case NAND_CMD_SEQIN:
+               mpc5121_nfc_command(mtd, NAND_CMD_READ0, column, page);
+               column = 0;
+               break;
+
+       case NAND_CMD_ERASE1:
+       case NAND_CMD_ERASE2:
+       case NAND_CMD_READID:
+       case NAND_CMD_STATUS:
+               break;
+
+       default:
+               return;
+       }
+
+       mpc5121_nfc_send_cmd(mtd, command);
+       mpc5121_nfc_addr_cycle(mtd, column, page);
+
+       switch (command) {
+       case NAND_CMD_READ0:
+               if (mtd->writesize > 512)
+                       mpc5121_nfc_send_cmd(mtd, NAND_CMD_READSTART);
+               mpc5121_nfc_send_read_page(mtd);
+               break;
+
+       case NAND_CMD_READID:
+               mpc5121_nfc_send_read_id(mtd);
+               break;
+
+       case NAND_CMD_STATUS:
+               mpc5121_nfc_send_read_status(mtd);
+               if (chip->options & NAND_BUSWIDTH_16)
+                       prv->column = 1;
+               else
+                       prv->column = 0;
+               break;
+       }
+}
+
+/* Copy data from/to NFC spare buffers. */
+static void mpc5121_nfc_copy_spare(struct mtd_info *mtd, uint offset,
+                                               u8 *buffer, uint size, int wr)
+{
+       struct nand_chip *nand = mtd->priv;
+       struct mpc5121_nfc_prv *prv = nand->priv;
+       uint o, s, sbsize, blksize;
+
+       /*
+        * NAND spare area is available through NFC spare buffers.
+        * The NFC divides spare area into (page_size / 512) chunks.
+        * Each chunk is placed into separate spare memory area, using
+        * first (spare_size / num_of_chunks) bytes of the buffer.
+        *
+        * For NAND device in which the spare area is not divided fully
+        * by the number of chunks, number of used bytes in each spare
+        * buffer is rounded down to the nearest even number of bytes,
+        * and all remaining bytes are added to the last used spare area.
+        *
+        * For more information read section 26.6.10 of MPC5121e
+        * Microcontroller Reference Manual, Rev. 3.
+        */
+
+       /* Calculate number of valid bytes in each spare buffer */
+       sbsize = (mtd->oobsize / (mtd->writesize / 512)) & ~1;
+
+       while (size) {
+               /* Calculate spare buffer number */
+               s = offset / sbsize;
+               if (s > NFC_SPARE_BUFFERS - 1)
+                       s = NFC_SPARE_BUFFERS - 1;
+
+               /*
+                * Calculate offset to requested data block in selected spare
+                * buffer and its size.
+                */
+               o = offset - (s * sbsize);
+               blksize = min(sbsize - o, size);
+
+               if (wr)
+                       memcpy_toio(prv->regs + NFC_SPARE_AREA(s) + o,
+                                                       buffer, blksize);
+               else
+                       memcpy_fromio(buffer,
+                               prv->regs + NFC_SPARE_AREA(s) + o, blksize);
+
+               buffer += blksize;
+               offset += blksize;
+               size -= blksize;
+       };
+}
+
+/* Copy data from/to NFC main and spare buffers */
+static void mpc5121_nfc_buf_copy(struct mtd_info *mtd, u_char *buf, int len,
+                                                                       int wr)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct mpc5121_nfc_prv *prv = chip->priv;
+       uint c = prv->column;
+       uint l;
+
+       /* Handle spare area access */
+       if (prv->spareonly || c >= mtd->writesize) {
+               /* Calculate offset from beginning of spare area */
+               if (c >= mtd->writesize)
+                       c -= mtd->writesize;
+
+               prv->column += len;
+               mpc5121_nfc_copy_spare(mtd, c, buf, len, wr);
+               return;
+       }
+
+       /*
+        * Handle main area access - limit copy length to prevent
+        * crossing main/spare boundary.
+        */
+       l = min((uint)len, mtd->writesize - c);
+       prv->column += l;
+
+       if (wr)
+               memcpy_toio(prv->regs + NFC_MAIN_AREA(0) + c, buf, l);
+       else
+               memcpy_fromio(buf, prv->regs + NFC_MAIN_AREA(0) + c, l);
+
+       /* Handle crossing main/spare boundary */
+       if (l != len) {
+               buf += l;
+               len -= l;
+               mpc5121_nfc_buf_copy(mtd, buf, len, wr);
+       }
+}
+
+/* Read data from NFC buffers */
+static void mpc5121_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+       mpc5121_nfc_buf_copy(mtd, buf, len, 0);
+}
+
+/* Write data to NFC buffers */
+static void mpc5121_nfc_write_buf(struct mtd_info *mtd,
+                                               const u_char *buf, int len)
+{
+       mpc5121_nfc_buf_copy(mtd, (u_char *)buf, len, 1);
+}
+
+/* Compare buffer with NAND flash */
+static int mpc5121_nfc_verify_buf(struct mtd_info *mtd,
+                                               const u_char *buf, int len)
+{
+       u_char tmp[256];
+       uint bsize;
+
+       while (len) {
+               bsize = min(len, 256);
+               mpc5121_nfc_read_buf(mtd, tmp, bsize);
+
+               if (memcmp(buf, tmp, bsize))
+                       return 1;
+
+               buf += bsize;
+               len -= bsize;
+       }
+
+       return 0;
+}
+
+/* Read byte from NFC buffers */
+static u8 mpc5121_nfc_read_byte(struct mtd_info *mtd)
+{
+       u8 tmp;
+
+       mpc5121_nfc_read_buf(mtd, &tmp, sizeof(tmp));
+
+       return tmp;
+}
+
+/* Read word from NFC buffers */
+static u16 mpc5121_nfc_read_word(struct mtd_info *mtd)
+{
+       u16 tmp;
+
+       mpc5121_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp));
+
+       return tmp;
+}
+
+/*
+ * Read NFC configuration from Reset Config Word
+ *
+ * NFC is configured during reset in basis of information stored
+ * in Reset Config Word. There is no other way to set NAND block
+ * size, spare size and bus width.
+ */
+static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct mpc5121_nfc_prv *prv = chip->priv;
+       struct mpc512x_reset_module *rm;
+       struct device_node *rmnode;
+       uint rcw_pagesize = 0;
+       uint rcw_sparesize = 0;
+       uint rcw_width;
+       uint rcwh;
+       uint romloc, ps;
+
+       rmnode = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-reset");
+       if (!rmnode) {
+               dev_err(prv->dev, "Missing 'fsl,mpc5121-reset' "
+                                       "node in device tree!\n");
+               return -ENODEV;
+       }
+
+       rm = of_iomap(rmnode, 0);
+       if (!rm) {
+               dev_err(prv->dev, "Error mapping reset module node!\n");
+               return -EBUSY;
+       }
+
+       rcwh = in_be32(&rm->rcwhr);
+
+       /* Bit 6: NFC bus width */
+       rcw_width = ((rcwh >> 6) & 0x1) ? 2 : 1;
+
+       /* Bit 7: NFC Page/Spare size */
+       ps = (rcwh >> 7) & 0x1;
+
+       /* Bits [22:21]: ROM Location */
+       romloc = (rcwh >> 21) & 0x3;
+
+       /* Decode RCW bits */
+       switch ((ps << 2) | romloc) {
+       case 0x00:
+       case 0x01:
+               rcw_pagesize = 512;
+               rcw_sparesize = 16;
+               break;
+       case 0x02:
+       case 0x03:
+               rcw_pagesize = 4096;
+               rcw_sparesize = 128;
+               break;
+       case 0x04:
+       case 0x05:
+               rcw_pagesize = 2048;
+               rcw_sparesize = 64;
+               break;
+       case 0x06:
+       case 0x07:
+               rcw_pagesize = 4096;
+               rcw_sparesize = 218;
+               break;
+       }
+
+       mtd->writesize = rcw_pagesize;
+       mtd->oobsize = rcw_sparesize;
+       if (rcw_width == 2)
+               chip->options |= NAND_BUSWIDTH_16;
+
+       dev_notice(prv->dev, "Configured for "
+                               "%u-bit NAND, page size %u "
+                               "with %u spare.\n",
+                               rcw_width * 8, rcw_pagesize,
+                               rcw_sparesize);
+       iounmap(rm);
+       of_node_put(rmnode);
+       return 0;
+}
+
+/* Free driver resources */
+static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct mpc5121_nfc_prv *prv = chip->priv;
+
+       if (prv->clk) {
+               clk_disable(prv->clk);
+               clk_put(prv->clk);
+       }
+
+       if (prv->csreg)
+               iounmap(prv->csreg);
+}
+
+static int __devinit mpc5121_nfc_probe(struct of_device *op,
+                                       const struct of_device_id *match)
+{
+       struct device_node *rootnode, *dn = op->node;
+       struct device *dev = &op->dev;
+       struct mpc5121_nfc_prv *prv;
+       struct resource res;
+       struct mtd_info *mtd;
+#ifdef CONFIG_MTD_PARTITIONS
+       struct mtd_partition *parts;
+#endif
+       struct nand_chip *chip;
+       unsigned long regs_paddr, regs_size;
+       const uint *chips_no;
+       int resettime = 0;
+       int retval = 0;
+       int rev, len;
+
+       /*
+        * Check SoC revision. This driver supports only NFC
+        * in MPC5121 revision 2.
+        */
+       rev = (mfspr(SPRN_SVR) >> 4) & 0xF;
+       if (rev != 2) {
+               dev_err(dev, "SoC revision %u is not supported!\n", rev);
+               return -ENXIO;
+       }
+
+       prv = devm_kzalloc(dev, sizeof(*prv), GFP_KERNEL);
+       if (!prv) {
+               dev_err(dev, "Memory exhausted!\n");
+               return -ENOMEM;
+       }
+
+       mtd = &prv->mtd;
+       chip = &prv->chip;
+
+       mtd->priv = chip;
+       chip->priv = prv;
+       prv->dev = dev;
+
+       /* Read NFC configuration from Reset Config Word */
+       retval = mpc5121_nfc_read_hw_config(mtd);
+       if (retval) {
+               dev_err(dev, "Unable to read NFC config!\n");
+               return retval;
+       }
+
+       prv->irq = irq_of_parse_and_map(dn, 0);
+       if (prv->irq == NO_IRQ) {
+               dev_err(dev, "Error mapping IRQ!\n");
+               return -EINVAL;
+       }
+
+       retval = of_address_to_resource(dn, 0, &res);
+       if (retval) {
+               dev_err(dev, "Error parsing memory region!\n");
+               return retval;
+       }
+
+       chips_no = of_get_property(dn, "chips", &len);
+       if (!chips_no || len != sizeof(*chips_no)) {
+               dev_err(dev, "Invalid/missing 'chips' property!\n");
+               return -EINVAL;
+       }
+
+       regs_paddr = res.start;
+       regs_size = res.end - res.start + 1;
+
+       if (!devm_request_mem_region(dev, regs_paddr, regs_size, DRV_NAME)) {
+               dev_err(dev, "Error requesting memory region!\n");
+               return -EBUSY;
+       }
+
+       prv->regs = devm_ioremap(dev, regs_paddr, regs_size);
+       if (!prv->regs) {
+               dev_err(dev, "Error mapping memory region!\n");
+               return -ENOMEM;
+       }
+
+       mtd->name = "MPC5121 NAND";
+       chip->dev_ready = mpc5121_nfc_dev_ready;
+       chip->cmdfunc = mpc5121_nfc_command;
+       chip->read_byte = mpc5121_nfc_read_byte;
+       chip->read_word = mpc5121_nfc_read_word;
+       chip->read_buf = mpc5121_nfc_read_buf;
+       chip->write_buf = mpc5121_nfc_write_buf;
+       chip->verify_buf = mpc5121_nfc_verify_buf;
+       chip->select_chip = mpc5121_nfc_select_chip;
+       chip->options = NAND_NO_AUTOINCR | NAND_USE_FLASH_BBT;
+       chip->ecc.mode = NAND_ECC_SOFT;
+
+       /* Support external chip-select logic on ADS5121 board */
+       rootnode = of_find_node_by_path("/");
+       if (of_device_is_compatible(rootnode, "fsl,mpc5121ads")) {
+               retval = ads5121_chipselect_init(mtd);
+               if (retval) {
+                       dev_err(dev, "Chipselect init error!\n");
+                       of_node_put(rootnode);
+                       return retval;
+               }
+
+               chip->select_chip = ads5121_select_chip;
+       }
+       of_node_put(rootnode);
+
+       /* Enable NFC clock */
+       prv->clk = clk_get(dev, "nfc_clk");
+       if (!prv->clk) {
+               dev_err(dev, "Unable to acquire NFC clock!\n");
+               retval = -ENODEV;
+               goto error;
+       }
+
+       clk_enable(prv->clk);
+
+       /* Reset NAND Flash controller */
+       nfc_set(mtd, NFC_CONFIG1, NFC_RESET);
+       while (nfc_read(mtd, NFC_CONFIG1) & NFC_RESET) {
+               if (resettime++ >= NFC_RESET_TIMEOUT) {
+                       dev_err(dev, "Timeout while resetting NFC!\n");
+                       retval = -EINVAL;
+                       goto error;
+               }
+
+               udelay(1);
+       }
+
+       /* Enable write to NFC memory */
+       nfc_write(mtd, NFC_CONFIG, NFC_BLS_UNLOCKED);
+
+       /* Enable write to all NAND pages */
+       nfc_write(mtd, NFC_UNLOCKSTART_BLK0, 0x0000);
+       nfc_write(mtd, NFC_UNLOCKEND_BLK0, 0xFFFF);
+       nfc_write(mtd, NFC_WRPROT, NFC_WPC_UNLOCK);
+
+       /*
+        * Setup NFC:
+        *      - Big Endian transfers,
+        *      - Interrupt after full page read/write.
+        */
+       nfc_write(mtd, NFC_CONFIG1, NFC_BIG_ENDIAN | NFC_INT_MASK |
+                                                       NFC_FULL_PAGE_INT);
+
+       /* Set spare area size */
+       nfc_write(mtd, NFC_SPAS, mtd->oobsize >> 1);
+
+       init_waitqueue_head(&prv->irq_waitq);
+       retval = devm_request_irq(dev, prv->irq, &mpc5121_nfc_irq, 0, DRV_NAME,
+                                                                       mtd);
+       if (retval) {
+               dev_err(dev, "Error requesting IRQ!\n");
+               goto error;
+       }
+
+       /* Detect NAND chips */
+       if (nand_scan(mtd, *chips_no)) {
+               dev_err(dev, "NAND Flash not found !\n");
+               devm_free_irq(dev, prv->irq, mtd);
+               retval = -ENXIO;
+               goto error;
+       }
+
+       /* Set erase block size */
+       switch (mtd->erasesize / mtd->writesize) {
+       case 32:
+               nfc_set(mtd, NFC_CONFIG1, NFC_PPB_32);
+               break;
+
+       case 64:
+               nfc_set(mtd, NFC_CONFIG1, NFC_PPB_64);
+               break;
+
+       case 128:
+               nfc_set(mtd, NFC_CONFIG1, NFC_PPB_128);
+               break;
+
+       case 256:
+               nfc_set(mtd, NFC_CONFIG1, NFC_PPB_256);
+               break;
+
+       default:
+               dev_err(dev, "Unsupported NAND flash!\n");
+               devm_free_irq(dev, prv->irq, mtd);
+               retval = -ENXIO;
+               goto error;
+       }
+
+       dev_set_drvdata(dev, mtd);
+
+       /* Register device in MTD */
+#ifdef CONFIG_MTD_PARTITIONS
+       retval = parse_mtd_partitions(mtd, mpc5121_nfc_pprobes, &parts, 0);
+#ifdef CONFIG_MTD_OF_PARTS
+       if (retval == 0)
+               retval = of_mtd_parse_partitions(dev, dn, &parts);
+#endif
+       if (retval < 0) {
+               dev_err(dev, "Error parsing MTD partitions!\n");
+               devm_free_irq(dev, prv->irq, mtd);
+               retval = -EINVAL;
+               goto error;
+       }
+
+       if (retval > 0)
+               retval = add_mtd_partitions(mtd, parts, retval);
+       else
+#endif
+               retval = add_mtd_device(mtd);
+
+       if (retval) {
+               dev_err(dev, "Error adding MTD device!\n");
+               devm_free_irq(dev, prv->irq, mtd);
+               goto error;
+       }
+
+       return 0;
+error:
+       mpc5121_nfc_free(dev, mtd);
+       return retval;
+}
+
+static int __devexit mpc5121_nfc_remove(struct of_device *op)
+{
+       struct device *dev = &op->dev;
+       struct mtd_info *mtd = dev_get_drvdata(dev);
+       struct nand_chip *chip = mtd->priv;
+       struct mpc5121_nfc_prv *prv = chip->priv;
+
+       nand_release(mtd);
+       devm_free_irq(dev, prv->irq, mtd);
+       mpc5121_nfc_free(dev, mtd);
+
+       return 0;
+}
+
+static struct of_device_id mpc5121_nfc_match[] __devinitdata = {
+       { .compatible = "fsl,mpc5121-nfc", },
+       {},
+};
+
+static struct of_platform_driver mpc5121_nfc_driver = {
+       .match_table    = mpc5121_nfc_match,
+       .probe          = mpc5121_nfc_probe,
+       .remove         = __devexit_p(mpc5121_nfc_remove),
+       .driver         = {
+               .name   = DRV_NAME,
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init mpc5121_nfc_init(void)
+{
+       return of_register_platform_driver(&mpc5121_nfc_driver);
+}
+
+module_init(mpc5121_nfc_init);
+
+static void __exit mpc5121_nfc_cleanup(void)
+{
+       of_unregister_platform_driver(&mpc5121_nfc_driver);
+}
+
+module_exit(mpc5121_nfc_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MPC5121 NAND MTD driver");
+MODULE_LICENSE("GPL");
index b2900d8..2ba3be1 100644 (file)
@@ -638,6 +638,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
 
        case NAND_CMD_ERASE1:
        case NAND_CMD_ERASE2:
+       case NAND_CMD_RESET:
                send_cmd(host, command, false);
                mxc_do_addr_cycle(mtd, column, page_addr);
 
@@ -818,7 +819,7 @@ static int __init mxcnd_probe(struct platform_device *pdev)
        }
 
        /* first scan to find the device and get the page size */
-       if (nand_scan_ident(mtd, 1)) {
+       if (nand_scan_ident(mtd, 1, NULL)) {
                err = -ENXIO;
                goto escan;
        }
@@ -886,11 +887,14 @@ static int mxcnd_suspend(struct platform_device *pdev, pm_message_t state)
        int ret = 0;
 
        DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND suspend\n");
-       if (mtd) {
-               ret = mtd->suspend(mtd);
-               /* Disable the NFC clock */
-               clk_disable(host->clk);
-       }
+
+       ret = mtd->suspend(mtd);
+
+       /*
+        * nand_suspend locks the device for exclusive access, so
+        * the clock must already be off.
+        */
+       BUG_ON(!ret && host->clk_act);
 
        return ret;
 }
@@ -904,11 +908,7 @@ static int mxcnd_resume(struct platform_device *pdev)
 
        DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND resume\n");
 
-       if (mtd) {
-               /* Enable the NFC clock */
-               clk_enable(host->clk);
-               mtd->resume(mtd);
-       }
+       mtd->resume(mtd);
 
        return ret;
 }
index 8f2958f..b9dc65c 100644 (file)
@@ -108,6 +108,35 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
  */
 DEFINE_LED_TRIGGER(nand_led_trigger);
 
+static int check_offs_len(struct mtd_info *mtd,
+                                       loff_t ofs, uint64_t len)
+{
+       struct nand_chip *chip = mtd->priv;
+       int ret = 0;
+
+       /* Start address must align on block boundary */
+       if (ofs & ((1 << chip->phys_erase_shift) - 1)) {
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__);
+               ret = -EINVAL;
+       }
+
+       /* Length must align on block boundary */
+       if (len & ((1 << chip->phys_erase_shift) - 1)) {
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n",
+                                       __func__);
+               ret = -EINVAL;
+       }
+
+       /* Do not allow past end of device */
+       if (ofs + len > mtd->size) {
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: Past end of device\n",
+                                       __func__);
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
 /**
  * nand_release_device - [GENERIC] release chip
  * @mtd:       MTD device structure
@@ -335,14 +364,18 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
                bad = cpu_to_le16(chip->read_word(mtd));
                if (chip->badblockpos & 0x1)
                        bad >>= 8;
-               if ((bad & 0xFF) != 0xff)
-                       res = 1;
+               else
+                       bad &= 0xFF;
        } else {
                chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page);
-               if (chip->read_byte(mtd) != 0xff)
-                       res = 1;
+               bad = chip->read_byte(mtd);
        }
 
+       if (likely(chip->badblockbits == 8))
+               res = bad != 0xFF;
+       else
+               res = hweight8(bad) < chip->badblockbits;
+
        if (getchip)
                nand_release_device(mtd);
 
@@ -401,6 +434,11 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
 static int nand_check_wp(struct mtd_info *mtd)
 {
        struct nand_chip *chip = mtd->priv;
+
+       /* broken xD cards report WP despite being writable */
+       if (chip->options & NAND_BROKEN_XD)
+               return 0;
+
        /* Check the WP bit */
        chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
        return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;
@@ -744,9 +782,6 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state)
                        chip->state = FL_PM_SUSPENDED;
                        spin_unlock(lock);
                        return 0;
-               } else {
-                       spin_unlock(lock);
-                       return -EAGAIN;
                }
        }
        set_current_state(TASK_UNINTERRUPTIBLE);
@@ -834,6 +869,168 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
        return status;
 }
 
+/**
+ * __nand_unlock - [REPLACABLE] unlocks specified locked blockes
+ *
+ * @param mtd - mtd info
+ * @param ofs - offset to start unlock from
+ * @param len - length to unlock
+ * @invert -  when = 0, unlock the range of blocks within the lower and
+ *                      upper boundary address
+ *            whne = 1, unlock the range of blocks outside the boundaries
+ *                      of the lower and upper boundary address
+ *
+ * @return - unlock status
+ */
+static int __nand_unlock(struct mtd_info *mtd, loff_t ofs,
+                                       uint64_t len, int invert)
+{
+       int ret = 0;
+       int status, page;
+       struct nand_chip *chip = mtd->priv;
+
+       /* Submit address of first page to unlock */
+       page = ofs >> chip->page_shift;
+       chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask);
+
+       /* Submit address of last page to unlock */
+       page = (ofs + len) >> chip->page_shift;
+       chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1,
+                               (page | invert) & chip->pagemask);
+
+       /* Call wait ready function */
+       status = chip->waitfunc(mtd, chip);
+       udelay(1000);
+       /* See if device thinks it succeeded */
+       if (status & 0x01) {
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: Error status = 0x%08x\n",
+                                       __func__, status);
+               ret = -EIO;
+       }
+
+       return ret;
+}
+
+/**
+ * nand_unlock - [REPLACABLE] unlocks specified locked blockes
+ *
+ * @param mtd - mtd info
+ * @param ofs - offset to start unlock from
+ * @param len - length to unlock
+ *
+ * @return - unlock status
+ */
+int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+       int ret = 0;
+       int chipnr;
+       struct nand_chip *chip = mtd->priv;
+
+       DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n",
+                       __func__, (unsigned long long)ofs, len);
+
+       if (check_offs_len(mtd, ofs, len))
+               ret = -EINVAL;
+
+       /* Align to last block address if size addresses end of the device */
+       if (ofs + len == mtd->size)
+               len -= mtd->erasesize;
+
+       nand_get_device(chip, mtd, FL_UNLOCKING);
+
+       /* Shift to get chip number */
+       chipnr = ofs >> chip->chip_shift;
+
+       chip->select_chip(mtd, chipnr);
+
+       /* Check, if it is write protected */
+       if (nand_check_wp(mtd)) {
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n",
+                                       __func__);
+               ret = -EIO;
+               goto out;
+       }
+
+       ret = __nand_unlock(mtd, ofs, len, 0);
+
+out:
+       /* de-select the NAND device */
+       chip->select_chip(mtd, -1);
+
+       nand_release_device(mtd);
+
+       return ret;
+}
+
+/**
+ * nand_lock - [REPLACABLE] locks all blockes present in the device
+ *
+ * @param mtd - mtd info
+ * @param ofs - offset to start unlock from
+ * @param len - length to unlock
+ *
+ * @return - lock status
+ *
+ * This feature is not support in many NAND parts. 'Micron' NAND parts
+ * do have this feature, but it allows only to lock all blocks not for
+ * specified range for block.
+ *
+ * Implementing 'lock' feature by making use of 'unlock', for now.
+ */
+int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+{
+       int ret = 0;
+       int chipnr, status, page;
+       struct nand_chip *chip = mtd->priv;
+
+       DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n",
+                       __func__, (unsigned long long)ofs, len);
+
+       if (check_offs_len(mtd, ofs, len))
+               ret = -EINVAL;
+
+       nand_get_device(chip, mtd, FL_LOCKING);
+
+       /* Shift to get chip number */
+       chipnr = ofs >> chip->chip_shift;
+
+       chip->select_chip(mtd, chipnr);
+
+       /* Check, if it is write protected */
+       if (nand_check_wp(mtd)) {
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n",
+                                       __func__);
+               status = MTD_ERASE_FAILED;
+               ret = -EIO;
+               goto out;
+       }
+
+       /* Submit address of first page to lock */
+       page = ofs >> chip->page_shift;
+       chip->cmdfunc(mtd, NAND_CMD_LOCK, -1, page & chip->pagemask);
+
+       /* Call wait ready function */
+       status = chip->waitfunc(mtd, chip);
+       udelay(1000);
+       /* See if device thinks it succeeded */
+       if (status & 0x01) {
+               DEBUG(MTD_DEBUG_LEVEL0, "%s: Error status = 0x%08x\n",
+                                       __func__, status);
+               ret = -EIO;
+               goto out;
+       }
+
+       ret = __nand_unlock(mtd, ofs, len, 0x1);
+
+out:
+       /* de-select the NAND device */
+       chip->select_chip(mtd, -1);
+
+       nand_release_device(mtd);
+
+       return ret;
+}
+
 /**
  * nand_read_page_raw - [Intern] read raw page data without ecc
  * @mtd:       mtd info structure
@@ -1232,6 +1429,9 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
        int ret = 0;
        uint32_t readlen = ops->len;
        uint32_t oobreadlen = ops->ooblen;
+       uint32_t max_oobsize = ops->mode == MTD_OOB_AUTO ?
+               mtd->oobavail : mtd->oobsize;
+
        uint8_t *bufpoi, *oob, *buf;
 
        stats = mtd->ecc_stats;
@@ -1282,18 +1482,14 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
                        buf += bytes;
 
                        if (unlikely(oob)) {
-                               /* Raw mode does data:oob:data:oob */
-                               if (ops->mode != MTD_OOB_RAW) {
-                                       int toread = min(oobreadlen,
-                                               chip->ecc.layout->oobavail);
-                                       if (toread) {
-                                               oob = nand_transfer_oob(chip,
-                                                       oob, ops, toread);
-                                               oobreadlen -= toread;
-                                       }
-                               } else
-                                       buf = nand_transfer_oob(chip,
-                                               buf, ops, mtd->oobsize);
+
+                               int toread = min(oobreadlen, max_oobsize);
+
+                               if (toread) {
+                                       oob = nand_transfer_oob(chip,
+                                               oob, ops, toread);
+                                       oobreadlen -= toread;
+                               }
                        }
 
                        if (!(chip->options & NAND_NO_READRDY)) {
@@ -1880,11 +2076,9 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
  * @oob:       oob data buffer
  * @ops:       oob ops structure
  */
-static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
-                                 struct mtd_oob_ops *ops)
+static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len,
+                                               struct mtd_oob_ops *ops)
 {
-       size_t len = ops->ooblen;
-
        switch(ops->mode) {
 
        case MTD_OOB_PLACE:
@@ -1939,6 +2133,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
        int chipnr, realpage, page, blockmask, column;
        struct nand_chip *chip = mtd->priv;
        uint32_t writelen = ops->len;
+
+       uint32_t oobwritelen = ops->ooblen;
+       uint32_t oobmaxlen = ops->mode == MTD_OOB_AUTO ?
+                               mtd->oobavail : mtd->oobsize;
+
        uint8_t *oob = ops->oobbuf;
        uint8_t *buf = ops->datbuf;
        int ret, subpage;
@@ -1980,6 +2179,10 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
        if (likely(!oob))
                memset(chip->oob_poi, 0xff, mtd->oobsize);
 
+       /* Don't allow multipage oob writes with offset */
+       if (ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen))
+               return -EINVAL;
+
        while(1) {
                int bytes = mtd->writesize;
                int cached = writelen > bytes && page != blockmask;
@@ -1995,8 +2198,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
                        wbuf = chip->buffers->databuf;
                }
 
-               if (unlikely(oob))
-                       oob = nand_fill_oob(chip, oob, ops);
+               if (unlikely(oob)) {
+                       size_t len = min(oobwritelen, oobmaxlen);
+                       oob = nand_fill_oob(chip, oob, len, ops);
+                       oobwritelen -= len;
+               }
 
                ret = chip->write_page(mtd, chip, wbuf, page, cached,
                                       (ops->mode == MTD_OOB_RAW));
@@ -2170,7 +2376,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
                chip->pagebuf = -1;
 
        memset(chip->oob_poi, 0xff, mtd->oobsize);
-       nand_fill_oob(chip, ops->oobbuf, ops);
+       nand_fill_oob(chip, ops->oobbuf, ops->ooblen, ops);
        status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
        memset(chip->oob_poi, 0xff, mtd->oobsize);
 
@@ -2293,25 +2499,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
                                __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, "%s: Unaligned address\n", __func__);
+       if (check_offs_len(mtd, instr->addr, instr->len))
                return -EINVAL;
-       }
-
-       /* Length must align on block boundary */
-       if (instr->len & ((1 << chip->phys_erase_shift) - 1)) {
-               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, "%s: Erase past end of device\n",
-                                       __func__);
-               return -EINVAL;
-       }
 
        instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
 
@@ -2582,10 +2771,10 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
  */
 static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                                                  struct nand_chip *chip,
-                                                 int busw, int *maf_id)
+                                                 int busw, int *maf_id,
+                                                 struct nand_flash_dev *type)
 {
-       struct nand_flash_dev *type = NULL;
-       int i, dev_id, maf_idx;
+       int dev_id, maf_idx;
        int tmp_id, tmp_manf;
 
        /* Select the device */
@@ -2624,15 +2813,14 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                return ERR_PTR(-ENODEV);
        }
 
-       /* Lookup the flash id */
-       for (i = 0; nand_flash_ids[i].name != NULL; i++) {
-               if (dev_id == nand_flash_ids[i].id) {
-                       type =  &nand_flash_ids[i];
-                       break;
-               }
-       }
-
        if (!type)
+               type = nand_flash_ids;
+
+       for (; type->name != NULL; type++)
+               if (dev_id == type->id)
+                        break;
+
+       if (!type->name)
                return ERR_PTR(-ENODEV);
 
        if (!mtd->name)
@@ -2704,6 +2892,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
        /* Set the bad block position */
        chip->badblockpos = mtd->writesize > 512 ?
                NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
+       chip->badblockbits = 8;
 
        /* Get chip options, preserve non chip based options */
        chip->options &= ~NAND_CHIPOPTIONS_MSK;
@@ -2741,13 +2930,15 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
  * nand_scan_ident - [NAND Interface] Scan for the NAND device
  * @mtd:            MTD device structure
  * @maxchips:       Number of chips to scan for
+ * @table:          Alternative NAND ID table
  *
  * This is the first phase of the normal nand_scan() function. It
  * reads the flash ID and sets up MTD fields accordingly.
  *
  * The mtd->owner field must be set to the module of the caller.
  */
-int nand_scan_ident(struct mtd_info *mtd, int maxchips)
+int nand_scan_ident(struct mtd_info *mtd, int maxchips,
+                   struct nand_flash_dev *table)
 {
        int i, busw, nand_maf_id;
        struct nand_chip *chip = mtd->priv;
@@ -2759,7 +2950,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips)
        nand_set_defaults(chip, busw);
 
        /* Read the flash type */
-       type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);
+       type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, table);
 
        if (IS_ERR(type)) {
                if (!(chip->options & NAND_SCAN_SILENT_NODEV))
@@ -2989,7 +3180,8 @@ int nand_scan_tail(struct mtd_info *mtd)
 
        /* Fill in remaining MTD driver data */
        mtd->type = MTD_NANDFLASH;
-       mtd->flags = MTD_CAP_NANDFLASH;
+       mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM :
+                                               MTD_CAP_NANDFLASH;
        mtd->erase = nand_erase;
        mtd->point = NULL;
        mtd->unpoint = NULL;
@@ -3050,7 +3242,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
                BUG();
        }
 
-       ret = nand_scan_ident(mtd, maxchips);
+       ret = nand_scan_ident(mtd, maxchips, NULL);
        if (!ret)
                ret = nand_scan_tail(mtd);
        return ret;
@@ -3077,6 +3269,8 @@ void nand_release(struct mtd_info *mtd)
                kfree(chip->buffers);
 }
 
+EXPORT_SYMBOL_GPL(nand_lock);
+EXPORT_SYMBOL_GPL(nand_unlock);
 EXPORT_SYMBOL_GPL(nand_scan);
 EXPORT_SYMBOL_GPL(nand_scan_ident);
 EXPORT_SYMBOL_GPL(nand_scan_tail);
index 55c23e5..387c45c 100644 (file)
@@ -237,15 +237,33 @@ static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
                         size_t len)
 {
        struct mtd_oob_ops ops;
+       int res;
 
        ops.mode = MTD_OOB_RAW;
        ops.ooboffs = 0;
        ops.ooblen = mtd->oobsize;
-       ops.oobbuf = buf;
-       ops.datbuf = buf;
-       ops.len = len;
 
-       return mtd->read_oob(mtd, offs, &ops);
+
+       while (len > 0) {
+               if (len <= mtd->writesize) {
+                       ops.oobbuf = buf + len;
+                       ops.datbuf = buf;
+                       ops.len = len;
+                       return mtd->read_oob(mtd, offs, &ops);
+               } else {
+                       ops.oobbuf = buf + mtd->writesize;
+                       ops.datbuf = buf;
+                       ops.len = mtd->writesize;
+                       res = mtd->read_oob(mtd, offs, &ops);
+
+                       if (res)
+                               return res;
+               }
+
+               buf += mtd->oobsize + mtd->writesize;
+               len -= mtd->writesize;
+       }
+       return 0;
 }
 
 /*
index 7cec2cd..198b304 100644 (file)
@@ -167,18 +167,27 @@ static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize,
        int numToRead = 16;     /* There are 16 bytes per sector in the OOB */
 
        /* ECC is already paused when this function is called */
+       if (pageSize != NAND_DATA_ACCESS_SIZE) {
+               /* skip BI */
+#if defined(__KERNEL__) && !defined(STANDALONE)
+               *oobp++ = REG_NAND_DATA8;
+#else
+               REG_NAND_DATA8;
+#endif
+               numToRead--;
+       }
 
-       if (pageSize == NAND_DATA_ACCESS_SIZE) {
-               while (numToRead > numEccBytes) {
-                       /* skip free oob region */
+       while (numToRead > numEccBytes) {
+               /* skip free oob region */
 #if defined(__KERNEL__) && !defined(STANDALONE)
-                       *oobp++ = REG_NAND_DATA8;
+               *oobp++ = REG_NAND_DATA8;
 #else
-                       REG_NAND_DATA8;
+               REG_NAND_DATA8;
 #endif
-                       numToRead--;
-               }
+               numToRead--;
+       }
 
+       if (pageSize == NAND_DATA_ACCESS_SIZE) {
                /* read ECC bytes before BI */
                nand_bcm_umi_bch_resume_read_ecc_calc();
 
@@ -190,6 +199,7 @@ static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize,
 #else
                        eccCalc[eccPos++] = REG_NAND_DATA8;
 #endif
+                       numToRead--;
                }
 
                nand_bcm_umi_bch_pause_read_ecc_calc();
@@ -204,49 +214,18 @@ static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize,
                        numToRead--;
                }
 
-               /* read ECC bytes */
-               nand_bcm_umi_bch_resume_read_ecc_calc();
-               while (numToRead) {
-#if defined(__KERNEL__) && !defined(STANDALONE)
-                       *oobp = REG_NAND_DATA8;
-                       eccCalc[eccPos++] = *oobp;
-                       oobp++;
-#else
-                       eccCalc[eccPos++] = REG_NAND_DATA8;
-#endif
-                       numToRead--;
-               }
-       } else {
-               /* skip BI */
+       }
+       /* read ECC bytes */
+       nand_bcm_umi_bch_resume_read_ecc_calc();
+       while (numToRead) {
 #if defined(__KERNEL__) && !defined(STANDALONE)
-               *oobp++ = REG_NAND_DATA8;
+               *oobp = REG_NAND_DATA8;
+               eccCalc[eccPos++] = *oobp;
+               oobp++;
 #else
-               REG_NAND_DATA8;
+               eccCalc[eccPos++] = REG_NAND_DATA8;
 #endif
                numToRead--;
-
-               while (numToRead > numEccBytes) {
-                       /* skip free oob region */
-#if defined(__KERNEL__) && !defined(STANDALONE)
-                       *oobp++ = REG_NAND_DATA8;
-#else
-                       REG_NAND_DATA8;
-#endif
-                       numToRead--;
-               }
-
-               /* read ECC bytes */
-               nand_bcm_umi_bch_resume_read_ecc_calc();
-               while (numToRead) {
-#if defined(__KERNEL__) && !defined(STANDALONE)
-                       *oobp = REG_NAND_DATA8;
-                       eccCalc[eccPos++] = *oobp;
-                       oobp++;
-#else
-                       eccCalc[eccPos++] = REG_NAND_DATA8;
-#endif
-                       numToRead--;
-               }
        }
 }
 
index 7281000..8a0a5d1 100644 (file)
@@ -80,6 +80,9 @@
 #ifndef CONFIG_NANDSIM_DBG
 #define CONFIG_NANDSIM_DBG        0
 #endif
+#ifndef CONFIG_NANDSIM_MAX_PARTS
+#define CONFIG_NANDSIM_MAX_PARTS  32
+#endif
 
 static uint first_id_byte  = CONFIG_NANDSIM_FIRST_ID_BYTE;
 static uint second_id_byte = CONFIG_NANDSIM_SECOND_ID_BYTE;
@@ -94,7 +97,7 @@ static uint bus_width      = CONFIG_NANDSIM_BUS_WIDTH;
 static uint do_delays      = CONFIG_NANDSIM_DO_DELAYS;
 static uint log            = CONFIG_NANDSIM_LOG;
 static uint dbg            = CONFIG_NANDSIM_DBG;
-static unsigned long parts[MAX_MTD_DEVICES];
+static unsigned long parts[CONFIG_NANDSIM_MAX_PARTS];
 static unsigned int parts_num;
 static char *badblocks = NULL;
 static char *weakblocks = NULL;
@@ -135,8 +138,8 @@ MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read I
 MODULE_PARM_DESC(access_delay,   "Initial page access delay (microseconds)");
 MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds");
 MODULE_PARM_DESC(erase_delay,    "Sector erase delay (milliseconds)");
-MODULE_PARM_DESC(output_cycle,   "Word output (from flash) time (nanodeconds)");
-MODULE_PARM_DESC(input_cycle,    "Word input (to flash) time (nanodeconds)");
+MODULE_PARM_DESC(output_cycle,   "Word output (from flash) time (nanoseconds)");
+MODULE_PARM_DESC(input_cycle,    "Word input (to flash) time (nanoseconds)");
 MODULE_PARM_DESC(bus_width,      "Chip's bus width (8- or 16-bit)");
 MODULE_PARM_DESC(do_delays,      "Simulate NAND delays using busy-waits if not zero");
 MODULE_PARM_DESC(log,            "Perform logging if not zero");
@@ -288,7 +291,7 @@ union ns_mem {
  * The structure which describes all the internal simulator data.
  */
 struct nandsim {
-       struct mtd_partition partitions[MAX_MTD_DEVICES];
+       struct mtd_partition partitions[CONFIG_NANDSIM_MAX_PARTS];
        unsigned int nbparts;
 
        uint busw;              /* flash chip bus width (8 or 16) */
index 6612341..59cbf66 100644 (file)
@@ -104,21 +104,21 @@ static int nomadik_nand_probe(struct platform_device *pdev)
                ret = -EIO;
                goto err_unmap;
        }
-       host->addr_va = ioremap(res->start, res->end - res->start + 1);
+       host->addr_va = ioremap(res->start, resource_size(res));
 
        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);
+       host->data_va = ioremap(res->start, resource_size(res));
 
        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);
+       host->cmd_va = ioremap(res->start, resource_size(res));
 
        if (!host->addr_va || !host->data_va || !host->cmd_va) {
                ret = -ENOMEM;
similarity index 63%
rename from drivers/mtd/nand/w90p910_nand.c
rename to drivers/mtd/nand/nuc900_nand.c
index 7680e73..6eddf73 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009 Nuvoton technology corporation.
+ * Copyright © 2009 Nuvoton technology corporation.
  *
  * Wan ZongShun <mcuos.com@gmail.com>
  *
@@ -55,7 +55,7 @@
 #define write_addr_reg(dev, val)       \
        __raw_writel((val), (dev)->reg + REG_SMADDR)
 
-struct w90p910_nand {
+struct nuc900_nand {
        struct mtd_info mtd;
        struct nand_chip chip;
        void __iomem *reg;
@@ -76,49 +76,49 @@ static const struct mtd_partition partitions[] = {
        }
 };
 
-static unsigned char w90p910_nand_read_byte(struct mtd_info *mtd)
+static unsigned char nuc900_nand_read_byte(struct mtd_info *mtd)
 {
        unsigned char ret;
-       struct w90p910_nand *nand;
+       struct nuc900_nand *nand;
 
-       nand = container_of(mtd, struct w90p910_nand, mtd);
+       nand = container_of(mtd, struct nuc900_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)
+static void nuc900_nand_read_buf(struct mtd_info *mtd,
+                                unsigned char *buf, int len)
 {
        int i;
-       struct w90p910_nand *nand;
+       struct nuc900_nand *nand;
 
-       nand = container_of(mtd, struct w90p910_nand, mtd);
+       nand = container_of(mtd, struct nuc900_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)
+static void nuc900_nand_write_buf(struct mtd_info *mtd,
+                                 const unsigned char *buf, int len)
 {
        int i;
-       struct w90p910_nand *nand;
+       struct nuc900_nand *nand;
 
-       nand = container_of(mtd, struct w90p910_nand, mtd);
+       nand = container_of(mtd, struct nuc900_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)
+static int nuc900_verify_buf(struct mtd_info *mtd,
+                            const unsigned char *buf, int len)
 {
        int i;
-       struct w90p910_nand *nand;
+       struct nuc900_nand *nand;
 
-       nand = container_of(mtd, struct w90p910_nand, mtd);
+       nand = container_of(mtd, struct nuc900_nand, mtd);
 
        for (i = 0; i < len; i++) {
                if (buf[i] != (unsigned char)read_data_reg(nand))
@@ -128,7 +128,7 @@ static int w90p910_verify_buf(struct mtd_info *mtd,
        return 0;
 }
 
-static int w90p910_check_rb(struct w90p910_nand *nand)
+static int nuc900_check_rb(struct nuc900_nand *nand)
 {
        unsigned int val;
        spin_lock(&nand->lock);
@@ -139,24 +139,24 @@ static int w90p910_check_rb(struct w90p910_nand *nand)
        return val;
 }
 
-static int w90p910_nand_devready(struct mtd_info *mtd)
+static int nuc900_nand_devready(struct mtd_info *mtd)
 {
-       struct w90p910_nand *nand;
+       struct nuc900_nand *nand;
        int ready;
 
-       nand = container_of(mtd, struct w90p910_nand, mtd);
+       nand = container_of(mtd, struct nuc900_nand, mtd);
 
-       ready = (w90p910_check_rb(nand)) ? 1 : 0;
+       ready = (nuc900_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)
+static void nuc900_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;
+       struct nuc900_nand *nand;
 
-       nand = container_of(mtd, struct w90p910_nand, mtd);
+       nand = container_of(mtd, struct nuc900_nand, mtd);
 
        if (command == NAND_CMD_READOOB) {
                column += mtd->writesize;
@@ -212,7 +212,7 @@ static void w90p910_nand_command_lp(struct mtd_info *mtd,
                write_cmd_reg(nand, NAND_CMD_STATUS);
                write_cmd_reg(nand, command);
 
-               while (!w90p910_check_rb(nand))
+               while (!nuc900_check_rb(nand))
                        ;
 
                return;
@@ -241,7 +241,7 @@ static void w90p910_nand_command_lp(struct mtd_info *mtd,
 }
 
 
-static void w90p910_nand_enable(struct w90p910_nand *nand)
+static void nuc900_nand_enable(struct nuc900_nand *nand)
 {
        unsigned int val;
        spin_lock(&nand->lock);
@@ -262,37 +262,37 @@ static void w90p910_nand_enable(struct w90p910_nand *nand)
        spin_unlock(&nand->lock);
 }
 
-static int __devinit w90p910_nand_probe(struct platform_device *pdev)
+static int __devinit nuc900_nand_probe(struct platform_device *pdev)
 {
-       struct w90p910_nand *w90p910_nand;
+       struct nuc900_nand *nuc900_nand;
        struct nand_chip *chip;
        int retval;
        struct resource *res;
 
        retval = 0;
 
-       w90p910_nand = kzalloc(sizeof(struct w90p910_nand), GFP_KERNEL);
-       if (!w90p910_nand)
+       nuc900_nand = kzalloc(sizeof(struct nuc900_nand), GFP_KERNEL);
+       if (!nuc900_nand)
                return -ENOMEM;
-       chip = &(w90p910_nand->chip);
+       chip = &(nuc900_nand->chip);
 
-       w90p910_nand->mtd.priv  = chip;
-       w90p910_nand->mtd.owner = THIS_MODULE;
-       spin_lock_init(&w90p910_nand->lock);
+       nuc900_nand->mtd.priv   = chip;
+       nuc900_nand->mtd.owner  = THIS_MODULE;
+       spin_lock_init(&nuc900_nand->lock);
 
-       w90p910_nand->clk = clk_get(&pdev->dev, NULL);
-       if (IS_ERR(w90p910_nand->clk)) {
+       nuc900_nand->clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(nuc900_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;
+       clk_enable(nuc900_nand->clk);
+
+       chip->cmdfunc           = nuc900_nand_command_lp;
+       chip->dev_ready         = nuc900_nand_devready;
+       chip->read_byte         = nuc900_nand_read_byte;
+       chip->write_buf         = nuc900_nand_write_buf;
+       chip->read_buf          = nuc900_nand_read_buf;
+       chip->verify_buf        = nuc900_verify_buf;
        chip->chip_delay        = 50;
        chip->options           = 0;
        chip->ecc.mode          = NAND_ECC_SOFT;
@@ -308,75 +308,75 @@ static int __devinit w90p910_nand_probe(struct platform_device *pdev)
                goto fail1;
        }
 
-       w90p910_nand->reg = ioremap(res->start, resource_size(res));
-       if (!w90p910_nand->reg) {
+       nuc900_nand->reg = ioremap(res->start, resource_size(res));
+       if (!nuc900_nand->reg) {
                retval = -ENOMEM;
                goto fail2;
        }
 
-       w90p910_nand_enable(w90p910_nand);
+       nuc900_nand_enable(nuc900_nand);
 
-       if (nand_scan(&(w90p910_nand->mtd), 1)) {
+       if (nand_scan(&(nuc900_nand->mtd), 1)) {
                retval = -ENXIO;
                goto fail3;
        }
 
-       add_mtd_partitions(&(w90p910_nand->mtd), partitions,
+       add_mtd_partitions(&(nuc900_nand->mtd), partitions,
                                                ARRAY_SIZE(partitions));
 
-       platform_set_drvdata(pdev, w90p910_nand);
+       platform_set_drvdata(pdev, nuc900_nand);
 
        return retval;
 
-fail3: iounmap(w90p910_nand->reg);
+fail3: iounmap(nuc900_nand->reg);
 fail2: release_mem_region(res->start, resource_size(res));
-fail1: kfree(w90p910_nand);
+fail1: kfree(nuc900_nand);
        return retval;
 }
 
-static int __devexit w90p910_nand_remove(struct platform_device *pdev)
+static int __devexit nuc900_nand_remove(struct platform_device *pdev)
 {
-       struct w90p910_nand *w90p910_nand = platform_get_drvdata(pdev);
+       struct nuc900_nand *nuc900_nand = platform_get_drvdata(pdev);
        struct resource *res;
 
-       iounmap(w90p910_nand->reg);
+       iounmap(nuc900_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);
+       clk_disable(nuc900_nand->clk);
+       clk_put(nuc900_nand->clk);
 
-       kfree(w90p910_nand);
+       kfree(nuc900_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),
+static struct platform_driver nuc900_nand_driver = {
+       .probe          = nuc900_nand_probe,
+       .remove         = __devexit_p(nuc900_nand_remove),
        .driver         = {
-               .name   = "w90p910-fmi",
+               .name   = "nuc900-fmi",
                .owner  = THIS_MODULE,
        },
 };
 
-static int __init w90p910_nand_init(void)
+static int __init nuc900_nand_init(void)
 {
-       return platform_driver_register(&w90p910_nand_driver);
+       return platform_driver_register(&nuc900_nand_driver);
 }
 
-static void __exit w90p910_nand_exit(void)
+static void __exit nuc900_nand_exit(void)
 {
-       platform_driver_unregister(&w90p910_nand_driver);
+       platform_driver_unregister(&nuc900_nand_driver);
 }
 
-module_init(w90p910_nand_init);
-module_exit(w90p910_nand_exit);
+module_init(nuc900_nand_init);
+module_exit(nuc900_nand_exit);
 
 MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
-MODULE_DESCRIPTION("w90p910 nand driver!");
+MODULE_DESCRIPTION("w90p910/NUC9xx nand driver!");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:w90p910-fmi");
+MODULE_ALIAS("platform:nuc900-fmi");
index 26aec00..ad07d39 100644 (file)
@@ -291,11 +291,14 @@ static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len)
        u32 *p = (u32 *)buf;
 
        /* take care of subpage reads */
-       for (; len % 4 != 0; ) {
-               *buf++ = __raw_readb(info->nand.IO_ADDR_R);
-               len--;
+       if (len % 4) {
+               if (info->nand.options & NAND_BUSWIDTH_16)
+                       omap_read_buf16(mtd, buf, len % 4);
+               else
+                       omap_read_buf8(mtd, buf, len % 4);
+               p = (u32 *) (buf + len % 4);
+               len -= len % 4;
        }
-       p = (u32 *) buf;
 
        /* configure and start prefetch transfer */
        ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x0);
@@ -501,7 +504,7 @@ static void omap_write_buf_dma_pref(struct mtd_info *mtd,
                omap_write_buf_pref(mtd, buf, len);
        else
                /* start transfer in DMA mode */
-               omap_nand_dma_transfer(mtd, buf, len, 0x1);
+               omap_nand_dma_transfer(mtd, (u_char *) buf, len, 0x1);
 }
 
 /**
@@ -1027,7 +1030,8 @@ out_free_info:
 static int omap_nand_remove(struct platform_device *pdev)
 {
        struct mtd_info *mtd = platform_get_drvdata(pdev);
-       struct omap_nand_info *info = mtd->priv;
+       struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+                                                       mtd);
 
        platform_set_drvdata(pdev, NULL);
        if (use_dma)
index f59c074..f16050c 100644 (file)
@@ -74,6 +74,7 @@ static int __init orion_nand_probe(struct platform_device *pdev)
        struct mtd_info *mtd;
        struct nand_chip *nc;
        struct orion_nand_data *board;
+       struct resource *res;
        void __iomem *io_base;
        int ret = 0;
 #ifdef CONFIG_MTD_PARTITIONS
@@ -89,8 +90,13 @@ static int __init orion_nand_probe(struct platform_device *pdev)
        }
        mtd = (struct mtd_info *)(nc + 1);
 
-       io_base = ioremap(pdev->resource[0].start,
-                       pdev->resource[0].end - pdev->resource[0].start + 1);
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               ret = -ENODEV;
+               goto no_res;
+       }
+
+       io_base = ioremap(res->start, resource_size(res));
        if (!io_base) {
                printk(KERN_ERR "orion_nand: ioremap failed\n");
                ret = -EIO;
index a8b9376..090a05c 100644 (file)
@@ -209,7 +209,7 @@ static int __devexit pasemi_nand_remove(struct of_device *ofdev)
        return 0;
 }
 
-static struct of_device_id pasemi_nand_match[] =
+static const struct of_device_id pasemi_nand_match[] =
 {
        {
                .compatible   = "pasemi,localbus-nand",
diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c
new file mode 100644 (file)
index 0000000..96bfbd8
--- /dev/null
@@ -0,0 +1,1139 @@
+/*
+ * Copyright © 2009 - Maxim Levitsky
+ * driver for Ricoh xD readers
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/delay.h>
+#include <asm/byteorder.h>
+#include <linux/sched.h>
+#include "sm_common.h"
+#include "r852.h"
+
+
+static int r852_enable_dma = 1;
+module_param(r852_enable_dma, bool, S_IRUGO);
+MODULE_PARM_DESC(r852_enable_dma, "Enable usage of the DMA (default)");
+
+static int debug;
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
+
+/* read register */
+static inline uint8_t r852_read_reg(struct r852_device *dev, int address)
+{
+       uint8_t reg = readb(dev->mmio + address);
+       return reg;
+}
+
+/* write register */
+static inline void r852_write_reg(struct r852_device *dev,
+                                               int address, uint8_t value)
+{
+       writeb(value, dev->mmio + address);
+       mmiowb();
+}
+
+
+/* read dword sized register */
+static inline uint32_t r852_read_reg_dword(struct r852_device *dev, int address)
+{
+       uint32_t reg = le32_to_cpu(readl(dev->mmio + address));
+       return reg;
+}
+
+/* write dword sized register */
+static inline void r852_write_reg_dword(struct r852_device *dev,
+                                                       int address, uint32_t value)
+{
+       writel(cpu_to_le32(value), dev->mmio + address);
+       mmiowb();
+}
+
+/* returns pointer to our private structure */
+static inline struct r852_device *r852_get_dev(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+       return (struct r852_device *)chip->priv;
+}
+
+
+/* check if controller supports dma */
+static void r852_dma_test(struct r852_device *dev)
+{
+       dev->dma_usable = (r852_read_reg(dev, R852_DMA_CAP) &
+               (R852_DMA1 | R852_DMA2)) == (R852_DMA1 | R852_DMA2);
+
+       if (!dev->dma_usable)
+               message("Non dma capable device detected, dma disabled");
+
+       if (!r852_enable_dma) {
+               message("disabling dma on user request");
+               dev->dma_usable = 0;
+       }
+}
+
+/*
+ * Enable dma. Enables ether first or second stage of the DMA,
+ * Expects dev->dma_dir and dev->dma_state be set
+ */
+static void r852_dma_enable(struct r852_device *dev)
+{
+       uint8_t dma_reg, dma_irq_reg;
+
+       /* Set up dma settings */
+       dma_reg = r852_read_reg_dword(dev, R852_DMA_SETTINGS);
+       dma_reg &= ~(R852_DMA_READ | R852_DMA_INTERNAL | R852_DMA_MEMORY);
+
+       if (dev->dma_dir)
+               dma_reg |= R852_DMA_READ;
+
+       if (dev->dma_state == DMA_INTERNAL) {
+               dma_reg |= R852_DMA_INTERNAL;
+               /* Precaution to make sure HW doesn't write */
+                       /* to random kernel memory */
+               r852_write_reg_dword(dev, R852_DMA_ADDR,
+                       cpu_to_le32(dev->phys_bounce_buffer));
+       } else {
+               dma_reg |= R852_DMA_MEMORY;
+               r852_write_reg_dword(dev, R852_DMA_ADDR,
+                       cpu_to_le32(dev->phys_dma_addr));
+       }
+
+       /* Precaution: make sure write reached the device */
+       r852_read_reg_dword(dev, R852_DMA_ADDR);
+
+       r852_write_reg_dword(dev, R852_DMA_SETTINGS, dma_reg);
+
+       /* Set dma irq */
+       dma_irq_reg = r852_read_reg_dword(dev, R852_DMA_IRQ_ENABLE);
+       r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE,
+               dma_irq_reg |
+               R852_DMA_IRQ_INTERNAL |
+               R852_DMA_IRQ_ERROR |
+               R852_DMA_IRQ_MEMORY);
+}
+
+/*
+ * Disable dma, called from the interrupt handler, which specifies
+ * success of the operation via 'error' argument
+ */
+static void r852_dma_done(struct r852_device *dev, int error)
+{
+       WARN_ON(dev->dma_stage == 0);
+
+       r852_write_reg_dword(dev, R852_DMA_IRQ_STA,
+                       r852_read_reg_dword(dev, R852_DMA_IRQ_STA));
+
+       r852_write_reg_dword(dev, R852_DMA_SETTINGS, 0);
+       r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, 0);
+
+       /* Precaution to make sure HW doesn't write to random kernel memory */
+       r852_write_reg_dword(dev, R852_DMA_ADDR,
+               cpu_to_le32(dev->phys_bounce_buffer));
+       r852_read_reg_dword(dev, R852_DMA_ADDR);
+
+       dev->dma_error = error;
+       dev->dma_stage = 0;
+
+       if (dev->phys_dma_addr && dev->phys_dma_addr != dev->phys_bounce_buffer)
+               pci_unmap_single(dev->pci_dev, dev->phys_dma_addr, R852_DMA_LEN,
+                       dev->dma_dir ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
+       complete(&dev->dma_done);
+}
+
+/*
+ * Wait, till dma is done, which includes both phases of it
+ */
+static int r852_dma_wait(struct r852_device *dev)
+{
+       long timeout = wait_for_completion_timeout(&dev->dma_done,
+                               msecs_to_jiffies(1000));
+       if (!timeout) {
+               dbg("timeout waiting for DMA interrupt");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+/*
+ * Read/Write one page using dma. Only pages can be read (512 bytes)
+*/
+static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read)
+{
+       int bounce = 0;
+       unsigned long flags;
+       int error;
+
+       dev->dma_error = 0;
+
+       /* Set dma direction */
+       dev->dma_dir = do_read;
+       dev->dma_stage = 1;
+
+       dbg_verbose("doing dma %s ", do_read ? "read" : "write");
+
+       /* Set intial dma state: for reading first fill on board buffer,
+         from device, for writes first fill the buffer  from memory*/
+       dev->dma_state = do_read ? DMA_INTERNAL : DMA_MEMORY;
+
+       /* if incoming buffer is not page aligned, we should do bounce */
+       if ((unsigned long)buf & (R852_DMA_LEN-1))
+               bounce = 1;
+
+       if (!bounce) {
+               dev->phys_dma_addr = pci_map_single(dev->pci_dev, (void *)buf,
+                       R852_DMA_LEN,
+                       (do_read ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE));
+
+               if (pci_dma_mapping_error(dev->pci_dev, dev->phys_dma_addr))
+                       bounce = 1;
+       }
+
+       if (bounce) {
+               dbg_verbose("dma: using bounce buffer");
+               dev->phys_dma_addr = dev->phys_bounce_buffer;
+               if (!do_read)
+                       memcpy(dev->bounce_buffer, buf, R852_DMA_LEN);
+       }
+
+       /* Enable DMA */
+       spin_lock_irqsave(&dev->irqlock, flags);
+       r852_dma_enable(dev);
+       spin_unlock_irqrestore(&dev->irqlock, flags);
+
+       /* Wait till complete */
+       error = r852_dma_wait(dev);
+
+       if (error) {
+               r852_dma_done(dev, error);
+               return;
+       }
+
+       if (do_read && bounce)
+               memcpy((void *)buf, dev->bounce_buffer, R852_DMA_LEN);
+}
+
+/*
+ * Program data lines of the nand chip to send data to it
+ */
+void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+       struct r852_device *dev = r852_get_dev(mtd);
+       uint32_t reg;
+
+       /* Don't allow any access to hardware if we suspect card removal */
+       if (dev->card_unstable)
+               return;
+
+       /* Special case for whole sector read */
+       if (len == R852_DMA_LEN && dev->dma_usable) {
+               r852_do_dma(dev, (uint8_t *)buf, 0);
+               return;
+       }
+
+       /* write DWORD chinks - faster */
+       while (len) {
+               reg = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
+               r852_write_reg_dword(dev, R852_DATALINE, reg);
+               buf += 4;
+               len -= 4;
+
+       }
+
+       /* write rest */
+       while (len)
+               r852_write_reg(dev, R852_DATALINE, *buf++);
+}
+
+/*
+ * Read data lines of the nand chip to retrieve data
+ */
+void r852_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+       struct r852_device *dev = r852_get_dev(mtd);
+       uint32_t reg;
+
+       if (dev->card_unstable) {
+               /* since we can't signal error here, at least, return
+                       predictable buffer */
+               memset(buf, 0, len);
+               return;
+       }
+
+       /* special case for whole sector read */
+       if (len == R852_DMA_LEN && dev->dma_usable) {
+               r852_do_dma(dev, buf, 1);
+               return;
+       }
+
+       /* read in dword sized chunks */
+       while (len >= 4) {
+
+               reg = r852_read_reg_dword(dev, R852_DATALINE);
+               *buf++ = reg & 0xFF;
+               *buf++ = (reg >> 8) & 0xFF;
+               *buf++ = (reg >> 16) & 0xFF;
+               *buf++ = (reg >> 24) & 0xFF;
+               len -= 4;
+       }
+
+       /* read the reset by bytes */
+       while (len--)
+               *buf++ = r852_read_reg(dev, R852_DATALINE);
+}
+
+/*
+ * Read one byte from nand chip
+ */
+static uint8_t r852_read_byte(struct mtd_info *mtd)
+{
+       struct r852_device *dev = r852_get_dev(mtd);
+
+       /* Same problem as in r852_read_buf.... */
+       if (dev->card_unstable)
+               return 0;
+
+       return r852_read_reg(dev, R852_DATALINE);
+}
+
+
+/*
+ * Readback the buffer to verify it
+ */
+int r852_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+       struct r852_device *dev = r852_get_dev(mtd);
+
+       /* We can't be sure about anything here... */
+       if (dev->card_unstable)
+               return -1;
+
+       /* This will never happen, unless you wired up a nand chip
+               with > 512 bytes page size to the reader */
+       if (len > SM_SECTOR_SIZE)
+               return 0;
+
+       r852_read_buf(mtd, dev->tmp_buffer, len);
+       return memcmp(buf, dev->tmp_buffer, len);
+}
+
+/*
+ * Control several chip lines & send commands
+ */
+void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl)
+{
+       struct r852_device *dev = r852_get_dev(mtd);
+
+       if (dev->card_unstable)
+               return;
+
+       if (ctrl & NAND_CTRL_CHANGE) {
+
+               dev->ctlreg &= ~(R852_CTL_DATA | R852_CTL_COMMAND |
+                                R852_CTL_ON | R852_CTL_CARDENABLE);
+
+               if (ctrl & NAND_ALE)
+                       dev->ctlreg |= R852_CTL_DATA;
+
+               if (ctrl & NAND_CLE)
+                       dev->ctlreg |= R852_CTL_COMMAND;
+
+               if (ctrl & NAND_NCE)
+                       dev->ctlreg |= (R852_CTL_CARDENABLE | R852_CTL_ON);
+               else
+                       dev->ctlreg &= ~R852_CTL_WRITE;
+
+               /* when write is stareted, enable write access */
+               if (dat == NAND_CMD_ERASE1)
+                       dev->ctlreg |= R852_CTL_WRITE;
+
+               r852_write_reg(dev, R852_CTL, dev->ctlreg);
+       }
+
+        /* HACK: NAND_CMD_SEQIN is called without NAND_CTRL_CHANGE, but we need
+               to set write mode */
+       if (dat == NAND_CMD_SEQIN && (dev->ctlreg & R852_CTL_COMMAND)) {
+               dev->ctlreg |= R852_CTL_WRITE;
+               r852_write_reg(dev, R852_CTL, dev->ctlreg);
+       }
+
+       if (dat != NAND_CMD_NONE)
+               r852_write_reg(dev, R852_DATALINE, dat);
+}
+
+/*
+ * Wait till card is ready.
+ * based on nand_wait, but returns errors on DMA error
+ */
+int r852_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+       struct r852_device *dev = (struct r852_device *)chip->priv;
+
+       unsigned long timeout;
+       int status;
+
+       timeout = jiffies + (chip->state == FL_ERASING ?
+               msecs_to_jiffies(400) : msecs_to_jiffies(20));
+
+       while (time_before(jiffies, timeout))
+               if (chip->dev_ready(mtd))
+                       break;
+
+       chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+       status = (int)chip->read_byte(mtd);
+
+       /* Unfortunelly, no way to send detailed error status... */
+       if (dev->dma_error) {
+               status |= NAND_STATUS_FAIL;
+               dev->dma_error = 0;
+       }
+       return status;
+}
+
+/*
+ * Check if card is ready
+ */
+
+int r852_ready(struct mtd_info *mtd)
+{
+       struct r852_device *dev = r852_get_dev(mtd);
+       return !(r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_BUSY);
+}
+
+
+/*
+ * Set ECC engine mode
+*/
+
+void r852_ecc_hwctl(struct mtd_info *mtd, int mode)
+{
+       struct r852_device *dev = r852_get_dev(mtd);
+
+       if (dev->card_unstable)
+               return;
+
+       switch (mode) {
+       case NAND_ECC_READ:
+       case NAND_ECC_WRITE:
+               /* enable ecc generation/check*/
+               dev->ctlreg |= R852_CTL_ECC_ENABLE;
+
+               /* flush ecc buffer */
+               r852_write_reg(dev, R852_CTL,
+                       dev->ctlreg | R852_CTL_ECC_ACCESS);
+
+               r852_read_reg_dword(dev, R852_DATALINE);
+               r852_write_reg(dev, R852_CTL, dev->ctlreg);
+               return;
+
+       case NAND_ECC_READSYN:
+               /* disable ecc generation */
+               dev->ctlreg &= ~R852_CTL_ECC_ENABLE;
+               r852_write_reg(dev, R852_CTL, dev->ctlreg);
+       }
+}
+
+/*
+ * Calculate ECC, only used for writes
+ */
+
+int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat,
+                                                       uint8_t *ecc_code)
+{
+       struct r852_device *dev = r852_get_dev(mtd);
+       struct sm_oob *oob = (struct sm_oob *)ecc_code;
+       uint32_t ecc1, ecc2;
+
+       if (dev->card_unstable)
+               return 0;
+
+       dev->ctlreg &= ~R852_CTL_ECC_ENABLE;
+       r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS);
+
+       ecc1 = r852_read_reg_dword(dev, R852_DATALINE);
+       ecc2 = r852_read_reg_dword(dev, R852_DATALINE);
+
+       oob->ecc1[0] = (ecc1) & 0xFF;
+       oob->ecc1[1] = (ecc1 >> 8) & 0xFF;
+       oob->ecc1[2] = (ecc1 >> 16) & 0xFF;
+
+       oob->ecc2[0] = (ecc2) & 0xFF;
+       oob->ecc2[1] = (ecc2 >> 8) & 0xFF;
+       oob->ecc2[2] = (ecc2 >> 16) & 0xFF;
+
+       r852_write_reg(dev, R852_CTL, dev->ctlreg);
+       return 0;
+}
+
+/*
+ * Correct the data using ECC, hw did almost everything for us
+ */
+
+int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat,
+                               uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+       uint16_t ecc_reg;
+       uint8_t ecc_status, err_byte;
+       int i, error = 0;
+
+       struct r852_device *dev = r852_get_dev(mtd);
+
+       if (dev->card_unstable)
+               return 0;
+
+       r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS);
+       ecc_reg = r852_read_reg_dword(dev, R852_DATALINE);
+       r852_write_reg(dev, R852_CTL, dev->ctlreg);
+
+       for (i = 0 ; i <= 1 ; i++) {
+
+               ecc_status = (ecc_reg >> 8) & 0xFF;
+
+               /* ecc uncorrectable error */
+               if (ecc_status & R852_ECC_FAIL) {
+                       dbg("ecc: unrecoverable error, in half %d", i);
+                       error = -1;
+                       goto exit;
+               }
+
+               /* correctable error */
+               if (ecc_status & R852_ECC_CORRECTABLE) {
+
+                       err_byte = ecc_reg & 0xFF;
+                       dbg("ecc: recoverable error, "
+                               "in half %d, byte %d, bit %d", i,
+                               err_byte, ecc_status & R852_ECC_ERR_BIT_MSK);
+
+                       dat[err_byte] ^=
+                               1 << (ecc_status & R852_ECC_ERR_BIT_MSK);
+                       error++;
+               }
+
+               dat += 256;
+               ecc_reg >>= 16;
+       }
+exit:
+       return error;
+}
+
+/*
+ * This is copy of nand_read_oob_std
+ * nand_read_oob_syndrome assumes we can send column address - we can't
+ */
+static int r852_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+                            int page, int sndcmd)
+{
+       if (sndcmd) {
+               chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+               sndcmd = 0;
+       }
+       chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+       return sndcmd;
+}
+
+/*
+ * Start the nand engine
+ */
+
+void r852_engine_enable(struct r852_device *dev)
+{
+       if (r852_read_reg_dword(dev, R852_HW) & R852_HW_UNKNOWN) {
+               r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON);
+               r852_write_reg_dword(dev, R852_HW, R852_HW_ENABLED);
+       } else {
+               r852_write_reg_dword(dev, R852_HW, R852_HW_ENABLED);
+               r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON);
+       }
+       msleep(300);
+       r852_write_reg(dev, R852_CTL, 0);
+}
+
+
+/*
+ * Stop the nand engine
+ */
+
+void r852_engine_disable(struct r852_device *dev)
+{
+       r852_write_reg_dword(dev, R852_HW, 0);
+       r852_write_reg(dev, R852_CTL, R852_CTL_RESET);
+}
+
+/*
+ * Test if card is present
+ */
+
+void r852_card_update_present(struct r852_device *dev)
+{
+       unsigned long flags;
+       uint8_t reg;
+
+       spin_lock_irqsave(&dev->irqlock, flags);
+       reg = r852_read_reg(dev, R852_CARD_STA);
+       dev->card_detected = !!(reg & R852_CARD_STA_PRESENT);
+       spin_unlock_irqrestore(&dev->irqlock, flags);
+}
+
+/*
+ * Update card detection IRQ state according to current card state
+ * which is read in r852_card_update_present
+ */
+void r852_update_card_detect(struct r852_device *dev)
+{
+       int card_detect_reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE);
+       dev->card_unstable = 0;
+
+       card_detect_reg &= ~(R852_CARD_IRQ_REMOVE | R852_CARD_IRQ_INSERT);
+       card_detect_reg |= R852_CARD_IRQ_GENABLE;
+
+       card_detect_reg |= dev->card_detected ?
+               R852_CARD_IRQ_REMOVE : R852_CARD_IRQ_INSERT;
+
+       r852_write_reg(dev, R852_CARD_IRQ_ENABLE, card_detect_reg);
+}
+
+ssize_t r852_media_type_show(struct device *sys_dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct mtd_info *mtd = container_of(sys_dev, struct mtd_info, dev);
+       struct r852_device *dev = r852_get_dev(mtd);
+       char *data = dev->sm ? "smartmedia" : "xd";
+
+       strcpy(buf, data);
+       return strlen(data);
+}
+
+DEVICE_ATTR(media_type, S_IRUGO, r852_media_type_show, NULL);
+
+
+/* Detect properties of card in slot */
+void r852_update_media_status(struct r852_device *dev)
+{
+       uint8_t reg;
+       unsigned long flags;
+       int readonly;
+
+       spin_lock_irqsave(&dev->irqlock, flags);
+       if (!dev->card_detected) {
+               message("card removed");
+               spin_unlock_irqrestore(&dev->irqlock, flags);
+               return ;
+       }
+
+       readonly  = r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_RO;
+       reg = r852_read_reg(dev, R852_DMA_CAP);
+       dev->sm = (reg & (R852_DMA1 | R852_DMA2)) && (reg & R852_SMBIT);
+
+       message("detected %s %s card in slot",
+               dev->sm ? "SmartMedia" : "xD",
+               readonly ? "readonly" : "writeable");
+
+       dev->readonly = readonly;
+       spin_unlock_irqrestore(&dev->irqlock, flags);
+}
+
+/*
+ * Register the nand device
+ * Called when the card is detected
+ */
+int r852_register_nand_device(struct r852_device *dev)
+{
+       dev->mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
+
+       if (!dev->mtd)
+               goto error1;
+
+       WARN_ON(dev->card_registred);
+
+       dev->mtd->owner = THIS_MODULE;
+       dev->mtd->priv = dev->chip;
+       dev->mtd->dev.parent = &dev->pci_dev->dev;
+
+       if (dev->readonly)
+               dev->chip->options |= NAND_ROM;
+
+       r852_engine_enable(dev);
+
+       if (sm_register_device(dev->mtd))
+               goto error2;
+
+       if (device_create_file(&dev->mtd->dev, &dev_attr_media_type))
+               message("can't create media type sysfs attribute");
+
+       dev->card_registred = 1;
+       return 0;
+error2:
+       kfree(dev->mtd);
+error1:
+       /* Force card redetect */
+       dev->card_detected = 0;
+       return -1;
+}
+
+/*
+ * Unregister the card
+ */
+
+void r852_unregister_nand_device(struct r852_device *dev)
+{
+       if (!dev->card_registred)
+               return;
+
+       device_remove_file(&dev->mtd->dev, &dev_attr_media_type);
+       nand_release(dev->mtd);
+       r852_engine_disable(dev);
+       dev->card_registred = 0;
+       kfree(dev->mtd);
+       dev->mtd = NULL;
+}
+
+/* Card state updater */
+void r852_card_detect_work(struct work_struct *work)
+{
+       struct r852_device *dev =
+               container_of(work, struct r852_device, card_detect_work.work);
+
+       r852_card_update_present(dev);
+       dev->card_unstable = 0;
+
+       /* False alarm */
+       if (dev->card_detected == dev->card_registred)
+               goto exit;
+
+       /* Read media properties */
+       r852_update_media_status(dev);
+
+       /* Register the card */
+       if (dev->card_detected)
+               r852_register_nand_device(dev);
+       else
+               r852_unregister_nand_device(dev);
+exit:
+       /* Update detection logic */
+       r852_update_card_detect(dev);
+}
+
+/* Ack + disable IRQ generation */
+static void r852_disable_irqs(struct r852_device *dev)
+{
+       uint8_t reg;
+       reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE);
+       r852_write_reg(dev, R852_CARD_IRQ_ENABLE, reg & ~R852_CARD_IRQ_MASK);
+
+       reg = r852_read_reg_dword(dev, R852_DMA_IRQ_ENABLE);
+       r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE,
+                                       reg & ~R852_DMA_IRQ_MASK);
+
+       r852_write_reg(dev, R852_CARD_IRQ_STA, R852_CARD_IRQ_MASK);
+       r852_write_reg_dword(dev, R852_DMA_IRQ_STA, R852_DMA_IRQ_MASK);
+}
+
+/* Interrupt handler */
+static irqreturn_t r852_irq(int irq, void *data)
+{
+       struct r852_device *dev = (struct r852_device *)data;
+
+       uint8_t card_status, dma_status;
+       unsigned long flags;
+       irqreturn_t ret = IRQ_NONE;
+
+       spin_lock_irqsave(&dev->irqlock, flags);
+
+       /* We can recieve shared interrupt while pci is suspended
+               in that case reads will return 0xFFFFFFFF.... */
+       if (dev->insuspend)
+               goto out;
+
+       /* handle card detection interrupts first */
+       card_status = r852_read_reg(dev, R852_CARD_IRQ_STA);
+       r852_write_reg(dev, R852_CARD_IRQ_STA, card_status);
+
+       if (card_status & (R852_CARD_IRQ_INSERT|R852_CARD_IRQ_REMOVE)) {
+
+               ret = IRQ_HANDLED;
+               dev->card_detected = !!(card_status & R852_CARD_IRQ_INSERT);
+
+               /* we shouldn't recieve any interrupts if we wait for card
+                       to settle */
+               WARN_ON(dev->card_unstable);
+
+               /* disable irqs while card is unstable */
+               /* this will timeout DMA if active, but better that garbage */
+               r852_disable_irqs(dev);
+
+               if (dev->card_unstable)
+                       goto out;
+
+               /* let, card state to settle a bit, and then do the work */
+               dev->card_unstable = 1;
+               queue_delayed_work(dev->card_workqueue,
+                       &dev->card_detect_work, msecs_to_jiffies(100));
+               goto out;
+       }
+
+
+       /* Handle dma interrupts */
+       dma_status = r852_read_reg_dword(dev, R852_DMA_IRQ_STA);
+       r852_write_reg_dword(dev, R852_DMA_IRQ_STA, dma_status);
+
+       if (dma_status & R852_DMA_IRQ_MASK) {
+
+               ret = IRQ_HANDLED;
+
+               if (dma_status & R852_DMA_IRQ_ERROR) {
+                       dbg("recieved dma error IRQ");
+                       r852_dma_done(dev, -EIO);
+                       goto out;
+               }
+
+               /* recieved DMA interrupt out of nowhere? */
+               WARN_ON_ONCE(dev->dma_stage == 0);
+
+               if (dev->dma_stage == 0)
+                       goto out;
+
+               /* done device access */
+               if (dev->dma_state == DMA_INTERNAL &&
+                               (dma_status & R852_DMA_IRQ_INTERNAL)) {
+
+                       dev->dma_state = DMA_MEMORY;
+                       dev->dma_stage++;
+               }
+
+               /* done memory DMA */
+               if (dev->dma_state == DMA_MEMORY &&
+                               (dma_status & R852_DMA_IRQ_MEMORY)) {
+                       dev->dma_state = DMA_INTERNAL;
+                       dev->dma_stage++;
+               }
+
+               /* Enable 2nd half of dma dance */
+               if (dev->dma_stage == 2)
+                       r852_dma_enable(dev);
+
+               /* Operation done */
+               if (dev->dma_stage == 3)
+                       r852_dma_done(dev, 0);
+               goto out;
+       }
+
+       /* Handle unknown interrupts */
+       if (dma_status)
+               dbg("bad dma IRQ status = %x", dma_status);
+
+       if (card_status & ~R852_CARD_STA_CD)
+               dbg("strange card status = %x", card_status);
+
+out:
+       spin_unlock_irqrestore(&dev->irqlock, flags);
+       return ret;
+}
+
+int  r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
+{
+       int error;
+       struct nand_chip *chip;
+       struct r852_device *dev;
+
+       /* pci initialization */
+       error = pci_enable_device(pci_dev);
+
+       if (error)
+               goto error1;
+
+       pci_set_master(pci_dev);
+
+       error = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32));
+       if (error)
+               goto error2;
+
+       error = pci_request_regions(pci_dev, DRV_NAME);
+
+       if (error)
+               goto error3;
+
+       error = -ENOMEM;
+
+       /* init nand chip, but register it only on card insert */
+       chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
+
+       if (!chip)
+               goto error4;
+
+       /* commands */
+       chip->cmd_ctrl = r852_cmdctl;
+       chip->waitfunc = r852_wait;
+       chip->dev_ready = r852_ready;
+
+       /* I/O */
+       chip->read_byte = r852_read_byte;
+       chip->read_buf = r852_read_buf;
+       chip->write_buf = r852_write_buf;
+       chip->verify_buf = r852_verify_buf;
+
+       /* ecc */
+       chip->ecc.mode = NAND_ECC_HW_SYNDROME;
+       chip->ecc.size = R852_DMA_LEN;
+       chip->ecc.bytes = SM_OOB_SIZE;
+       chip->ecc.hwctl = r852_ecc_hwctl;
+       chip->ecc.calculate = r852_ecc_calculate;
+       chip->ecc.correct = r852_ecc_correct;
+
+       /* TODO: hack */
+       chip->ecc.read_oob = r852_read_oob;
+
+       /* init our device structure */
+       dev = kzalloc(sizeof(struct r852_device), GFP_KERNEL);
+
+       if (!dev)
+               goto error5;
+
+       chip->priv = dev;
+       dev->chip = chip;
+       dev->pci_dev = pci_dev;
+       pci_set_drvdata(pci_dev, dev);
+
+       dev->bounce_buffer = pci_alloc_consistent(pci_dev, R852_DMA_LEN,
+               &dev->phys_bounce_buffer);
+
+       if (!dev->bounce_buffer)
+               goto error6;
+
+
+       error = -ENODEV;
+       dev->mmio = pci_ioremap_bar(pci_dev, 0);
+
+       if (!dev->mmio)
+               goto error7;
+
+       error = -ENOMEM;
+       dev->tmp_buffer = kzalloc(SM_SECTOR_SIZE, GFP_KERNEL);
+
+       if (!dev->tmp_buffer)
+               goto error8;
+
+       init_completion(&dev->dma_done);
+
+       dev->card_workqueue = create_freezeable_workqueue(DRV_NAME);
+
+       if (!dev->card_workqueue)
+               goto error9;
+
+       INIT_DELAYED_WORK(&dev->card_detect_work, r852_card_detect_work);
+
+       /* shutdown everything - precation */
+       r852_engine_disable(dev);
+       r852_disable_irqs(dev);
+
+       r852_dma_test(dev);
+
+       /*register irq handler*/
+       error = -ENODEV;
+       if (request_irq(pci_dev->irq, &r852_irq, IRQF_SHARED,
+                         DRV_NAME, dev))
+               goto error10;
+
+       dev->irq = pci_dev->irq;
+       spin_lock_init(&dev->irqlock);
+
+       /* kick initial present test */
+       dev->card_detected = 0;
+       r852_card_update_present(dev);
+       queue_delayed_work(dev->card_workqueue,
+               &dev->card_detect_work, 0);
+
+
+       printk(KERN_NOTICE DRV_NAME ": driver loaded succesfully\n");
+       return 0;
+
+error10:
+       destroy_workqueue(dev->card_workqueue);
+error9:
+       kfree(dev->tmp_buffer);
+error8:
+       pci_iounmap(pci_dev, dev->mmio);
+error7:
+       pci_free_consistent(pci_dev, R852_DMA_LEN,
+               dev->bounce_buffer, dev->phys_bounce_buffer);
+error6:
+       kfree(dev);
+error5:
+       kfree(chip);
+error4:
+       pci_release_regions(pci_dev);
+error3:
+error2:
+       pci_disable_device(pci_dev);
+error1:
+       return error;
+}
+
+void r852_remove(struct pci_dev *pci_dev)
+{
+       struct r852_device *dev = pci_get_drvdata(pci_dev);
+
+       /* Stop detect workqueue -
+               we are going to unregister the device anyway*/
+       cancel_delayed_work_sync(&dev->card_detect_work);
+       destroy_workqueue(dev->card_workqueue);
+
+       /* Unregister the device, this might make more IO */
+       r852_unregister_nand_device(dev);
+
+       /* Stop interrupts */
+       r852_disable_irqs(dev);
+       synchronize_irq(dev->irq);
+       free_irq(dev->irq, dev);
+
+       /* Cleanup */
+       kfree(dev->tmp_buffer);
+       pci_iounmap(pci_dev, dev->mmio);
+       pci_free_consistent(pci_dev, R852_DMA_LEN,
+               dev->bounce_buffer, dev->phys_bounce_buffer);
+
+       kfree(dev->chip);
+       kfree(dev);
+
+       /* Shutdown the PCI device */
+       pci_release_regions(pci_dev);
+       pci_disable_device(pci_dev);
+}
+
+void r852_shutdown(struct pci_dev *pci_dev)
+{
+       struct r852_device *dev = pci_get_drvdata(pci_dev);
+
+       cancel_delayed_work_sync(&dev->card_detect_work);
+       r852_disable_irqs(dev);
+       synchronize_irq(dev->irq);
+       pci_disable_device(pci_dev);
+}
+
+#ifdef CONFIG_PM
+int r852_suspend(struct device *device)
+{
+       struct r852_device *dev = pci_get_drvdata(to_pci_dev(device));
+       unsigned long flags;
+
+       if (dev->ctlreg & R852_CTL_CARDENABLE)
+               return -EBUSY;
+
+       /* First make sure the detect work is gone */
+       cancel_delayed_work_sync(&dev->card_detect_work);
+
+       /* Turn off the interrupts and stop the device */
+       r852_disable_irqs(dev);
+       r852_engine_disable(dev);
+
+       spin_lock_irqsave(&dev->irqlock, flags);
+       dev->insuspend = 1;
+       spin_unlock_irqrestore(&dev->irqlock, flags);
+
+       /* At that point, even if interrupt handler is running, it will quit */
+       /* So wait for this to happen explictly */
+       synchronize_irq(dev->irq);
+
+       /* If card was pulled off just during the suspend, which is very
+               unlikely, we will remove it on resume, it too late now
+               anyway... */
+       dev->card_unstable = 0;
+
+       pci_save_state(to_pci_dev(device));
+       return pci_prepare_to_sleep(to_pci_dev(device));
+}
+
+int r852_resume(struct device *device)
+{
+       struct r852_device *dev = pci_get_drvdata(to_pci_dev(device));
+       unsigned long flags;
+
+       /* Turn on the hardware */
+       pci_back_from_sleep(to_pci_dev(device));
+       pci_restore_state(to_pci_dev(device));
+
+       r852_disable_irqs(dev);
+       r852_card_update_present(dev);
+       r852_engine_disable(dev);
+
+
+       /* Now its safe for IRQ to run */
+       spin_lock_irqsave(&dev->irqlock, flags);
+       dev->insuspend = 0;
+       spin_unlock_irqrestore(&dev->irqlock, flags);
+
+
+       /* If card status changed, just do the work */
+       if (dev->card_detected != dev->card_registred) {
+               dbg("card was %s during low power state",
+                       dev->card_detected ? "added" : "removed");
+
+               queue_delayed_work(dev->card_workqueue,
+               &dev->card_detect_work, 1000);
+               return 0;
+       }
+
+       /* Otherwise, initialize the card */
+       if (dev->card_registred) {
+               r852_engine_enable(dev);
+               dev->chip->select_chip(dev->mtd, 0);
+               dev->chip->cmdfunc(dev->mtd, NAND_CMD_RESET, -1, -1);
+               dev->chip->select_chip(dev->mtd, -1);
+       }
+
+       /* Program card detection IRQ */
+       r852_update_card_detect(dev);
+       return 0;
+}
+#else
+#define r852_suspend   NULL
+#define r852_resume    NULL
+#endif
+
+static const struct pci_device_id r852_pci_id_tbl[] = {
+
+       { PCI_VDEVICE(RICOH, 0x0852), },
+       { },
+};
+
+MODULE_DEVICE_TABLE(pci, r852_pci_id_tbl);
+
+SIMPLE_DEV_PM_OPS(r852_pm_ops, r852_suspend, r852_resume);
+
+
+static struct pci_driver r852_pci_driver = {
+       .name           = DRV_NAME,
+       .id_table       = r852_pci_id_tbl,
+       .probe          = r852_probe,
+       .remove         = r852_remove,
+       .shutdown       = r852_shutdown,
+       .driver.pm      = &r852_pm_ops,
+};
+
+static __init int r852_module_init(void)
+{
+       return pci_register_driver(&r852_pci_driver);
+}
+
+static void __exit r852_module_exit(void)
+{
+       pci_unregister_driver(&r852_pci_driver);
+}
+
+module_init(r852_module_init);
+module_exit(r852_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>");
+MODULE_DESCRIPTION("Ricoh 85xx xD/smartmedia card reader driver");
diff --git a/drivers/mtd/nand/r852.h b/drivers/mtd/nand/r852.h
new file mode 100644 (file)
index 0000000..8096cc2
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright © 2009 - Maxim Levitsky
+ * driver for Ricoh xD readers
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/pci.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+#include <linux/mtd/nand.h>
+#include <linux/spinlock.h>
+
+
+/* nand interface + ecc
+   byte write/read does one cycle on nand data lines.
+   dword write/read does 4 cycles
+   if R852_CTL_ECC_ACCESS is set in R852_CTL, then dword read reads
+   results of ecc correction, if DMA read was done before.
+   If write was done two dword reads read generated ecc checksums
+*/
+#define        R852_DATALINE           0x00
+
+/* control register */
+#define R852_CTL               0x04
+#define R852_CTL_COMMAND       0x01    /* send command (#CLE)*/
+#define R852_CTL_DATA          0x02    /* read/write data (#ALE)*/
+#define R852_CTL_ON            0x04    /* only seem to controls the hd led, */
+                                       /* but has to be set on start...*/
+#define R852_CTL_RESET         0x08    /* unknown, set only on start once*/
+#define R852_CTL_CARDENABLE    0x10    /* probably (#CE) - always set*/
+#define R852_CTL_ECC_ENABLE    0x20    /* enable ecc engine */
+#define R852_CTL_ECC_ACCESS    0x40    /* read/write ecc via reg #0*/
+#define R852_CTL_WRITE         0x80    /* set when performing writes (#WP) */
+
+/* card detection status */
+#define R852_CARD_STA          0x05
+
+#define R852_CARD_STA_CD       0x01    /* state of #CD line, same as 0x04 */
+#define R852_CARD_STA_RO       0x02    /* card is readonly */
+#define R852_CARD_STA_PRESENT  0x04    /* card is present (#CD) */
+#define R852_CARD_STA_ABSENT   0x08    /* card is absent */
+#define R852_CARD_STA_BUSY     0x80    /* card is busy - (#R/B) */
+
+/* card detection irq status & enable*/
+#define R852_CARD_IRQ_STA      0x06    /* IRQ status */
+#define R852_CARD_IRQ_ENABLE   0x07    /* IRQ enable */
+
+#define R852_CARD_IRQ_CD       0x01    /* fire when #CD lights, same as 0x04*/
+#define R852_CARD_IRQ_REMOVE   0x04    /* detect card removal */
+#define R852_CARD_IRQ_INSERT   0x08    /* detect card insert */
+#define R852_CARD_IRQ_UNK1     0x10    /* unknown */
+#define R852_CARD_IRQ_GENABLE  0x80    /* general enable */
+#define R852_CARD_IRQ_MASK     0x1D
+
+
+
+/* hardware enable */
+#define R852_HW                        0x08
+#define R852_HW_ENABLED                0x01    /* hw enabled */
+#define R852_HW_UNKNOWN                0x80
+
+
+/* dma capabilities */
+#define R852_DMA_CAP           0x09
+#define R852_SMBIT             0x20    /* if set with bit #6 or bit #7, then */
+                                       /* hw is smartmedia */
+#define R852_DMA1              0x40    /* if set w/bit #7, dma is supported */
+#define R852_DMA2              0x80    /* if set w/bit #6, dma is supported */
+
+
+/* physical DMA address - 32 bit value*/
+#define R852_DMA_ADDR          0x0C
+
+
+/* dma settings */
+#define R852_DMA_SETTINGS      0x10
+#define R852_DMA_MEMORY                0x01    /* (memory <-> internal hw buffer) */
+#define R852_DMA_READ          0x02    /* 0 = write, 1 = read */
+#define R852_DMA_INTERNAL      0x04    /* (internal hw buffer <-> card) */
+
+/* dma IRQ status */
+#define R852_DMA_IRQ_STA               0x14
+
+/* dma IRQ enable */
+#define R852_DMA_IRQ_ENABLE    0x18
+
+#define R852_DMA_IRQ_MEMORY    0x01    /* (memory <-> internal hw buffer) */
+#define R852_DMA_IRQ_ERROR     0x02    /* error did happen */
+#define R852_DMA_IRQ_INTERNAL  0x04    /* (internal hw buffer <-> card) */
+#define R852_DMA_IRQ_MASK      0x07    /* mask of all IRQ bits */
+
+
+/* ECC syndrome format - read from reg #0 will return two copies of these for
+   each half of the page.
+   first byte is error byte location, and second, bit location + flags */
+#define R852_ECC_ERR_BIT_MSK   0x07    /* error bit location */
+#define R852_ECC_CORRECT               0x10    /* no errors - (guessed) */
+#define R852_ECC_CORRECTABLE   0x20    /* correctable error exist */
+#define R852_ECC_FAIL          0x40    /* non correctable error detected */
+
+#define R852_DMA_LEN           512
+
+#define DMA_INTERNAL   0
+#define DMA_MEMORY     1
+
+struct r852_device {
+       void __iomem *mmio;             /* mmio */
+       struct mtd_info *mtd;           /* mtd backpointer */
+       struct nand_chip *chip;         /* nand chip backpointer */
+       struct pci_dev *pci_dev;        /* pci backpointer */
+
+       /* dma area */
+       dma_addr_t phys_dma_addr;       /* bus address of buffer*/
+       struct completion dma_done;     /* data transfer done */
+
+       dma_addr_t phys_bounce_buffer;  /* bus address of bounce buffer */
+       uint8_t *bounce_buffer;         /* virtual address of bounce buffer */
+
+       int dma_dir;                    /* 1 = read, 0 = write */
+       int dma_stage;                  /* 0 - idle, 1 - first step,
+                                          2 - second step */
+
+       int dma_state;                  /* 0 = internal, 1 = memory */
+       int dma_error;                  /* dma errors */
+       int dma_usable;                 /* is it possible to use dma */
+
+       /* card status area */
+       struct delayed_work card_detect_work;
+       struct workqueue_struct *card_workqueue;
+       int card_registred;             /* card registered with mtd */
+       int card_detected;              /* card detected in slot */
+       int card_unstable;              /* whenever the card is inserted,
+                                          is not known yet */
+       int readonly;                   /* card is readonly */
+       int sm;                         /* Is card smartmedia */
+
+       /* interrupt handling */
+       spinlock_t irqlock;             /* IRQ protecting lock */
+       int irq;                        /* irq num */
+       int insuspend;                  /* device is suspended */
+
+       /* misc */
+       void *tmp_buffer;               /* temporary buffer */
+       uint8_t ctlreg;                 /* cached contents of control reg */
+};
+
+#define DRV_NAME "r852"
+
+
+#define dbg(format, ...) \
+       if (debug) \
+               printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__)
+
+#define dbg_verbose(format, ...) \
+       if (debug > 1) \
+               printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__)
+
+
+#define message(format, ...) \
+       printk(KERN_INFO DRV_NAME ": " format "\n", ## __VA_ARGS__)
index fa6e9c7..dc02dcd 100644 (file)
@@ -957,7 +957,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
 
        /* currently we assume we have the one resource */
        res  = pdev->resource;
-       size = res->end - res->start + 1;
+       size = resource_size(res);
 
        info->area = request_mem_region(res->start, size, pdev->name);
 
@@ -1013,7 +1013,8 @@ static int s3c24xx_nand_probe(struct platform_device *pdev)
                s3c2410_nand_init_chip(info, nmtd, sets);
 
                nmtd->scan_res = nand_scan_ident(&nmtd->mtd,
-                                                (sets) ? sets->nr_chips : 1);
+                                                (sets) ? sets->nr_chips : 1,
+                                                NULL);
 
                if (nmtd->scan_res == 0) {
                        s3c2410_nand_update_chip(info, nmtd);
index 1842df8..88c802c 100644 (file)
@@ -854,7 +854,7 @@ static int __devinit flctl_probe(struct platform_device *pdev)
                nand->read_word = flctl_read_word;
        }
 
-       ret = nand_scan_ident(flctl_mtd, 1);
+       ret = nand_scan_ident(flctl_mtd, 1, NULL);
        if (ret)
                goto err;
 
diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c
new file mode 100644 (file)
index 0000000..aae0b9a
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright © 2009 - Maxim Levitsky
+ * Common routines & support for xD format
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/mtd/nand.h>
+#include "sm_common.h"
+
+static struct nand_ecclayout nand_oob_sm = {
+       .eccbytes = 6,
+       .eccpos = {8, 9, 10, 13, 14, 15},
+       .oobfree = {
+               {.offset = 0 , .length = 4}, /* reserved */
+               {.offset = 6 , .length = 2}, /* LBA1 */
+               {.offset = 11, .length = 2}  /* LBA2 */
+       }
+};
+
+/* NOTE: This layout is is not compatabable with SmartMedia, */
+/* because the 256 byte devices have page depenent oob layout */
+/* However it does preserve the bad block markers */
+/* If you use smftl, it will bypass this and work correctly */
+/* If you not, then you break SmartMedia compliance anyway */
+
+static struct nand_ecclayout nand_oob_sm_small = {
+       .eccbytes = 3,
+       .eccpos = {0, 1, 2},
+       .oobfree = {
+               {.offset = 3 , .length = 2}, /* reserved */
+               {.offset = 6 , .length = 2}, /* LBA1 */
+       }
+};
+
+
+static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+       struct mtd_oob_ops ops;
+       struct sm_oob oob;
+       int ret, error = 0;
+
+       memset(&oob, -1, SM_OOB_SIZE);
+       oob.block_status = 0x0F;
+
+       /* As long as this function is called on erase block boundaries
+               it will work correctly for 256 byte nand */
+       ops.mode = MTD_OOB_PLACE;
+       ops.ooboffs = 0;
+       ops.ooblen = mtd->oobsize;
+       ops.oobbuf = (void *)&oob;
+       ops.datbuf = NULL;
+
+
+       ret = mtd->write_oob(mtd, ofs, &ops);
+       if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) {
+               printk(KERN_NOTICE
+                       "sm_common: can't mark sector at %i as bad\n",
+                                                               (int)ofs);
+               error = -EIO;
+       } else
+               mtd->ecc_stats.badblocks++;
+
+       return error;
+}
+
+
+static struct nand_flash_dev nand_smartmedia_flash_ids[] = {
+
+       /* SmartMedia */
+       {"SmartMedia 1MiB 5V",          0x6e, 256, 1, 0x1000, 0},
+       {"SmartMedia 1MiB 3,3V",        0xe8, 256, 1, 0x1000, 0},
+       {"SmartMedia 1MiB 3,3V",        0xec, 256, 1, 0x1000, 0},
+       {"SmartMedia 2MiB 3,3V",        0xea, 256, 2, 0x1000, 0},
+       {"SmartMedia 2MiB 5V",          0x64, 256, 2, 0x1000, 0},
+       {"SmartMedia 2MiB 3,3V ROM",    0x5d, 512, 2, 0x2000, NAND_ROM},
+       {"SmartMedia 4MiB 3,3V",        0xe3, 512, 4, 0x2000, 0},
+       {"SmartMedia 4MiB 3,3/5V",      0xe5, 512, 4, 0x2000, 0},
+       {"SmartMedia 4MiB 5V",          0x6b, 512, 4, 0x2000, 0},
+       {"SmartMedia 4MiB 3,3V ROM",    0xd5, 512, 4, 0x2000, NAND_ROM},
+       {"SmartMedia 8MiB 3,3V",        0xe6, 512, 8, 0x2000, 0},
+       {"SmartMedia 8MiB 3,3V ROM",    0xd6, 512, 8, 0x2000, NAND_ROM},
+
+#define XD_TYPEM       (NAND_NO_AUTOINCR | NAND_BROKEN_XD)
+       /* xD / SmartMedia */
+       {"SmartMedia/xD 16MiB 3,3V",    0x73, 512, 16, 0x4000, 0},
+       {"SmartMedia 16MiB 3,3V ROM",   0x57, 512, 16, 0x4000, NAND_ROM},
+       {"SmartMedia/xD 32MiB 3,3V",    0x75, 512, 32, 0x4000, 0},
+       {"SmartMedia 32MiB 3,3V ROM",   0x58, 512, 32, 0x4000, NAND_ROM},
+       {"SmartMedia/xD 64MiB 3,3V",    0x76, 512, 64, 0x4000, 0},
+       {"SmartMedia 64MiB 3,3V ROM",   0xd9, 512, 64, 0x4000, NAND_ROM},
+       {"SmartMedia/xD 128MiB 3,3V",   0x79, 512, 128, 0x4000, 0},
+       {"SmartMedia 128MiB 3,3V ROM",  0xda, 512, 128, 0x4000, NAND_ROM},
+       {"SmartMedia/xD 256MiB 3,3V",   0x71, 512, 256, 0x4000, XD_TYPEM},
+       {"SmartMedia 256MiB 3,3V ROM",  0x5b, 512, 256, 0x4000, NAND_ROM},
+
+       /* xD only */
+       {"xD 512MiB 3,3V",              0xDC, 512, 512, 0x4000, XD_TYPEM},
+       {"xD 1GiB 3,3V",                0xD3, 512, 1024, 0x4000, XD_TYPEM},
+       {"xD 2GiB 3,3V",                0xD5, 512, 2048, 0x4000, XD_TYPEM},
+       {NULL,}
+};
+
+int sm_register_device(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = (struct nand_chip *)mtd->priv;
+       int ret;
+
+       chip->options |= NAND_SKIP_BBTSCAN;
+
+       /* Scan for card properties */
+       ret = nand_scan_ident(mtd, 1, nand_smartmedia_flash_ids);
+
+       if (ret)
+               return ret;
+
+       /* Bad block marker postion */
+       chip->badblockpos = 0x05;
+       chip->badblockbits = 7;
+       chip->block_markbad = sm_block_markbad;
+
+       /* ECC layout */
+       if (mtd->writesize == SM_SECTOR_SIZE)
+               chip->ecc.layout = &nand_oob_sm;
+       else if (mtd->writesize == SM_SMALL_PAGE)
+               chip->ecc.layout = &nand_oob_sm_small;
+       else
+               return -ENODEV;
+
+       ret = nand_scan_tail(mtd);
+
+       if (ret)
+               return ret;
+
+       return add_mtd_device(mtd);
+}
+EXPORT_SYMBOL_GPL(sm_register_device);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>");
+MODULE_DESCRIPTION("Common SmartMedia/xD functions");
diff --git a/drivers/mtd/nand/sm_common.h b/drivers/mtd/nand/sm_common.h
new file mode 100644 (file)
index 0000000..18284f5
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright © 2009 - Maxim Levitsky
+ * Common routines & support for SmartMedia/xD format
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/bitops.h>
+#include <linux/mtd/mtd.h>
+
+/* Full oob structure as written on the flash */
+struct sm_oob {
+       uint32_t reserved;
+       uint8_t data_status;
+       uint8_t block_status;
+       uint8_t lba_copy1[2];
+       uint8_t ecc2[3];
+       uint8_t lba_copy2[2];
+       uint8_t ecc1[3];
+} __attribute__((packed));
+
+
+/* one sector is always 512 bytes, but it can consist of two nand pages */
+#define SM_SECTOR_SIZE         512
+
+/* oob area is also 16 bytes, but might be from two pages */
+#define SM_OOB_SIZE            16
+
+/* This is maximum zone size, and all devices that have more that one zone
+   have this size */
+#define SM_MAX_ZONE_SIZE       1024
+
+/* support for small page nand */
+#define SM_SMALL_PAGE          256
+#define SM_SMALL_OOB_SIZE      8
+
+
+extern int sm_register_device(struct mtd_info *mtd);
+
+
+static inline int sm_sector_valid(struct sm_oob *oob)
+{
+       return hweight16(oob->data_status) >= 5;
+}
+
+static inline int sm_block_valid(struct sm_oob *oob)
+{
+       return hweight16(oob->block_status) >= 7;
+}
+
+static inline int sm_block_erased(struct sm_oob *oob)
+{
+       static const uint32_t erased_pattern[4] = {
+               0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+
+       /* First test for erased block */
+       if (!memcmp(oob, erased_pattern, sizeof(*oob)))
+               return 1;
+       return 0;
+}
index a4519a7..b37cbde 100644 (file)
@@ -220,7 +220,7 @@ static int __devinit socrates_nand_probe(struct of_device *ofdev,
        dev_set_drvdata(&ofdev->dev, host);
 
        /* first scan to find the device and get the page size */
-       if (nand_scan_ident(mtd, 1)) {
+       if (nand_scan_ident(mtd, 1, NULL)) {
                res = -ENXIO;
                goto out;
        }
@@ -290,7 +290,7 @@ static int __devexit socrates_nand_remove(struct of_device *ofdev)
        return 0;
 }
 
-static struct of_device_id socrates_nand_match[] =
+static const struct of_device_id socrates_nand_match[] =
 {
        {
                .compatible   = "abb,socrates-nand",
index 92c7334..65fa469 100644 (file)
@@ -318,7 +318,7 @@ static int tmio_nand_correct_data(struct mtd_info *mtd, unsigned char *buf,
 
 static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio)
 {
-       struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data;
+       struct mfd_cell *cell = dev_get_platdata(&dev->dev);
        int ret;
 
        if (cell->enable) {
@@ -362,7 +362,7 @@ static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio)
 
 static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio)
 {
-       struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data;
+       struct mfd_cell *cell = dev_get_platdata(&dev->dev);
 
        tmio_iowrite8(FCR_MODE_POWER_OFF, tmio->fcr + FCR_MODE);
        if (cell->disable)
@@ -371,7 +371,7 @@ static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio)
 
 static int tmio_probe(struct platform_device *dev)
 {
-       struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data;
+       struct mfd_cell *cell = dev_get_platdata(&dev->dev);
        struct tmio_nand_data *data = cell->driver_data;
        struct resource *fcr = platform_get_resource(dev,
                        IORESOURCE_MEM, 0);
@@ -404,14 +404,14 @@ static int tmio_probe(struct platform_device *dev)
        mtd->priv = nand_chip;
        mtd->name = "tmio-nand";
 
-       tmio->ccr = ioremap(ccr->start, ccr->end - ccr->start + 1);
+       tmio->ccr = ioremap(ccr->start, resource_size(ccr));
        if (!tmio->ccr) {
                retval = -EIO;
                goto err_iomap_ccr;
        }
 
        tmio->fcr_base = fcr->start & 0xfffff;
-       tmio->fcr = ioremap(fcr->start, fcr->end - fcr->start + 1);
+       tmio->fcr = ioremap(fcr->start, resource_size(fcr));
        if (!tmio->fcr) {
                retval = -EIO;
                goto err_iomap_fcr;
@@ -515,7 +515,7 @@ static int tmio_remove(struct platform_device *dev)
 #ifdef CONFIG_PM
 static int tmio_suspend(struct platform_device *dev, pm_message_t state)
 {
-       struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data;
+       struct mfd_cell *cell = dev_get_platdata(&dev->dev);
 
        if (cell->suspend)
                cell->suspend(dev);
@@ -526,7 +526,7 @@ static int tmio_suspend(struct platform_device *dev, pm_message_t state)
 
 static int tmio_resume(struct platform_device *dev)
 {
-       struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data;
+       struct mfd_cell *cell = dev_get_platdata(&dev->dev);
 
        /* FIXME - is this required or merely another attack of the broken
         * SHARP platform? Looks suspicious.
diff --git a/drivers/mtd/nand/ts7250.c b/drivers/mtd/nand/ts7250.c
deleted file mode 100644 (file)
index 0f5562a..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * drivers/mtd/nand/ts7250.c
- *
- * Copyright (C) 2004 Technologic Systems (support@embeddedARM.com)
- *
- * Derived from drivers/mtd/nand/edb7312.c
- *   Copyright (C) 2004 Marius Gröger (mag@sysgo.de)
- *
- * Derived from drivers/mtd/nand/autcpu12.c
- *   Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Overview:
- *   This is a device driver for the NAND flash device found on the
- *   TS-7250 board which utilizes a Samsung 32 Mbyte part.
- */
-
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
-#include <linux/mtd/partitions.h>
-#include <linux/io.h>
-
-#include <mach/hardware.h>
-#include <mach/ts72xx.h>
-
-#include <asm/sizes.h>
-#include <asm/mach-types.h>
-
-/*
- * MTD structure for TS7250 board
- */
-static struct mtd_info *ts7250_mtd = NULL;
-
-#ifdef CONFIG_MTD_PARTITIONS
-static const char *part_probes[] = { "cmdlinepart", NULL };
-
-#define NUM_PARTITIONS 3
-
-/*
- * Define static partitions for flash device
- */
-static struct mtd_partition partition_info32[] = {
-       {
-               .name           = "TS-BOOTROM",
-               .offset         = 0x00000000,
-               .size           = 0x00004000,
-       }, {
-               .name           = "Linux",
-               .offset         = 0x00004000,
-               .size           = 0x01d00000,
-       }, {
-               .name           = "RedBoot",
-               .offset         = 0x01d04000,
-               .size           = 0x002fc000,
-       },
-};
-
-/*
- * Define static partitions for flash device
- */
-static struct mtd_partition partition_info128[] = {
-       {
-               .name           = "TS-BOOTROM",
-               .offset         = 0x00000000,
-               .size           = 0x00004000,
-       }, {
-               .name           = "Linux",
-               .offset         = 0x00004000,
-               .size           = 0x07d00000,
-       }, {
-               .name           = "RedBoot",
-               .offset         = 0x07d04000,
-               .size           = 0x002fc000,
-       },
-};
-#endif
-
-
-/*
- *     hardware specific access to control-lines
- *
- *     ctrl:
- *     NAND_NCE: bit 0 -> bit 2
- *     NAND_CLE: bit 1 -> bit 1
- *     NAND_ALE: bit 2 -> bit 0
- */
-static void ts7250_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
-{
-       struct nand_chip *chip = mtd->priv;
-
-       if (ctrl & NAND_CTRL_CHANGE) {
-               unsigned long addr = TS72XX_NAND_CONTROL_VIRT_BASE;
-               unsigned char bits;
-
-               bits = (ctrl & NAND_NCE) << 2;
-               bits |= ctrl & NAND_CLE;
-               bits |= (ctrl & NAND_ALE) >> 2;
-
-               __raw_writeb((__raw_readb(addr) & ~0x7) | bits, addr);
-       }
-
-       if (cmd != NAND_CMD_NONE)
-               writeb(cmd, chip->IO_ADDR_W);
-}
-
-/*
- *     read device ready pin
- */
-static int ts7250_device_ready(struct mtd_info *mtd)
-{
-       return __raw_readb(TS72XX_NAND_BUSY_VIRT_BASE) & 0x20;
-}
-
-/*
- * Main initialization routine
- */
-static int __init ts7250_init(void)
-{
-       struct nand_chip *this;
-       const char *part_type = 0;
-       int mtd_parts_nb = 0;
-       struct mtd_partition *mtd_parts = 0;
-
-       if (!machine_is_ts72xx() || board_is_ts7200())
-               return -ENXIO;
-
-       /* Allocate memory for MTD device structure and private data */
-       ts7250_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
-       if (!ts7250_mtd) {
-               printk("Unable to allocate TS7250 NAND MTD device structure.\n");
-               return -ENOMEM;
-       }
-
-       /* Get pointer to private data */
-       this = (struct nand_chip *)(&ts7250_mtd[1]);
-
-       /* Initialize structures */
-       memset(ts7250_mtd, 0, sizeof(struct mtd_info));
-       memset(this, 0, sizeof(struct nand_chip));
-
-       /* Link the private data with the MTD structure */
-       ts7250_mtd->priv = this;
-       ts7250_mtd->owner = THIS_MODULE;
-
-       /* insert callbacks */
-       this->IO_ADDR_R = (void *)TS72XX_NAND_DATA_VIRT_BASE;
-       this->IO_ADDR_W = (void *)TS72XX_NAND_DATA_VIRT_BASE;
-       this->cmd_ctrl = ts7250_hwcontrol;
-       this->dev_ready = ts7250_device_ready;
-       this->chip_delay = 15;
-       this->ecc.mode = NAND_ECC_SOFT;
-
-       printk("Searching for NAND flash...\n");
-       /* Scan to find existence of the device */
-       if (nand_scan(ts7250_mtd, 1)) {
-               kfree(ts7250_mtd);
-               return -ENXIO;
-       }
-#ifdef CONFIG_MTD_PARTITIONS
-       ts7250_mtd->name = "ts7250-nand";
-       mtd_parts_nb = parse_mtd_partitions(ts7250_mtd, part_probes, &mtd_parts, 0);
-       if (mtd_parts_nb > 0)
-               part_type = "command line";
-       else
-               mtd_parts_nb = 0;
-#endif
-       if (mtd_parts_nb == 0) {
-               mtd_parts = partition_info32;
-               if (ts7250_mtd->size >= (128 * 0x100000))
-                       mtd_parts = partition_info128;
-               mtd_parts_nb = NUM_PARTITIONS;
-               part_type = "static";
-       }
-
-       /* Register the partitions */
-       printk(KERN_NOTICE "Using %s partition definition\n", part_type);
-       add_mtd_partitions(ts7250_mtd, mtd_parts, mtd_parts_nb);
-
-       /* Return happy */
-       return 0;
-}
-
-module_init(ts7250_init);
-
-/*
- * Clean up routine
- */
-static void __exit ts7250_cleanup(void)
-{
-       /* Unregister the device */
-       del_mtd_device(ts7250_mtd);
-
-       /* Free the MTD device structure */
-       kfree(ts7250_mtd);
-}
-
-module_exit(ts7250_cleanup);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Jesse Off <joff@embeddedARM.com>");
-MODULE_DESCRIPTION("MTD map driver for Technologic Systems TS-7250 board");
index 863513c..054a41c 100644 (file)
@@ -274,7 +274,7 @@ static int txx9ndfmc_nand_scan(struct mtd_info *mtd)
        struct nand_chip *chip = mtd->priv;
        int ret;
 
-       ret = nand_scan_ident(mtd, 1);
+       ret = nand_scan_ident(mtd, 1, NULL);
        if (!ret) {
                if (mtd->writesize >= 512) {
                        chip->ecc.size = mtd->writesize;
index 1002e18..a4578bf 100644 (file)
@@ -126,7 +126,6 @@ static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
        del_mtd_blktrans_dev(dev);
        kfree(nftl->ReplUnitTable);
        kfree(nftl->EUNtable);
-       kfree(nftl);
 }
 
 /*
index 75f38b9..dfbab6c 100644 (file)
@@ -308,7 +308,7 @@ static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area,
                goto out_copy;
 
        /* panic_write() may be in an interrupt context */
-       if (in_interrupt())
+       if (in_interrupt() || oops_in_progress)
                goto out_copy;
 
        if (buf >= high_memory) {
@@ -385,7 +385,7 @@ static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area,
                goto out_copy;
 
        /* panic_write() may be in an interrupt context */
-       if (in_interrupt())
+       if (in_interrupt() || oops_in_progress)
                goto out_copy;
 
        if (buf >= high_memory) {
index d2aa9c4..63b83c0 100644 (file)
@@ -817,7 +817,6 @@ static void rfd_ftl_remove_dev(struct mtd_blktrans_dev *dev)
        vfree(part->sector_map);
        kfree(part->header_cache);
        kfree(part->blocks);
-       kfree(part);
 }
 
 static struct mtd_blktrans_ops rfd_ftl_tr = {
diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c
new file mode 100644 (file)
index 0000000..9fb56c7
--- /dev/null
@@ -0,0 +1,1287 @@
+/*
+ * Copyright © 2009 - Maxim Levitsky
+ * SmartMedia/xD translation layer
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/hdreg.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/sysfs.h>
+#include <linux/bitops.h>
+#include "nand/sm_common.h"
+#include "sm_ftl.h"
+
+#ifdef CONFIG_SM_FTL_MUSEUM
+#include <linux/mtd/nand_ecc.h>
+#endif
+
+
+struct workqueue_struct *cache_flush_workqueue;
+
+static int cache_timeout = 1000;
+module_param(cache_timeout, bool, S_IRUGO);
+MODULE_PARM_DESC(cache_timeout,
+       "Timeout (in ms) for cache flush (1000 ms default");
+
+static int debug;
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug level (0-2)");
+
+
+/* ------------------- sysfs attributtes ---------------------------------- */
+struct sm_sysfs_attribute {
+       struct device_attribute dev_attr;
+       char *data;
+       int len;
+};
+
+ssize_t sm_attr_show(struct device *dev, struct device_attribute *attr,
+                    char *buf)
+{
+       struct sm_sysfs_attribute *sm_attr =
+               container_of(attr, struct sm_sysfs_attribute, dev_attr);
+
+       strncpy(buf, sm_attr->data, sm_attr->len);
+       return sm_attr->len;
+}
+
+
+#define NUM_ATTRIBUTES 1
+#define SM_CIS_VENDOR_OFFSET 0x59
+struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl)
+{
+       struct attribute_group *attr_group;
+       struct attribute **attributes;
+       struct sm_sysfs_attribute *vendor_attribute;
+
+       int vendor_len = strnlen(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET,
+                                       SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET);
+
+       char *vendor = kmalloc(vendor_len, GFP_KERNEL);
+       memcpy(vendor, ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, vendor_len);
+       vendor[vendor_len] = 0;
+
+       /* Initialize sysfs attributes */
+       vendor_attribute =
+               kzalloc(sizeof(struct sm_sysfs_attribute), GFP_KERNEL);
+
+       vendor_attribute->data = vendor;
+       vendor_attribute->len = vendor_len;
+       vendor_attribute->dev_attr.attr.name = "vendor";
+       vendor_attribute->dev_attr.attr.mode = S_IRUGO;
+       vendor_attribute->dev_attr.show = sm_attr_show;
+
+
+       /* Create array of pointers to the attributes */
+       attributes = kzalloc(sizeof(struct attribute *) * (NUM_ATTRIBUTES + 1),
+                                                               GFP_KERNEL);
+       attributes[0] = &vendor_attribute->dev_attr.attr;
+
+       /* Finally create the attribute group */
+       attr_group = kzalloc(sizeof(struct attribute_group), GFP_KERNEL);
+       attr_group->attrs = attributes;
+       return attr_group;
+}
+
+void sm_delete_sysfs_attributes(struct sm_ftl *ftl)
+{
+       struct attribute **attributes = ftl->disk_attributes->attrs;
+       int i;
+
+       for (i = 0; attributes[i] ; i++) {
+
+               struct device_attribute *dev_attr = container_of(attributes[i],
+                       struct device_attribute, attr);
+
+               struct sm_sysfs_attribute *sm_attr =
+                       container_of(dev_attr,
+                               struct sm_sysfs_attribute, dev_attr);
+
+               kfree(sm_attr->data);
+               kfree(sm_attr);
+       }
+
+       kfree(ftl->disk_attributes->attrs);
+       kfree(ftl->disk_attributes);
+}
+
+
+/* ----------------------- oob helpers -------------------------------------- */
+
+static int sm_get_lba(uint8_t *lba)
+{
+       /* check fixed bits */
+       if ((lba[0] & 0xF8) != 0x10)
+               return -2;
+
+       /* check parity - endianess doesn't matter */
+       if (hweight16(*(uint16_t *)lba) & 1)
+               return -2;
+
+       return (lba[1] >> 1) | ((lba[0] & 0x07) << 7);
+}
+
+
+/*
+ * Read LBA asscociated with block
+ * returns -1, if block is erased
+ * returns -2 if error happens
+ */
+static int sm_read_lba(struct sm_oob *oob)
+{
+       static const uint32_t erased_pattern[4] = {
+               0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
+
+       uint16_t lba_test;
+       int lba;
+
+       /* First test for erased block */
+       if (!memcmp(oob, erased_pattern, SM_OOB_SIZE))
+               return -1;
+
+       /* Now check is both copies of the LBA differ too much */
+       lba_test = *(uint16_t *)oob->lba_copy1 ^ *(uint16_t*)oob->lba_copy2;
+       if (lba_test && !is_power_of_2(lba_test))
+               return -2;
+
+       /* And read it */
+       lba = sm_get_lba(oob->lba_copy1);
+
+       if (lba == -2)
+               lba = sm_get_lba(oob->lba_copy2);
+
+       return lba;
+}
+
+static void sm_write_lba(struct sm_oob *oob, uint16_t lba)
+{
+       uint8_t tmp[2];
+
+       WARN_ON(lba >= 1000);
+
+       tmp[0] = 0x10 | ((lba >> 7) & 0x07);
+       tmp[1] = (lba << 1) & 0xFF;
+
+       if (hweight16(*(uint16_t *)tmp) & 0x01)
+               tmp[1] |= 1;
+
+       oob->lba_copy1[0] = oob->lba_copy2[0] = tmp[0];
+       oob->lba_copy1[1] = oob->lba_copy2[1] = tmp[1];
+}
+
+
+/* Make offset from parts */
+static loff_t sm_mkoffset(struct sm_ftl *ftl, int zone, int block, int boffset)
+{
+       WARN_ON(boffset & (SM_SECTOR_SIZE - 1));
+       WARN_ON(zone < 0 || zone >= ftl->zone_count);
+       WARN_ON(block >= ftl->zone_size);
+       WARN_ON(boffset >= ftl->block_size);
+
+       if (block == -1)
+               return -1;
+
+       return (zone * SM_MAX_ZONE_SIZE + block) * ftl->block_size + boffset;
+}
+
+/* Breaks offset into parts */
+static void sm_break_offset(struct sm_ftl *ftl, loff_t offset,
+                           int *zone, int *block, int *boffset)
+{
+       *boffset = do_div(offset, ftl->block_size);
+       *block = do_div(offset, ftl->max_lba);
+       *zone = offset >= ftl->zone_count ? -1 : offset;
+}
+
+/* ---------------------- low level IO ------------------------------------- */
+
+static int sm_correct_sector(uint8_t *buffer, struct sm_oob *oob)
+{
+#ifdef CONFIG_SM_FTL_MUSEUM
+       uint8_t ecc[3];
+
+       __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc);
+       if (__nand_correct_data(buffer, ecc, oob->ecc1, SM_SMALL_PAGE) < 0)
+               return -EIO;
+
+       buffer += SM_SMALL_PAGE;
+
+       __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc);
+       if (__nand_correct_data(buffer, ecc, oob->ecc2, SM_SMALL_PAGE) < 0)
+               return -EIO;
+#endif
+       return 0;
+}
+
+/* Reads a sector + oob*/
+static int sm_read_sector(struct sm_ftl *ftl,
+                         int zone, int block, int boffset,
+                         uint8_t *buffer, struct sm_oob *oob)
+{
+       struct mtd_info *mtd = ftl->trans->mtd;
+       struct mtd_oob_ops ops;
+       struct sm_oob tmp_oob;
+       int ret = -EIO;
+       int try = 0;
+
+       /* FTL can contain -1 entries that are by default filled with bits */
+       if (block == -1) {
+               memset(buffer, 0xFF, SM_SECTOR_SIZE);
+               return 0;
+       }
+
+       /* User might not need the oob, but we do for data vertification */
+       if (!oob)
+               oob = &tmp_oob;
+
+       ops.mode = ftl->smallpagenand ? MTD_OOB_RAW : MTD_OOB_PLACE;
+       ops.ooboffs = 0;
+       ops.ooblen = SM_OOB_SIZE;
+       ops.oobbuf = (void *)oob;
+       ops.len = SM_SECTOR_SIZE;
+       ops.datbuf = buffer;
+
+again:
+       if (try++) {
+               /* Avoid infinite recursion on CIS reads, sm_recheck_media
+                       won't help anyway */
+               if (zone == 0 && block == ftl->cis_block && boffset ==
+                       ftl->cis_boffset)
+                       return ret;
+
+               /* Test if media is stable */
+               if (try == 3 || sm_recheck_media(ftl))
+                       return ret;
+       }
+
+       /* Unfortunelly, oob read will _always_ succeed,
+               despite card removal..... */
+       ret = mtd->read_oob(mtd, sm_mkoffset(ftl, zone, block, boffset), &ops);
+
+       /* Test for unknown errors */
+       if (ret != 0 && ret != -EUCLEAN && ret != -EBADMSG) {
+               dbg("read of block %d at zone %d, failed due to error (%d)",
+                       block, zone, ret);
+               goto again;
+       }
+
+       /* Do a basic test on the oob, to guard against returned garbage */
+       if (oob->reserved != 0xFFFFFFFF && !is_power_of_2(~oob->reserved))
+               goto again;
+
+       /* This should never happen, unless there is a bug in the mtd driver */
+       WARN_ON(ops.oobretlen != SM_OOB_SIZE);
+       WARN_ON(buffer && ops.retlen != SM_SECTOR_SIZE);
+
+       if (!buffer)
+               return 0;
+
+       /* Test if sector marked as bad */
+       if (!sm_sector_valid(oob)) {
+               dbg("read of block %d at zone %d, failed because it is marked"
+                       " as bad" , block, zone);
+               goto again;
+       }
+
+       /* Test ECC*/
+       if (ret == -EBADMSG ||
+               (ftl->smallpagenand && sm_correct_sector(buffer, oob))) {
+
+               dbg("read of block %d at zone %d, failed due to ECC error",
+                       block, zone);
+               goto again;
+       }
+
+       return 0;
+}
+
+/* Writes a sector to media */
+static int sm_write_sector(struct sm_ftl *ftl,
+                          int zone, int block, int boffset,
+                          uint8_t *buffer, struct sm_oob *oob)
+{
+       struct mtd_oob_ops ops;
+       struct mtd_info *mtd = ftl->trans->mtd;
+       int ret;
+
+       BUG_ON(ftl->readonly);
+
+       if (zone == 0 && (block == ftl->cis_block || block == 0)) {
+               dbg("attempted to write the CIS!");
+               return -EIO;
+       }
+
+       if (ftl->unstable)
+               return -EIO;
+
+       ops.mode = ftl->smallpagenand ? MTD_OOB_RAW : MTD_OOB_PLACE;
+       ops.len = SM_SECTOR_SIZE;
+       ops.datbuf = buffer;
+       ops.ooboffs = 0;
+       ops.ooblen = SM_OOB_SIZE;
+       ops.oobbuf = (void *)oob;
+
+       ret = mtd->write_oob(mtd, sm_mkoffset(ftl, zone, block, boffset), &ops);
+
+       /* Now we assume that hardware will catch write bitflip errors */
+       /* If you are paranoid, use CONFIG_MTD_NAND_VERIFY_WRITE */
+
+       if (ret) {
+               dbg("write to block %d at zone %d, failed with error %d",
+                       block, zone, ret);
+
+               sm_recheck_media(ftl);
+               return ret;
+       }
+
+       /* This should never happen, unless there is a bug in the driver */
+       WARN_ON(ops.oobretlen != SM_OOB_SIZE);
+       WARN_ON(buffer && ops.retlen != SM_SECTOR_SIZE);
+
+       return 0;
+}
+
+/* ------------------------ block IO ------------------------------------- */
+
+/* Write a block using data and lba, and invalid sector bitmap */
+static int sm_write_block(struct sm_ftl *ftl, uint8_t *buf,
+                         int zone, int block, int lba,
+                         unsigned long invalid_bitmap)
+{
+       struct sm_oob oob;
+       int boffset;
+       int retry = 0;
+
+       /* Initialize the oob with requested values */
+       memset(&oob, 0xFF, SM_OOB_SIZE);
+       sm_write_lba(&oob, lba);
+restart:
+       if (ftl->unstable)
+               return -EIO;
+
+       for (boffset = 0; boffset < ftl->block_size;
+                               boffset += SM_SECTOR_SIZE) {
+
+               oob.data_status = 0xFF;
+
+               if (test_bit(boffset / SM_SECTOR_SIZE, &invalid_bitmap)) {
+
+                       sm_printk("sector %d of block at LBA %d of zone %d"
+                               " coudn't be read, marking it as invalid",
+                               boffset / SM_SECTOR_SIZE, lba, zone);
+
+                       oob.data_status = 0;
+               }
+
+#ifdef CONFIG_SM_FTL_MUSEUM
+               if (ftl->smallpagenand) {
+                       __nand_calculate_ecc(buf + boffset,
+                                               SM_SMALL_PAGE, oob.ecc1);
+
+                       __nand_calculate_ecc(buf + boffset + SM_SMALL_PAGE,
+                                               SM_SMALL_PAGE, oob.ecc2);
+               }
+#endif
+               if (!sm_write_sector(ftl, zone, block, boffset,
+                                                       buf + boffset, &oob))
+                       continue;
+
+               if (!retry) {
+
+                       /* If write fails. try to erase the block */
+                       /* This is safe, because we never write in blocks
+                               that contain valuable data.
+                       This is intended to repair block that are marked
+                       as erased, but that isn't fully erased*/
+
+                       if (sm_erase_block(ftl, zone, block, 0))
+                               return -EIO;
+
+                       retry = 1;
+                       goto restart;
+               } else {
+                       sm_mark_block_bad(ftl, zone, block);
+                       return -EIO;
+               }
+       }
+       return 0;
+}
+
+
+/* Mark whole block at offset 'offs' as bad. */
+static void sm_mark_block_bad(struct sm_ftl *ftl, int zone, int block)
+{
+       struct sm_oob oob;
+       int boffset;
+
+       memset(&oob, 0xFF, SM_OOB_SIZE);
+       oob.block_status = 0xF0;
+
+       if (ftl->unstable)
+               return;
+
+       if (sm_recheck_media(ftl))
+               return;
+
+       sm_printk("marking block %d of zone %d as bad", block, zone);
+
+       /* We aren't checking the return value, because we don't care */
+       /* This also fails on fake xD cards, but I guess these won't expose
+               any bad blocks till fail completly */
+       for (boffset = 0; boffset < ftl->block_size; boffset += SM_SECTOR_SIZE)
+               sm_write_sector(ftl, zone, block, boffset, NULL, &oob);
+}
+
+/*
+ * Erase a block within a zone
+ * If erase succedes, it updates free block fifo, otherwise marks block as bad
+ */
+static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block,
+                         int put_free)
+{
+       struct ftl_zone *zone = &ftl->zones[zone_num];
+       struct mtd_info *mtd = ftl->trans->mtd;
+       struct erase_info erase;
+
+       erase.mtd = mtd;
+       erase.callback = sm_erase_callback;
+       erase.addr = sm_mkoffset(ftl, zone_num, block, 0);
+       erase.len = ftl->block_size;
+       erase.priv = (u_long)ftl;
+
+       if (ftl->unstable)
+               return -EIO;
+
+       BUG_ON(ftl->readonly);
+
+       if (zone_num == 0 && (block == ftl->cis_block || block == 0)) {
+               sm_printk("attempted to erase the CIS!");
+               return -EIO;
+       }
+
+       if (mtd->erase(mtd, &erase)) {
+               sm_printk("erase of block %d in zone %d failed",
+                                                       block, zone_num);
+               goto error;
+       }
+
+       if (erase.state == MTD_ERASE_PENDING)
+               wait_for_completion(&ftl->erase_completion);
+
+       if (erase.state != MTD_ERASE_DONE) {
+               sm_printk("erase of block %d in zone %d failed after wait",
+                       block, zone_num);
+               goto error;
+       }
+
+       if (put_free)
+               kfifo_in(&zone->free_sectors,
+                       (const unsigned char *)&block, sizeof(block));
+
+       return 0;
+error:
+       sm_mark_block_bad(ftl, zone_num, block);
+       return -EIO;
+}
+
+static void sm_erase_callback(struct erase_info *self)
+{
+       struct sm_ftl *ftl = (struct sm_ftl *)self->priv;
+       complete(&ftl->erase_completion);
+}
+
+/* Throughtly test that block is valid. */
+static int sm_check_block(struct sm_ftl *ftl, int zone, int block)
+{
+       int boffset;
+       struct sm_oob oob;
+       int lbas[] = { -3, 0, 0, 0 };
+       int i = 0;
+       int test_lba;
+
+
+       /* First just check that block doesn't look fishy */
+       /* Only blocks that are valid or are sliced in two parts, are
+               accepted */
+       for (boffset = 0; boffset < ftl->block_size;
+                                       boffset += SM_SECTOR_SIZE) {
+
+               /* This shoudn't happen anyway */
+               if (sm_read_sector(ftl, zone, block, boffset, NULL, &oob))
+                       return -2;
+
+               test_lba = sm_read_lba(&oob);
+
+               if (lbas[i] != test_lba)
+                       lbas[++i] = test_lba;
+
+               /* If we found three different LBAs, something is fishy */
+               if (i == 3)
+                       return -EIO;
+       }
+
+       /* If the block is sliced (partialy erased usually) erase it */
+       if (i == 2) {
+               sm_erase_block(ftl, zone, block, 1);
+               return 1;
+       }
+
+       return 0;
+}
+
+/* ----------------- media scanning --------------------------------- */
+static const struct chs_entry chs_table[] = {
+       { 1,    125,  4,  4  },
+       { 2,    125,  4,  8  },
+       { 4,    250,  4,  8  },
+       { 8,    250,  4,  16 },
+       { 16,   500,  4,  16 },
+       { 32,   500,  8,  16 },
+       { 64,   500,  8,  32 },
+       { 128,  500,  16, 32 },
+       { 256,  1000, 16, 32 },
+       { 512,  1015, 32, 63 },
+       { 1024, 985,  33, 63 },
+       { 2048, 985,  33, 63 },
+       { 0 },
+};
+
+
+static const uint8_t cis_signature[] = {
+       0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20
+};
+/* Find out media parameters.
+ * This ideally has to be based on nand id, but for now device size is enough */
+int sm_get_media_info(struct sm_ftl *ftl, struct mtd_info *mtd)
+{
+       int i;
+       int size_in_megs = mtd->size / (1024 * 1024);
+
+       ftl->readonly = mtd->type == MTD_ROM;
+
+       /* Manual settings for very old devices */
+       ftl->zone_count = 1;
+       ftl->smallpagenand = 0;
+
+       switch (size_in_megs) {
+       case 1:
+               /* 1 MiB flash/rom SmartMedia card (256 byte pages)*/
+               ftl->zone_size = 256;
+               ftl->max_lba = 250;
+               ftl->block_size = 8 * SM_SECTOR_SIZE;
+               ftl->smallpagenand = 1;
+
+               break;
+       case 2:
+               /* 2 MiB flash SmartMedia (256 byte pages)*/
+               if (mtd->writesize == SM_SMALL_PAGE) {
+                       ftl->zone_size = 512;
+                       ftl->max_lba = 500;
+                       ftl->block_size = 8 * SM_SECTOR_SIZE;
+                       ftl->smallpagenand = 1;
+               /* 2 MiB rom SmartMedia */
+               } else {
+
+                       if (!ftl->readonly)
+                               return -ENODEV;
+
+                       ftl->zone_size = 256;
+                       ftl->max_lba = 250;
+                       ftl->block_size = 16 * SM_SECTOR_SIZE;
+               }
+               break;
+       case 4:
+               /* 4 MiB flash/rom SmartMedia device */
+               ftl->zone_size = 512;
+               ftl->max_lba = 500;
+               ftl->block_size = 16 * SM_SECTOR_SIZE;
+               break;
+       case 8:
+               /* 8 MiB flash/rom SmartMedia device */
+               ftl->zone_size = 1024;
+               ftl->max_lba = 1000;
+               ftl->block_size = 16 * SM_SECTOR_SIZE;
+       }
+
+       /* Minimum xD size is 16MiB. Also, all xD cards have standard zone
+          sizes. SmartMedia cards exist up to 128 MiB and have same layout*/
+       if (size_in_megs >= 16) {
+               ftl->zone_count = size_in_megs / 16;
+               ftl->zone_size = 1024;
+               ftl->max_lba = 1000;
+               ftl->block_size = 32 * SM_SECTOR_SIZE;
+       }
+
+       /* Test for proper write,erase and oob sizes */
+       if (mtd->erasesize > ftl->block_size)
+               return -ENODEV;
+
+       if (mtd->writesize > SM_SECTOR_SIZE)
+               return -ENODEV;
+
+       if (ftl->smallpagenand && mtd->oobsize < SM_SMALL_OOB_SIZE)
+               return -ENODEV;
+
+       if (!ftl->smallpagenand && mtd->oobsize < SM_OOB_SIZE)
+               return -ENODEV;
+
+       /* We use these functions for IO */
+       if (!mtd->read_oob || !mtd->write_oob)
+               return -ENODEV;
+
+       /* Find geometry information */
+       for (i = 0 ; i < ARRAY_SIZE(chs_table) ; i++) {
+               if (chs_table[i].size == size_in_megs) {
+                       ftl->cylinders = chs_table[i].cyl;
+                       ftl->heads = chs_table[i].head;
+                       ftl->sectors = chs_table[i].sec;
+                       return 0;
+               }
+       }
+
+       sm_printk("media has unknown size : %dMiB", size_in_megs);
+       ftl->cylinders = 985;
+       ftl->heads =  33;
+       ftl->sectors = 63;
+       return 0;
+}
+
+/* Validate the CIS */
+static int sm_read_cis(struct sm_ftl *ftl)
+{
+       struct sm_oob oob;
+
+       if (sm_read_sector(ftl,
+               0, ftl->cis_block, ftl->cis_boffset, ftl->cis_buffer, &oob))
+                       return -EIO;
+
+       if (!sm_sector_valid(&oob) || !sm_block_valid(&oob))
+               return -EIO;
+
+       if (!memcmp(ftl->cis_buffer + ftl->cis_page_offset,
+                       cis_signature, sizeof(cis_signature))) {
+               return 0;
+       }
+
+       return -EIO;
+}
+
+/* Scan the media for the CIS */
+static int sm_find_cis(struct sm_ftl *ftl)
+{
+       struct sm_oob oob;
+       int block, boffset;
+       int block_found = 0;
+       int cis_found = 0;
+
+       /* Search for first valid block */
+       for (block = 0 ; block < ftl->zone_size - ftl->max_lba ; block++) {
+
+               if (sm_read_sector(ftl, 0, block, 0, NULL, &oob))
+                       continue;
+
+               if (!sm_block_valid(&oob))
+                       continue;
+               block_found = 1;
+               break;
+       }
+
+       if (!block_found)
+               return -EIO;
+
+       /* Search for first valid sector in this block */
+       for (boffset = 0 ; boffset < ftl->block_size;
+                                               boffset += SM_SECTOR_SIZE) {
+
+               if (sm_read_sector(ftl, 0, block, boffset, NULL, &oob))
+                       continue;
+
+               if (!sm_sector_valid(&oob))
+                       continue;
+               break;
+       }
+
+       if (boffset == ftl->block_size)
+               return -EIO;
+
+       ftl->cis_block = block;
+       ftl->cis_boffset = boffset;
+       ftl->cis_page_offset = 0;
+
+       cis_found = !sm_read_cis(ftl);
+
+       if (!cis_found) {
+               ftl->cis_page_offset = SM_SMALL_PAGE;
+               cis_found = !sm_read_cis(ftl);
+       }
+
+       if (cis_found) {
+               dbg("CIS block found at offset %x",
+                       block * ftl->block_size +
+                               boffset + ftl->cis_page_offset);
+               return 0;
+       }
+       return -EIO;
+}
+
+/* Basic test to determine if underlying mtd device if functional */
+static int sm_recheck_media(struct sm_ftl *ftl)
+{
+       if (sm_read_cis(ftl)) {
+
+               if (!ftl->unstable) {
+                       sm_printk("media unstable, not allowing writes");
+                       ftl->unstable = 1;
+               }
+               return -EIO;
+       }
+       return 0;
+}
+
+/* Initialize a FTL zone */
+static int sm_init_zone(struct sm_ftl *ftl, int zone_num)
+{
+       struct ftl_zone *zone = &ftl->zones[zone_num];
+       struct sm_oob oob;
+       uint16_t block;
+       int lba;
+       int i = 0;
+       int len;
+
+       dbg("initializing zone %d", zone_num);
+
+       /* Allocate memory for FTL table */
+       zone->lba_to_phys_table = kmalloc(ftl->max_lba * 2, GFP_KERNEL);
+
+       if (!zone->lba_to_phys_table)
+               return -ENOMEM;
+       memset(zone->lba_to_phys_table, -1, ftl->max_lba * 2);
+
+
+       /* Allocate memory for free sectors FIFO */
+       if (kfifo_alloc(&zone->free_sectors, ftl->zone_size * 2, GFP_KERNEL)) {
+               kfree(zone->lba_to_phys_table);
+               return -ENOMEM;
+       }
+
+       /* Now scan the zone */
+       for (block = 0 ; block < ftl->zone_size ; block++) {
+
+               /* Skip blocks till the CIS (including) */
+               if (zone_num == 0 && block <= ftl->cis_block)
+                       continue;
+
+               /* Read the oob of first sector */
+               if (sm_read_sector(ftl, zone_num, block, 0, NULL, &oob))
+                       return -EIO;
+
+               /* Test to see if block is erased. It is enough to test
+                       first sector, because erase happens in one shot */
+               if (sm_block_erased(&oob)) {
+                       kfifo_in(&zone->free_sectors,
+                               (unsigned char *)&block, 2);
+                       continue;
+               }
+
+               /* If block is marked as bad, skip it */
+               /* This assumes we can trust first sector*/
+               /* However the way the block valid status is defined, ensures
+                       very low probability of failure here */
+               if (!sm_block_valid(&oob)) {
+                       dbg("PH %04d <-> <marked bad>", block);
+                       continue;
+               }
+
+
+               lba = sm_read_lba(&oob);
+
+               /* Invalid LBA means that block is damaged. */
+               /* We can try to erase it, or mark it as bad, but
+                       lets leave that to recovery application */
+               if (lba == -2 || lba >= ftl->max_lba) {
+                       dbg("PH %04d <-> LBA %04d(bad)", block, lba);
+                       continue;
+               }
+
+
+               /* If there is no collision,
+                       just put the sector in the FTL table */
+               if (zone->lba_to_phys_table[lba] < 0) {
+                       dbg_verbose("PH %04d <-> LBA %04d", block, lba);
+                       zone->lba_to_phys_table[lba] = block;
+                       continue;
+               }
+
+               sm_printk("collision"
+                       " of LBA %d between blocks %d and %d in zone %d",
+                       lba, zone->lba_to_phys_table[lba], block, zone_num);
+
+               /* Test that this block is valid*/
+               if (sm_check_block(ftl, zone_num, block))
+                       continue;
+
+               /* Test now the old block */
+               if (sm_check_block(ftl, zone_num,
+                                       zone->lba_to_phys_table[lba])) {
+                       zone->lba_to_phys_table[lba] = block;
+                       continue;
+               }
+
+               /* If both blocks are valid and share same LBA, it means that
+                       they hold different versions of same data. It not
+                       known which is more recent, thus just erase one of them
+               */
+               sm_printk("both blocks are valid, erasing the later");
+               sm_erase_block(ftl, zone_num, block, 1);
+       }
+
+       dbg("zone initialized");
+       zone->initialized = 1;
+
+       /* No free sectors, means that the zone is heavily damaged, write won't
+               work, but it can still can be (partially) read */
+       if (!kfifo_len(&zone->free_sectors)) {
+               sm_printk("no free blocks in zone %d", zone_num);
+               return 0;
+       }
+
+       /* Randomize first block we write to */
+       get_random_bytes(&i, 2);
+       i %= (kfifo_len(&zone->free_sectors) / 2);
+
+       while (i--) {
+               len = kfifo_out(&zone->free_sectors,
+                                       (unsigned char *)&block, 2);
+               WARN_ON(len != 2);
+               kfifo_in(&zone->free_sectors, (const unsigned char *)&block, 2);
+       }
+       return 0;
+}
+
+/* Get and automaticly initialize an FTL mapping for one zone */
+struct ftl_zone *sm_get_zone(struct sm_ftl *ftl, int zone_num)
+{
+       struct ftl_zone *zone;
+       int error;
+
+       BUG_ON(zone_num >= ftl->zone_count);
+       zone = &ftl->zones[zone_num];
+
+       if (!zone->initialized) {
+               error = sm_init_zone(ftl, zone_num);
+
+               if (error)
+                       return ERR_PTR(error);
+       }
+       return zone;
+}
+
+
+/* ----------------- cache handling ------------------------------------------*/
+
+/* Initialize the one block cache */
+void sm_cache_init(struct sm_ftl *ftl)
+{
+       ftl->cache_data_invalid_bitmap = 0xFFFFFFFF;
+       ftl->cache_clean = 1;
+       ftl->cache_zone = -1;
+       ftl->cache_block = -1;
+       /*memset(ftl->cache_data, 0xAA, ftl->block_size);*/
+}
+
+/* Put sector in one block cache */
+void sm_cache_put(struct sm_ftl *ftl, char *buffer, int boffset)
+{
+       memcpy(ftl->cache_data + boffset, buffer, SM_SECTOR_SIZE);
+       clear_bit(boffset / SM_SECTOR_SIZE, &ftl->cache_data_invalid_bitmap);
+       ftl->cache_clean = 0;
+}
+
+/* Read a sector from the cache */
+int sm_cache_get(struct sm_ftl *ftl, char *buffer, int boffset)
+{
+       if (test_bit(boffset / SM_SECTOR_SIZE,
+               &ftl->cache_data_invalid_bitmap))
+                       return -1;
+
+       memcpy(buffer, ftl->cache_data + boffset, SM_SECTOR_SIZE);
+       return 0;
+}
+
+/* Write the cache to hardware */
+int sm_cache_flush(struct sm_ftl *ftl)
+{
+       struct ftl_zone *zone;
+
+       int sector_num;
+       uint16_t write_sector;
+       int zone_num = ftl->cache_zone;
+       int block_num;
+
+       if (ftl->cache_clean)
+               return 0;
+
+       if (ftl->unstable)
+               return -EIO;
+
+       BUG_ON(zone_num < 0);
+       zone = &ftl->zones[zone_num];
+       block_num = zone->lba_to_phys_table[ftl->cache_block];
+
+
+       /* Try to read all unread areas of the cache block*/
+       for_each_bit(sector_num, &ftl->cache_data_invalid_bitmap,
+               ftl->block_size / SM_SECTOR_SIZE) {
+
+               if (!sm_read_sector(ftl,
+                       zone_num, block_num, sector_num * SM_SECTOR_SIZE,
+                       ftl->cache_data + sector_num * SM_SECTOR_SIZE, NULL))
+                               clear_bit(sector_num,
+                                       &ftl->cache_data_invalid_bitmap);
+       }
+restart:
+
+       if (ftl->unstable)
+               return -EIO;
+
+       /* If there are no spare blocks, */
+       /* we could still continue by erasing/writing the current block,
+               but for such worn out media it doesn't worth the trouble,
+                       and the dangers */
+       if (kfifo_out(&zone->free_sectors,
+                               (unsigned char *)&write_sector, 2) != 2) {
+               dbg("no free sectors for write!");
+               return -EIO;
+       }
+
+
+       if (sm_write_block(ftl, ftl->cache_data, zone_num, write_sector,
+               ftl->cache_block, ftl->cache_data_invalid_bitmap))
+                       goto restart;
+
+       /* Update the FTL table */
+       zone->lba_to_phys_table[ftl->cache_block] = write_sector;
+
+       /* Write succesfull, so erase and free the old block */
+       if (block_num > 0)
+               sm_erase_block(ftl, zone_num, block_num, 1);
+
+       sm_cache_init(ftl);
+       return 0;
+}
+
+
+/* flush timer, runs a second after last write */
+static void sm_cache_flush_timer(unsigned long data)
+{
+       struct sm_ftl *ftl = (struct sm_ftl *)data;
+       queue_work(cache_flush_workqueue, &ftl->flush_work);
+}
+
+/* cache flush work, kicked by timer */
+static void sm_cache_flush_work(struct work_struct *work)
+{
+       struct sm_ftl *ftl = container_of(work, struct sm_ftl, flush_work);
+       mutex_lock(&ftl->mutex);
+       sm_cache_flush(ftl);
+       mutex_unlock(&ftl->mutex);
+       return;
+}
+
+/* ---------------- outside interface -------------------------------------- */
+
+/* outside interface: read a sector */
+static int sm_read(struct mtd_blktrans_dev *dev,
+                  unsigned long sect_no, char *buf)
+{
+       struct sm_ftl *ftl = dev->priv;
+       struct ftl_zone *zone;
+       int error = 0, in_cache = 0;
+       int zone_num, block, boffset;
+
+       sm_break_offset(ftl, sect_no << 9, &zone_num, &block, &boffset);
+       mutex_lock(&ftl->mutex);
+
+
+       zone = sm_get_zone(ftl, zone_num);
+       if (IS_ERR(zone)) {
+               error = PTR_ERR(zone);
+               goto unlock;
+       }
+
+       /* Have to look at cache first */
+       if (ftl->cache_zone == zone_num && ftl->cache_block == block) {
+               in_cache = 1;
+               if (!sm_cache_get(ftl, buf, boffset))
+                       goto unlock;
+       }
+
+       /* Translate the block and return if doesn't exist in the table */
+       block = zone->lba_to_phys_table[block];
+
+       if (block == -1) {
+               memset(buf, 0xFF, SM_SECTOR_SIZE);
+               goto unlock;
+       }
+
+       if (sm_read_sector(ftl, zone_num, block, boffset, buf, NULL)) {
+               error = -EIO;
+               goto unlock;
+       }
+
+       if (in_cache)
+               sm_cache_put(ftl, buf, boffset);
+unlock:
+       mutex_unlock(&ftl->mutex);
+       return error;
+}
+
+/* outside interface: write a sector */
+static int sm_write(struct mtd_blktrans_dev *dev,
+                               unsigned long sec_no, char *buf)
+{
+       struct sm_ftl *ftl = dev->priv;
+       struct ftl_zone *zone;
+       int error, zone_num, block, boffset;
+
+       BUG_ON(ftl->readonly);
+       sm_break_offset(ftl, sec_no << 9, &zone_num, &block, &boffset);
+
+       /* No need in flush thread running now */
+       del_timer(&ftl->timer);
+       mutex_lock(&ftl->mutex);
+
+       zone = sm_get_zone(ftl, zone_num);
+       if (IS_ERR(zone)) {
+               error = PTR_ERR(zone);
+               goto unlock;
+       }
+
+       /* If entry is not in cache, flush it */
+       if (ftl->cache_block != block || ftl->cache_zone != zone_num) {
+
+               error = sm_cache_flush(ftl);
+               if (error)
+                       goto unlock;
+
+               ftl->cache_block = block;
+               ftl->cache_zone = zone_num;
+       }
+
+       sm_cache_put(ftl, buf, boffset);
+unlock:
+       mod_timer(&ftl->timer, jiffies + msecs_to_jiffies(cache_timeout));
+       mutex_unlock(&ftl->mutex);
+       return error;
+}
+
+/* outside interface: flush everything */
+static int sm_flush(struct mtd_blktrans_dev *dev)
+{
+       struct sm_ftl *ftl = dev->priv;
+       int retval;
+
+       mutex_lock(&ftl->mutex);
+       retval =  sm_cache_flush(ftl);
+       mutex_unlock(&ftl->mutex);
+       return retval;
+}
+
+/* outside interface: device is released */
+static int sm_release(struct mtd_blktrans_dev *dev)
+{
+       struct sm_ftl *ftl = dev->priv;
+
+       mutex_lock(&ftl->mutex);
+       del_timer_sync(&ftl->timer);
+       cancel_work_sync(&ftl->flush_work);
+       sm_cache_flush(ftl);
+       mutex_unlock(&ftl->mutex);
+       return 0;
+}
+
+/* outside interface: get geometry */
+static int sm_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
+{
+       struct sm_ftl *ftl = dev->priv;
+       geo->heads = ftl->heads;
+       geo->sectors = ftl->sectors;
+       geo->cylinders = ftl->cylinders;
+       return 0;
+}
+
+/* external interface: main initialization function */
+static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
+{
+       struct mtd_blktrans_dev *trans;
+       struct sm_ftl *ftl;
+
+       /* Allocate & initialize our private structure */
+       ftl = kzalloc(sizeof(struct sm_ftl), GFP_KERNEL);
+       if (!ftl)
+               goto error1;
+
+
+       mutex_init(&ftl->mutex);
+       setup_timer(&ftl->timer, sm_cache_flush_timer, (unsigned long)ftl);
+       INIT_WORK(&ftl->flush_work, sm_cache_flush_work);
+       init_completion(&ftl->erase_completion);
+
+       /* Read media information */
+       if (sm_get_media_info(ftl, mtd)) {
+               dbg("found unsupported mtd device, aborting");
+               goto error2;
+       }
+
+
+       /* Allocate temporary CIS buffer for read retry support */
+       ftl->cis_buffer = kzalloc(SM_SECTOR_SIZE, GFP_KERNEL);
+       if (!ftl->cis_buffer)
+               goto error2;
+
+       /* Allocate zone array, it will be initialized on demand */
+       ftl->zones = kzalloc(sizeof(struct ftl_zone) * ftl->zone_count,
+                                                               GFP_KERNEL);
+       if (!ftl->zones)
+               goto error3;
+
+       /* Allocate the cache*/
+       ftl->cache_data = kzalloc(ftl->block_size, GFP_KERNEL);
+
+       if (!ftl->cache_data)
+               goto error4;
+
+       sm_cache_init(ftl);
+
+
+       /* Allocate upper layer structure and initialize it */
+       trans = kzalloc(sizeof(struct mtd_blktrans_dev), GFP_KERNEL);
+       if (!trans)
+               goto error5;
+
+       ftl->trans = trans;
+       trans->priv = ftl;
+
+       trans->tr = tr;
+       trans->mtd = mtd;
+       trans->devnum = -1;
+       trans->size = (ftl->block_size * ftl->max_lba * ftl->zone_count) >> 9;
+       trans->readonly = ftl->readonly;
+
+       if (sm_find_cis(ftl)) {
+               dbg("CIS not found on mtd device, aborting");
+               goto error6;
+       }
+
+       ftl->disk_attributes = sm_create_sysfs_attributes(ftl);
+       trans->disk_attributes = ftl->disk_attributes;
+
+       sm_printk("Found %d MiB xD/SmartMedia FTL on mtd%d",
+               (int)(mtd->size / (1024 * 1024)), mtd->index);
+
+       dbg("FTL layout:");
+       dbg("%d zone(s), each consists of %d blocks (+%d spares)",
+               ftl->zone_count, ftl->max_lba,
+               ftl->zone_size - ftl->max_lba);
+       dbg("each block consists of %d bytes",
+               ftl->block_size);
+
+
+       /* Register device*/
+       if (add_mtd_blktrans_dev(trans)) {
+               dbg("error in mtdblktrans layer");
+               goto error6;
+       }
+       return;
+error6:
+       kfree(trans);
+error5:
+       kfree(ftl->cache_data);
+error4:
+       kfree(ftl->zones);
+error3:
+       kfree(ftl->cis_buffer);
+error2:
+       kfree(ftl);
+error1:
+       return;
+}
+
+/* main interface: device {surprise,} removal */
+static void sm_remove_dev(struct mtd_blktrans_dev *dev)
+{
+       struct sm_ftl *ftl = dev->priv;
+       int i;
+
+       del_mtd_blktrans_dev(dev);
+       ftl->trans = NULL;
+
+       for (i = 0 ; i < ftl->zone_count; i++) {
+
+               if (!ftl->zones[i].initialized)
+                       continue;
+
+               kfree(ftl->zones[i].lba_to_phys_table);
+               kfifo_free(&ftl->zones[i].free_sectors);
+       }
+
+       sm_delete_sysfs_attributes(ftl);
+       kfree(ftl->cis_buffer);
+       kfree(ftl->zones);
+       kfree(ftl->cache_data);
+       kfree(ftl);
+}
+
+static struct mtd_blktrans_ops sm_ftl_ops = {
+       .name           = "smblk",
+       .major          = -1,
+       .part_bits      = SM_FTL_PARTN_BITS,
+       .blksize        = SM_SECTOR_SIZE,
+       .getgeo         = sm_getgeo,
+
+       .add_mtd        = sm_add_mtd,
+       .remove_dev     = sm_remove_dev,
+
+       .readsect       = sm_read,
+       .writesect      = sm_write,
+
+       .flush          = sm_flush,
+       .release        = sm_release,
+
+       .owner          = THIS_MODULE,
+};
+
+static __init int sm_module_init(void)
+{
+       int error = 0;
+       cache_flush_workqueue = create_freezeable_workqueue("smflush");
+
+       if (IS_ERR(cache_flush_workqueue))
+               return PTR_ERR(cache_flush_workqueue);
+
+       error = register_mtd_blktrans(&sm_ftl_ops);
+       if (error)
+               destroy_workqueue(cache_flush_workqueue);
+       return error;
+
+}
+
+static void __exit sm_module_exit(void)
+{
+       destroy_workqueue(cache_flush_workqueue);
+       deregister_mtd_blktrans(&sm_ftl_ops);
+}
+
+module_init(sm_module_init);
+module_exit(sm_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>");
+MODULE_DESCRIPTION("Smartmedia/xD mtd translation layer");
diff --git a/drivers/mtd/sm_ftl.h b/drivers/mtd/sm_ftl.h
new file mode 100644 (file)
index 0000000..e30e48e
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright © 2009 - Maxim Levitsky
+ * SmartMedia/xD translation layer
+ *
+ * Based loosly on ssfdc.c which is
+ *  © 2005 Eptar srl
+ *  Author: Claudio Lanconelli <lanconelli.claudio@eptar.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/mtd/blktrans.h>
+#include <linux/kfifo.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/mtd/mtd.h>
+
+
+
+struct ftl_zone {
+       int initialized;
+       int16_t *lba_to_phys_table;             /* LBA to physical table */
+       struct kfifo free_sectors;      /* queue of free sectors */
+};
+
+struct sm_ftl {
+       struct mtd_blktrans_dev *trans;
+
+       struct mutex mutex;             /* protects the structure */
+       struct ftl_zone *zones;         /* FTL tables for each zone */
+
+       /* Media information */
+       int block_size;                 /* block size in bytes */
+       int zone_size;                  /* zone size in blocks */
+       int zone_count;                 /* number of zones */
+       int max_lba;                    /* maximum lba in a zone */
+       int smallpagenand;              /* 256 bytes/page nand */
+       int readonly;                   /* is FS readonly */
+       int unstable;
+       int cis_block;                  /* CIS block location */
+       int cis_boffset;                /* CIS offset in the block */
+       int cis_page_offset;            /* CIS offset in the page */
+       void *cis_buffer;               /* tmp buffer for cis reads */
+
+       /* Cache */
+       int cache_block;                /* block number of cached block */
+       int cache_zone;                 /* zone of cached block */
+       unsigned char *cache_data;      /* cached block data */
+       long unsigned int cache_data_invalid_bitmap;
+       int cache_clean;
+       struct work_struct flush_work;
+       struct timer_list timer;
+
+       /* Async erase stuff */
+       struct completion erase_completion;
+
+       /* Geometry stuff */
+       int heads;
+       int sectors;
+       int cylinders;
+
+       struct attribute_group *disk_attributes;
+};
+
+struct chs_entry {
+       unsigned long size;
+       unsigned short cyl;
+       unsigned char head;
+       unsigned char sec;
+};
+
+
+#define SM_FTL_PARTN_BITS      3
+
+#define sm_printk(format, ...) \
+       printk(KERN_WARNING "sm_ftl" ": " format "\n", ## __VA_ARGS__)
+
+#define dbg(format, ...) \
+       if (debug) \
+               printk(KERN_DEBUG "sm_ftl" ": " format "\n", ## __VA_ARGS__)
+
+#define dbg_verbose(format, ...) \
+       if (debug > 1) \
+               printk(KERN_DEBUG "sm_ftl" ": " format "\n", ## __VA_ARGS__)
+
+
+static void sm_erase_callback(struct erase_info *self);
+static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block,
+                                                               int put_free);
+static void sm_mark_block_bad(struct sm_ftl *ftl, int zone_num, int block);
+
+static int sm_recheck_media(struct sm_ftl *ftl);
index 3f67e00..81c4ecd 100644 (file)
@@ -375,7 +375,6 @@ static void ssfdcr_remove_dev(struct mtd_blktrans_dev *dev)
 
        del_mtd_blktrans_dev(dev);
        kfree(ssfdc->logic_block_map);
-       kfree(ssfdc);
 }
 
 static int ssfdcr_readsect(struct mtd_blktrans_dev *dev,
index 3451a81..86e0821 100644 (file)
@@ -313,8 +313,8 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino)
        case S_IFBLK:
        case S_IFCHR:
                /* Read the device numbers from the media */
-               if (f->metadata->size != sizeof(jdev.old) &&
-                   f->metadata->size != sizeof(jdev.new)) {
+               if (f->metadata->size != sizeof(jdev.old_id) &&
+                   f->metadata->size != sizeof(jdev.new_id)) {
                        printk(KERN_NOTICE "Device node has strange size %d\n", f->metadata->size);
                        goto error_io;
                }
@@ -325,10 +325,10 @@ struct inode *jffs2_iget(struct super_block *sb, unsigned long ino)
                        printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
                        goto error;
                }
-               if (f->metadata->size == sizeof(jdev.old))
-                       rdev = old_decode_dev(je16_to_cpu(jdev.old));
+               if (f->metadata->size == sizeof(jdev.old_id))
+                       rdev = old_decode_dev(je16_to_cpu(jdev.old_id));
                else
-                       rdev = new_decode_dev(je32_to_cpu(jdev.new));
+                       rdev = new_decode_dev(je32_to_cpu(jdev.new_id));
 
        case S_IFSOCK:
        case S_IFIFO:
index 507ed6e..36d7a84 100644 (file)
@@ -312,11 +312,11 @@ static inline int jffs2_blocks_use_vmalloc(struct jffs2_sb_info *c)
 static inline int jffs2_encode_dev(union jffs2_device_node *jdev, dev_t rdev)
 {
        if (old_valid_dev(rdev)) {
-               jdev->old = cpu_to_je16(old_encode_dev(rdev));
-               return sizeof(jdev->old);
+               jdev->old_id = cpu_to_je16(old_encode_dev(rdev));
+               return sizeof(jdev->old_id);
        } else {
-               jdev->new = cpu_to_je32(new_encode_dev(rdev));
-               return sizeof(jdev->new);
+               jdev->new_id = cpu_to_je32(new_encode_dev(rdev));
+               return sizeof(jdev->new_id);
        }
 }
 
index 2b32d63..0874ab5 100644 (file)
@@ -215,8 +215,8 @@ union jffs2_node_union
 
 /* Data payload for device nodes. */
 union jffs2_device_node {
-       jint16_t old;
-       jint32_t new;
+       jint16_t old_id;
+       jint32_t new_id;
 };
 
 #endif /* __LINUX_JFFS2_H__ */
index 8b4aa05..b481ccd 100644 (file)
@@ -9,6 +9,8 @@
 #define __MTD_TRANS_H__
 
 #include <linux/mutex.h>
+#include <linux/kref.h>
+#include <linux/sysfs.h>
 
 struct hd_geometry;
 struct mtd_info;
@@ -24,11 +26,16 @@ struct mtd_blktrans_dev {
        int devnum;
        unsigned long size;
        int readonly;
-       void *blkcore_priv; /* gendisk in 2.5, devfs_handle in 2.4 */
+       int open;
+       struct kref ref;
+       struct gendisk *disk;
+       struct attribute_group *disk_attributes;
+       struct task_struct *thread;
+       struct request_queue *rq;
+       spinlock_t queue_lock;
+       void *priv;
 };
 
-struct blkcore_priv; /* Differs for 2.4 and 2.5 kernels; private */
-
 struct mtd_blktrans_ops {
        char *name;
        int major;
@@ -60,8 +67,6 @@ struct mtd_blktrans_ops {
        struct list_head devs;
        struct list_head list;
        struct module *owner;
-
-       struct mtd_blkcore_priv *blkcore_priv;
 };
 
 extern int register_mtd_blktrans(struct mtd_blktrans_ops *tr);
index df89f42..cee05b1 100644 (file)
@@ -297,7 +297,7 @@ static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs,
         * and 32bit devices on 16 bit busses
         * set the low bit of the alternating bit sequence of the address.
         */
-       if (((type * interleave) > bankwidth) && ((uint8_t)cmd_ofs == 0xaa))
+       if (((type * interleave) > bankwidth) && ((cmd_ofs & 0xff) == 0xaa))
                addr |= (type >> 1)*interleave;
 
        return  addr;
@@ -518,11 +518,13 @@ struct cfi_fixup {
 #define CFI_MFR_ANY 0xffff
 #define CFI_ID_ANY  0xffff
 
-#define CFI_MFR_AMD    0x0001
-#define CFI_MFR_INTEL  0x0089
-#define CFI_MFR_ATMEL  0x001F
-#define CFI_MFR_SAMSUNG        0x00EC
-#define CFI_MFR_ST     0x0020 /* STMicroelectronics */
+#define CFI_MFR_AMD            0x0001
+#define CFI_MFR_ATMEL          0x001F
+#define CFI_MFR_INTEL          0x0089
+#define CFI_MFR_MACRONIX       0x00C2
+#define CFI_MFR_SAMSUNG                0x00EC
+#define CFI_MFR_SST            0x00BF
+#define CFI_MFR_ST             0x0020 /* STMicroelectronics */
 
 void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups);
 
index 0f32a9b..5326435 100644 (file)
@@ -20,7 +20,6 @@
 
 #define MTD_CHAR_MAJOR 90
 #define MTD_BLOCK_MAJOR 31
-#define MAX_MTD_DEVICES 32
 
 #define MTD_ERASE_PENDING              0x01
 #define MTD_ERASING            0x02
@@ -61,9 +60,7 @@ struct mtd_erase_region_info {
  * MTD_OOB_PLACE:      oob data are placed at the given offset
  * MTD_OOB_AUTO:       oob data are automatically placed at the free areas
  *                     which are defined by the ecclayout
- * MTD_OOB_RAW:                mode to read raw data+oob in one chunk. The oob data
- *                     is inserted into the data. Thats a raw image of the
- *                     flash contents.
+ * MTD_OOB_RAW:                mode to read oob and data without doing ECC checking
  */
 typedef enum {
        MTD_OOB_PLACE,
@@ -290,8 +287,9 @@ extern int add_mtd_device(struct mtd_info *mtd);
 extern int del_mtd_device (struct mtd_info *mtd);
 
 extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num);
+extern int __get_mtd_device(struct mtd_info *mtd);
+extern void __put_mtd_device(struct mtd_info *mtd);
 extern struct mtd_info *get_mtd_device_nm(const char *name);
-
 extern void put_mtd_device(struct mtd_info *mtd);
 
 
index ccab9df..8bdacb8 100644 (file)
 #include <linux/mtd/bbm.h>
 
 struct mtd_info;
+struct nand_flash_dev;
 /* Scan and identify a NAND device */
 extern int nand_scan (struct mtd_info *mtd, int max_chips);
 /* Separate phases of nand_scan(), allowing board driver to intervene
  * and override command or ECC setup according to flash type */
-extern int nand_scan_ident(struct mtd_info *mtd, int max_chips);
+extern int nand_scan_ident(struct mtd_info *mtd, int max_chips,
+                          struct nand_flash_dev *table);
 extern int nand_scan_tail(struct mtd_info *mtd);
 
 /* Free resources held by the NAND device */
@@ -38,6 +40,12 @@ extern void nand_release (struct mtd_info *mtd);
 /* Internal helper for board drivers which need to override command function */
 extern void nand_wait_ready(struct mtd_info *mtd);
 
+/* locks all blockes present in the device */
+extern int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
+
+/* unlocks specified locked blockes */
+extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len);
+
 /* The maximum number of NAND chips in an array */
 #define NAND_MAX_CHIPS         8
 
@@ -82,6 +90,10 @@ extern void nand_wait_ready(struct mtd_info *mtd);
 #define NAND_CMD_ERASE2                0xd0
 #define NAND_CMD_RESET         0xff
 
+#define NAND_CMD_LOCK          0x2a
+#define NAND_CMD_UNLOCK1       0x23
+#define NAND_CMD_UNLOCK2       0x24
+
 /* Extended commands for large page devices */
 #define NAND_CMD_READSTART     0x30
 #define NAND_CMD_RNDOUTSTART   0xE0
@@ -170,6 +182,12 @@ typedef enum {
 /* Chip does not allow subpage writes */
 #define NAND_NO_SUBPAGE_WRITE  0x00000200
 
+/* Device is one of 'new' xD cards that expose fake nand command set */
+#define NAND_BROKEN_XD         0x00000400
+
+/* Device behaves just like nand, but is readonly */
+#define NAND_ROM               0x00000800
+
 /* Options valid for Samsung large page devices */
 #define NAND_SAMSUNG_LP_OPTIONS \
        (NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK)
@@ -391,6 +409,7 @@ struct nand_chip {
        int             subpagesize;
        uint8_t         cellinfo;
        int             badblockpos;
+       int             badblockbits;
 
        flstate_t       state;
 
index 2eb1dca..422a9d5 100644 (file)
--- a/lib/idr.c
+++ b/lib/idr.c
@@ -623,7 +623,7 @@ void *idr_get_next(struct idr *idp, int *nextidp)
        }
        return NULL;
 }
-
+EXPORT_SYMBOL(idr_get_next);
 
 
 /**