diff options
Diffstat (limited to 'drivers/mtd')
114 files changed, 3449 insertions, 4134 deletions
diff --git a/drivers/mtd/bcm47xxpart.c b/drivers/mtd/bcm47xxpart.c index 9279a9174f84..7a6384b0962a 100644 --- a/drivers/mtd/bcm47xxpart.c +++ b/drivers/mtd/bcm47xxpart.c @@ -27,11 +27,13 @@ /* Magics */ #define BOARD_DATA_MAGIC 0x5246504D /* MPFR */ +#define FACTORY_MAGIC 0x59544346 /* FCTY */ #define POT_MAGIC1 0x54544f50 /* POTT */ #define POT_MAGIC2 0x504f /* OP */ #define ML_MAGIC1 0x39685a42 #define ML_MAGIC2 0x26594131 #define TRX_MAGIC 0x30524448 +#define SQSH_MAGIC 0x71736873 /* shsq */ struct trx_header { uint32_t magic; @@ -71,7 +73,14 @@ static int bcm47xxpart_parse(struct mtd_info *master, /* Alloc */ parts = kzalloc(sizeof(struct mtd_partition) * BCM47XXPART_MAX_PARTS, GFP_KERNEL); + if (!parts) + return -ENOMEM; + buf = kzalloc(BCM47XXPART_BYTES_TO_READ, GFP_KERNEL); + if (!buf) { + kfree(parts); + return -ENOMEM; + } /* Parse block by block looking for magics */ for (offset = 0; offset <= master->size - blocksize; @@ -110,6 +119,13 @@ static int bcm47xxpart_parse(struct mtd_info *master, continue; } + /* Found on Huawei E970 */ + if (buf[0x000 / 4] == FACTORY_MAGIC) { + bcm47xxpart_add_part(&parts[curr_part++], "factory", + offset, MTD_WRITEABLE); + continue; + } + /* POT(TOP) */ if (buf[0x000 / 4] == POT_MAGIC1 && (buf[0x004 / 4] & 0xFFFF) == POT_MAGIC2) { @@ -167,6 +183,13 @@ static int bcm47xxpart_parse(struct mtd_info *master, offset = rounddown(offset + trx->length, blocksize); continue; } + + /* Squashfs on devices not using TRX */ + if (buf[0x000 / 4] == SQSH_MAGIC) { + bcm47xxpart_add_part(&parts[curr_part++], "rootfs", + offset, 0); + continue; + } } /* Look for NVRAM at the end of the last block. */ diff --git a/drivers/mtd/bcm63xxpart.c b/drivers/mtd/bcm63xxpart.c index 6eeb84c81bc2..5c813907661c 100644 --- a/drivers/mtd/bcm63xxpart.c +++ b/drivers/mtd/bcm63xxpart.c @@ -4,7 +4,7 @@ * Copyright © 2006-2008 Florian Fainelli <florian@openwrt.org> * Mike Albon <malbon@openwrt.org> * Copyright © 2009-2010 Daniel Dickinson <openwrt@cshore.neomailbox.net> - * Copyright © 2011-2012 Jonas Gorski <jonas.gorski@gmail.com> + * Copyright © 2011-2013 Jonas Gorski <jonas.gorski@gmail.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,17 +27,19 @@ #include <linux/crc32.h> #include <linux/module.h> #include <linux/kernel.h> +#include <linux/sizes.h> #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> +#include <asm/mach-bcm63xx/bcm63xx_nvram.h> #include <asm/mach-bcm63xx/bcm963xx_tag.h> #include <asm/mach-bcm63xx/board_bcm963xx.h> #define BCM63XX_EXTENDED_SIZE 0xBFC00000 /* Extended flash address */ -#define BCM63XX_CFE_BLOCK_SIZE 0x10000 /* always at least 64KiB */ +#define BCM63XX_CFE_BLOCK_SIZE SZ_64K /* always at least 64KiB */ #define BCM63XX_CFE_MAGIC_OFFSET 0x4e0 @@ -90,7 +92,8 @@ static int bcm63xx_parse_cfe_partitions(struct mtd_info *master, BCM63XX_CFE_BLOCK_SIZE); cfelen = cfe_erasesize; - nvramlen = cfe_erasesize; + nvramlen = bcm63xx_nvram_get_psi_size() * SZ_1K; + nvramlen = roundup(nvramlen, cfe_erasesize); /* Allocate memory for buffer */ buf = vmalloc(sizeof(struct bcm_tag)); diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index fff665d59a0d..89b9d6891532 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -1571,8 +1571,8 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, xip_enable(map, chip, adr); /* FIXME - should have reset delay before continuing */ - printk(KERN_WARNING "MTD %s(): software timeout\n", - __func__ ); + printk(KERN_WARNING "MTD %s(): software timeout, address:0x%.8lx.\n", + __func__, adr); ret = -EIO; op_done: diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index 74dbb6bcf488..ffb36ba8a6e0 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -211,9 +211,7 @@ static inline struct mtd_info *cfi_cmdset_unknown(struct map_info *map, probe_function = __symbol_get(probename); if (!probe_function) { - char modname[sizeof("cfi_cmdset_%4.4X")]; - sprintf(modname, "cfi_cmdset_%4.4X", type); - request_module(modname); + request_module("cfi_cmdset_%4.4X", type); probe_function = __symbol_get(probename); } diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index c443f527a53a..7c0b27d132b1 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -120,7 +120,7 @@ #define PM49FL008 0x006A /* Sharp */ -#define LH28F640BF 0x00b0 +#define LH28F640BF 0x00B0 /* ST - www.st.com */ #define M29F800AB 0x0058 @@ -1299,13 +1299,14 @@ static const struct amd_flash_info jedec_table[] = { .mfr_id = CFI_MFR_SHARP, .dev_id = LH28F640BF, .name = "LH28F640BF", - .devtypes = CFI_DEVICETYPE_X8, + .devtypes = CFI_DEVICETYPE_X16, .uaddr = MTD_UADDR_UNNECESSARY, - .dev_size = SIZE_4MiB, - .cmd_set = P_ID_INTEL_STD, - .nr_regions = 1, + .dev_size = SIZE_8MiB, + .cmd_set = P_ID_INTEL_EXT, + .nr_regions = 2, .regions = { - ERASEINFO(0x40000,16), + ERASEINFO(0x10000, 127), + ERASEINFO(0x02000, 8), } }, { .mfr_id = CFI_MFR_SST, diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 2a4d55e4b362..01281382180b 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -95,13 +95,6 @@ config MTD_M25P80 if you want to specify device partitioning or to use a device which doesn't support the JEDEC ID instruction. -config M25PXX_USE_FAST_READ - bool "Use FAST_READ OPCode allowing SPI CLK >= 50MHz" - depends on MTD_M25P80 - default y - help - This option enables FAST_READ access supported by ST M25Pxx. - config MTD_SPEAR_SMI tristate "SPEAR MTD NOR Support through SMI controller" depends on PLAT_SPEAR @@ -224,59 +217,4 @@ config BCH_CONST_T default 4 endif -config MTD_DOCPROBE - tristate - select MTD_DOCECC - -config MTD_DOCECC - tristate - -config MTD_DOCPROBE_ADVANCED - bool "Advanced detection options for DiskOnChip" - depends on MTD_DOCPROBE - help - This option allows you to specify nonstandard address at which to - probe for a DiskOnChip, or to change the detection options. You - are unlikely to need any of this unless you are using LinuxBIOS. - Say 'N'. - -config MTD_DOCPROBE_ADDRESS - hex "Physical address of DiskOnChip" if MTD_DOCPROBE_ADVANCED - depends on MTD_DOCPROBE - default "0x0" - ---help--- - By default, the probe for DiskOnChip devices will look for a - DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. - This option allows you to specify a single address at which to probe - for the device, which is useful if you have other devices in that - range which get upset when they are probed. - - (Note that on PowerPC, the normal probe will only check at - 0xE4000000.) - - Normally, you should leave this set to zero, to allow the probe at - the normal addresses. - -config MTD_DOCPROBE_HIGH - bool "Probe high addresses" - depends on MTD_DOCPROBE_ADVANCED - help - By default, the probe for DiskOnChip devices will look for a - DiskOnChip at every multiple of 0x2000 between 0xC8000 and 0xEE000. - This option changes to make it probe between 0xFFFC8000 and - 0xFFFEE000. Unless you are using LinuxBIOS, this is unlikely to be - useful to you. Say 'N'. - -config MTD_DOCPROBE_55AA - bool "Probe for 0x55 0xAA BIOS Extension Signature" - depends on MTD_DOCPROBE_ADVANCED - help - Check for the 0x55 0xAA signature of a DiskOnChip, and do not - continue with probing if it is absent. The signature will always be - present for a DiskOnChip 2000 or a normal DiskOnChip Millennium. - Only if you have overwritten the first block of a DiskOnChip - Millennium will it be absent. Enable this option if you are using - LinuxBIOS or if you need to recover a DiskOnChip Millennium on which - you have managed to wipe the first block. - endmenu diff --git a/drivers/mtd/devices/bcm47xxsflash.c b/drivers/mtd/devices/bcm47xxsflash.c index 18e7761137a3..77de29bc02ba 100644 --- a/drivers/mtd/devices/bcm47xxsflash.c +++ b/drivers/mtd/devices/bcm47xxsflash.c @@ -1,6 +1,7 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/delay.h> #include <linux/mtd/mtd.h> #include <linux/platform_device.h> #include <linux/bcma/bcma.h> @@ -12,6 +13,93 @@ MODULE_DESCRIPTION("Serial flash driver for BCMA bus"); static const char * const probes[] = { "bcm47xxpart", NULL }; +/************************************************** + * Various helpers + **************************************************/ + +static void bcm47xxsflash_cmd(struct bcm47xxsflash *b47s, u32 opcode) +{ + int i; + + b47s->cc_write(b47s, BCMA_CC_FLASHCTL, BCMA_CC_FLASHCTL_START | opcode); + for (i = 0; i < 1000; i++) { + if (!(b47s->cc_read(b47s, BCMA_CC_FLASHCTL) & + BCMA_CC_FLASHCTL_BUSY)) + return; + cpu_relax(); + } + pr_err("Control command failed (timeout)!\n"); +} + +static int bcm47xxsflash_poll(struct bcm47xxsflash *b47s, int timeout) +{ + unsigned long deadline = jiffies + timeout; + + do { + switch (b47s->type) { + case BCM47XXSFLASH_TYPE_ST: + bcm47xxsflash_cmd(b47s, OPCODE_ST_RDSR); + if (!(b47s->cc_read(b47s, BCMA_CC_FLASHDATA) & + SR_ST_WIP)) + return 0; + break; + case BCM47XXSFLASH_TYPE_ATMEL: + bcm47xxsflash_cmd(b47s, OPCODE_AT_STATUS); + if (b47s->cc_read(b47s, BCMA_CC_FLASHDATA) & + SR_AT_READY) + return 0; + break; + } + + cpu_relax(); + udelay(1); + } while (!time_after_eq(jiffies, deadline)); + + pr_err("Timeout waiting for flash to be ready!\n"); + + return -EBUSY; +} + +/************************************************** + * MTD ops + **************************************************/ + +static int bcm47xxsflash_erase(struct mtd_info *mtd, struct erase_info *erase) +{ + struct bcm47xxsflash *b47s = mtd->priv; + int err; + + switch (b47s->type) { + case BCM47XXSFLASH_TYPE_ST: + bcm47xxsflash_cmd(b47s, OPCODE_ST_WREN); + b47s->cc_write(b47s, BCMA_CC_FLASHADDR, erase->addr); + /* Newer flashes have "sub-sectors" which can be erased + * independently with a new command: ST_SSE. The ST_SE command + * erases 64KB just as before. + */ + if (b47s->blocksize < (64 * 1024)) + bcm47xxsflash_cmd(b47s, OPCODE_ST_SSE); + else + bcm47xxsflash_cmd(b47s, OPCODE_ST_SE); + break; + case BCM47XXSFLASH_TYPE_ATMEL: + b47s->cc_write(b47s, BCMA_CC_FLASHADDR, erase->addr << 1); + bcm47xxsflash_cmd(b47s, OPCODE_AT_PAGE_ERASE); + break; + } + + err = bcm47xxsflash_poll(b47s, HZ); + if (err) + erase->state = MTD_ERASE_FAILED; + else + erase->state = MTD_ERASE_DONE; + + if (erase->callback) + erase->callback(erase); + + return err; +} + static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { @@ -28,6 +116,127 @@ static int bcm47xxsflash_read(struct mtd_info *mtd, loff_t from, size_t len, return len; } +static int bcm47xxsflash_write_st(struct mtd_info *mtd, u32 offset, size_t len, + const u_char *buf) +{ + struct bcm47xxsflash *b47s = mtd->priv; + int written = 0; + + /* Enable writes */ + bcm47xxsflash_cmd(b47s, OPCODE_ST_WREN); + + /* Write first byte */ + b47s->cc_write(b47s, BCMA_CC_FLASHADDR, offset); + b47s->cc_write(b47s, BCMA_CC_FLASHDATA, *buf++); + + /* Program page */ + if (b47s->bcma_cc->core->id.rev < 20) { + bcm47xxsflash_cmd(b47s, OPCODE_ST_PP); + return 1; /* 1B written */ + } + + /* Program page and set CSA (on newer chips we can continue writing) */ + bcm47xxsflash_cmd(b47s, OPCODE_ST_CSA | OPCODE_ST_PP); + offset++; + len--; + written++; + + while (len > 0) { + /* Page boundary, another function call is needed */ + if ((offset & 0xFF) == 0) + break; + + bcm47xxsflash_cmd(b47s, OPCODE_ST_CSA | *buf++); + offset++; + len--; + written++; + } + + /* All done, drop CSA & poll */ + b47s->cc_write(b47s, BCMA_CC_FLASHCTL, 0); + udelay(1); + if (bcm47xxsflash_poll(b47s, HZ / 10)) + pr_err("Flash rejected dropping CSA\n"); + + return written; +} + +static int bcm47xxsflash_write_at(struct mtd_info *mtd, u32 offset, size_t len, + const u_char *buf) +{ + struct bcm47xxsflash *b47s = mtd->priv; + u32 mask = b47s->blocksize - 1; + u32 page = (offset & ~mask) << 1; + u32 byte = offset & mask; + int written = 0; + + /* If we don't overwrite whole page, read it to the buffer first */ + if (byte || (len < b47s->blocksize)) { + int err; + + b47s->cc_write(b47s, BCMA_CC_FLASHADDR, page); + bcm47xxsflash_cmd(b47s, OPCODE_AT_BUF1_LOAD); + /* 250 us for AT45DB321B */ + err = bcm47xxsflash_poll(b47s, HZ / 1000); + if (err) { + pr_err("Timeout reading page 0x%X info buffer\n", page); + return err; + } + } + + /* Change buffer content with our data */ + while (len > 0) { + /* Page boundary, another function call is needed */ + if (byte == b47s->blocksize) + break; + + b47s->cc_write(b47s, BCMA_CC_FLASHADDR, byte++); + b47s->cc_write(b47s, BCMA_CC_FLASHDATA, *buf++); + bcm47xxsflash_cmd(b47s, OPCODE_AT_BUF1_WRITE); + len--; + written++; + } + + /* Program page with the buffer content */ + b47s->cc_write(b47s, BCMA_CC_FLASHADDR, page); + bcm47xxsflash_cmd(b47s, OPCODE_AT_BUF1_PROGRAM); + + return written; +} + +static int bcm47xxsflash_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct bcm47xxsflash *b47s = mtd->priv; + int written; + + /* Writing functions can return without writing all passed data, for + * example when the hardware is too old or when we git page boundary. + */ + while (len > 0) { + switch (b47s->type) { + case BCM47XXSFLASH_TYPE_ST: + written = bcm47xxsflash_write_st(mtd, to, len, buf); + break; + case BCM47XXSFLASH_TYPE_ATMEL: + written = bcm47xxsflash_write_at(mtd, to, len, buf); + break; + default: + BUG_ON(1); + } + if (written < 0) { + pr_err("Error writing at offset 0x%llX\n", to); + return written; + } + to += (loff_t)written; + len -= written; + *retlen += written; + buf += written; + } + + return 0; +} + static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s) { struct mtd_info *mtd = &b47s->mtd; @@ -35,33 +244,48 @@ static void bcm47xxsflash_fill_mtd(struct bcm47xxsflash *b47s) mtd->priv = b47s; mtd->name = "bcm47xxsflash"; mtd->owner = THIS_MODULE; - mtd->type = MTD_ROM; + + mtd->type = MTD_NORFLASH; + mtd->flags = MTD_CAP_NORFLASH; mtd->size = b47s->size; - mtd->_read = bcm47xxsflash_read; + mtd->erasesize = b47s->blocksize; + mtd->writesize = 1; + mtd->writebufsize = 1; - /* TODO: implement writing support and verify/change following code */ - mtd->flags = MTD_CAP_ROM; - mtd->writebufsize = mtd->writesize = 1; + mtd->_erase = bcm47xxsflash_erase; + mtd->_read = bcm47xxsflash_read; + mtd->_write = bcm47xxsflash_write; } /************************************************** * BCMA **************************************************/ +static int bcm47xxsflash_bcma_cc_read(struct bcm47xxsflash *b47s, u16 offset) +{ + return bcma_cc_read32(b47s->bcma_cc, offset); +} + +static void bcm47xxsflash_bcma_cc_write(struct bcm47xxsflash *b47s, u16 offset, + u32 value) +{ + bcma_cc_write32(b47s->bcma_cc, offset, value); +} + static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) { struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev); struct bcm47xxsflash *b47s; int err; - b47s = kzalloc(sizeof(*b47s), GFP_KERNEL); - if (!b47s) { - err = -ENOMEM; - goto out; - } + b47s = devm_kzalloc(&pdev->dev, sizeof(*b47s), GFP_KERNEL); + if (!b47s) + return -ENOMEM; sflash->priv = b47s; b47s->bcma_cc = container_of(sflash, struct bcma_drv_cc, sflash); + b47s->cc_read = bcm47xxsflash_bcma_cc_read; + b47s->cc_write = bcm47xxsflash_bcma_cc_write; switch (b47s->bcma_cc->capabilities & BCMA_CC_CAP_FLASHT) { case BCMA_CC_FLASHT_STSER: @@ -81,15 +305,13 @@ static int bcm47xxsflash_bcma_probe(struct platform_device *pdev) err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0); if (err) { pr_err("Failed to register MTD device: %d\n", err); - goto err_dev_reg; + return err; } - return 0; + if (bcm47xxsflash_poll(b47s, HZ / 10)) + pr_warn("Serial flash busy\n"); -err_dev_reg: - kfree(&b47s->mtd); -out: - return err; + return 0; } static int bcm47xxsflash_bcma_remove(struct platform_device *pdev) @@ -98,7 +320,6 @@ static int bcm47xxsflash_bcma_remove(struct platform_device *pdev) struct bcm47xxsflash *b47s = sflash->priv; mtd_device_unregister(&b47s->mtd); - kfree(b47s); return 0; } @@ -116,22 +337,4 @@ static struct platform_driver bcma_sflash_driver = { * Init **************************************************/ -static int __init bcm47xxsflash_init(void) -{ - int err; - - err = platform_driver_register(&bcma_sflash_driver); - if (err) - pr_err("Failed to register BCMA serial flash driver: %d\n", - err); - - return err; -} - -static void __exit bcm47xxsflash_exit(void) -{ - platform_driver_unregister(&bcma_sflash_driver); -} - -module_init(bcm47xxsflash_init); -module_exit(bcm47xxsflash_exit); +module_platform_driver(bcma_sflash_driver); diff --git a/drivers/mtd/devices/bcm47xxsflash.h b/drivers/mtd/devices/bcm47xxsflash.h index f22f8c46dfc0..fe93daf4f489 100644 --- a/drivers/mtd/devices/bcm47xxsflash.h +++ b/drivers/mtd/devices/bcm47xxsflash.h @@ -60,6 +60,8 @@ enum bcm47xxsflash_type { struct bcm47xxsflash { struct bcma_drv_cc *bcma_cc; + int (*cc_read)(struct bcm47xxsflash *b47s, u16 offset); + void (*cc_write)(struct bcm47xxsflash *b47s, u16 offset, u32 value); enum bcm47xxsflash_type type; diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index e081bfeaaf7d..d9fd87a4c8dc 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -6,6 +6,9 @@ * * Licence: GPL */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/fs.h> #include <linux/blkdev.h> @@ -17,10 +20,7 @@ #include <linux/mutex.h> #include <linux/mount.h> #include <linux/slab.h> - -#define ERROR(fmt, args...) printk(KERN_ERR "block2mtd: " fmt "\n" , ## args) -#define INFO(fmt, args...) printk(KERN_INFO "block2mtd: " fmt "\n" , ## args) - +#include <linux/major.h> /* Info for the block device */ struct block2mtd_dev { @@ -84,7 +84,7 @@ static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr) err = _block2mtd_erase(dev, from, len); mutex_unlock(&dev->write_mutex); if (err) { - ERROR("erase failed err = %d", err); + pr_err("erase failed err = %d\n", err); instr->state = MTD_ERASE_FAILED; } else instr->state = MTD_ERASE_DONE; @@ -239,13 +239,13 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) #endif if (IS_ERR(bdev)) { - ERROR("error: cannot open device %s", devname); + pr_err("error: cannot open device %s\n", devname); goto devinit_err; } dev->blkdev = bdev; if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) { - ERROR("attempting to use an MTD device as a block device"); + pr_err("attempting to use an MTD device as a block device\n"); goto devinit_err; } @@ -277,9 +277,10 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) goto devinit_err; } list_add(&dev->list, &blkmtd_device_list); - INFO("mtd%d: [%s] erase_size = %dKiB [%d]", dev->mtd.index, - dev->mtd.name + strlen("block2mtd: "), - dev->mtd.erasesize >> 10, dev->mtd.erasesize); + pr_info("mtd%d: [%s] erase_size = %dKiB [%d]\n", + dev->mtd.index, + dev->mtd.name + strlen("block2mtd: "), + dev->mtd.erasesize >> 10, dev->mtd.erasesize); return dev; devinit_err: @@ -339,17 +340,11 @@ static inline void kill_final_newline(char *str) } -#define parse_err(fmt, args...) do { \ - ERROR(fmt, ## args); \ - return 0; \ -} while (0) - #ifndef MODULE static int block2mtd_init_called = 0; static char block2mtd_paramline[80 + 12]; /* 80 for device, 12 for erase size */ #endif - static int block2mtd_setup2(const char *val) { char buf[80 + 12]; /* 80 for device, 12 for erase size */ @@ -359,8 +354,10 @@ static int block2mtd_setup2(const char *val) size_t erase_size = PAGE_SIZE; int i, ret; - if (strnlen(val, sizeof(buf)) >= sizeof(buf)) - parse_err("parameter too long"); + if (strnlen(val, sizeof(buf)) >= sizeof(buf)) { + pr_err("parameter too long\n"); + return 0; + } strcpy(str, val); kill_final_newline(str); @@ -368,20 +365,27 @@ static int block2mtd_setup2(const char *val) for (i = 0; i < 2; i++) token[i] = strsep(&str, ","); - if (str) - parse_err("too many arguments"); + if (str) { + pr_err("too many arguments\n"); + return 0; + } - if (!token[0]) - parse_err("no argument"); + if (!token[0]) { + pr_err("no argument\n"); + return 0; + } name = token[0]; - if (strlen(name) + 1 > 80) - parse_err("device name too long"); + if (strlen(name) + 1 > 80) { + pr_err("device name too long\n"); + return 0; + } if (token[1]) { ret = parse_num(&erase_size, token[1]); if (ret) { - parse_err("illegal erase size"); + pr_err("illegal erase size\n"); + return 0; } } @@ -444,8 +448,9 @@ static void block2mtd_exit(void) struct block2mtd_dev *dev = list_entry(pos, typeof(*dev), list); block2mtd_sync(&dev->mtd); mtd_device_unregister(&dev->mtd); - INFO("mtd%d: [%s] removed", dev->mtd.index, - dev->mtd.name + strlen("block2mtd: ")); + pr_info("mtd%d: [%s] removed\n", + dev->mtd.index, + dev->mtd.name + strlen("block2mtd: ")); list_del(&dev->list); block2mtd_free_device(dev); } diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 3e1b0a0ef4db..4f091c1a9981 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c @@ -2097,7 +2097,7 @@ notfound: ret = -ENODEV; dev_info(dev, "No supported DiskOnChip found\n"); err_probe: - kfree(cascade->bch); + free_bch(cascade->bch); for (floor = 0; floor < DOC_MAX_NBFLOORS; floor++) if (cascade->floors[floor]) doc_release_device(cascade->floors[floor]); diff --git a/drivers/mtd/devices/elm.c b/drivers/mtd/devices/elm.c index dccef9fdc1f2..d1dd6a33a050 100644 --- a/drivers/mtd/devices/elm.c +++ b/drivers/mtd/devices/elm.c @@ -20,14 +20,21 @@ #include <linux/interrupt.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/sched.h> #include <linux/pm_runtime.h> #include <linux/platform_data/elm.h> +#define ELM_SYSCONFIG 0x010 #define ELM_IRQSTATUS 0x018 #define ELM_IRQENABLE 0x01c #define ELM_LOCATION_CONFIG 0x020 #define ELM_PAGE_CTRL 0x080 #define ELM_SYNDROME_FRAGMENT_0 0x400 +#define ELM_SYNDROME_FRAGMENT_1 0x404 +#define ELM_SYNDROME_FRAGMENT_2 0x408 +#define ELM_SYNDROME_FRAGMENT_3 0x40c +#define ELM_SYNDROME_FRAGMENT_4 0x410 +#define ELM_SYNDROME_FRAGMENT_5 0x414 #define ELM_SYNDROME_FRAGMENT_6 0x418 #define ELM_LOCATION_STATUS 0x800 #define ELM_ERROR_LOCATION_0 0x880 @@ -56,12 +63,27 @@ #define SYNDROME_FRAGMENT_REG_SIZE 0x40 #define ERROR_LOCATION_SIZE 0x100 +struct elm_registers { + u32 elm_irqenable; + u32 elm_sysconfig; + u32 elm_location_config; + u32 elm_page_ctrl; + u32 elm_syndrome_fragment_6[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_5[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_4[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_3[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_2[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_1[ERROR_VECTOR_MAX]; + u32 elm_syndrome_fragment_0[ERROR_VECTOR_MAX]; +}; + struct elm_info { struct device *dev; void __iomem *elm_base; struct completion elm_completion; struct list_head list; enum bch_ecc bch_type; + struct elm_registers elm_regs; }; static LIST_HEAD(elm_devices); @@ -346,14 +368,9 @@ static int elm_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "no memory resource defined\n"); - return -ENODEV; - } - - info->elm_base = devm_request_and_ioremap(&pdev->dev, res); - if (!info->elm_base) - return -EADDRNOTAVAIL; + info->elm_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->elm_base)) + return PTR_ERR(info->elm_base); ret = devm_request_irq(&pdev->dev, irq->start, elm_isr, 0, pdev->name, info); @@ -381,10 +398,103 @@ static int elm_remove(struct platform_device *pdev) { pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - platform_set_drvdata(pdev, NULL); return 0; } +/** + * elm_context_save + * saves ELM configurations to preserve them across Hardware powered-down + */ +static int elm_context_save(struct elm_info *info) +{ + struct elm_registers *regs = &info->elm_regs; + enum bch_ecc bch_type = info->bch_type; + u32 offset = 0, i; + + regs->elm_irqenable = elm_read_reg(info, ELM_IRQENABLE); + regs->elm_sysconfig = elm_read_reg(info, ELM_SYSCONFIG); + regs->elm_location_config = elm_read_reg(info, ELM_LOCATION_CONFIG); + regs->elm_page_ctrl = elm_read_reg(info, ELM_PAGE_CTRL); + for (i = 0; i < ERROR_VECTOR_MAX; i++) { + offset = i * SYNDROME_FRAGMENT_REG_SIZE; + switch (bch_type) { + case BCH8_ECC: + regs->elm_syndrome_fragment_3[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_3 + offset); + regs->elm_syndrome_fragment_2[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_2 + offset); + case BCH4_ECC: + regs->elm_syndrome_fragment_1[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_1 + offset); + regs->elm_syndrome_fragment_0[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_0 + offset); + default: + return -EINVAL; + } + /* ELM SYNDROME_VALID bit in SYNDROME_FRAGMENT_6[] needs + * to be saved for all BCH schemes*/ + regs->elm_syndrome_fragment_6[i] = elm_read_reg(info, + ELM_SYNDROME_FRAGMENT_6 + offset); + } + return 0; +} + +/** + * elm_context_restore + * writes configurations saved duing power-down back into ELM registers + */ +static int elm_context_restore(struct elm_info *info) +{ + struct elm_registers *regs = &info->elm_regs; + enum bch_ecc bch_type = info->bch_type; + u32 offset = 0, i; + + elm_write_reg(info, ELM_IRQENABLE, regs->elm_irqenable); + elm_write_reg(info, ELM_SYSCONFIG, regs->elm_sysconfig); + elm_write_reg(info, ELM_LOCATION_CONFIG, regs->elm_location_config); + elm_write_reg(info, ELM_PAGE_CTRL, regs->elm_page_ctrl); + for (i = 0; i < ERROR_VECTOR_MAX; i++) { + offset = i * SYNDROME_FRAGMENT_REG_SIZE; + switch (bch_type) { + case BCH8_ECC: + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_3 + offset, + regs->elm_syndrome_fragment_3[i]); + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_2 + offset, + regs->elm_syndrome_fragment_2[i]); + case BCH4_ECC: + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_1 + offset, + regs->elm_syndrome_fragment_1[i]); + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_0 + offset, + regs->elm_syndrome_fragment_0[i]); + default: + return -EINVAL; + } + /* ELM_SYNDROME_VALID bit to be set in last to trigger FSM */ + elm_write_reg(info, ELM_SYNDROME_FRAGMENT_6 + offset, + regs->elm_syndrome_fragment_6[i] & + ELM_SYNDROME_VALID); + } + return 0; +} + +static int elm_suspend(struct device *dev) +{ + struct elm_info *info = dev_get_drvdata(dev); + elm_context_save(info); + pm_runtime_put_sync(dev); + return 0; +} + +static int elm_resume(struct device *dev) +{ + struct elm_info *info = dev_get_drvdata(dev); + pm_runtime_get_sync(dev); + elm_context_restore(info); + return 0; +} + +static SIMPLE_DEV_PM_OPS(elm_pm_ops, elm_suspend, elm_resume); + #ifdef CONFIG_OF static const struct of_device_id elm_of_match[] = { { .compatible = "ti,am3352-elm" }, @@ -398,6 +508,7 @@ static struct platform_driver elm_driver = { .name = "elm", .owner = THIS_MODULE, .of_match_table = of_match_ptr(elm_of_match), + .pm = &elm_pm_ops, }, .probe = elm_probe, .remove = elm_remove, diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 2f3d2a5ff349..7eda71dbc183 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -43,17 +43,24 @@ #define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ #define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ #define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ +#define OPCODE_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */ #define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ #define OPCODE_CHIP_ERASE 0xc7 /* Erase whole flash chip */ #define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ #define OPCODE_RDID 0x9f /* Read JEDEC ID */ +/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ +#define OPCODE_NORM_READ_4B 0x13 /* Read data bytes (low frequency) */ +#define OPCODE_FAST_READ_4B 0x0c /* Read data bytes (high frequency) */ +#define OPCODE_PP_4B 0x12 /* Page program (up to 256 bytes) */ +#define OPCODE_SE_4B 0xdc /* Sector erase (usually 64KiB) */ + /* Used for SST flashes only. */ #define OPCODE_BP 0x02 /* Byte program */ #define OPCODE_WRDI 0x04 /* Write disable */ #define OPCODE_AAI_WP 0xad /* Auto address increment word program */ -/* Used for Macronix flashes only. */ +/* Used for Macronix and Winbond flashes. */ #define OPCODE_EN4B 0xb7 /* Enter 4-byte mode */ #define OPCODE_EX4B 0xe9 /* Exit 4-byte mode */ @@ -71,7 +78,7 @@ /* Define max times to check status register before we give up. */ #define MAX_READY_WAIT_JIFFIES (40 * HZ) /* M25P16 specs 40s max chip erase */ -#define MAX_CMD_SIZE 5 +#define MAX_CMD_SIZE 6 #define JEDEC_MFR(_jedec_id) ((_jedec_id) >> 16) @@ -84,6 +91,8 @@ struct m25p { u16 page_size; u16 addr_width; u8 erase_opcode; + u8 read_opcode; + u8 program_opcode; u8 *command; bool fast_read; }; @@ -159,11 +168,25 @@ static inline int write_disable(struct m25p *flash) */ static inline int set_4byte(struct m25p *flash, u32 jedec_id, int enable) { + int status; + bool need_wren = false; + switch (JEDEC_MFR(jedec_id)) { + case CFI_MFR_ST: /* Micron, actually */ + /* Some Micron need WREN command; all will accept it */ + need_wren = true; case CFI_MFR_MACRONIX: case 0xEF /* winbond */: + if (need_wren) + write_enable(flash); + flash->command[0] = enable ? OPCODE_EN4B : OPCODE_EX4B; - return spi_write(flash->spi, flash->command, 1); + status = spi_write(flash->spi, flash->command, 1); + + if (need_wren) + write_disable(flash); + + return status; default: /* Spansion style */ flash->command[0] = OPCODE_BRWR; @@ -344,10 +367,6 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, spi_message_init(&m); memset(t, 0, (sizeof t)); - /* NOTE: - * OPCODE_FAST_READ (if available) is faster. - * Should add 1 byte DUMMY_BYTE. - */ t[0].tx_buf = flash->command; t[0].len = m25p_cmdsz(flash) + (flash->fast_read ? 1 : 0); spi_message_add_tail(&t[0], &m); @@ -365,13 +384,8 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, return 1; } - /* FIXME switch to OPCODE_FAST_READ. It's required for higher - * clocks; and at this writing, every chip this driver handles - * supports that opcode. - */ - /* Set up the write data buffer. */ - opcode = flash->fast_read ? OPCODE_FAST_READ : OPCODE_NORM_READ; + opcode = flash->read_opcode; flash->command[0] = opcode; m25p_addr2cmd(flash, from, flash->command); @@ -422,7 +436,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, write_enable(flash); /* Set up the opcode in the write buffer. */ - flash->command[0] = OPCODE_PP; + flash->command[0] = flash->program_opcode; m25p_addr2cmd(flash, to, flash->command); page_offset = to & (flash->page_size - 1); @@ -682,6 +696,8 @@ struct flash_info { #define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ #define M25P_NO_ERASE 0x02 /* No erase command needed */ #define SST_WRITE 0x04 /* use SST byte programming */ +#define M25P_NO_FR 0x08 /* Can't do fastread */ +#define SECT_4K_PMC 0x10 /* OPCODE_BE_4K_PMC works uniformly */ }; #define INFO(_jedec_id, _ext_id, _sector_size, _n_sectors, _flags) \ @@ -694,13 +710,13 @@ struct flash_info { .flags = (_flags), \ }) -#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width) \ +#define CAT25_INFO(_sector_size, _n_sectors, _page_size, _addr_width, _flags) \ ((kernel_ulong_t)&(struct flash_info) { \ .sector_size = (_sector_size), \ .n_sectors = (_n_sectors), \ .page_size = (_page_size), \ .addr_width = (_addr_width), \ - .flags = M25P_NO_ERASE, \ + .flags = (_flags), \ }) /* NOTE: double check command sets and memory organization when you add @@ -724,15 +740,19 @@ static const struct spi_device_id m25p_ids[] = { { "at45db081d", INFO(0x1f2500, 0, 64 * 1024, 16, SECT_4K) }, /* EON -- en25xxx */ - { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) }, - { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) }, - { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, - { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, - { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, - { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, + { "en25f32", INFO(0x1c3116, 0, 64 * 1024, 64, SECT_4K) }, + { "en25p32", INFO(0x1c2016, 0, 64 * 1024, 64, 0) }, + { "en25q32b", INFO(0x1c3016, 0, 64 * 1024, 64, 0) }, + { "en25p64", INFO(0x1c2017, 0, 64 * 1024, 128, 0) }, + { "en25q64", INFO(0x1c3017, 0, 64 * 1024, 128, SECT_4K) }, + { "en25qh256", INFO(0x1c7019, 0, 64 * 1024, 512, 0) }, + + /* ESMT */ + { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) }, /* Everspin */ - { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2) }, + { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, M25P_NO_ERASE | M25P_NO_FR) }, + { "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, M25P_NO_ERASE | M25P_NO_FR) }, /* GigaDevice */ { "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64, SECT_4K) }, @@ -749,6 +769,7 @@ static const struct spi_device_id m25p_ids[] = { { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) }, { "mx25l3205d", INFO(0xc22016, 0, 64 * 1024, 64, 0) }, + { "mx25l3255e", INFO(0xc29e16, 0, 64 * 1024, 64, SECT_4K) }, { "mx25l6405d", INFO(0xc22017, 0, 64 * 1024, 128, 0) }, { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) }, { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) }, @@ -757,10 +778,16 @@ static const struct spi_device_id m25p_ids[] = { { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, 0) }, /* Micron */ - { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) }, - { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) }, - { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, - { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, + { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, 0) }, + { "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, 0) }, + { "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, 0) }, + { "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K) }, + { "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K) }, + + /* PMC */ + { "pm25lv512", INFO(0, 0, 32 * 1024, 2, SECT_4K_PMC) }, + { "pm25lv010", INFO(0, 0, 32 * 1024, 4, SECT_4K_PMC) }, + { "pm25lq032", INFO(0x7f9d46, 0, 64 * 1024, 64, SECT_4K) }, /* Spansion -- single (large) sector size only, at least * for the chips listed here (without boot sectors). @@ -840,17 +867,18 @@ static const struct spi_device_id m25p_ids[] = { { "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64, SECT_4K) }, { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128, SECT_4K) }, { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128, SECT_4K) }, + { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) }, { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) }, { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) }, { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) }, /* Catalyst / On Semiconductor -- non-JEDEC */ - { "cat25c11", CAT25_INFO( 16, 8, 16, 1) }, - { "cat25c03", CAT25_INFO( 32, 8, 16, 2) }, - { "cat25c09", CAT25_INFO( 128, 8, 32, 2) }, - { "cat25c17", CAT25_INFO( 256, 8, 32, 2) }, - { "cat25128", CAT25_INFO(2048, 8, 64, 2) }, + { "cat25c11", CAT25_INFO( 16, 8, 16, 1, M25P_NO_ERASE | M25P_NO_FR) }, + { "cat25c03", CAT25_INFO( 32, 8, 16, 2, M25P_NO_ERASE | M25P_NO_FR) }, + { "cat25c09", CAT25_INFO( 128, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) }, + { "cat25c17", CAT25_INFO( 256, 8, 32, 2, M25P_NO_ERASE | M25P_NO_FR) }, + { "cat25128", CAT25_INFO(2048, 8, 64, 2, M25P_NO_ERASE | M25P_NO_FR) }, { }, }; MODULE_DEVICE_TABLE(spi, m25p_ids); @@ -908,19 +936,14 @@ static int m25p_probe(struct spi_device *spi) struct flash_info *info; unsigned i; struct mtd_part_parser_data ppdata; - struct device_node __maybe_unused *np = spi->dev.of_node; - -#ifdef CONFIG_MTD_OF_PARTS - if (!of_device_is_available(np)) - return -ENODEV; -#endif + struct device_node *np = spi->dev.of_node; /* Platform data helps sort out which chip type we have, as * well as how this board partitions it. If we don't have * a chip ID, try the JEDEC id commands; they'll work for most * newer chips, even if we don't recognize the particular chip. */ - data = spi->dev.platform_data; + data = dev_get_platdata(&spi->dev); if (data && data->type) { const struct spi_device_id *plat_id; @@ -960,19 +983,17 @@ static int m25p_probe(struct spi_device *spi) } } - flash = kzalloc(sizeof *flash, GFP_KERNEL); + flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL); if (!flash) return -ENOMEM; - flash->command = kmalloc(MAX_CMD_SIZE + (flash->fast_read ? 1 : 0), - GFP_KERNEL); - if (!flash->command) { - kfree(flash); + + flash->command = devm_kzalloc(&spi->dev, MAX_CMD_SIZE, GFP_KERNEL); + if (!flash->command) return -ENOMEM; - } flash->spi = spi; mutex_init(&flash->lock); - dev_set_drvdata(&spi->dev, flash); + spi_set_drvdata(spi, flash); /* * Atmel, SST and Intel/Numonyx serial flash tend to power @@ -1014,6 +1035,9 @@ static int m25p_probe(struct spi_device *spi) if (info->flags & SECT_4K) { flash->erase_opcode = OPCODE_BE_4K; flash->mtd.erasesize = 4096; + } else if (info->flags & SECT_4K_PMC) { + flash->erase_opcode = OPCODE_BE_4K_PMC; + flash->mtd.erasesize = 4096; } else { flash->erase_opcode = OPCODE_SE; flash->mtd.erasesize = info->sector_size; @@ -1027,25 +1051,43 @@ static int m25p_probe(struct spi_device *spi) flash->page_size = info->page_size; flash->mtd.writebufsize = flash->page_size; - flash->fast_read = false; -#ifdef CONFIG_OF - if (np && of_property_read_bool(np, "m25p,fast-read")) + if (np) + /* If we were instantiated by DT, use it */ + flash->fast_read = of_property_read_bool(np, "m25p,fast-read"); + else + /* If we weren't instantiated by DT, default to fast-read */ flash->fast_read = true; -#endif -#ifdef CONFIG_M25PXX_USE_FAST_READ - flash->fast_read = true; -#endif + /* Some devices cannot do fast-read, no matter what DT tells us */ + if (info->flags & M25P_NO_FR) + flash->fast_read = false; + + /* Default commands */ + if (flash->fast_read) + flash->read_opcode = OPCODE_FAST_READ; + else + flash->read_opcode = OPCODE_NORM_READ; + + flash->program_opcode = OPCODE_PP; if (info->addr_width) flash->addr_width = info->addr_width; - else { + else if (flash->mtd.size > 0x1000000) { /* enable 4-byte addressing if the device exceeds 16MiB */ - if (flash->mtd.size > 0x1000000) { - flash->addr_width = 4; - set_4byte(flash, info->jedec_id, 1); + flash->addr_width = 4; + if (JEDEC_MFR(info->jedec_id) == CFI_MFR_AMD) { + /* Dedicated 4-byte command set */ + flash->read_opcode = flash->fast_read ? + OPCODE_FAST_READ_4B : + OPCODE_NORM_READ_4B; + flash->program_opcode = OPCODE_PP_4B; + /* No small sector erase for 4-byte command set */ + flash->erase_opcode = OPCODE_SE_4B; + flash->mtd.erasesize = info->sector_size; } else - flash->addr_width = 3; + set_4byte(flash, info->jedec_id, 1); + } else { + flash->addr_width = 3; } dev_info(&spi->dev, "%s (%lld Kbytes)\n", id->name, @@ -1080,16 +1122,10 @@ static int m25p_probe(struct spi_device *spi) static int m25p_remove(struct spi_device *spi) { - struct m25p *flash = dev_get_drvdata(&spi->dev); - int status; + struct m25p *flash = spi_get_drvdata(spi); /* Clean up MTD stuff. */ - status = mtd_device_unregister(&flash->mtd); - if (status == 0) { - kfree(flash->command); - kfree(flash); - } - return 0; + return mtd_device_unregister(&flash->mtd); } diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index 28779b6dfcd9..4a47b0266d4e 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -88,8 +88,6 @@ struct dataflash { uint8_t command[4]; char name[24]; - unsigned partitioned:1; - unsigned short page_offset; /* offset in flash address */ unsigned int page_size; /* of bytes per page */ @@ -622,7 +620,7 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages, struct dataflash *priv; struct mtd_info *device; struct mtd_part_parser_data ppdata; - struct flash_platform_data *pdata = spi->dev.platform_data; + struct flash_platform_data *pdata = dev_get_platdata(&spi->dev); char *otp_tag = ""; int err = 0; @@ -661,7 +659,7 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages, dev_info(&spi->dev, "%s (%lld KBytes) pagesize %d bytes%s\n", name, (long long)((device->size + 1023) >> 10), pagesize, otp_tag); - dev_set_drvdata(&spi->dev, priv); + spi_set_drvdata(spi, priv); ppdata.of_node = spi->dev.of_node; err = mtd_device_parse_register(device, NULL, &ppdata, @@ -671,7 +669,7 @@ static int add_dataflash_otp(struct spi_device *spi, char *name, int nr_pages, if (!err) return 0; - dev_set_drvdata(&spi->dev, NULL); + spi_set_drvdata(spi, NULL); kfree(priv); return err; } @@ -881,7 +879,7 @@ static int dataflash_probe(struct spi_device *spi) break; /* obsolete AT45DB1282 not (yet?) supported */ default: - pr_debug("%s: unsupported device (%x)\n", dev_name(&spi->dev), + dev_info(&spi->dev, "unsupported device (%x)\n", status & 0x3c); status = -ENODEV; } @@ -895,14 +893,14 @@ static int dataflash_probe(struct spi_device *spi) static int dataflash_remove(struct spi_device *spi) { - struct dataflash *flash = dev_get_drvdata(&spi->dev); + struct dataflash *flash = spi_get_drvdata(spi); int status; pr_debug("%s: remove\n", dev_name(&spi->dev)); status = mtd_device_unregister(&flash->mtd); if (status == 0) { - dev_set_drvdata(&spi->dev, NULL); + spi_set_drvdata(spi, NULL); kfree(flash); } return status; diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 67823de68db6..e1f2aebaa489 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -94,7 +94,7 @@ static void unregister_devices(void) } } -static int register_device(char *name, unsigned long start, unsigned long len) +static int register_device(char *name, phys_addr_t start, size_t len) { struct phram_mtd_list *new; int ret = -ENOMEM; @@ -141,35 +141,35 @@ out0: return ret; } -static int ustrtoul(const char *cp, char **endp, unsigned int base) +static int parse_num64(uint64_t *num64, char *token) { - unsigned long result = simple_strtoul(cp, endp, base); - - switch (**endp) { - case 'G': - result *= 1024; - case 'M': - result *= 1024; - case 'k': - result *= 1024; + size_t len; + int shift = 0; + int ret; + + len = strlen(token); /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */ - if ((*endp)[1] == 'i') - (*endp) += 2; + if (len > 2) { + if (token[len - 1] == 'i') { + switch (token[len - 2]) { + case 'G': + shift += 10; + case 'M': + shift += 10; + case 'k': + shift += 10; + token[len - 2] = 0; + break; + default: + return -EINVAL; + } + } } - return result; -} -static int parse_num32(uint32_t *num32, const char *token) -{ - char *endp; - unsigned long n; + ret = kstrtou64(token, 0, num64); + *num64 <<= shift; - n = ustrtoul(token, &endp, 0); - if (*endp) - return -EINVAL; - - *num32 = n; - return 0; + return ret; } static int parse_name(char **pname, const char *token) @@ -209,19 +209,19 @@ static inline void kill_final_newline(char *str) * This shall contain the module parameter if any. It is of the form: * - phram=<device>,<address>,<size> for module case * - phram.phram=<device>,<address>,<size> for built-in case - * We leave 64 bytes for the device name, 12 for the address and 12 for the + * We leave 64 bytes for the device name, 20 for the address and 20 for the * size. * Example: phram.phram=rootfs,0xa0000000,512Mi */ -static __initdata char phram_paramline[64+12+12]; +static __initdata char phram_paramline[64 + 20 + 20]; static int __init phram_setup(const char *val) { - char buf[64+12+12], *str = buf; + char buf[64 + 20 + 20], *str = buf; char *token[3]; char *name; - uint32_t start; - uint32_t len; + uint64_t start; + uint64_t len; int i, ret; if (strnlen(val, sizeof(buf)) >= sizeof(buf)) @@ -243,13 +243,13 @@ static int __init phram_setup(const char *val) if (ret) return ret; - ret = parse_num32(&start, token[1]); + ret = parse_num64(&start, token[1]); if (ret) { kfree(name); parse_err("illegal start address\n"); } - ret = parse_num32(&len, token[2]); + ret = parse_num64(&len, token[2]); if (ret) { kfree(name); parse_err("illegal device length\n"); @@ -257,7 +257,7 @@ static int __init phram_setup(const char *val) ret = register_device(name, start, len); if (!ret) - pr_info("%s device: %#x at %#x\n", name, len, start); + pr_info("%s device: %#llx at %#llx\n", name, len, start); else kfree(name); diff --git a/drivers/mtd/devices/spear_smi.c b/drivers/mtd/devices/spear_smi.c index 8a82b8bc21e1..423821412062 100644 --- a/drivers/mtd/devices/spear_smi.c +++ b/drivers/mtd/devices/spear_smi.c @@ -550,7 +550,7 @@ static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, { struct spear_snor_flash *flash = get_flash_data(mtd); struct spear_smi *dev = mtd->priv; - void *src; + void __iomem *src; u32 ctrlreg1, val; int ret; @@ -583,7 +583,7 @@ static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, writel(val, dev->io_base + SMI_CR1); - memcpy_fromio(buf, (u8 *)src, len); + memcpy_fromio(buf, src, len); /* restore ctrl reg1 */ writel(ctrlreg1, dev->io_base + SMI_CR1); @@ -596,7 +596,7 @@ static int spear_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, } static inline int spear_smi_cpy_toio(struct spear_smi *dev, u32 bank, - void *dest, const void *src, size_t len) + void __iomem *dest, const void *src, size_t len) { int ret; u32 ctrlreg1; @@ -643,7 +643,7 @@ static int spear_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, { struct spear_snor_flash *flash = get_flash_data(mtd); struct spear_smi *dev = mtd->priv; - void *dest; + void __iomem *dest; u32 page_offset, page_size; int ret; @@ -995,14 +995,12 @@ static int spear_smi_probe(struct platform_device *pdev) ret = spear_smi_setup_banks(pdev, i, pdata->np[i]); if (ret) { dev_err(&dev->pdev->dev, "bank setup failed\n"); - goto err_bank_setup; + goto err_irq; } } return 0; -err_bank_setup: - platform_set_drvdata(pdev, NULL); err_irq: clk_disable_unprepare(dev->clk); err: @@ -1040,12 +1038,11 @@ static int spear_smi_remove(struct platform_device *pdev) } clk_disable_unprepare(dev->clk); - platform_set_drvdata(pdev, NULL); return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int spear_smi_suspend(struct device *dev) { struct spear_smi *sdev = dev_get_drvdata(dev); @@ -1068,9 +1065,9 @@ static int spear_smi_resume(struct device *dev) spear_smi_hw_init(sdev); return ret; } +#endif static SIMPLE_DEV_PM_OPS(spear_smi_pm_ops, spear_smi_suspend, spear_smi_resume); -#endif #ifdef CONFIG_OF static const struct of_device_id spear_smi_id_table[] = { @@ -1086,9 +1083,7 @@ static struct platform_driver spear_smi_driver = { .bus = &platform_bus_type, .owner = THIS_MODULE, .of_match_table = of_match_ptr(spear_smi_id_table), -#ifdef CONFIG_PM .pm = &spear_smi_pm_ops, -#endif }, .probe = spear_smi_probe, .remove = spear_smi_remove, diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index 8091b0163694..687bf27ec850 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -364,15 +364,15 @@ static int sst25l_probe(struct spi_device *spi) if (!flash_info) return -ENODEV; - flash = kzalloc(sizeof(struct sst25l_flash), GFP_KERNEL); + flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL); if (!flash) return -ENOMEM; flash->spi = spi; mutex_init(&flash->lock); - dev_set_drvdata(&spi->dev, flash); + spi_set_drvdata(spi, flash); - data = spi->dev.platform_data; + data = dev_get_platdata(&spi->dev); if (data && data->name) flash->mtd.name = data->name; else @@ -402,24 +402,17 @@ static int sst25l_probe(struct spi_device *spi) ret = mtd_device_parse_register(&flash->mtd, NULL, NULL, data ? data->parts : NULL, data ? data->nr_parts : 0); - if (ret) { - kfree(flash); - dev_set_drvdata(&spi->dev, NULL); + if (ret) return -ENODEV; - } return 0; } static int sst25l_remove(struct spi_device *spi) { - struct sst25l_flash *flash = dev_get_drvdata(&spi->dev); - int ret; + struct sst25l_flash *flash = spi_get_drvdata(spi); - ret = mtd_device_unregister(&flash->mtd); - if (ret == 0) - kfree(flash); - return ret; + return mtd_device_unregister(&flash->mtd); } static struct spi_driver sst25l_driver = { diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index 3af351484098..b66b541877f0 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -50,7 +50,7 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) struct INFTLrecord *inftl; unsigned long temp; - if (mtd->type != MTD_NANDFLASH || mtd->size > UINT_MAX) + if (!mtd_type_is_nand(mtd) || mtd->size > UINT_MAX) return; /* OK, this is moderately ugly. But probably safe. Alternatives? */ if (memcmp(mtd->name, "DiskOnChip", 10)) diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c index d3cfe26beeaa..2ef19aa0086b 100644 --- a/drivers/mtd/lpddr/lpddr_cmds.c +++ b/drivers/mtd/lpddr/lpddr_cmds.c @@ -703,7 +703,7 @@ static int lpddr_erase(struct mtd_info *mtd, struct erase_info *instr) #define DO_XXLOCK_LOCK 1 #define DO_XXLOCK_UNLOCK 2 -int do_xxlock(struct mtd_info *mtd, loff_t adr, uint32_t len, int thunk) +static int do_xxlock(struct mtd_info *mtd, loff_t adr, uint32_t len, int thunk) { int ret = 0; struct map_info *map = mtd->priv; diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 8b27ca054c59..310dc7c93425 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -157,24 +157,6 @@ config MTD_PXA2XX help This provides a driver for the NOR flash attached to a PXA2xx chip. -config MTD_OCTAGON - tristate "JEDEC Flash device mapped on Octagon 5066 SBC" - depends on X86 && MTD_JEDEC && MTD_COMPLEX_MAPPINGS - help - This provides a 'mapping' driver which supports the way in which - the flash chips are connected in the Octagon-5066 Single Board - Computer. More information on the board is available at - <http://www.octagonsystems.com/products/5066.aspx>. - -config MTD_VMAX - tristate "JEDEC Flash device mapped on Tempustech VMAX SBC301" - depends on X86 && MTD_JEDEC && MTD_COMPLEX_MAPPINGS - help - This provides a 'mapping' driver which supports the way in which - the flash chips are connected in the Tempustech VMAX SBC301 Single - Board Computer. More information on the board is available at - <http://www.tempustech.com/>. - config MTD_SCx200_DOCFLASH tristate "Flash device mapped with DOCCS on NatSemi SCx200" depends on SCx200 && MTD_CFI diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 9fdbd4ba6441..141c91a5b24c 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -16,7 +16,6 @@ obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o -obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o obj-$(CONFIG_MTD_PISMO) += pismo.o @@ -28,7 +27,6 @@ obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o obj-$(CONFIG_MTD_NETSC520) += netsc520.o obj-$(CONFIG_MTD_TS5500) += ts5500_flash.o obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o -obj-$(CONFIG_MTD_VMAX) += vmax301.o obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o obj-$(CONFIG_MTD_PCI) += pci.o diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c index 319b04a6c9d1..5434d8ded015 100644 --- a/drivers/mtd/maps/bfin-async-flash.c +++ b/drivers/mtd/maps/bfin-async-flash.c @@ -128,7 +128,7 @@ static const char * const part_probe_types[] = { static int bfin_flash_probe(struct platform_device *pdev) { int ret; - struct physmap_flash_data *pdata = pdev->dev.platform_data; + struct physmap_flash_data *pdata = dev_get_platdata(&pdev->dev); struct resource *memory = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct resource *flash_ambctl = platform_get_resource(pdev, IORESOURCE_MEM, 1); struct async_state *state; diff --git a/drivers/mtd/maps/cfi_flagadm.c b/drivers/mtd/maps/cfi_flagadm.c index d16fc9d3b8cd..d504b3d1791d 100644 --- a/drivers/mtd/maps/cfi_flagadm.c +++ b/drivers/mtd/maps/cfi_flagadm.c @@ -55,13 +55,13 @@ #define FLASH_PARTITION3_SIZE 0x001C0000 -struct map_info flagadm_map = { +static struct map_info flagadm_map = { .name = "FlagaDM flash device", .size = FLASH_SIZE, .bankwidth = 2, }; -struct mtd_partition flagadm_parts[] = { +static struct mtd_partition flagadm_parts[] = { { .name = "Bootloader", .offset = FLASH_PARTITION0_ADDR, @@ -112,7 +112,7 @@ static int __init init_flagadm(void) return 0; } - iounmap((void *)flagadm_map.virt); + iounmap((void __iomem *)flagadm_map.virt); return -ENXIO; } @@ -123,8 +123,8 @@ static void __exit cleanup_flagadm(void) map_destroy(mymtd); } if (flagadm_map.virt) { - iounmap((void *)flagadm_map.virt); - flagadm_map.virt = 0; + iounmap((void __iomem *)flagadm_map.virt); + flagadm_map.virt = NULL; } } diff --git a/drivers/mtd/maps/gpio-addr-flash.c b/drivers/mtd/maps/gpio-addr-flash.c index 5ede28294f9e..1adba86474a5 100644 --- a/drivers/mtd/maps/gpio-addr-flash.c +++ b/drivers/mtd/maps/gpio-addr-flash.c @@ -196,7 +196,7 @@ static int gpio_flash_probe(struct platform_device *pdev) struct resource *gpios; struct async_state *state; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); memory = platform_get_resource(pdev, IORESOURCE_MEM, 0); gpios = platform_get_resource(pdev, IORESOURCE_IRQ, 0); diff --git a/drivers/mtd/maps/impa7.c b/drivers/mtd/maps/impa7.c index 49686744d93c..15bbda03be65 100644 --- a/drivers/mtd/maps/impa7.c +++ b/drivers/mtd/maps/impa7.c @@ -79,7 +79,7 @@ static int __init init_impa7(void) } simple_map_init(&impa7_map[i]); - impa7_mtd[i] = 0; + impa7_mtd[i] = NULL; type = rom_probe_types; for(; !impa7_mtd[i] && *type; type++) { impa7_mtd[i] = do_map_probe(*type, &impa7_map[i]); @@ -91,9 +91,9 @@ static int __init init_impa7(void) mtd_device_parse_register(impa7_mtd[i], NULL, NULL, partitions, ARRAY_SIZE(partitions)); + } else { + iounmap((void __iomem *)impa7_map[i].virt); } - else - iounmap((void *)impa7_map[i].virt); } return devicesfound == 0 ? -ENXIO : 0; } @@ -105,8 +105,8 @@ static void __exit cleanup_impa7(void) if (impa7_mtd[i]) { mtd_device_unregister(impa7_mtd[i]); map_destroy(impa7_mtd[i]); - iounmap((void *)impa7_map[i].virt); - impa7_map[i].virt = 0; + iounmap((void __iomem *)impa7_map[i].virt); + impa7_map[i].virt = NULL; } } } diff --git a/drivers/mtd/maps/intel_vr_nor.c b/drivers/mtd/maps/intel_vr_nor.c index f581ac1cf022..46d195fca942 100644 --- a/drivers/mtd/maps/intel_vr_nor.c +++ b/drivers/mtd/maps/intel_vr_nor.c @@ -180,7 +180,6 @@ static void vr_nor_pci_remove(struct pci_dev *dev) { struct vr_nor_mtd *p = pci_get_drvdata(dev); - pci_set_drvdata(dev, NULL); vr_nor_destroy_partitions(p); vr_nor_destroy_mtd_setup(p); vr_nor_destroy_maps(p); diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index 52b3410a105c..10debfea81e7 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -152,11 +152,9 @@ static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL }; static int ixp4xx_flash_remove(struct platform_device *dev) { - struct flash_platform_data *plat = dev->dev.platform_data; + struct flash_platform_data *plat = dev_get_platdata(&dev->dev); struct ixp4xx_flash_info *info = platform_get_drvdata(dev); - platform_set_drvdata(dev, NULL); - if(!info) return 0; @@ -180,7 +178,7 @@ static int ixp4xx_flash_remove(struct platform_device *dev) static int ixp4xx_flash_probe(struct platform_device *dev) { - struct flash_platform_data *plat = dev->dev.platform_data; + struct flash_platform_data *plat = dev_get_platdata(&dev->dev); struct ixp4xx_flash_info *info; struct mtd_part_parser_data ppdata = { .origin = dev->resource->start, diff --git a/drivers/mtd/maps/latch-addr-flash.c b/drivers/mtd/maps/latch-addr-flash.c index ab0fead56b83..98bb5d5375d7 100644 --- a/drivers/mtd/maps/latch-addr-flash.c +++ b/drivers/mtd/maps/latch-addr-flash.c @@ -102,9 +102,8 @@ static int latch_addr_flash_remove(struct platform_device *dev) info = platform_get_drvdata(dev); if (info == NULL) return 0; - platform_set_drvdata(dev, NULL); - latch_addr_data = dev->dev.platform_data; + latch_addr_data = dev_get_platdata(&dev->dev); if (info->mtd != NULL) { mtd_device_unregister(info->mtd); @@ -135,7 +134,7 @@ static int latch_addr_flash_probe(struct platform_device *dev) int chipsel; int err; - latch_addr_data = dev->dev.platform_data; + latch_addr_data = dev_get_platdata(&dev->dev); if (latch_addr_data == NULL) return -ENODEV; diff --git a/drivers/mtd/maps/octagon-5066.c b/drivers/mtd/maps/octagon-5066.c deleted file mode 100644 index 807ac2a2e686..000000000000 --- a/drivers/mtd/maps/octagon-5066.c +++ /dev/null @@ -1,246 +0,0 @@ -/* ###################################################################### - - Octagon 5066 MTD Driver. - - The Octagon 5066 is a SBC based on AMD's 586-WB running at 133 MHZ. It - comes with a builtin AMD 29F016 flash chip and a socketed EEPROM that - is replacable by flash. Both units are mapped through a multiplexer - into a 32k memory window at 0xe8000. The control register for the - multiplexing unit is located at IO 0x208 with a bit map of - 0-5 Page Selection in 32k increments - 6-7 Device selection: - 00 SSD off - 01 SSD 0 (Socket) - 10 SSD 1 (Flash chip) - 11 undefined - - On each SSD, the first 128k is reserved for use by the bios - (actually it IS the bios..) This only matters if you are booting off the - flash, you must not put a file system starting there. - - The driver tries to do a detection algorithm to guess what sort of devices - are plugged into the sockets. - - ##################################################################### */ - -#include <linux/module.h> -#include <linux/ioport.h> -#include <linux/init.h> -#include <asm/io.h> - -#include <linux/mtd/map.h> -#include <linux/mtd/mtd.h> - -#define WINDOW_START 0xe8000 -#define WINDOW_LENGTH 0x8000 -#define WINDOW_SHIFT 27 -#define WINDOW_MASK 0x7FFF -#define PAGE_IO 0x208 - -static volatile char page_n_dev = 0; -static unsigned long iomapadr; -static DEFINE_SPINLOCK(oct5066_spin); - -/* - * We use map_priv_1 to identify which device we are. - */ - -static void __oct5066_page(struct map_info *map, __u8 byte) -{ - outb(byte,PAGE_IO); - page_n_dev = byte; -} - -static inline void oct5066_page(struct map_info *map, unsigned long ofs) -{ - __u8 byte = map->map_priv_1 | (ofs >> WINDOW_SHIFT); - - if (page_n_dev != byte) - __oct5066_page(map, byte); -} - - -static map_word oct5066_read8(struct map_info *map, unsigned long ofs) -{ - map_word ret; - spin_lock(&oct5066_spin); - oct5066_page(map, ofs); - ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK)); - spin_unlock(&oct5066_spin); - return ret; -} - -static void oct5066_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - while(len) { - unsigned long thislen = len; - if (len > (WINDOW_LENGTH - (from & WINDOW_MASK))) - thislen = WINDOW_LENGTH-(from & WINDOW_MASK); - - spin_lock(&oct5066_spin); - oct5066_page(map, from); - memcpy_fromio(to, iomapadr + from, thislen); - spin_unlock(&oct5066_spin); - to += thislen; - from += thislen; - len -= thislen; - } -} - -static void oct5066_write8(struct map_info *map, map_word d, unsigned long adr) -{ - spin_lock(&oct5066_spin); - oct5066_page(map, adr); - writeb(d.x[0], iomapadr + (adr & WINDOW_MASK)); - spin_unlock(&oct5066_spin); -} - -static void oct5066_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - while(len) { - unsigned long thislen = len; - if (len > (WINDOW_LENGTH - (to & WINDOW_MASK))) - thislen = WINDOW_LENGTH-(to & WINDOW_MASK); - - spin_lock(&oct5066_spin); - oct5066_page(map, to); - memcpy_toio(iomapadr + to, from, thislen); - spin_unlock(&oct5066_spin); - to += thislen; - from += thislen; - len -= thislen; - } -} - -static struct map_info oct5066_map[2] = { - { - .name = "Octagon 5066 Socket", - .phys = NO_XIP, - .size = 512 * 1024, - .bankwidth = 1, - .read = oct5066_read8, - .copy_from = oct5066_copy_from, - .write = oct5066_write8, - .copy_to = oct5066_copy_to, - .map_priv_1 = 1<<6 - }, - { - .name = "Octagon 5066 Internal Flash", - .phys = NO_XIP, - .size = 2 * 1024 * 1024, - .bankwidth = 1, - .read = oct5066_read8, - .copy_from = oct5066_copy_from, - .write = oct5066_write8, - .copy_to = oct5066_copy_to, - .map_priv_1 = 2<<6 - } -}; - -static struct mtd_info *oct5066_mtd[2] = {NULL, NULL}; - -// OctProbe - Sense if this is an octagon card -// --------------------------------------------------------------------- -/* Perform a simple validity test, we map the window select SSD0 and - change pages while monitoring the window. A change in the window, - controlled by the PAGE_IO port is a functioning 5066 board. This will - fail if the thing in the socket is set to a uniform value. */ -static int __init OctProbe(void) -{ - unsigned int Base = (1 << 6); - unsigned long I; - unsigned long Values[10]; - for (I = 0; I != 20; I++) - { - outb(Base + (I%10),PAGE_IO); - if (I < 10) - { - // Record the value and check for uniqueness - Values[I%10] = readl(iomapadr); - if (I > 0 && Values[I%10] == Values[0]) - return -EAGAIN; - } - else - { - // Make sure we get the same values on the second pass - if (Values[I%10] != readl(iomapadr)) - return -EAGAIN; - } - } - return 0; -} - -void cleanup_oct5066(void) -{ - int i; - for (i=0; i<2; i++) { - if (oct5066_mtd[i]) { - mtd_device_unregister(oct5066_mtd[i]); - map_destroy(oct5066_mtd[i]); - } - } - iounmap((void *)iomapadr); - release_region(PAGE_IO, 1); -} - -static int __init init_oct5066(void) -{ - int i; - int ret = 0; - - // Do an autoprobe sequence - if (!request_region(PAGE_IO,1,"Octagon SSD")) { - printk(KERN_NOTICE "5066: Page Register in Use\n"); - return -EAGAIN; - } - iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH); - if (!iomapadr) { - printk(KERN_NOTICE "Failed to ioremap memory region\n"); - ret = -EIO; - goto out_rel; - } - if (OctProbe() != 0) { - printk(KERN_NOTICE "5066: Octagon Probe Failed, is this an Octagon 5066 SBC?\n"); - iounmap((void *)iomapadr); - ret = -EAGAIN; - goto out_unmap; - } - - // Print out our little header.. - printk("Octagon 5066 SSD IO:0x%x MEM:0x%x-0x%x\n",PAGE_IO,WINDOW_START, - WINDOW_START+WINDOW_LENGTH); - - for (i=0; i<2; i++) { - oct5066_mtd[i] = do_map_probe("cfi_probe", &oct5066_map[i]); - if (!oct5066_mtd[i]) - oct5066_mtd[i] = do_map_probe("jedec", &oct5066_map[i]); - if (!oct5066_mtd[i]) - oct5066_mtd[i] = do_map_probe("map_ram", &oct5066_map[i]); - if (!oct5066_mtd[i]) - oct5066_mtd[i] = do_map_probe("map_rom", &oct5066_map[i]); - if (oct5066_mtd[i]) { - oct5066_mtd[i]->owner = THIS_MODULE; - mtd_device_register(oct5066_mtd[i], NULL, 0); - } - } - - if (!oct5066_mtd[0] && !oct5066_mtd[1]) { - cleanup_oct5066(); - return -ENXIO; - } - - return 0; - - out_unmap: - iounmap((void *)iomapadr); - out_rel: - release_region(PAGE_IO, 1); - return ret; -} - -module_init(init_oct5066); -module_exit(cleanup_oct5066); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jason Gunthorpe <jgg@deltatee.com>, David Woodhouse <dwmw2@infradead.org>"); -MODULE_DESCRIPTION("MTD map driver for Octagon 5066 Single Board Computer"); diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c index c2604f8b2a5e..36da518915b5 100644 --- a/drivers/mtd/maps/pci.c +++ b/drivers/mtd/maps/pci.c @@ -316,7 +316,6 @@ static void mtd_pci_remove(struct pci_dev *dev) map->exit(dev, map); kfree(map); - pci_set_drvdata(dev, NULL); pci_release_regions(dev); } diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index e7a592c8c765..f73cd461257c 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -40,9 +40,8 @@ static int physmap_flash_remove(struct platform_device *dev) info = platform_get_drvdata(dev); if (info == NULL) return 0; - platform_set_drvdata(dev, NULL); - physmap_data = dev->dev.platform_data; + physmap_data = dev_get_platdata(&dev->dev); if (info->cmtd) { mtd_device_unregister(info->cmtd); @@ -69,7 +68,7 @@ static void physmap_set_vpp(struct map_info *map, int state) unsigned long flags; pdev = (struct platform_device *)map->map_priv_1; - physmap_data = pdev->dev.platform_data; + physmap_data = dev_get_platdata(&pdev->dev); if (!physmap_data->set_vpp) return; @@ -103,7 +102,7 @@ static int physmap_flash_probe(struct platform_device *dev) int i; int devices_found = 0; - physmap_data = dev->dev.platform_data; + physmap_data = dev_get_platdata(&dev->dev); if (physmap_data == NULL) return -ENODEV; diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 71fdda29594b..10196f5a897d 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -55,7 +55,7 @@ struct platram_info { static inline struct platram_info *to_platram_info(struct platform_device *dev) { - return (struct platram_info *)platform_get_drvdata(dev); + return platform_get_drvdata(dev); } /* platram_setrw @@ -84,8 +84,6 @@ static int platram_remove(struct platform_device *pdev) { struct platram_info *info = to_platram_info(pdev); - platform_set_drvdata(pdev, NULL); - dev_dbg(&pdev->dev, "removing device\n"); if (info == NULL) @@ -130,13 +128,13 @@ static int platram_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "probe entered\n"); - if (pdev->dev.platform_data == NULL) { + if (dev_get_platdata(&pdev->dev) == NULL) { dev_err(&pdev->dev, "no platform data supplied\n"); err = -ENOENT; goto exit_error; } - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); info = kzalloc(sizeof(*info), GFP_KERNEL); if (info == NULL) { @@ -259,21 +257,7 @@ static struct platform_driver platram_driver = { }, }; -/* module init/exit */ - -static int __init platram_init(void) -{ - printk("Generic platform RAM MTD, (c) 2004 Simtec Electronics\n"); - return platform_driver_register(&platram_driver); -} - -static void __exit platram_exit(void) -{ - platform_driver_unregister(&platram_driver); -} - -module_init(platram_init); -module_exit(platram_exit); +module_platform_driver(platram_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c index acb1dbcf7ce5..d210d131fef2 100644 --- a/drivers/mtd/maps/pxa2xx-flash.c +++ b/drivers/mtd/maps/pxa2xx-flash.c @@ -49,7 +49,7 @@ static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL }; static int pxa2xx_flash_probe(struct platform_device *pdev) { - struct flash_platform_data *flash = pdev->dev.platform_data; + struct flash_platform_data *flash = dev_get_platdata(&pdev->dev); struct pxa2xx_flash_info *info; struct resource *res; @@ -107,8 +107,6 @@ static int pxa2xx_flash_remove(struct platform_device *dev) { struct pxa2xx_flash_info *info = platform_get_drvdata(dev); - platform_set_drvdata(dev, NULL); - mtd_device_unregister(info->mtd); map_destroy(info->mtd); diff --git a/drivers/mtd/maps/rbtx4939-flash.c b/drivers/mtd/maps/rbtx4939-flash.c index ac02fbffd6df..93525121d69d 100644 --- a/drivers/mtd/maps/rbtx4939-flash.c +++ b/drivers/mtd/maps/rbtx4939-flash.c @@ -34,10 +34,9 @@ static int rbtx4939_flash_remove(struct platform_device *dev) info = platform_get_drvdata(dev); if (!info) return 0; - platform_set_drvdata(dev, NULL); if (info->mtd) { - struct rbtx4939_flash_data *pdata = dev->dev.platform_data; + struct rbtx4939_flash_data *pdata = dev_get_platdata(&dev->dev); mtd_device_unregister(info->mtd); map_destroy(info->mtd); @@ -57,7 +56,7 @@ static int rbtx4939_flash_probe(struct platform_device *dev) int err = 0; unsigned long size; - pdata = dev->dev.platform_data; + pdata = dev_get_platdata(&dev->dev); if (!pdata) return -ENODEV; diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index 29e3dcaa1d90..8fc06bf111c4 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -248,7 +248,7 @@ static const char * const part_probes[] = { "cmdlinepart", "RedBoot", NULL }; static int sa1100_mtd_probe(struct platform_device *pdev) { - struct flash_platform_data *plat = pdev->dev.platform_data; + struct flash_platform_data *plat = dev_get_platdata(&pdev->dev); struct sa_info *info; int err; @@ -277,9 +277,8 @@ static int sa1100_mtd_probe(struct platform_device *pdev) static int __exit sa1100_mtd_remove(struct platform_device *pdev) { struct sa_info *info = platform_get_drvdata(pdev); - struct flash_platform_data *plat = pdev->dev.platform_data; + struct flash_platform_data *plat = dev_get_platdata(&pdev->dev); - platform_set_drvdata(pdev, NULL); sa1100_destroy(info, plat); return 0; diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c index c77b68c9412f..3051c4c36240 100644 --- a/drivers/mtd/maps/scb2_flash.c +++ b/drivers/mtd/maps/scb2_flash.c @@ -212,7 +212,6 @@ static void scb2_flash_remove(struct pci_dev *dev) if (!region_fail) release_mem_region(SCB2_ADDR, SCB2_WINDOW); - pci_set_drvdata(dev, NULL); } static struct pci_device_id scb2_flash_pci_ids[] = { diff --git a/drivers/mtd/maps/vmax301.c b/drivers/mtd/maps/vmax301.c deleted file mode 100644 index 5e68de73eabc..000000000000 --- a/drivers/mtd/maps/vmax301.c +++ /dev/null @@ -1,196 +0,0 @@ -/* ###################################################################### - - Tempustech VMAX SBC301 MTD Driver. - - The VMAx 301 is a SBC based on . It - comes with three builtin AMD 29F016B flash chips and a socket for SRAM or - more flash. Each unit has it's own 8k mapping into a settable region - (0xD8000). There are two 8k mappings for each MTD, the first is always set - to the lower 8k of the device the second is paged. Writing a 16 bit page - value to anywhere in the first 8k will cause the second 8k to page around. - - To boot the device a bios extension must be installed into the first 8k - of flash that is smart enough to copy itself down, page in the rest of - itself and begin executing. - - ##################################################################### */ - -#include <linux/module.h> -#include <linux/ioport.h> -#include <linux/init.h> -#include <linux/spinlock.h> -#include <asm/io.h> - -#include <linux/mtd/map.h> -#include <linux/mtd/mtd.h> - - -#define WINDOW_START 0xd8000 -#define WINDOW_LENGTH 0x2000 -#define WINDOW_SHIFT 25 -#define WINDOW_MASK 0x1FFF - -/* Actually we could use two spinlocks, but we'd have to have - more private space in the struct map_info. We lose a little - performance like this, but we'd probably lose more by having - the extra indirection from having one of the map->map_priv - fields pointing to yet another private struct. -*/ -static DEFINE_SPINLOCK(vmax301_spin); - -static void __vmax301_page(struct map_info *map, unsigned long page) -{ - writew(page, map->map_priv_2 - WINDOW_LENGTH); - map->map_priv_1 = page; -} - -static inline void vmax301_page(struct map_info *map, - unsigned long ofs) -{ - unsigned long page = (ofs >> WINDOW_SHIFT); - if (map->map_priv_1 != page) - __vmax301_page(map, page); -} - -static map_word vmax301_read8(struct map_info *map, unsigned long ofs) -{ - map_word ret; - spin_lock(&vmax301_spin); - vmax301_page(map, ofs); - ret.x[0] = readb(map->map_priv_2 + (ofs & WINDOW_MASK)); - spin_unlock(&vmax301_spin); - return ret; -} - -static void vmax301_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - while(len) { - unsigned long thislen = len; - if (len > (WINDOW_LENGTH - (from & WINDOW_MASK))) - thislen = WINDOW_LENGTH-(from & WINDOW_MASK); - spin_lock(&vmax301_spin); - vmax301_page(map, from); - memcpy_fromio(to, map->map_priv_2 + from, thislen); - spin_unlock(&vmax301_spin); - to += thislen; - from += thislen; - len -= thislen; - } -} - -static void vmax301_write8(struct map_info *map, map_word d, unsigned long adr) -{ - spin_lock(&vmax301_spin); - vmax301_page(map, adr); - writeb(d.x[0], map->map_priv_2 + (adr & WINDOW_MASK)); - spin_unlock(&vmax301_spin); -} - -static void vmax301_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - while(len) { - unsigned long thislen = len; - if (len > (WINDOW_LENGTH - (to & WINDOW_MASK))) - thislen = WINDOW_LENGTH-(to & WINDOW_MASK); - - spin_lock(&vmax301_spin); - vmax301_page(map, to); - memcpy_toio(map->map_priv_2 + to, from, thislen); - spin_unlock(&vmax301_spin); - to += thislen; - from += thislen; - len -= thislen; - } -} - -static struct map_info vmax_map[2] = { - { - .name = "VMAX301 Internal Flash", - .phys = NO_XIP, - .size = 3*2*1024*1024, - .bankwidth = 1, - .read = vmax301_read8, - .copy_from = vmax301_copy_from, - .write = vmax301_write8, - .copy_to = vmax301_copy_to, - .map_priv_1 = WINDOW_START + WINDOW_LENGTH, - .map_priv_2 = 0xFFFFFFFF - }, - { - .name = "VMAX301 Socket", - .phys = NO_XIP, - .size = 0, - .bankwidth = 1, - .read = vmax301_read8, - .copy_from = vmax301_copy_from, - .write = vmax301_write8, - .copy_to = vmax301_copy_to, - .map_priv_1 = WINDOW_START + (3*WINDOW_LENGTH), - .map_priv_2 = 0xFFFFFFFF - } -}; - -static struct mtd_info *vmax_mtd[2] = {NULL, NULL}; - -static void __exit cleanup_vmax301(void) -{ - int i; - - for (i=0; i<2; i++) { - if (vmax_mtd[i]) { - mtd_device_unregister(vmax_mtd[i]); - map_destroy(vmax_mtd[i]); - } - } - iounmap((void *)vmax_map[0].map_priv_1 - WINDOW_START); -} - -static int __init init_vmax301(void) -{ - int i; - unsigned long iomapadr; - // Print out our little header.. - printk("Tempustech VMAX 301 MEM:0x%x-0x%x\n",WINDOW_START, - WINDOW_START+4*WINDOW_LENGTH); - - iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH*4); - if (!iomapadr) { - printk("Failed to ioremap memory region\n"); - return -EIO; - } - /* Put the address in the map's private data area. - We store the actual MTD IO address rather than the - address of the first half, because it's used more - often. - */ - vmax_map[0].map_priv_2 = iomapadr + WINDOW_START; - vmax_map[1].map_priv_2 = iomapadr + (3*WINDOW_START); - - for (i=0; i<2; i++) { - vmax_mtd[i] = do_map_probe("cfi_probe", &vmax_map[i]); - if (!vmax_mtd[i]) - vmax_mtd[i] = do_map_probe("jedec", &vmax_map[i]); - if (!vmax_mtd[i]) - vmax_mtd[i] = do_map_probe("map_ram", &vmax_map[i]); - if (!vmax_mtd[i]) - vmax_mtd[i] = do_map_probe("map_rom", &vmax_map[i]); - if (vmax_mtd[i]) { - vmax_mtd[i]->owner = THIS_MODULE; - mtd_device_register(vmax_mtd[i], NULL, 0); - } - } - - if (!vmax_mtd[0] && !vmax_mtd[1]) { - iounmap((void *)iomapadr); - return -ENXIO; - } - - return 0; -} - -module_init(init_vmax301); -module_exit(cleanup_vmax301); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); -MODULE_DESCRIPTION("MTD map driver for Tempustech VMAX SBC301 board"); diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 2aef5dda522b..485ea751c7f9 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -32,6 +32,7 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/blktrans.h> #include <linux/mutex.h> +#include <linux/major.h> struct mtdblk_dev { @@ -373,7 +374,7 @@ static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) static struct mtd_blktrans_ops mtdblock_tr = { .name = "mtdblock", - .major = 31, + .major = MTD_BLOCK_MAJOR, .part_bits = 0, .blksize = 512, .open = mtdblock_open, diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c index 92759a9d2985..fb5dc89369de 100644 --- a/drivers/mtd/mtdblock_ro.c +++ b/drivers/mtd/mtdblock_ro.c @@ -24,6 +24,7 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/blktrans.h> #include <linux/module.h> +#include <linux/major.h> static int mtdblock_readsect(struct mtd_blktrans_dev *dev, unsigned long block, char *buf) @@ -70,7 +71,7 @@ static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) static struct mtd_blktrans_ops mtdblock_tr = { .name = "mtdblock", - .major = 31, + .major = MTD_BLOCK_MAJOR, .part_bits = 0, .blksize = 512, .readsect = mtdblock_readsect, diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 684bfa39e4ee..2147e733533b 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -32,6 +32,7 @@ #include <linux/mount.h> #include <linux/blkpg.h> #include <linux/magic.h> +#include <linux/major.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> #include <linux/mtd/map.h> @@ -1099,7 +1100,7 @@ static unsigned long mtdchar_get_unmapped_area(struct file *file, return (unsigned long) -EINVAL; ret = mtd_get_unmapped_area(mtd, len, offset, flags); - return ret == -EOPNOTSUPP ? -ENOSYS : ret; + return ret == -EOPNOTSUPP ? -ENODEV : ret; } #endif @@ -1124,9 +1125,9 @@ static int mtdchar_mmap(struct file *file, struct vm_area_struct *vma) #endif return vm_iomap_memory(vma, map->phys, map->size); } - return -ENOSYS; + return -ENODEV; #else - return vma->vm_flags & VM_SHARED ? 0 : -ENOSYS; + return vma->vm_flags & VM_SHARED ? 0 : -EACCES; #endif } diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 048c823f5c51..92311a56939f 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -157,6 +157,9 @@ static ssize_t mtd_type_show(struct device *dev, case MTD_UBIVOLUME: type = "ubi"; break; + case MTD_MLCNANDFLASH: + type = "mlc-nand"; + break; default: type = "unknown"; } @@ -285,6 +288,16 @@ static DEVICE_ATTR(bitflip_threshold, S_IRUGO | S_IWUSR, mtd_bitflip_threshold_show, mtd_bitflip_threshold_store); +static ssize_t mtd_ecc_step_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", mtd->ecc_step_size); + +} +static DEVICE_ATTR(ecc_step_size, S_IRUGO, mtd_ecc_step_size_show, NULL); + static struct attribute *mtd_attrs[] = { &dev_attr_type.attr, &dev_attr_flags.attr, @@ -296,6 +309,7 @@ static struct attribute *mtd_attrs[] = { &dev_attr_numeraseregions.attr, &dev_attr_name.attr, &dev_attr_ecc_strength.attr, + &dev_attr_ecc_step_size.attr, &dev_attr_bitflip_threshold.attr, NULL, }; diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 301493382cd0..6e732c3820c1 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -516,6 +516,7 @@ static struct mtd_part *allocate_partition(struct mtd_info *master, } slave->mtd.ecclayout = master->ecclayout; + slave->mtd.ecc_step_size = master->ecc_step_size; slave->mtd.ecc_strength = master->ecc_strength; slave->mtd.bitflip_threshold = master->bitflip_threshold; diff --git a/drivers/mtd/mtdsuper.c b/drivers/mtd/mtdsuper.c index 334da5f583c0..20c02a3b7417 100644 --- a/drivers/mtd/mtdsuper.c +++ b/drivers/mtd/mtdsuper.c @@ -17,6 +17,7 @@ #include <linux/export.h> #include <linux/ctype.h> #include <linux/slab.h> +#include <linux/major.h> /* * compare superblocks to see if they're equivalent diff --git a/drivers/mtd/mtdswap.c b/drivers/mtd/mtdswap.c index c92f0f6bc130..8b33b26eb12b 100644 --- a/drivers/mtd/mtdswap.c +++ b/drivers/mtd/mtdswap.c @@ -1425,7 +1425,7 @@ static void mtdswap_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) return; while ((this_opt = strsep(&parts, ",")) != NULL) { - if (strict_strtoul(this_opt, 0, &part) < 0) + if (kstrtoul(this_opt, 0, &part) < 0) return; if (mtd->index == part) diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 50543f166215..93ae6a6d94f7 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -43,6 +43,7 @@ config MTD_SM_COMMON config MTD_NAND_DENALI tristate "Support Denali NAND controller" + depends on HAS_DMA help Enable support for the Denali NAND controller. This should be combined with either the PCI or platform drivers to provide device @@ -75,7 +76,7 @@ config MTD_NAND_DENALI_SCRATCH_REG_ADDR config MTD_NAND_GPIO tristate "GPIO NAND Flash driver" - depends on GPIOLIB && ARM + depends on GPIOLIB help This enables a GPIO based NAND flash driver. @@ -95,43 +96,15 @@ config MTD_NAND_OMAP2 config MTD_NAND_OMAP_BCH depends on MTD_NAND && MTD_NAND_OMAP2 && ARCH_OMAP3 - tristate "Enable support for hardware BCH error correction" + tristate "Support hardware based BCH error correction" default n select BCH - select BCH_CONST_PARAMS help - Support for hardware BCH error correction. - -choice - prompt "BCH error correction capability" - depends on MTD_NAND_OMAP_BCH - -config MTD_NAND_OMAP_BCH8 - bool "8 bits / 512 bytes (recommended)" - help - Support correcting up to 8 bitflips per 512-byte block. - This will use 13 bytes of spare area per 512 bytes of page data. - This is the recommended mode, as 4-bit mode does not work - on some OMAP3 revisions, due to a hardware bug. - -config MTD_NAND_OMAP_BCH4 - bool "4 bits / 512 bytes" - help - Support correcting up to 4 bitflips per 512-byte block. - This will use 7 bytes of spare area per 512 bytes of page data. - Note that this mode does not work on some OMAP3 revisions, due to a - hardware bug. Please check your OMAP datasheet before selecting this - mode. - -endchoice - -if MTD_NAND_OMAP_BCH -config BCH_CONST_M - default 13 -config BCH_CONST_T - default 4 if MTD_NAND_OMAP_BCH4 - default 8 if MTD_NAND_OMAP_BCH8 -endif + This config enables the ELM hardware engine, which can be used to + locate and correct errors when using BCH ECC scheme. This offloads + the cpu from doing ECC error searching and correction. However some + legacy OMAP families like OMAP2xxx, OMAP3xxx do not have ELM engine + so they should not enable this config symbol. config MTD_NAND_IDS tristate @@ -354,7 +327,7 @@ config MTD_NAND_ATMEL config MTD_NAND_PXA3xx tristate "Support for NAND flash devices on PXA3xx" - depends on PXA3xx || ARCH_MMP + depends on PXA3xx || ARCH_MMP || PLAT_ORION help This enables the driver for the NAND flash device found on PXA3xx processors @@ -432,13 +405,6 @@ config MTD_NAND_PLATFORM devices. You will need to provide platform-specific functions via platform_data. -config MTD_ALAUDA - tristate "MTD driver for Olympus MAUSB-10 and Fujifilm DPC-R1" - depends on USB - help - These two (and possibly other) Alauda-based cardreaders for - SmartMedia and xD allow raw flash access. - config MTD_NAND_ORION tristate "NAND Flash support for Marvell Orion SoC" depends on PLAT_ORION diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index bb8189172f62..542b5689eb63 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -31,7 +31,6 @@ obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o -obj-$(CONFIG_MTD_ALAUDA) += alauda.o obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c deleted file mode 100644 index 60a0dfdb0808..000000000000 --- a/drivers/mtd/nand/alauda.c +++ /dev/null @@ -1,723 +0,0 @@ -/* - * MTD driver for Alauda chips - * - * Copyright (C) 2007 Joern Engel <joern@logfs.org> - * - * Based on drivers/usb/usb-skeleton.c which is: - * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) - * and on drivers/usb/storage/alauda.c, which is: - * (c) 2005 Daniel Drake <dsd@gentoo.org> - * - * Idea and initial work by Arnd Bergmann <arnd@arndb.de> - */ -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/kref.h> -#include <linux/usb.h> -#include <linux/mutex.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand_ecc.h> - -/* Control commands */ -#define ALAUDA_GET_XD_MEDIA_STATUS 0x08 -#define ALAUDA_ACK_XD_MEDIA_CHANGE 0x0a -#define ALAUDA_GET_XD_MEDIA_SIG 0x86 - -/* Common prefix */ -#define ALAUDA_BULK_CMD 0x40 - -/* The two ports */ -#define ALAUDA_PORT_XD 0x00 -#define ALAUDA_PORT_SM 0x01 - -/* Bulk commands */ -#define ALAUDA_BULK_READ_PAGE 0x84 -#define ALAUDA_BULK_READ_OOB 0x85 /* don't use, there's a chip bug */ -#define ALAUDA_BULK_READ_BLOCK 0x94 -#define ALAUDA_BULK_ERASE_BLOCK 0xa3 -#define ALAUDA_BULK_WRITE_PAGE 0xa4 -#define ALAUDA_BULK_WRITE_BLOCK 0xb4 -#define ALAUDA_BULK_RESET_MEDIA 0xe0 - -/* Address shifting */ -#define PBA_LO(pba) ((pba & 0xF) << 5) -#define PBA_HI(pba) (pba >> 3) -#define PBA_ZONE(pba) (pba >> 11) - -#define TIMEOUT HZ - -static const struct usb_device_id alauda_table[] = { - { USB_DEVICE(0x0584, 0x0008) }, /* Fujifilm DPC-R1 */ - { USB_DEVICE(0x07b4, 0x010a) }, /* Olympus MAUSB-10 */ - { } -}; -MODULE_DEVICE_TABLE(usb, alauda_table); - -struct alauda_card { - u8 id; /* id byte */ - u8 chipshift; /* 1<<chipshift total size */ - u8 pageshift; /* 1<<pageshift page size */ - u8 blockshift; /* 1<<blockshift block size */ -}; - -struct alauda { - struct usb_device *dev; - struct usb_interface *interface; - struct mtd_info *mtd; - struct alauda_card *card; - struct mutex card_mutex; - u32 pagemask; - u32 bytemask; - u32 blockmask; - unsigned int write_out; - unsigned int bulk_in; - unsigned int bulk_out; - u8 port; - struct kref kref; -}; - -static struct alauda_card alauda_card_ids[] = { - /* NAND flash */ - { 0x6e, 20, 8, 12}, /* 1 MB */ - { 0xe8, 20, 8, 12}, /* 1 MB */ - { 0xec, 20, 8, 12}, /* 1 MB */ - { 0x64, 21, 8, 12}, /* 2 MB */ - { 0xea, 21, 8, 12}, /* 2 MB */ - { 0x6b, 22, 9, 13}, /* 4 MB */ - { 0xe3, 22, 9, 13}, /* 4 MB */ - { 0xe5, 22, 9, 13}, /* 4 MB */ - { 0xe6, 23, 9, 13}, /* 8 MB */ - { 0x73, 24, 9, 14}, /* 16 MB */ - { 0x75, 25, 9, 14}, /* 32 MB */ - { 0x76, 26, 9, 14}, /* 64 MB */ - { 0x79, 27, 9, 14}, /* 128 MB */ - { 0x71, 28, 9, 14}, /* 256 MB */ - - /* MASK ROM */ - { 0x5d, 21, 9, 13}, /* 2 MB */ - { 0xd5, 22, 9, 13}, /* 4 MB */ - { 0xd6, 23, 9, 13}, /* 8 MB */ - { 0x57, 24, 9, 13}, /* 16 MB */ - { 0x58, 25, 9, 13}, /* 32 MB */ - { } -}; - -static struct alauda_card *get_card(u8 id) -{ - struct alauda_card *card; - - for (card = alauda_card_ids; card->id; card++) - if (card->id == id) - return card; - return NULL; -} - -static void alauda_delete(struct kref *kref) -{ - struct alauda *al = container_of(kref, struct alauda, kref); - - if (al->mtd) { - mtd_device_unregister(al->mtd); - kfree(al->mtd); - } - usb_put_dev(al->dev); - kfree(al); -} - -static int alauda_get_media_status(struct alauda *al, void *buf) -{ - int ret; - - mutex_lock(&al->card_mutex); - ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0), - ALAUDA_GET_XD_MEDIA_STATUS, 0xc0, 0, 1, buf, 2, HZ); - mutex_unlock(&al->card_mutex); - return ret; -} - -static int alauda_ack_media(struct alauda *al) -{ - int ret; - - mutex_lock(&al->card_mutex); - ret = usb_control_msg(al->dev, usb_sndctrlpipe(al->dev, 0), - ALAUDA_ACK_XD_MEDIA_CHANGE, 0x40, 0, 1, NULL, 0, HZ); - mutex_unlock(&al->card_mutex); - return ret; -} - -static int alauda_get_media_signatures(struct alauda *al, void *buf) -{ - int ret; - - mutex_lock(&al->card_mutex); - ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0), - ALAUDA_GET_XD_MEDIA_SIG, 0xc0, 0, 0, buf, 4, HZ); - mutex_unlock(&al->card_mutex); - return ret; -} - -static void alauda_reset(struct alauda *al) -{ - u8 command[] = { - ALAUDA_BULK_CMD, ALAUDA_BULK_RESET_MEDIA, 0, 0, - 0, 0, 0, 0, al->port - }; - mutex_lock(&al->card_mutex); - usb_bulk_msg(al->dev, al->bulk_out, command, 9, NULL, HZ); - mutex_unlock(&al->card_mutex); -} - -static void correct_data(void *buf, void *read_ecc, - int *corrected, int *uncorrected) -{ - u8 calc_ecc[3]; - int err; - - nand_calculate_ecc(NULL, buf, calc_ecc); - err = nand_correct_data(NULL, buf, read_ecc, calc_ecc); - if (err) { - if (err > 0) - (*corrected)++; - else - (*uncorrected)++; - } -} - -struct alauda_sg_request { - struct urb *urb[3]; - struct completion comp; -}; - -static void alauda_complete(struct urb *urb) -{ - struct completion *comp = urb->context; - - if (comp) - complete(comp); -} - -static int __alauda_read_page(struct mtd_info *mtd, loff_t from, void *buf, - void *oob) -{ - struct alauda_sg_request sg; - struct alauda *al = mtd->priv; - u32 pba = from >> al->card->blockshift; - u32 page = (from >> al->card->pageshift) & al->pagemask; - u8 command[] = { - ALAUDA_BULK_CMD, ALAUDA_BULK_READ_PAGE, PBA_HI(pba), - PBA_ZONE(pba), 0, PBA_LO(pba) + page, 1, 0, al->port - }; - int i, err; - - for (i=0; i<3; i++) - sg.urb[i] = NULL; - - err = -ENOMEM; - for (i=0; i<3; i++) { - sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); - if (!sg.urb[i]) - goto out; - } - init_completion(&sg.comp); - usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, - alauda_complete, NULL); - usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, mtd->writesize, - alauda_complete, NULL); - usb_fill_bulk_urb(sg.urb[2], al->dev, al->bulk_in, oob, 16, - alauda_complete, &sg.comp); - - mutex_lock(&al->card_mutex); - for (i=0; i<3; i++) { - err = usb_submit_urb(sg.urb[i], GFP_NOIO); - if (err) - goto cancel; - } - if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { - err = -ETIMEDOUT; -cancel: - for (i=0; i<3; i++) { - usb_kill_urb(sg.urb[i]); - } - } - mutex_unlock(&al->card_mutex); - -out: - usb_free_urb(sg.urb[0]); - usb_free_urb(sg.urb[1]); - usb_free_urb(sg.urb[2]); - return err; -} - -static int alauda_read_page(struct mtd_info *mtd, loff_t from, - void *buf, u8 *oob, int *corrected, int *uncorrected) -{ - int err; - - err = __alauda_read_page(mtd, from, buf, oob); - if (err) - return err; - correct_data(buf, oob+13, corrected, uncorrected); - correct_data(buf+256, oob+8, corrected, uncorrected); - return 0; -} - -static int alauda_write_page(struct mtd_info *mtd, loff_t to, void *buf, - void *oob) -{ - struct alauda_sg_request sg; - struct alauda *al = mtd->priv; - u32 pba = to >> al->card->blockshift; - u32 page = (to >> al->card->pageshift) & al->pagemask; - u8 command[] = { - ALAUDA_BULK_CMD, ALAUDA_BULK_WRITE_PAGE, PBA_HI(pba), - PBA_ZONE(pba), 0, PBA_LO(pba) + page, 32, 0, al->port - }; - int i, err; - - for (i=0; i<3; i++) - sg.urb[i] = NULL; - - err = -ENOMEM; - for (i=0; i<3; i++) { - sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); - if (!sg.urb[i]) - goto out; - } - init_completion(&sg.comp); - usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, - alauda_complete, NULL); - usb_fill_bulk_urb(sg.urb[1], al->dev, al->write_out, buf,mtd->writesize, - alauda_complete, NULL); - usb_fill_bulk_urb(sg.urb[2], al->dev, al->write_out, oob, 16, - alauda_complete, &sg.comp); - - mutex_lock(&al->card_mutex); - for (i=0; i<3; i++) { - err = usb_submit_urb(sg.urb[i], GFP_NOIO); - if (err) - goto cancel; - } - if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { - err = -ETIMEDOUT; -cancel: - for (i=0; i<3; i++) { - usb_kill_urb(sg.urb[i]); - } - } - mutex_unlock(&al->card_mutex); - -out: - usb_free_urb(sg.urb[0]); - usb_free_urb(sg.urb[1]); - usb_free_urb(sg.urb[2]); - return err; -} - -static int alauda_erase_block(struct mtd_info *mtd, loff_t ofs) -{ - struct alauda_sg_request sg; - struct alauda *al = mtd->priv; - u32 pba = ofs >> al->card->blockshift; - u8 command[] = { - ALAUDA_BULK_CMD, ALAUDA_BULK_ERASE_BLOCK, PBA_HI(pba), - PBA_ZONE(pba), 0, PBA_LO(pba), 0x02, 0, al->port - }; - u8 buf[2]; - int i, err; - - for (i=0; i<2; i++) - sg.urb[i] = NULL; - - err = -ENOMEM; - for (i=0; i<2; i++) { - sg.urb[i] = usb_alloc_urb(0, GFP_NOIO); - if (!sg.urb[i]) - goto out; - } - init_completion(&sg.comp); - usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9, - alauda_complete, NULL); - usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, 2, - alauda_complete, &sg.comp); - - mutex_lock(&al->card_mutex); - for (i=0; i<2; i++) { - err = usb_submit_urb(sg.urb[i], GFP_NOIO); - if (err) - goto cancel; - } - if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) { - err = -ETIMEDOUT; -cancel: - for (i=0; i<2; i++) { - usb_kill_urb(sg.urb[i]); - } - } - mutex_unlock(&al->card_mutex); - -out: - usb_free_urb(sg.urb[0]); - usb_free_urb(sg.urb[1]); - return err; -} - -static int alauda_read_oob(struct mtd_info *mtd, loff_t from, void *oob) -{ - static u8 ignore_buf[512]; /* write only */ - - return __alauda_read_page(mtd, from, ignore_buf, oob); -} - -static int alauda_isbad(struct mtd_info *mtd, loff_t ofs) -{ - u8 oob[16]; - int err; - - err = alauda_read_oob(mtd, ofs, oob); - if (err) - return err; - - /* A block is marked bad if two or more bits are zero */ - return hweight8(oob[5]) >= 7 ? 0 : 1; -} - -static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - struct alauda *al = mtd->priv; - void *bounce_buf; - int err, corrected=0, uncorrected=0; - - bounce_buf = kmalloc(mtd->writesize, GFP_KERNEL); - if (!bounce_buf) - return -ENOMEM; - - *retlen = len; - while (len) { - u8 oob[16]; - size_t byte = from & al->bytemask; - size_t cplen = min(len, mtd->writesize - byte); - - err = alauda_read_page(mtd, from, bounce_buf, oob, - &corrected, &uncorrected); - if (err) - goto out; - - memcpy(buf, bounce_buf + byte, cplen); - buf += cplen; - from += cplen; - len -= cplen; - } - err = 0; - if (corrected) - err = 1; /* return max_bitflips per ecc step */ - if (uncorrected) - err = -EBADMSG; -out: - kfree(bounce_buf); - return err; -} - -static int alauda_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - struct alauda *al = mtd->priv; - int err, corrected=0, uncorrected=0; - - if ((from & al->bytemask) || (len & al->bytemask)) - return alauda_bounce_read(mtd, from, len, retlen, buf); - - *retlen = len; - while (len) { - u8 oob[16]; - - err = alauda_read_page(mtd, from, buf, oob, - &corrected, &uncorrected); - if (err) - return err; - - buf += mtd->writesize; - from += mtd->writesize; - len -= mtd->writesize; - } - err = 0; - if (corrected) - err = 1; /* return max_bitflips per ecc step */ - if (uncorrected) - err = -EBADMSG; - return err; -} - -static int alauda_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct alauda *al = mtd->priv; - int err; - - if ((to & al->bytemask) || (len & al->bytemask)) - return -EINVAL; - - *retlen = len; - while (len) { - u32 page = (to >> al->card->pageshift) & al->pagemask; - u8 oob[16] = { 'h', 'e', 'l', 'l', 'o', 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - - /* don't write to bad blocks */ - if (page == 0) { - err = alauda_isbad(mtd, to); - if (err) { - return -EIO; - } - } - nand_calculate_ecc(mtd, buf, &oob[13]); - nand_calculate_ecc(mtd, buf+256, &oob[8]); - - err = alauda_write_page(mtd, to, (void*)buf, oob); - if (err) - return err; - - buf += mtd->writesize; - to += mtd->writesize; - len -= mtd->writesize; - } - return 0; -} - -static int __alauda_erase(struct mtd_info *mtd, struct erase_info *instr) -{ - struct alauda *al = mtd->priv; - u32 ofs = instr->addr; - u32 len = instr->len; - int err; - - if ((ofs & al->blockmask) || (len & al->blockmask)) - return -EINVAL; - - while (len) { - /* don't erase bad blocks */ - err = alauda_isbad(mtd, ofs); - if (err > 0) - err = -EIO; - if (err < 0) - return err; - - err = alauda_erase_block(mtd, ofs); - if (err < 0) - return err; - - ofs += mtd->erasesize; - len -= mtd->erasesize; - } - return 0; -} - -static int alauda_erase(struct mtd_info *mtd, struct erase_info *instr) -{ - int err; - - err = __alauda_erase(mtd, instr); - instr->state = err ? MTD_ERASE_FAILED : MTD_ERASE_DONE; - mtd_erase_callback(instr); - return err; -} - -static int alauda_init_media(struct alauda *al) -{ - u8 buf[4], *b0=buf, *b1=buf+1; - struct alauda_card *card; - struct mtd_info *mtd; - int err; - - mtd = kzalloc(sizeof(*mtd), GFP_KERNEL); - if (!mtd) - return -ENOMEM; - - for (;;) { - err = alauda_get_media_status(al, buf); - if (err < 0) - goto error; - if (*b0 & 0x10) - break; - msleep(20); - } - - err = alauda_ack_media(al); - if (err) - goto error; - - msleep(10); - - err = alauda_get_media_status(al, buf); - if (err < 0) - goto error; - - if (*b0 != 0x14) { - /* media not ready */ - err = -EIO; - goto error; - } - err = alauda_get_media_signatures(al, buf); - if (err < 0) - goto error; - - card = get_card(*b1); - if (!card) { - printk(KERN_ERR"Alauda: unknown card id %02x\n", *b1); - err = -EIO; - goto error; - } - printk(KERN_INFO"pagesize=%x\nerasesize=%x\nsize=%xMiB\n", - 1<<card->pageshift, 1<<card->blockshift, - 1<<(card->chipshift-20)); - al->card = card; - al->pagemask = (1 << (card->blockshift - card->pageshift)) - 1; - al->bytemask = (1 << card->pageshift) - 1; - al->blockmask = (1 << card->blockshift) - 1; - - mtd->name = "alauda"; - mtd->size = 1<<card->chipshift; - mtd->erasesize = 1<<card->blockshift; - mtd->writesize = 1<<card->pageshift; - mtd->type = MTD_NANDFLASH; - mtd->flags = MTD_CAP_NANDFLASH; - mtd->_read = alauda_read; - mtd->_write = alauda_write; - mtd->_erase = alauda_erase; - mtd->_block_isbad = alauda_isbad; - mtd->priv = al; - mtd->owner = THIS_MODULE; - mtd->ecc_strength = 1; - - err = mtd_device_register(mtd, NULL, 0); - if (err) { - err = -ENFILE; - goto error; - } - - al->mtd = mtd; - alauda_reset(al); /* no clue whether this is necessary */ - return 0; -error: - kfree(mtd); - return err; -} - -static int alauda_check_media(struct alauda *al) -{ - u8 buf[2], *b0 = buf, *b1 = buf+1; - int err; - - err = alauda_get_media_status(al, buf); - if (err < 0) - return err; - - if ((*b1 & 0x01) == 0) { - /* door open */ - return -EIO; - } - if ((*b0 & 0x80) || ((*b0 & 0x1F) == 0x10)) { - /* no media ? */ - return -EIO; - } - if (*b0 & 0x08) { - /* media change ? */ - return alauda_init_media(al); - } - return 0; -} - -static int alauda_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct alauda *al; - struct usb_host_interface *iface; - struct usb_endpoint_descriptor *ep, - *ep_in=NULL, *ep_out=NULL, *ep_wr=NULL; - int i, err = -ENOMEM; - - al = kzalloc(2*sizeof(*al), GFP_KERNEL); - if (!al) - goto error; - - kref_init(&al->kref); - usb_set_intfdata(interface, al); - - al->dev = usb_get_dev(interface_to_usbdev(interface)); - al->interface = interface; - - iface = interface->cur_altsetting; - for (i = 0; i < iface->desc.bNumEndpoints; ++i) { - ep = &iface->endpoint[i].desc; - - if (usb_endpoint_is_bulk_in(ep)) { - ep_in = ep; - } else if (usb_endpoint_is_bulk_out(ep)) { - if (i==0) - ep_wr = ep; - else - ep_out = ep; - } - } - err = -EIO; - if (!ep_wr || !ep_in || !ep_out) - goto error; - - al->write_out = usb_sndbulkpipe(al->dev, - usb_endpoint_num(ep_wr)); - al->bulk_in = usb_rcvbulkpipe(al->dev, - usb_endpoint_num(ep_in)); - al->bulk_out = usb_sndbulkpipe(al->dev, - usb_endpoint_num(ep_out)); - - /* second device is identical up to now */ - memcpy(al+1, al, sizeof(*al)); - - mutex_init(&al[0].card_mutex); - mutex_init(&al[1].card_mutex); - - al[0].port = ALAUDA_PORT_XD; - al[1].port = ALAUDA_PORT_SM; - - dev_info(&interface->dev, "alauda probed\n"); - alauda_check_media(al); - alauda_check_media(al+1); - - return 0; - -error: - if (al) - kref_put(&al->kref, alauda_delete); - return err; -} - -static void alauda_disconnect(struct usb_interface *interface) -{ - struct alauda *al; - - al = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); - - /* FIXME: prevent more I/O from starting */ - - /* decrement our usage count */ - if (al) - kref_put(&al->kref, alauda_delete); - - dev_info(&interface->dev, "alauda gone"); -} - -static struct usb_driver alauda_driver = { - .name = "alauda", - .probe = alauda_probe, - .disconnect = alauda_disconnect, - .id_table = alauda_table, -}; - -module_usb_driver(alauda_driver); - -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/ams-delta.c b/drivers/mtd/nand/ams-delta.c index f1d71cdc8aac..8611eb4b45fc 100644 --- a/drivers/mtd/nand/ams-delta.c +++ b/drivers/mtd/nand/ams-delta.c @@ -258,7 +258,6 @@ static int ams_delta_init(struct platform_device *pdev) out_mtd: gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio)); out_gpio: - platform_set_drvdata(pdev, NULL); gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB); iounmap(io_base); out_free: diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 2d23d2929438..d78a97d4153a 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -18,6 +18,9 @@ * Add Programmable Multibit ECC support for various AT91 SoC * © Copyright 2012 ATMEL, Hong Xu * + * Add Nand Flash Controller support for SAMA5 SoC + * © Copyright 2013 ATMEL, Josh Wu (josh.wu@atmel.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. @@ -37,13 +40,12 @@ #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> +#include <linux/delay.h> #include <linux/dmaengine.h> #include <linux/gpio.h> +#include <linux/interrupt.h> #include <linux/io.h> #include <linux/platform_data/atmel.h> -#include <linux/pinctrl/consumer.h> - -#include <mach/cpu.h> static int use_dma = 1; module_param(use_dma, int, 0); @@ -58,6 +60,7 @@ module_param(on_flash_bbt, int, 0); __raw_writel((value), add + ATMEL_ECC_##reg) #include "atmel_nand_ecc.h" /* Hardware ECC registers */ +#include "atmel_nand_nfc.h" /* Nand Flash Controller definition */ /* oob layout for large page size * bad block info is on bytes 0 and 1 @@ -85,6 +88,23 @@ static struct nand_ecclayout atmel_oobinfo_small = { }, }; +struct atmel_nfc { + void __iomem *base_cmd_regs; + void __iomem *hsmc_regs; + void __iomem *sram_bank0; + dma_addr_t sram_bank0_phys; + bool use_nfc_sram; + bool write_by_sram; + + bool is_initialized; + struct completion comp_nfc; + + /* Point to the sram bank which include readed data via NFC */ + void __iomem *data_in_sram; + bool will_write_sram; +}; +static struct atmel_nfc nand_nfc; + struct atmel_nand_host { struct nand_chip nand_chip; struct mtd_info mtd; @@ -97,6 +117,8 @@ struct atmel_nand_host { struct completion comp; struct dma_chan *dma_chan; + struct atmel_nfc *nfc; + bool has_pmecc; u8 pmecc_corr_cap; u16 pmecc_sector_size; @@ -128,11 +150,6 @@ struct atmel_nand_host { static struct nand_ecclayout atmel_pmecc_oobinfo; -static int cpu_has_dma(void) -{ - return cpu_is_at91sam9rl() || cpu_is_at91sam9g45(); -} - /* * Enable NAND. */ @@ -186,21 +203,103 @@ static int atmel_nand_device_ready(struct mtd_info *mtd) !!host->board.rdy_pin_active_low; } +/* Set up for hardware ready pin and enable pin. */ +static int atmel_nand_set_enable_ready_pins(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct atmel_nand_host *host = chip->priv; + int res = 0; + + if (gpio_is_valid(host->board.rdy_pin)) { + res = devm_gpio_request(host->dev, + host->board.rdy_pin, "nand_rdy"); + if (res < 0) { + dev_err(host->dev, + "can't request rdy gpio %d\n", + host->board.rdy_pin); + return res; + } + + res = gpio_direction_input(host->board.rdy_pin); + if (res < 0) { + dev_err(host->dev, + "can't request input direction rdy gpio %d\n", + host->board.rdy_pin); + return res; + } + + chip->dev_ready = atmel_nand_device_ready; + } + + if (gpio_is_valid(host->board.enable_pin)) { + res = devm_gpio_request(host->dev, + host->board.enable_pin, "nand_enable"); + if (res < 0) { + dev_err(host->dev, + "can't request enable gpio %d\n", + host->board.enable_pin); + return res; + } + + res = gpio_direction_output(host->board.enable_pin, 1); + if (res < 0) { + dev_err(host->dev, + "can't request output direction enable gpio %d\n", + host->board.enable_pin); + return res; + } + } + + return res; +} + +static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size) +{ + int i; + u32 *t = trg; + const __iomem u32 *s = src; + + for (i = 0; i < (size >> 2); i++) + *t++ = readl_relaxed(s++); +} + +static void memcpy32_toio(void __iomem *trg, const void *src, int size) +{ + int i; + u32 __iomem *t = trg; + const u32 *s = src; + + for (i = 0; i < (size >> 2); i++) + writel_relaxed(*s++, t++); +} + /* * Minimal-overhead PIO for data access. */ static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len) { struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; - __raw_readsb(nand_chip->IO_ADDR_R, buf, len); + if (host->nfc && host->nfc->use_nfc_sram && host->nfc->data_in_sram) { + memcpy32_fromio(buf, host->nfc->data_in_sram, len); + host->nfc->data_in_sram += len; + } else { + __raw_readsb(nand_chip->IO_ADDR_R, buf, len); + } } static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len) { struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; - __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2); + if (host->nfc && host->nfc->use_nfc_sram && host->nfc->data_in_sram) { + memcpy32_fromio(buf, host->nfc->data_in_sram, len); + host->nfc->data_in_sram += len; + } else { + __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2); + } } static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len) @@ -222,6 +321,40 @@ static void dma_complete_func(void *completion) complete(completion); } +static int nfc_set_sram_bank(struct atmel_nand_host *host, unsigned int bank) +{ + /* NFC only has two banks. Must be 0 or 1 */ + if (bank > 1) + return -EINVAL; + + if (bank) { + /* Only for a 2k-page or lower flash, NFC can handle 2 banks */ + if (host->mtd.writesize > 2048) + return -EINVAL; + nfc_writel(host->nfc->hsmc_regs, BANK, ATMEL_HSMC_NFC_BANK1); + } else { + nfc_writel(host->nfc->hsmc_regs, BANK, ATMEL_HSMC_NFC_BANK0); + } + + return 0; +} + +static uint nfc_get_sram_off(struct atmel_nand_host *host) +{ + if (nfc_readl(host->nfc->hsmc_regs, BANK) & ATMEL_HSMC_NFC_BANK1) + return NFC_SRAM_BANK1_OFFSET; + else + return 0; +} + +static dma_addr_t nfc_sram_phys(struct atmel_nand_host *host) +{ + if (nfc_readl(host->nfc->hsmc_regs, BANK) & ATMEL_HSMC_NFC_BANK1) + return host->nfc->sram_bank0_phys + NFC_SRAM_BANK1_OFFSET; + else + return host->nfc->sram_bank0_phys; +} + static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len, int is_read) { @@ -235,6 +368,7 @@ static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len, void *p = buf; int err = -EIO; enum dma_data_direction dir = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE; + struct atmel_nfc *nfc = host->nfc; if (buf >= high_memory) goto err_buf; @@ -251,11 +385,20 @@ static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len, } if (is_read) { - dma_src_addr = host->io_phys; + if (nfc && nfc->data_in_sram) + dma_src_addr = nfc_sram_phys(host) + (nfc->data_in_sram + - (nfc->sram_bank0 + nfc_get_sram_off(host))); + else + dma_src_addr = host->io_phys; + dma_dst_addr = phys_addr; } else { dma_src_addr = phys_addr; - dma_dst_addr = host->io_phys; + + if (nfc && nfc->write_by_sram) + dma_dst_addr = nfc_sram_phys(host); + else + dma_dst_addr = host->io_phys; } tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr, @@ -278,6 +421,10 @@ static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len, dma_async_issue_pending(host->dma_chan); wait_for_completion(&host->comp); + if (is_read && nfc && nfc->data_in_sram) + /* After read data from SRAM, need to increase the position */ + nfc->data_in_sram += len; + err = 0; err_dma: @@ -366,43 +513,34 @@ static void __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host) table_size * sizeof(int16_t); } -static void pmecc_data_free(struct atmel_nand_host *host) -{ - kfree(host->pmecc_partial_syn); - kfree(host->pmecc_si); - kfree(host->pmecc_lmu); - kfree(host->pmecc_smu); - kfree(host->pmecc_mu); - kfree(host->pmecc_dmu); - kfree(host->pmecc_delta); -} - static int pmecc_data_alloc(struct atmel_nand_host *host) { const int cap = host->pmecc_corr_cap; + int size; + + size = (2 * cap + 1) * sizeof(int16_t); + host->pmecc_partial_syn = devm_kzalloc(host->dev, size, GFP_KERNEL); + host->pmecc_si = devm_kzalloc(host->dev, size, GFP_KERNEL); + host->pmecc_lmu = devm_kzalloc(host->dev, + (cap + 1) * sizeof(int16_t), GFP_KERNEL); + host->pmecc_smu = devm_kzalloc(host->dev, + (cap + 2) * size, GFP_KERNEL); + + size = (cap + 1) * sizeof(int); + host->pmecc_mu = devm_kzalloc(host->dev, size, GFP_KERNEL); + host->pmecc_dmu = devm_kzalloc(host->dev, size, GFP_KERNEL); + host->pmecc_delta = devm_kzalloc(host->dev, size, GFP_KERNEL); + + if (!host->pmecc_partial_syn || + !host->pmecc_si || + !host->pmecc_lmu || + !host->pmecc_smu || + !host->pmecc_mu || + !host->pmecc_dmu || + !host->pmecc_delta) + return -ENOMEM; - host->pmecc_partial_syn = kzalloc((2 * cap + 1) * sizeof(int16_t), - GFP_KERNEL); - host->pmecc_si = kzalloc((2 * cap + 1) * sizeof(int16_t), GFP_KERNEL); - host->pmecc_lmu = kzalloc((cap + 1) * sizeof(int16_t), GFP_KERNEL); - host->pmecc_smu = kzalloc((cap + 2) * (2 * cap + 1) * sizeof(int16_t), - GFP_KERNEL); - host->pmecc_mu = kzalloc((cap + 1) * sizeof(int), GFP_KERNEL); - host->pmecc_dmu = kzalloc((cap + 1) * sizeof(int), GFP_KERNEL); - host->pmecc_delta = kzalloc((cap + 1) * sizeof(int), GFP_KERNEL); - - if (host->pmecc_partial_syn && - host->pmecc_si && - host->pmecc_lmu && - host->pmecc_smu && - host->pmecc_mu && - host->pmecc_dmu && - host->pmecc_delta) - return 0; - - /* error happened */ - pmecc_data_free(host); - return -ENOMEM; + return 0; } static void pmecc_gen_syndrome(struct mtd_info *mtd, int sector) @@ -763,6 +901,30 @@ normal_check: return total_err; } +static void pmecc_enable(struct atmel_nand_host *host, int ecc_op) +{ + u32 val; + + if (ecc_op != NAND_ECC_READ && ecc_op != NAND_ECC_WRITE) { + dev_err(host->dev, "atmel_nand: wrong pmecc operation type!"); + return; + } + + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST); + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE); + val = pmecc_readl_relaxed(host->ecc, CFG); + + if (ecc_op == NAND_ECC_READ) + pmecc_writel(host->ecc, CFG, (val & ~PMECC_CFG_WRITE_OP) + | PMECC_CFG_AUTO_ENABLE); + else + pmecc_writel(host->ecc, CFG, (val | PMECC_CFG_WRITE_OP) + & ~PMECC_CFG_AUTO_ENABLE); + + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE); + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DATA); +} + static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { @@ -774,13 +936,8 @@ static int atmel_nand_pmecc_read_page(struct mtd_info *mtd, unsigned long end_time; int bitflips = 0; - pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST); - pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE); - pmecc_writel(host->ecc, CFG, (pmecc_readl_relaxed(host->ecc, CFG) - & ~PMECC_CFG_WRITE_OP) | PMECC_CFG_AUTO_ENABLE); - - pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE); - pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DATA); + if (!host->nfc || !host->nfc->use_nfc_sram) + pmecc_enable(host, NAND_ECC_READ); chip->read_buf(mtd, buf, eccsize); chip->read_buf(mtd, oob, mtd->oobsize); @@ -813,16 +970,10 @@ static int atmel_nand_pmecc_write_page(struct mtd_info *mtd, int i, j; unsigned long end_time; - pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST); - pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE); - - pmecc_writel(host->ecc, CFG, (pmecc_readl_relaxed(host->ecc, CFG) | - PMECC_CFG_WRITE_OP) & ~PMECC_CFG_AUTO_ENABLE); - - pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE); - pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DATA); - - chip->write_buf(mtd, (u8 *)buf, mtd->writesize); + if (!host->nfc || !host->nfc->write_by_sram) { + pmecc_enable(host, NAND_ECC_WRITE); + chip->write_buf(mtd, (u8 *)buf, mtd->writesize); + } end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS); while ((pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY)) { @@ -911,67 +1062,39 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd) } /* - * Get ECC requirement in ONFI parameters, returns -1 if ONFI - * parameters is not supported. - * return 0 if success to get the ECC requirement. - */ -static int get_onfi_ecc_param(struct nand_chip *chip, - int *ecc_bits, int *sector_size) -{ - *ecc_bits = *sector_size = 0; - - if (chip->onfi_params.ecc_bits == 0xff) - /* TODO: the sector_size and ecc_bits need to be find in - * extended ecc parameter, currently we don't support it. - */ - return -1; - - *ecc_bits = chip->onfi_params.ecc_bits; - - /* The default sector size (ecc codeword size) is 512 */ - *sector_size = 512; - - return 0; -} - -/* - * Get ecc requirement from ONFI parameters ecc requirement. + * Get minimum ecc requirements from NAND. * If pmecc-cap, pmecc-sector-size in DTS are not specified, this function - * will set them according to ONFI ecc requirement. Otherwise, use the + * will set them according to minimum ecc requirement. Otherwise, use the * value in DTS file. * return 0 if success. otherwise return error code. */ static int pmecc_choose_ecc(struct atmel_nand_host *host, int *cap, int *sector_size) { - /* Get ECC requirement from ONFI parameters */ - *cap = *sector_size = 0; - if (host->nand_chip.onfi_version) { - if (!get_onfi_ecc_param(&host->nand_chip, cap, sector_size)) - dev_info(host->dev, "ONFI params, minimum required ECC: %d bits in %d bytes\n", + /* Get minimum ECC requirements */ + if (host->nand_chip.ecc_strength_ds) { + *cap = host->nand_chip.ecc_strength_ds; + *sector_size = host->nand_chip.ecc_step_ds; + dev_info(host->dev, "minimum ECC: %d bits in %d bytes\n", *cap, *sector_size); - else - dev_info(host->dev, "NAND chip ECC reqirement is in Extended ONFI parameter, we don't support yet.\n"); } else { - dev_info(host->dev, "NAND chip is not ONFI compliant, assume ecc_bits is 2 in 512 bytes"); - } - if (*cap == 0 && *sector_size == 0) { *cap = 2; *sector_size = 512; + dev_info(host->dev, "can't detect min. ECC, assume 2 bits in 512 bytes\n"); } - /* If dts file doesn't specify then use the one in ONFI parameters */ + /* If device tree doesn't specify, use NAND's minimum ECC parameters */ if (host->pmecc_corr_cap == 0) { /* use the most fitable ecc bits (the near bigger one ) */ if (*cap <= 2) host->pmecc_corr_cap = 2; else if (*cap <= 4) host->pmecc_corr_cap = 4; - else if (*cap < 8) + else if (*cap <= 8) host->pmecc_corr_cap = 8; - else if (*cap < 12) + else if (*cap <= 12) host->pmecc_corr_cap = 12; - else if (*cap < 24) + else if (*cap <= 24) host->pmecc_corr_cap = 24; else return -EINVAL; @@ -988,7 +1111,7 @@ static int pmecc_choose_ecc(struct atmel_nand_host *host, return 0; } -static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, +static int atmel_pmecc_nand_init_params(struct platform_device *pdev, struct atmel_nand_host *host) { struct mtd_info *mtd = &host->mtd; @@ -1002,7 +1125,7 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, return err_no; } - if (cap != host->pmecc_corr_cap || + if (cap > host->pmecc_corr_cap || sector_size != host->pmecc_sector_size) dev_info(host->dev, "WARNING: Be Caution! Using different PMECC parameters from Nand ONFI ECC reqirement.\n"); @@ -1023,27 +1146,28 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, return 0; } - host->ecc = ioremap(regs->start, resource_size(regs)); - if (host->ecc == NULL) { + host->ecc = devm_ioremap_resource(&pdev->dev, regs); + if (IS_ERR(host->ecc)) { dev_err(host->dev, "ioremap failed\n"); - err_no = -EIO; - goto err_pmecc_ioremap; + err_no = PTR_ERR(host->ecc); + goto err; } regs_pmerr = platform_get_resource(pdev, IORESOURCE_MEM, 2); - regs_rom = platform_get_resource(pdev, IORESOURCE_MEM, 3); - if (regs_pmerr && regs_rom) { - host->pmerrloc_base = ioremap(regs_pmerr->start, - resource_size(regs_pmerr)); - host->pmecc_rom_base = ioremap(regs_rom->start, - resource_size(regs_rom)); + host->pmerrloc_base = devm_ioremap_resource(&pdev->dev, regs_pmerr); + if (IS_ERR(host->pmerrloc_base)) { + dev_err(host->dev, + "Can not get I/O resource for PMECC ERRLOC controller!\n"); + err_no = PTR_ERR(host->pmerrloc_base); + goto err; } - if (!host->pmerrloc_base || !host->pmecc_rom_base) { - dev_err(host->dev, - "Can not get I/O resource for PMECC ERRLOC controller or ROM!\n"); - err_no = -EIO; - goto err_pmloc_ioremap; + regs_rom = platform_get_resource(pdev, IORESOURCE_MEM, 3); + host->pmecc_rom_base = devm_ioremap_resource(&pdev->dev, regs_rom); + if (IS_ERR(host->pmecc_rom_base)) { + dev_err(host->dev, "Can not get I/O resource for ROM!\n"); + err_no = PTR_ERR(host->pmecc_rom_base); + goto err; } /* ECC is calculated for the whole page (1 step) */ @@ -1052,7 +1176,8 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, /* set ECC page size and oob layout */ switch (mtd->writesize) { case 2048: - host->pmecc_degree = PMECC_GF_DIMENSION_13; + host->pmecc_degree = (sector_size == 512) ? + PMECC_GF_DIMENSION_13 : PMECC_GF_DIMENSION_14; host->pmecc_cw_len = (1 << host->pmecc_degree) - 1; host->pmecc_sector_number = mtd->writesize / sector_size; host->pmecc_bytes_per_sector = pmecc_get_ecc_bytes( @@ -1068,7 +1193,7 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, if (nand_chip->ecc.bytes > mtd->oobsize - 2) { dev_err(host->dev, "No room for ECC bytes\n"); err_no = -EINVAL; - goto err_no_ecc_room; + goto err; } pmecc_config_ecc_layout(&atmel_pmecc_oobinfo, mtd->oobsize, @@ -1093,7 +1218,7 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, if (err_no) { dev_err(host->dev, "Cannot allocate memory for PMECC computation!\n"); - goto err_pmecc_data_alloc; + goto err; } nand_chip->ecc.read_page = atmel_nand_pmecc_read_page; @@ -1103,15 +1228,7 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, return 0; -err_pmecc_data_alloc: -err_no_ecc_room: -err_pmloc_ioremap: - iounmap(host->ecc); - if (host->pmerrloc_base) - iounmap(host->pmerrloc_base); - if (host->pmecc_rom_base) - iounmap(host->pmecc_rom_base); -err_pmecc_ioremap: +err: return err_no; } @@ -1174,10 +1291,9 @@ static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, * Workaround: Reset the parity registers before reading the * actual data. */ - if (cpu_is_at32ap7000()) { - struct atmel_nand_host *host = chip->priv; + struct atmel_nand_host *host = chip->priv; + if (host->board.need_reset_workaround) ecc_writel(host->ecc, CR, ATMEL_ECC_RST); - } /* read the page */ chip->read_buf(mtd, p, eccsize); @@ -1298,14 +1414,13 @@ static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat, */ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) { - if (cpu_is_at32ap7000()) { - struct nand_chip *nand_chip = mtd->priv; - struct atmel_nand_host *host = nand_chip->priv; + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + + if (host->board.need_reset_workaround) ecc_writel(host->ecc, CR, ATMEL_ECC_RST); - } } -#if defined(CONFIG_OF) static int atmel_of_init_port(struct atmel_nand_host *host, struct device_node *np) { @@ -1313,7 +1428,7 @@ static int atmel_of_init_port(struct atmel_nand_host *host, u32 offset[2]; int ecc_mode; struct atmel_nand_data *board = &host->board; - enum of_gpio_flags flags; + enum of_gpio_flags flags = 0; if (of_property_read_u32(np, "atmel,nand-addr-offset", &val) == 0) { if (val >= 32) { @@ -1337,6 +1452,8 @@ static int atmel_of_init_port(struct atmel_nand_host *host, board->on_flash_bbt = of_get_nand_on_flash_bbt(np); + board->has_dma = of_property_read_bool(np, "atmel,nand-has-dma"); + if (of_get_nand_bus_width(np) == 16) board->bus_width_16 = 1; @@ -1348,6 +1465,9 @@ static int atmel_of_init_port(struct atmel_nand_host *host, host->has_pmecc = of_property_read_bool(np, "atmel,has-pmecc"); + /* load the nfc driver if there is */ + of_platform_populate(np, NULL, NULL, host->dev); + if (!(board->ecc_mode == NAND_ECC_HW) || !host->has_pmecc) return 0; /* Not using PMECC */ @@ -1391,15 +1511,8 @@ static int atmel_of_init_port(struct atmel_nand_host *host, return 0; } -#else -static int atmel_of_init_port(struct atmel_nand_host *host, - struct device_node *np) -{ - return -EINVAL; -} -#endif -static int __init atmel_hw_nand_init_params(struct platform_device *pdev, +static int atmel_hw_nand_init_params(struct platform_device *pdev, struct atmel_nand_host *host) { struct mtd_info *mtd = &host->mtd; @@ -1414,10 +1527,10 @@ static int __init atmel_hw_nand_init_params(struct platform_device *pdev, return 0; } - host->ecc = ioremap(regs->start, resource_size(regs)); - if (host->ecc == NULL) { + host->ecc = devm_ioremap_resource(&pdev->dev, regs); + if (IS_ERR(host->ecc)) { dev_err(host->dev, "ioremap failed\n"); - return -EIO; + return PTR_ERR(host->ecc); } /* ECC is calculated for the whole page (1 step) */ @@ -1459,50 +1572,424 @@ static int __init atmel_hw_nand_init_params(struct platform_device *pdev, return 0; } +/* SMC interrupt service routine */ +static irqreturn_t hsmc_interrupt(int irq, void *dev_id) +{ + struct atmel_nand_host *host = dev_id; + u32 status, mask, pending; + irqreturn_t ret = IRQ_HANDLED; + + status = nfc_readl(host->nfc->hsmc_regs, SR); + mask = nfc_readl(host->nfc->hsmc_regs, IMR); + pending = status & mask; + + if (pending & NFC_SR_XFR_DONE) { + complete(&host->nfc->comp_nfc); + nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_XFR_DONE); + } else if (pending & NFC_SR_RB_EDGE) { + complete(&host->nfc->comp_nfc); + nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_RB_EDGE); + } else if (pending & NFC_SR_CMD_DONE) { + complete(&host->nfc->comp_nfc); + nfc_writel(host->nfc->hsmc_regs, IDR, NFC_SR_CMD_DONE); + } else { + ret = IRQ_NONE; + } + + return ret; +} + +/* NFC(Nand Flash Controller) related functions */ +static int nfc_wait_interrupt(struct atmel_nand_host *host, u32 flag) +{ + unsigned long timeout; + init_completion(&host->nfc->comp_nfc); + + /* Enable interrupt that need to wait for */ + nfc_writel(host->nfc->hsmc_regs, IER, flag); + + timeout = wait_for_completion_timeout(&host->nfc->comp_nfc, + msecs_to_jiffies(NFC_TIME_OUT_MS)); + if (timeout) + return 0; + + /* Time out to wait for the interrupt */ + dev_err(host->dev, "Time out to wait for interrupt: 0x%08x\n", flag); + return -ETIMEDOUT; +} + +static int nfc_send_command(struct atmel_nand_host *host, + unsigned int cmd, unsigned int addr, unsigned char cycle0) +{ + unsigned long timeout; + dev_dbg(host->dev, + "nfc_cmd: 0x%08x, addr1234: 0x%08x, cycle0: 0x%02x\n", + cmd, addr, cycle0); + + timeout = jiffies + msecs_to_jiffies(NFC_TIME_OUT_MS); + while (nfc_cmd_readl(NFCADDR_CMD_NFCBUSY, host->nfc->base_cmd_regs) + & NFCADDR_CMD_NFCBUSY) { + if (time_after(jiffies, timeout)) { + dev_err(host->dev, + "Time out to wait CMD_NFCBUSY ready!\n"); + return -ETIMEDOUT; + } + } + nfc_writel(host->nfc->hsmc_regs, CYCLE0, cycle0); + nfc_cmd_addr1234_writel(cmd, addr, host->nfc->base_cmd_regs); + return nfc_wait_interrupt(host, NFC_SR_CMD_DONE); +} + +static int nfc_device_ready(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + if (!nfc_wait_interrupt(host, NFC_SR_RB_EDGE)) + return 1; + return 0; +} + +static void nfc_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + + if (chip == -1) + nfc_writel(host->nfc->hsmc_regs, CTRL, NFC_CTRL_DISABLE); + else + nfc_writel(host->nfc->hsmc_regs, CTRL, NFC_CTRL_ENABLE); +} + +static int nfc_make_addr(struct mtd_info *mtd, int column, int page_addr, + unsigned int *addr1234, unsigned int *cycle0) +{ + struct nand_chip *chip = mtd->priv; + + int acycle = 0; + unsigned char addr_bytes[8]; + int index = 0, bit_shift; + + BUG_ON(addr1234 == NULL || cycle0 == NULL); + + *cycle0 = 0; + *addr1234 = 0; + + if (column != -1) { + if (chip->options & NAND_BUSWIDTH_16) + column >>= 1; + addr_bytes[acycle++] = column & 0xff; + if (mtd->writesize > 512) + addr_bytes[acycle++] = (column >> 8) & 0xff; + } + + if (page_addr != -1) { + addr_bytes[acycle++] = page_addr & 0xff; + addr_bytes[acycle++] = (page_addr >> 8) & 0xff; + if (chip->chipsize > (128 << 20)) + addr_bytes[acycle++] = (page_addr >> 16) & 0xff; + } + + if (acycle > 4) + *cycle0 = addr_bytes[index++]; + + for (bit_shift = 0; index < acycle; bit_shift += 8) + *addr1234 += addr_bytes[index++] << bit_shift; + + /* return acycle in cmd register */ + return acycle << NFCADDR_CMD_ACYCLE_BIT_POS; +} + +static void nfc_nand_command(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct nand_chip *chip = mtd->priv; + struct atmel_nand_host *host = chip->priv; + unsigned long timeout; + unsigned int nfc_addr_cmd = 0; + + unsigned int cmd1 = command << NFCADDR_CMD_CMD1_BIT_POS; + + /* Set default settings: no cmd2, no addr cycle. read from nand */ + unsigned int cmd2 = 0; + unsigned int vcmd2 = 0; + int acycle = NFCADDR_CMD_ACYCLE_NONE; + int csid = NFCADDR_CMD_CSID_3; + int dataen = NFCADDR_CMD_DATADIS; + int nfcwr = NFCADDR_CMD_NFCRD; + unsigned int addr1234 = 0; + unsigned int cycle0 = 0; + bool do_addr = true; + host->nfc->data_in_sram = NULL; + + dev_dbg(host->dev, "%s: cmd = 0x%02x, col = 0x%08x, page = 0x%08x\n", + __func__, command, column, page_addr); + + switch (command) { + case NAND_CMD_RESET: + nfc_addr_cmd = cmd1 | acycle | csid | dataen | nfcwr; + nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0); + udelay(chip->chip_delay); + + nfc_nand_command(mtd, NAND_CMD_STATUS, -1, -1); + timeout = jiffies + msecs_to_jiffies(NFC_TIME_OUT_MS); + while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) { + if (time_after(jiffies, timeout)) { + dev_err(host->dev, + "Time out to wait status ready!\n"); + break; + } + } + return; + case NAND_CMD_STATUS: + do_addr = false; + break; + case NAND_CMD_PARAM: + case NAND_CMD_READID: + do_addr = false; + acycle = NFCADDR_CMD_ACYCLE_1; + if (column != -1) + addr1234 = column; + break; + case NAND_CMD_RNDOUT: + cmd2 = NAND_CMD_RNDOUTSTART << NFCADDR_CMD_CMD2_BIT_POS; + vcmd2 = NFCADDR_CMD_VCMD2; + break; + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + if (command == NAND_CMD_READOOB) { + column += mtd->writesize; + command = NAND_CMD_READ0; /* only READ0 is valid */ + cmd1 = command << NFCADDR_CMD_CMD1_BIT_POS; + } + if (host->nfc->use_nfc_sram) { + /* Enable Data transfer to sram */ + dataen = NFCADDR_CMD_DATAEN; + + /* Need enable PMECC now, since NFC will transfer + * data in bus after sending nfc read command. + */ + if (chip->ecc.mode == NAND_ECC_HW && host->has_pmecc) + pmecc_enable(host, NAND_ECC_READ); + } + + cmd2 = NAND_CMD_READSTART << NFCADDR_CMD_CMD2_BIT_POS; + vcmd2 = NFCADDR_CMD_VCMD2; + break; + /* For prgramming command, the cmd need set to write enable */ + case NAND_CMD_PAGEPROG: + case NAND_CMD_SEQIN: + case NAND_CMD_RNDIN: + nfcwr = NFCADDR_CMD_NFCWR; + if (host->nfc->will_write_sram && command == NAND_CMD_SEQIN) + dataen = NFCADDR_CMD_DATAEN; + break; + default: + break; + } + + if (do_addr) + acycle = nfc_make_addr(mtd, column, page_addr, &addr1234, + &cycle0); + + nfc_addr_cmd = cmd1 | cmd2 | vcmd2 | acycle | csid | dataen | nfcwr; + nfc_send_command(host, nfc_addr_cmd, addr1234, cycle0); + + if (dataen == NFCADDR_CMD_DATAEN) + if (nfc_wait_interrupt(host, NFC_SR_XFR_DONE)) + dev_err(host->dev, "something wrong, No XFR_DONE interrupt comes.\n"); + + /* + * Program and erase have their own busy handlers status, sequential + * in, and deplete1 need no delay. + */ + switch (command) { + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_RNDIN: + case NAND_CMD_STATUS: + case NAND_CMD_RNDOUT: + case NAND_CMD_SEQIN: + case NAND_CMD_READID: + return; + + case NAND_CMD_READ0: + if (dataen == NFCADDR_CMD_DATAEN) { + host->nfc->data_in_sram = host->nfc->sram_bank0 + + nfc_get_sram_off(host); + return; + } + /* fall through */ + default: + nfc_wait_interrupt(host, NFC_SR_RB_EDGE); + } +} + +static int nfc_sram_write_page(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t offset, int data_len, const uint8_t *buf, + int oob_required, int page, int cached, int raw) +{ + int cfg, len; + int status = 0; + struct atmel_nand_host *host = chip->priv; + void __iomem *sram = host->nfc->sram_bank0 + nfc_get_sram_off(host); + + /* Subpage write is not supported */ + if (offset || (data_len < mtd->writesize)) + return -EINVAL; + + cfg = nfc_readl(host->nfc->hsmc_regs, CFG); + len = mtd->writesize; + + if (unlikely(raw)) { + len += mtd->oobsize; + nfc_writel(host->nfc->hsmc_regs, CFG, cfg | NFC_CFG_WSPARE); + } else + nfc_writel(host->nfc->hsmc_regs, CFG, cfg & ~NFC_CFG_WSPARE); + + /* Copy page data to sram that will write to nand via NFC */ + if (use_dma) { + if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) != 0) + /* Fall back to use cpu copy */ + memcpy32_toio(sram, buf, len); + } else { + memcpy32_toio(sram, buf, len); + } + + if (chip->ecc.mode == NAND_ECC_HW && host->has_pmecc) + /* + * When use NFC sram, need set up PMECC before send + * NAND_CMD_SEQIN command. Since when the nand command + * is sent, nfc will do transfer from sram and nand. + */ + pmecc_enable(host, NAND_ECC_WRITE); + + host->nfc->will_write_sram = true; + chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); + host->nfc->will_write_sram = false; + + if (likely(!raw)) + /* Need to write ecc into oob */ + status = chip->ecc.write_page(mtd, chip, buf, oob_required); + + if (status < 0) + return status; + + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + + if ((status & NAND_STATUS_FAIL) && (chip->errstat)) + status = chip->errstat(mtd, chip, FL_WRITING, status, page); + + if (status & NAND_STATUS_FAIL) + return -EIO; + + return 0; +} + +static int nfc_sram_init(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct atmel_nand_host *host = chip->priv; + int res = 0; + + /* Initialize the NFC CFG register */ + unsigned int cfg_nfc = 0; + + /* set page size and oob layout */ + switch (mtd->writesize) { + case 512: + cfg_nfc = NFC_CFG_PAGESIZE_512; + break; + case 1024: + cfg_nfc = NFC_CFG_PAGESIZE_1024; + break; + case 2048: + cfg_nfc = NFC_CFG_PAGESIZE_2048; + break; + case 4096: + cfg_nfc = NFC_CFG_PAGESIZE_4096; + break; + case 8192: + cfg_nfc = NFC_CFG_PAGESIZE_8192; + break; + default: + dev_err(host->dev, "Unsupported page size for NFC.\n"); + res = -ENXIO; + return res; + } + + /* oob bytes size = (NFCSPARESIZE + 1) * 4 + * Max support spare size is 512 bytes. */ + cfg_nfc |= (((mtd->oobsize / 4) - 1) << NFC_CFG_NFC_SPARESIZE_BIT_POS + & NFC_CFG_NFC_SPARESIZE); + /* default set a max timeout */ + cfg_nfc |= NFC_CFG_RSPARE | + NFC_CFG_NFC_DTOCYC | NFC_CFG_NFC_DTOMUL; + + nfc_writel(host->nfc->hsmc_regs, CFG, cfg_nfc); + + host->nfc->will_write_sram = false; + nfc_set_sram_bank(host, 0); + + /* Use Write page with NFC SRAM only for PMECC or ECC NONE. */ + if (host->nfc->write_by_sram) { + if ((chip->ecc.mode == NAND_ECC_HW && host->has_pmecc) || + chip->ecc.mode == NAND_ECC_NONE) + chip->write_page = nfc_sram_write_page; + else + host->nfc->write_by_sram = false; + } + + dev_info(host->dev, "Using NFC Sram read %s\n", + host->nfc->write_by_sram ? "and write" : ""); + return 0; +} + +static struct platform_driver atmel_nand_nfc_driver; /* * Probe for the NAND device. */ -static int __init atmel_nand_probe(struct platform_device *pdev) +static int atmel_nand_probe(struct platform_device *pdev) { struct atmel_nand_host *host; struct mtd_info *mtd; struct nand_chip *nand_chip; struct resource *mem; struct mtd_part_parser_data ppdata = {}; - int res; - struct pinctrl *pinctrl; - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - printk(KERN_ERR "atmel_nand: can't get I/O resource mem\n"); - return -ENXIO; - } + int res, irq; /* Allocate memory for the device structure (and zero it) */ - host = kzalloc(sizeof(struct atmel_nand_host), GFP_KERNEL); + host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL); if (!host) { printk(KERN_ERR "atmel_nand: failed to allocate device structure.\n"); return -ENOMEM; } - host->io_phys = (dma_addr_t)mem->start; + res = platform_driver_register(&atmel_nand_nfc_driver); + if (res) + dev_err(&pdev->dev, "atmel_nand: can't register NFC driver\n"); - host->io_base = ioremap(mem->start, resource_size(mem)); - if (host->io_base == NULL) { - printk(KERN_ERR "atmel_nand: ioremap failed\n"); - res = -EIO; + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->io_base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(host->io_base)) { + dev_err(&pdev->dev, "atmel_nand: ioremap resource failed\n"); + res = PTR_ERR(host->io_base); goto err_nand_ioremap; } + host->io_phys = (dma_addr_t)mem->start; mtd = &host->mtd; nand_chip = &host->nand_chip; host->dev = &pdev->dev; - if (pdev->dev.of_node) { + if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { + /* Only when CONFIG_OF is enabled of_node can be parsed */ res = atmel_of_init_port(host, pdev->dev.of_node); if (res) - goto err_ecc_ioremap; + goto err_nand_ioremap; } else { - memcpy(&host->board, pdev->dev.platform_data, + memcpy(&host->board, dev_get_platdata(&pdev->dev), sizeof(struct atmel_nand_data)); } @@ -1513,51 +2000,36 @@ static int __init atmel_nand_probe(struct platform_device *pdev) /* Set address of NAND IO lines */ nand_chip->IO_ADDR_R = host->io_base; nand_chip->IO_ADDR_W = host->io_base; - nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl; - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) { - dev_err(host->dev, "Failed to request pinctrl\n"); - res = PTR_ERR(pinctrl); - goto err_ecc_ioremap; - } + if (nand_nfc.is_initialized) { + /* NFC driver is probed and initialized */ + host->nfc = &nand_nfc; - if (gpio_is_valid(host->board.rdy_pin)) { - res = gpio_request(host->board.rdy_pin, "nand_rdy"); - if (res < 0) { - dev_err(&pdev->dev, - "can't request rdy gpio %d\n", - host->board.rdy_pin); - goto err_ecc_ioremap; - } + nand_chip->select_chip = nfc_select_chip; + nand_chip->dev_ready = nfc_device_ready; + nand_chip->cmdfunc = nfc_nand_command; - res = gpio_direction_input(host->board.rdy_pin); - if (res < 0) { - dev_err(&pdev->dev, - "can't request input direction rdy gpio %d\n", - host->board.rdy_pin); - goto err_ecc_ioremap; + /* Initialize the interrupt for NFC */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(host->dev, "Cannot get HSMC irq!\n"); + res = irq; + goto err_nand_ioremap; } - nand_chip->dev_ready = atmel_nand_device_ready; - } - - if (gpio_is_valid(host->board.enable_pin)) { - res = gpio_request(host->board.enable_pin, "nand_enable"); - if (res < 0) { - dev_err(&pdev->dev, - "can't request enable gpio %d\n", - host->board.enable_pin); - goto err_ecc_ioremap; + res = devm_request_irq(&pdev->dev, irq, hsmc_interrupt, + 0, "hsmc", host); + if (res) { + dev_err(&pdev->dev, "Unable to request HSMC irq %d\n", + irq); + goto err_nand_ioremap; } + } else { + res = atmel_nand_set_enable_ready_pins(mtd); + if (res) + goto err_nand_ioremap; - res = gpio_direction_output(host->board.enable_pin, 1); - if (res < 0) { - dev_err(&pdev->dev, - "can't request output direction enable gpio %d\n", - host->board.enable_pin); - goto err_ecc_ioremap; - } + nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl; } nand_chip->ecc.mode = host->board.ecc_mode; @@ -1573,7 +2045,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev) atmel_nand_enable(host); if (gpio_is_valid(host->board.det_pin)) { - res = gpio_request(host->board.det_pin, "nand_det"); + res = devm_gpio_request(&pdev->dev, + host->board.det_pin, "nand_det"); if (res < 0) { dev_err(&pdev->dev, "can't request det gpio %d\n", @@ -1601,7 +2074,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) nand_chip->bbt_options |= NAND_BBT_USE_FLASH; } - if (!cpu_has_dma()) + if (!host->board.has_dma) use_dma = 0; if (use_dma) { @@ -1637,6 +2110,15 @@ static int __init atmel_nand_probe(struct platform_device *pdev) goto err_hw_ecc; } + /* initialize the nfc configuration register */ + if (host->nfc && host->nfc->use_nfc_sram) { + res = nfc_sram_init(mtd); + if (res) { + host->nfc->use_nfc_sram = false; + dev_err(host->dev, "Disable use nfc sram for data transfer.\n"); + } + } + /* second phase scan */ if (nand_scan_tail(mtd)) { res = -ENXIO; @@ -1651,34 +2133,22 @@ static int __init atmel_nand_probe(struct platform_device *pdev) return res; err_scan_tail: - if (host->has_pmecc && host->nand_chip.ecc.mode == NAND_ECC_HW) { + if (host->has_pmecc && host->nand_chip.ecc.mode == NAND_ECC_HW) pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE); - pmecc_data_free(host); - } - if (host->ecc) - iounmap(host->ecc); - if (host->pmerrloc_base) - iounmap(host->pmerrloc_base); - if (host->pmecc_rom_base) - iounmap(host->pmecc_rom_base); err_hw_ecc: err_scan_ident: err_no_card: atmel_nand_disable(host); - platform_set_drvdata(pdev, NULL); if (host->dma_chan) dma_release_channel(host->dma_chan); -err_ecc_ioremap: - iounmap(host->io_base); err_nand_ioremap: - kfree(host); return res; } /* * Remove a NAND device. */ -static int __exit atmel_nand_remove(struct platform_device *pdev) +static int atmel_nand_remove(struct platform_device *pdev) { struct atmel_nand_host *host = platform_get_drvdata(pdev); struct mtd_info *mtd = &host->mtd; @@ -1691,45 +2161,78 @@ static int __exit atmel_nand_remove(struct platform_device *pdev) pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE); pmerrloc_writel(host->pmerrloc_base, ELDIS, PMERRLOC_DISABLE); - pmecc_data_free(host); } - if (gpio_is_valid(host->board.det_pin)) - gpio_free(host->board.det_pin); - - if (gpio_is_valid(host->board.enable_pin)) - gpio_free(host->board.enable_pin); - - if (gpio_is_valid(host->board.rdy_pin)) - gpio_free(host->board.rdy_pin); - - if (host->ecc) - iounmap(host->ecc); - if (host->pmecc_rom_base) - iounmap(host->pmecc_rom_base); - if (host->pmerrloc_base) - iounmap(host->pmerrloc_base); - if (host->dma_chan) dma_release_channel(host->dma_chan); - iounmap(host->io_base); - kfree(host); + platform_driver_unregister(&atmel_nand_nfc_driver); return 0; } -#if defined(CONFIG_OF) static const struct of_device_id atmel_nand_dt_ids[] = { { .compatible = "atmel,at91rm9200-nand" }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, atmel_nand_dt_ids); -#endif + +static int atmel_nand_nfc_probe(struct platform_device *pdev) +{ + struct atmel_nfc *nfc = &nand_nfc; + struct resource *nfc_cmd_regs, *nfc_hsmc_regs, *nfc_sram; + + nfc_cmd_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + nfc->base_cmd_regs = devm_ioremap_resource(&pdev->dev, nfc_cmd_regs); + if (IS_ERR(nfc->base_cmd_regs)) + return PTR_ERR(nfc->base_cmd_regs); + + nfc_hsmc_regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); + nfc->hsmc_regs = devm_ioremap_resource(&pdev->dev, nfc_hsmc_regs); + if (IS_ERR(nfc->hsmc_regs)) + return PTR_ERR(nfc->hsmc_regs); + + nfc_sram = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (nfc_sram) { + nfc->sram_bank0 = devm_ioremap_resource(&pdev->dev, nfc_sram); + if (IS_ERR(nfc->sram_bank0)) { + dev_warn(&pdev->dev, "Fail to ioremap the NFC sram with error: %ld. So disable NFC sram.\n", + PTR_ERR(nfc->sram_bank0)); + } else { + nfc->use_nfc_sram = true; + nfc->sram_bank0_phys = (dma_addr_t)nfc_sram->start; + + if (pdev->dev.of_node) + nfc->write_by_sram = of_property_read_bool( + pdev->dev.of_node, + "atmel,write-by-sram"); + } + } + + nfc->is_initialized = true; + dev_info(&pdev->dev, "NFC is probed.\n"); + return 0; +} + +static const struct of_device_id atmel_nand_nfc_match[] = { + { .compatible = "atmel,sama5d3-nfc" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, atmel_nand_nfc_match); + +static struct platform_driver atmel_nand_nfc_driver = { + .driver = { + .name = "atmel_nand_nfc", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(atmel_nand_nfc_match), + }, + .probe = atmel_nand_nfc_probe, +}; static struct platform_driver atmel_nand_driver = { - .remove = __exit_p(atmel_nand_remove), + .probe = atmel_nand_probe, + .remove = atmel_nand_remove, .driver = { .name = "atmel_nand", .owner = THIS_MODULE, @@ -1737,7 +2240,7 @@ static struct platform_driver atmel_nand_driver = { }, }; -module_platform_driver_probe(atmel_nand_driver, atmel_nand_probe); +module_platform_driver(atmel_nand_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Rick Bronson"); diff --git a/drivers/mtd/nand/atmel_nand_nfc.h b/drivers/mtd/nand/atmel_nand_nfc.h new file mode 100644 index 000000000000..4efd117cd3a3 --- /dev/null +++ b/drivers/mtd/nand/atmel_nand_nfc.h @@ -0,0 +1,98 @@ +/* + * Atmel Nand Flash Controller (NFC) - System peripherals regsters. + * Based on SAMA5D3 datasheet. + * + * © Copyright 2013 Atmel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef ATMEL_NAND_NFC_H +#define ATMEL_NAND_NFC_H + +/* + * HSMC NFC registers + */ +#define ATMEL_HSMC_NFC_CFG 0x00 /* NFC Configuration Register */ +#define NFC_CFG_PAGESIZE (7 << 0) +#define NFC_CFG_PAGESIZE_512 (0 << 0) +#define NFC_CFG_PAGESIZE_1024 (1 << 0) +#define NFC_CFG_PAGESIZE_2048 (2 << 0) +#define NFC_CFG_PAGESIZE_4096 (3 << 0) +#define NFC_CFG_PAGESIZE_8192 (4 << 0) +#define NFC_CFG_WSPARE (1 << 8) +#define NFC_CFG_RSPARE (1 << 9) +#define NFC_CFG_NFC_DTOCYC (0xf << 16) +#define NFC_CFG_NFC_DTOMUL (0x7 << 20) +#define NFC_CFG_NFC_SPARESIZE (0x7f << 24) +#define NFC_CFG_NFC_SPARESIZE_BIT_POS 24 + +#define ATMEL_HSMC_NFC_CTRL 0x04 /* NFC Control Register */ +#define NFC_CTRL_ENABLE (1 << 0) +#define NFC_CTRL_DISABLE (1 << 1) + +#define ATMEL_HSMC_NFC_SR 0x08 /* NFC Status Register */ +#define NFC_SR_XFR_DONE (1 << 16) +#define NFC_SR_CMD_DONE (1 << 17) +#define NFC_SR_RB_EDGE (1 << 24) + +#define ATMEL_HSMC_NFC_IER 0x0c +#define ATMEL_HSMC_NFC_IDR 0x10 +#define ATMEL_HSMC_NFC_IMR 0x14 +#define ATMEL_HSMC_NFC_CYCLE0 0x18 /* NFC Address Cycle Zero */ +#define ATMEL_HSMC_NFC_ADDR_CYCLE0 (0xff) + +#define ATMEL_HSMC_NFC_BANK 0x1c /* NFC Bank Register */ +#define ATMEL_HSMC_NFC_BANK0 (0 << 0) +#define ATMEL_HSMC_NFC_BANK1 (1 << 0) + +#define nfc_writel(addr, reg, value) \ + writel((value), (addr) + ATMEL_HSMC_NFC_##reg) + +#define nfc_readl(addr, reg) \ + readl_relaxed((addr) + ATMEL_HSMC_NFC_##reg) + +/* + * NFC Address Command definitions + */ +#define NFCADDR_CMD_CMD1 (0xff << 2) /* Command for Cycle 1 */ +#define NFCADDR_CMD_CMD1_BIT_POS 2 +#define NFCADDR_CMD_CMD2 (0xff << 10) /* Command for Cycle 2 */ +#define NFCADDR_CMD_CMD2_BIT_POS 10 +#define NFCADDR_CMD_VCMD2 (0x1 << 18) /* Valid Cycle 2 Command */ +#define NFCADDR_CMD_ACYCLE (0x7 << 19) /* Number of Address required */ +#define NFCADDR_CMD_ACYCLE_NONE (0x0 << 19) +#define NFCADDR_CMD_ACYCLE_1 (0x1 << 19) +#define NFCADDR_CMD_ACYCLE_2 (0x2 << 19) +#define NFCADDR_CMD_ACYCLE_3 (0x3 << 19) +#define NFCADDR_CMD_ACYCLE_4 (0x4 << 19) +#define NFCADDR_CMD_ACYCLE_5 (0x5 << 19) +#define NFCADDR_CMD_ACYCLE_BIT_POS 19 +#define NFCADDR_CMD_CSID (0x7 << 22) /* Chip Select Identifier */ +#define NFCADDR_CMD_CSID_0 (0x0 << 22) +#define NFCADDR_CMD_CSID_1 (0x1 << 22) +#define NFCADDR_CMD_CSID_2 (0x2 << 22) +#define NFCADDR_CMD_CSID_3 (0x3 << 22) +#define NFCADDR_CMD_CSID_4 (0x4 << 22) +#define NFCADDR_CMD_CSID_5 (0x5 << 22) +#define NFCADDR_CMD_CSID_6 (0x6 << 22) +#define NFCADDR_CMD_CSID_7 (0x7 << 22) +#define NFCADDR_CMD_DATAEN (0x1 << 25) /* Data Transfer Enable */ +#define NFCADDR_CMD_DATADIS (0x0 << 25) /* Data Transfer Disable */ +#define NFCADDR_CMD_NFCRD (0x0 << 26) /* NFC Read Enable */ +#define NFCADDR_CMD_NFCWR (0x1 << 26) /* NFC Write Enable */ +#define NFCADDR_CMD_NFCBUSY (0x1 << 27) /* NFC Busy */ + +#define nfc_cmd_addr1234_writel(cmd, addr1234, nfc_base) \ + writel((addr1234), (cmd) + nfc_base) + +#define nfc_cmd_readl(bitstatus, nfc_base) \ + readl_relaxed((bitstatus) + nfc_base) + +#define NFC_TIME_OUT_MS 100 +#define NFC_SRAM_BANK1_OFFSET 0x1200 + +#endif diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 217459d02b2f..ae8dd7c41039 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -411,7 +411,7 @@ static int au1550nd_probe(struct platform_device *pdev) struct resource *r; int ret, cs; - pd = pdev->dev.platform_data; + pd = dev_get_platdata(&pdev->dev); if (!pd) { dev_err(&pdev->dev, "missing platform data\n"); return -ENODEV; diff --git a/drivers/mtd/nand/bcm47xxnflash/main.c b/drivers/mtd/nand/bcm47xxnflash/main.c index 7bae569fdc79..107445911315 100644 --- a/drivers/mtd/nand/bcm47xxnflash/main.c +++ b/drivers/mtd/nand/bcm47xxnflash/main.c @@ -29,11 +29,9 @@ static int bcm47xxnflash_probe(struct platform_device *pdev) struct bcm47xxnflash *b47n; int err = 0; - b47n = kzalloc(sizeof(*b47n), GFP_KERNEL); - if (!b47n) { - err = -ENOMEM; - goto out; - } + b47n = devm_kzalloc(&pdev->dev, sizeof(*b47n), GFP_KERNEL); + if (!b47n) + return -ENOMEM; b47n->nand_chip.priv = b47n; b47n->mtd.owner = THIS_MODULE; @@ -48,22 +46,16 @@ static int bcm47xxnflash_probe(struct platform_device *pdev) } if (err) { pr_err("Initialization failed: %d\n", err); - goto err_init; + return err; } err = mtd_device_parse_register(&b47n->mtd, probes, NULL, NULL, 0); if (err) { pr_err("Failed to register MTD device: %d\n", err); - goto err_dev_reg; + return err; } return 0; - -err_dev_reg: -err_init: - kfree(b47n); -out: - return err; } static int bcm47xxnflash_remove(struct platform_device *pdev) @@ -85,22 +77,4 @@ static struct platform_driver bcm47xxnflash_driver = { }, }; -static int __init bcm47xxnflash_init(void) -{ - int err; - - err = platform_driver_register(&bcm47xxnflash_driver); - if (err) - pr_err("Failed to register bcm47xx nand flash driver: %d\n", - err); - - return err; -} - -static void __exit bcm47xxnflash_exit(void) -{ - platform_driver_unregister(&bcm47xxnflash_driver); -} - -module_init(bcm47xxnflash_init); -module_exit(bcm47xxnflash_exit); +module_platform_driver(bcm47xxnflash_driver); diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 776df3694f75..2c42e125720f 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -171,7 +171,7 @@ static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev) static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev) { - return pdev->dev.platform_data; + return dev_get_platdata(&pdev->dev); } /* @@ -671,8 +671,6 @@ static int bf5xx_nand_remove(struct platform_device *pdev) { struct bf5xx_nand_info *info = to_nand_info(pdev); - platform_set_drvdata(pdev, NULL); - /* first thing we need to do is release all our mtds * and their partitions, then go through freeing the * resources used @@ -832,7 +830,6 @@ static int bf5xx_nand_probe(struct platform_device *pdev) out_err_nand_scan: bf5xx_nand_dma_remove(info); out_err_hw_init: - platform_set_drvdata(pdev, NULL); kfree(info); out_err_kzalloc: peripheral_free_list(bfin_nfc_pin_req); diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index 2cdeab8bebc4..d469a9a1dea0 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -197,7 +197,7 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) } /* Allocate memory for MTD device structure and private data */ - new_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); + new_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); if (!new_mtd) { printk(KERN_WARNING "Unable to allocate CS553X NAND MTD device structure.\n"); err = -ENOMEM; @@ -207,10 +207,6 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr) /* Get pointer to private data */ this = (struct nand_chip *)(&new_mtd[1]); - /* Initialize structures */ - memset(new_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - /* Link the private data with the MTD structure */ new_mtd->priv = this; new_mtd->owner = THIS_MODULE; diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index c3e15a558173..b77a01efb483 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -530,7 +530,7 @@ MODULE_DEVICE_TABLE(of, davinci_nand_of_match); static struct davinci_nand_pdata *nand_davinci_get_pdata(struct platform_device *pdev) { - if (!pdev->dev.platform_data && pdev->dev.of_node) { + if (!dev_get_platdata(&pdev->dev) && pdev->dev.of_node) { struct davinci_nand_pdata *pdata; const char *mode; u32 prop; @@ -575,13 +575,13 @@ static struct davinci_nand_pdata pdata->bbt_options = NAND_BBT_USE_FLASH; } - return pdev->dev.platform_data; + return dev_get_platdata(&pdev->dev); } #else static struct davinci_nand_pdata *nand_davinci_get_pdata(struct platform_device *pdev) { - return pdev->dev.platform_data; + return dev_get_platdata(&pdev->dev); } #endif @@ -623,11 +623,14 @@ static int __init nand_davinci_probe(struct platform_device *pdev) goto err_nomem; } - vaddr = devm_request_and_ioremap(&pdev->dev, res1); - base = devm_request_and_ioremap(&pdev->dev, res2); - if (!vaddr || !base) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -EADDRNOTAVAIL; + vaddr = devm_ioremap_resource(&pdev->dev, res1); + if (IS_ERR(vaddr)) { + ret = PTR_ERR(vaddr); + goto err_ioremap; + } + base = devm_ioremap_resource(&pdev->dev, res2); + if (IS_ERR(base)) { + ret = PTR_ERR(base); goto err_ioremap; } diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 0c8bb6bf8424..370b9dd7a278 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1394,7 +1394,7 @@ static struct nand_bbt_descr bbt_mirror_descr = { }; /* initialize driver data structures */ -void denali_drv_init(struct denali_nand_info *denali) +static void denali_drv_init(struct denali_nand_info *denali) { denali->idx = 0; @@ -1520,7 +1520,7 @@ int denali_init(struct denali_nand_info *denali) * so just let controller do 15bit ECC for MLC and 8bit ECC for * SLC if possible. * */ - if (denali->nand.cellinfo & 0xc && + if (!nand_is_slc(&denali->nand) && (denali->mtd.oobsize > (denali->bbtskipbytes + ECC_15BITS * (denali->mtd.writesize / ECC_SECTOR_SIZE)))) { diff --git a/drivers/mtd/nand/denali_pci.c b/drivers/mtd/nand/denali_pci.c index e3e46623b2b4..033f177a6369 100644 --- a/drivers/mtd/nand/denali_pci.c +++ b/drivers/mtd/nand/denali_pci.c @@ -119,7 +119,6 @@ static void denali_pci_remove(struct pci_dev *dev) iounmap(denali->flash_mem); pci_release_regions(dev); pci_disable_device(dev); - pci_set_drvdata(dev, NULL); kfree(denali); } diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 81fa5784f98b..b68a4959f700 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -38,7 +38,7 @@ #define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0 #endif -static unsigned long __initdata doc_locations[] = { +static unsigned long doc_locations[] __initdata = { #if defined (__alpha__) || defined(__i386__) || defined(__x86_64__) #ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, @@ -46,13 +46,13 @@ static unsigned long __initdata doc_locations[] = { 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000, 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000, -#else /* CONFIG_MTD_DOCPROBE_HIGH */ +#else 0xc8000, 0xca000, 0xcc000, 0xce000, 0xd0000, 0xd2000, 0xd4000, 0xd6000, 0xd8000, 0xda000, 0xdc000, 0xde000, 0xe0000, 0xe2000, 0xe4000, 0xe6000, 0xe8000, 0xea000, 0xec000, 0xee000, -#endif /* CONFIG_MTD_DOCPROBE_HIGH */ +#endif #endif 0xffffffff }; diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c index a10491af52aa..1b0265e85a06 100644 --- a/drivers/mtd/nand/docg4.c +++ b/drivers/mtd/nand/docg4.c @@ -44,6 +44,7 @@ #include <linux/mtd/nand.h> #include <linux/bch.h> #include <linux/bitrev.h> +#include <linux/jiffies.h> /* * In "reliable mode" consecutive 2k pages are used in parallel (in some @@ -269,7 +270,7 @@ static int poll_status(struct docg4_priv *doc) */ uint16_t flash_status; - unsigned int timeo; + unsigned long timeo; void __iomem *docptr = doc->virtadr; dev_dbg(doc->dev, "%s...\n", __func__); @@ -277,22 +278,18 @@ static int poll_status(struct docg4_priv *doc) /* hardware quirk requires reading twice initially */ flash_status = readw(docptr + DOC_FLASHCONTROL); - timeo = 1000; + timeo = jiffies + msecs_to_jiffies(200); /* generous timeout */ do { cpu_relax(); flash_status = readb(docptr + DOC_FLASHCONTROL); - } while (!(flash_status & DOC_CTRL_FLASHREADY) && --timeo); + } while (!(flash_status & DOC_CTRL_FLASHREADY) && + time_before(jiffies, timeo)); - - if (!timeo) { + if (unlikely(!(flash_status & DOC_CTRL_FLASHREADY))) { dev_err(doc->dev, "%s: timed out!\n", __func__); return NAND_STATUS_FAIL; } - if (unlikely(timeo < 50)) - dev_warn(doc->dev, "%s: nearly timed out; %d remaining\n", - __func__, timeo); - return 0; } @@ -1093,7 +1090,6 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) struct nand_chip *nand = mtd->priv; struct docg4_priv *doc = nand->priv; struct nand_bbt_descr *bbtd = nand->badblock_pattern; - int block = (int)(ofs >> nand->bbt_erase_shift); int page = (int)(ofs >> nand->page_shift); uint32_t g4_addr = mtd_to_docg4_address(page, 0); @@ -1108,9 +1104,6 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) if (buf == NULL) return -ENOMEM; - /* update bbt in memory */ - nand->bbt[block / 4] |= 0x01 << ((block & 0x03) * 2); - /* write bit-wise negation of pattern to oob buffer */ memset(nand->oob_poi, 0xff, mtd->oobsize); for (i = 0; i < bbtd->len; i++) @@ -1120,8 +1113,6 @@ static int docg4_block_markbad(struct mtd_info *mtd, loff_t ofs) write_page_prologue(mtd, g4_addr); docg4_write_page(mtd, nand, buf, 1); ret = pageprog(mtd); - if (!ret) - mtd->ecc_stats.badblocks++; kfree(buf); @@ -1245,7 +1236,6 @@ static void __init init_mtd_structs(struct mtd_info *mtd) nand->block_markbad = docg4_block_markbad; nand->read_buf = docg4_read_buf; nand->write_buf = docg4_write_buf16; - nand->scan_bbt = nand_default_bbt; nand->erase_cmd = docg4_erase_block; nand->ecc.read_page = docg4_read_page; nand->ecc.write_page = docg4_write_page; @@ -1368,7 +1358,6 @@ static int __init probe_docg4(struct platform_device *pdev) struct nand_chip *nand = mtd->priv; struct docg4_priv *doc = nand->priv; nand_release(mtd); /* deletes partitions and mtd devices */ - platform_set_drvdata(pdev, NULL); free_bch(doc->bch); kfree(mtd); } @@ -1380,7 +1369,6 @@ static int __exit cleanup_docg4(struct platform_device *pdev) { struct docg4_priv *doc = platform_get_drvdata(pdev); nand_release(doc->mtd); - platform_set_drvdata(pdev, NULL); free_bch(doc->bch); kfree(doc->mtd); iounmap(doc->virtadr); diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 20657209a472..c966fc7474ce 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -28,6 +28,7 @@ #include <linux/kernel.h> #include <linux/string.h> #include <linux/ioport.h> +#include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/slab.h> @@ -650,8 +651,6 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) chip->page_shift); dev_dbg(priv->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n", chip->phys_erase_shift); - dev_dbg(priv->dev, "fsl_elbc_init: nand->ecclayout = %p\n", - chip->ecclayout); dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.mode = %d\n", chip->ecc.mode); dev_dbg(priv->dev, "fsl_elbc_init: nand->ecc.steps = %d\n", diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c index f1f7f12ab501..43355779cff5 100644 --- a/drivers/mtd/nand/fsl_ifc_nand.c +++ b/drivers/mtd/nand/fsl_ifc_nand.c @@ -24,6 +24,7 @@ #include <linux/types.h> #include <linux/init.h> #include <linux/kernel.h> +#include <linux/of_address.h> #include <linux/slab.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> @@ -135,6 +136,69 @@ static struct nand_ecclayout oob_4096_ecc8 = { .oobfree = { {2, 6}, {136, 82} }, }; +/* 8192-byte page size with 4-bit ECC */ +static struct nand_ecclayout oob_8192_ecc4 = { + .eccbytes = 128, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, + }, + .oobfree = { {2, 6}, {136, 208} }, +}; + +/* 8192-byte page size with 8-bit ECC -- requires 218-byte OOB */ +static struct nand_ecclayout oob_8192_ecc8 = { + .eccbytes = 256, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, + 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, + 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 195, 196, 197, 198, 199, + 200, 201, 202, 203, 204, 205, 206, 207, + 208, 209, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, + 232, 233, 234, 235, 236, 237, 238, 239, + 240, 241, 242, 243, 244, 245, 246, 247, + 248, 249, 250, 251, 252, 253, 254, 255, + 256, 257, 258, 259, 260, 261, 262, 263, + }, + .oobfree = { {2, 6}, {264, 80} }, +}; /* * Generic flash bbt descriptors @@ -441,20 +505,29 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, if (mtd->writesize > 512) { nand_fcr0 = (NAND_CMD_SEQIN << IFC_NAND_FCR0_CMD0_SHIFT) | - (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD1_SHIFT); + (NAND_CMD_STATUS << IFC_NAND_FCR0_CMD1_SHIFT) | + (NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD2_SHIFT); iowrite32be( - (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | - (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | - (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | - (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) | - (IFC_FIR_OP_CW1 << IFC_NAND_FIR0_OP4_SHIFT), - &ifc->ifc_nand.nand_fir0); + (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | + (IFC_FIR_OP_CA0 << IFC_NAND_FIR0_OP1_SHIFT) | + (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP2_SHIFT) | + (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP3_SHIFT) | + (IFC_FIR_OP_CMD2 << IFC_NAND_FIR0_OP4_SHIFT), + &ifc->ifc_nand.nand_fir0); + iowrite32be( + (IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT) | + (IFC_FIR_OP_RDSTAT << + IFC_NAND_FIR1_OP6_SHIFT) | + (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP7_SHIFT), + &ifc->ifc_nand.nand_fir1); } else { nand_fcr0 = ((NAND_CMD_PAGEPROG << IFC_NAND_FCR0_CMD1_SHIFT) | (NAND_CMD_SEQIN << - IFC_NAND_FCR0_CMD2_SHIFT)); + IFC_NAND_FCR0_CMD2_SHIFT) | + (NAND_CMD_STATUS << + IFC_NAND_FCR0_CMD3_SHIFT)); iowrite32be( (IFC_FIR_OP_CW0 << IFC_NAND_FIR0_OP0_SHIFT) | @@ -463,8 +536,13 @@ static void fsl_ifc_cmdfunc(struct mtd_info *mtd, unsigned int command, (IFC_FIR_OP_RA0 << IFC_NAND_FIR0_OP3_SHIFT) | (IFC_FIR_OP_WBCD << IFC_NAND_FIR0_OP4_SHIFT), &ifc->ifc_nand.nand_fir0); - iowrite32be(IFC_FIR_OP_CW1 << IFC_NAND_FIR1_OP5_SHIFT, - &ifc->ifc_nand.nand_fir1); + iowrite32be( + (IFC_FIR_OP_CMD1 << IFC_NAND_FIR1_OP5_SHIFT) | + (IFC_FIR_OP_CW3 << IFC_NAND_FIR1_OP6_SHIFT) | + (IFC_FIR_OP_RDSTAT << + IFC_NAND_FIR1_OP7_SHIFT) | + (IFC_FIR_OP_NOP << IFC_NAND_FIR1_OP8_SHIFT), + &ifc->ifc_nand.nand_fir1); if (column >= mtd->writesize) nand_fcr0 |= @@ -718,8 +796,6 @@ static int fsl_ifc_chip_init_tail(struct mtd_info *mtd) chip->page_shift); dev_dbg(priv->dev, "%s: nand->phys_erase_shift = %d\n", __func__, chip->phys_erase_shift); - dev_dbg(priv->dev, "%s: nand->ecclayout = %p\n", __func__, - chip->ecclayout); dev_dbg(priv->dev, "%s: nand->ecc.mode = %d\n", __func__, chip->ecc.mode); dev_dbg(priv->dev, "%s: nand->ecc.steps = %d\n", __func__, @@ -823,7 +899,7 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) /* set up nand options */ chip->bbt_options = NAND_BBT_USE_FLASH; - + chip->options = NAND_NO_SUBPAGE_WRITE; if (ioread32be(&ifc->cspr_cs[priv->bank].cspr) & CSPR_PORT_SIZE_16) { chip->read_byte = fsl_ifc_read_byte16; @@ -872,11 +948,25 @@ static int fsl_ifc_chip_init(struct fsl_ifc_mtd *priv) } else { layout = &oob_4096_ecc8; chip->ecc.bytes = 16; + chip->ecc.strength = 8; } priv->bufnum_mask = 1; break; + case CSOR_NAND_PGS_8K: + if ((csor & CSOR_NAND_ECC_MODE_MASK) == + CSOR_NAND_ECC_MODE_4) { + layout = &oob_8192_ecc4; + } else { + layout = &oob_8192_ecc8; + chip->ecc.bytes = 16; + chip->ecc.strength = 8; + } + + priv->bufnum_mask = 0; + break; + default: dev_err(priv->dev, "bad csor %#x: bad page size\n", csor); return -ENODEV; @@ -907,8 +997,6 @@ static int fsl_ifc_chip_remove(struct fsl_ifc_mtd *priv) iounmap(priv->vbase); ifc_nand_ctrl->chips[priv->bank] = NULL; - dev_set_drvdata(priv->dev, NULL); - kfree(priv); return 0; } @@ -1083,25 +1171,7 @@ static struct platform_driver fsl_ifc_nand_driver = { .remove = fsl_ifc_nand_remove, }; -static int __init fsl_ifc_nand_init(void) -{ - int ret; - - ret = platform_driver_register(&fsl_ifc_nand_driver); - if (ret) - printk(KERN_ERR "fsl-ifc: Failed to register platform" - "driver\n"); - - return ret; -} - -static void __exit fsl_ifc_nand_exit(void) -{ - platform_driver_unregister(&fsl_ifc_nand_driver); -} - -module_init(fsl_ifc_nand_init); -module_exit(fsl_ifc_nand_exit); +module_platform_driver(fsl_ifc_nand_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Freescale"); diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index 04e07252d74b..4d203e84e8ca 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -18,6 +18,7 @@ #include <linux/mtd/nand_ecc.h> #include <linux/mtd/partitions.h> #include <linux/mtd/mtd.h> +#include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/of_gpio.h> #include <linux/io.h> diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c index 911e2433fe30..3dc1a7564d87 100644 --- a/drivers/mtd/nand/fsmc_nand.c +++ b/drivers/mtd/nand/fsmc_nand.c @@ -889,6 +889,24 @@ static int fsmc_nand_probe_config_dt(struct platform_device *pdev, if (of_get_property(np, "nand-skip-bbtscan", NULL)) pdata->options = NAND_SKIP_BBTSCAN; + pdata->nand_timings = devm_kzalloc(&pdev->dev, + sizeof(*pdata->nand_timings), GFP_KERNEL); + if (!pdata->nand_timings) { + dev_err(&pdev->dev, "no memory for nand_timing\n"); + return -ENOMEM; + } + of_property_read_u8_array(np, "timings", (u8 *)pdata->nand_timings, + sizeof(*pdata->nand_timings)); + + /* Set default NAND bank to 0 */ + pdata->bank = 0; + if (!of_property_read_u32(np, "bank", &val)) { + if (val > 3) { + dev_err(&pdev->dev, "invalid bank %u\n", val); + return -EINVAL; + } + pdata->bank = val; + } return 0; } #else @@ -940,9 +958,6 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); - if (!res) - return -EINVAL; - host->data_va = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->data_va)) return PTR_ERR(host->data_va); @@ -950,25 +965,16 @@ static int __init fsmc_nand_probe(struct platform_device *pdev) host->data_pa = (dma_addr_t)res->start; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_addr"); - if (!res) - return -EINVAL; - host->addr_va = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->addr_va)) return PTR_ERR(host->addr_va); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd"); - if (!res) - return -EINVAL; - host->cmd_va = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->cmd_va)) return PTR_ERR(host->cmd_va); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fsmc_regs"); - if (!res) - return -EINVAL; - host->regs_va = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->regs_va)) return PTR_ERR(host->regs_va); @@ -1174,8 +1180,6 @@ static int fsmc_nand_remove(struct platform_device *pdev) { struct fsmc_nand_data *host = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - if (host) { nand_release(&host->mtd); @@ -1190,7 +1194,7 @@ static int fsmc_nand_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int fsmc_nand_suspend(struct device *dev) { struct fsmc_nand_data *host = dev_get_drvdata(dev); @@ -1210,9 +1214,9 @@ static int fsmc_nand_resume(struct device *dev) } return 0; } +#endif static SIMPLE_DEV_PM_OPS(fsmc_nand_pm_ops, fsmc_nand_suspend, fsmc_nand_resume); -#endif #ifdef CONFIG_OF static const struct of_device_id fsmc_nand_id_table[] = { @@ -1229,9 +1233,7 @@ static struct platform_driver fsmc_nand_driver = { .owner = THIS_MODULE, .name = "fsmc-nand", .of_match_table = of_match_ptr(fsmc_nand_id_table), -#ifdef CONFIG_PM .pm = &fsmc_nand_pm_ops, -#endif }, }; diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index 89065dd83d64..e826f898241f 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -17,6 +17,7 @@ */ #include <linux/kernel.h> +#include <linux/err.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/module.h> @@ -86,59 +87,11 @@ static void gpio_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) gpio_nand_dosync(gpiomtd); } -static void gpio_nand_writebuf(struct mtd_info *mtd, const u_char *buf, int len) -{ - struct nand_chip *this = mtd->priv; - - iowrite8_rep(this->IO_ADDR_W, buf, len); -} - -static void gpio_nand_readbuf(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *this = mtd->priv; - - ioread8_rep(this->IO_ADDR_R, buf, len); -} - -static void gpio_nand_writebuf16(struct mtd_info *mtd, const u_char *buf, - int len) -{ - struct nand_chip *this = mtd->priv; - - if (IS_ALIGNED((unsigned long)buf, 2)) { - iowrite16_rep(this->IO_ADDR_W, buf, len>>1); - } else { - int i; - unsigned short *ptr = (unsigned short *)buf; - - for (i = 0; i < len; i += 2, ptr++) - writew(*ptr, this->IO_ADDR_W); - } -} - -static void gpio_nand_readbuf16(struct mtd_info *mtd, u_char *buf, int len) -{ - struct nand_chip *this = mtd->priv; - - if (IS_ALIGNED((unsigned long)buf, 2)) { - ioread16_rep(this->IO_ADDR_R, buf, len>>1); - } else { - int i; - unsigned short *ptr = (unsigned short *)buf; - - for (i = 0; i < len; i += 2, ptr++) - *ptr = readw(this->IO_ADDR_R); - } -} - static int gpio_nand_devready(struct mtd_info *mtd) { struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); - if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) - return gpio_get_value(gpiomtd->plat.gpio_rdy); - - return 1; + return gpio_get_value(gpiomtd->plat.gpio_rdy); } #ifdef CONFIG_OF @@ -153,6 +106,9 @@ static int gpio_nand_get_config_of(const struct device *dev, { u32 val; + if (!dev->of_node) + return -ENODEV; + if (!of_property_read_u32(dev->of_node, "bank-width", &val)) { if (val == 2) { plat->options |= NAND_BUSWIDTH_16; @@ -211,8 +167,8 @@ static inline int gpio_nand_get_config(const struct device *dev, if (!ret) return ret; - if (dev->platform_data) { - memcpy(plat, dev->platform_data, sizeof(*plat)); + if (dev_get_platdata(dev)) { + memcpy(plat, dev_get_platdata(dev), sizeof(*plat)); return 0; } @@ -230,145 +186,100 @@ gpio_nand_get_io_sync(struct platform_device *pdev) return platform_get_resource(pdev, IORESOURCE_MEM, 1); } -static int gpio_nand_remove(struct platform_device *dev) +static int gpio_nand_remove(struct platform_device *pdev) { - struct gpiomtd *gpiomtd = platform_get_drvdata(dev); - struct resource *res; + struct gpiomtd *gpiomtd = platform_get_drvdata(pdev); nand_release(&gpiomtd->mtd_info); - res = gpio_nand_get_io_sync(dev); - iounmap(gpiomtd->io_sync); - if (res) - 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, resource_size(res)); - if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) gpio_set_value(gpiomtd->plat.gpio_nwp, 0); gpio_set_value(gpiomtd->plat.gpio_nce, 1); - gpio_free(gpiomtd->plat.gpio_cle); - gpio_free(gpiomtd->plat.gpio_ale); - gpio_free(gpiomtd->plat.gpio_nce); - if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) - gpio_free(gpiomtd->plat.gpio_nwp); - if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) - gpio_free(gpiomtd->plat.gpio_rdy); - return 0; } -static void __iomem *request_and_remap(struct resource *res, size_t size, - const char *name, int *err) -{ - void __iomem *ptr; - - 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, resource_size(res)); - *err = -ENOMEM; - } - return ptr; -} - -static int gpio_nand_probe(struct platform_device *dev) +static int gpio_nand_probe(struct platform_device *pdev) { struct gpiomtd *gpiomtd; - struct nand_chip *this; - struct resource *res0, *res1; + struct nand_chip *chip; + struct resource *res; struct mtd_part_parser_data ppdata = {}; int ret = 0; - if (!dev->dev.of_node && !dev->dev.platform_data) - return -EINVAL; - - res0 = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res0) + if (!pdev->dev.of_node && !dev_get_platdata(&pdev->dev)) return -EINVAL; - gpiomtd = devm_kzalloc(&dev->dev, sizeof(*gpiomtd), GFP_KERNEL); - if (gpiomtd == NULL) { - dev_err(&dev->dev, "failed to create NAND MTD\n"); + gpiomtd = devm_kzalloc(&pdev->dev, sizeof(*gpiomtd), GFP_KERNEL); + if (!gpiomtd) { + dev_err(&pdev->dev, "failed to create NAND MTD\n"); return -ENOMEM; } - this = &gpiomtd->nand_chip; - this->IO_ADDR_R = request_and_remap(res0, 2, "NAND", &ret); - if (!this->IO_ADDR_R) { - dev_err(&dev->dev, "unable to map NAND\n"); - goto err_map; - } + chip = &gpiomtd->nand_chip; - res1 = gpio_nand_get_io_sync(dev); - if (res1) { - gpiomtd->io_sync = request_and_remap(res1, 4, "NAND sync", &ret); - if (!gpiomtd->io_sync) { - dev_err(&dev->dev, "unable to map sync NAND\n"); - goto err_sync; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + chip->IO_ADDR_R = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(chip->IO_ADDR_R)) + return PTR_ERR(chip->IO_ADDR_R); + + res = gpio_nand_get_io_sync(pdev); + if (res) { + gpiomtd->io_sync = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(gpiomtd->io_sync)) + return PTR_ERR(gpiomtd->io_sync); } - ret = gpio_nand_get_config(&dev->dev, &gpiomtd->plat); + ret = gpio_nand_get_config(&pdev->dev, &gpiomtd->plat); if (ret) - goto err_nce; + return ret; - ret = gpio_request(gpiomtd->plat.gpio_nce, "NAND NCE"); + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nce, "NAND NCE"); if (ret) - goto err_nce; + return ret; gpio_direction_output(gpiomtd->plat.gpio_nce, 1); + if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) { - ret = gpio_request(gpiomtd->plat.gpio_nwp, "NAND NWP"); + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_nwp, + "NAND NWP"); if (ret) - goto err_nwp; - gpio_direction_output(gpiomtd->plat.gpio_nwp, 1); + return ret; } - ret = gpio_request(gpiomtd->plat.gpio_ale, "NAND ALE"); + + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_ale, "NAND ALE"); if (ret) - goto err_ale; + return ret; gpio_direction_output(gpiomtd->plat.gpio_ale, 0); - ret = gpio_request(gpiomtd->plat.gpio_cle, "NAND CLE"); + + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_cle, "NAND CLE"); if (ret) - goto err_cle; + return ret; gpio_direction_output(gpiomtd->plat.gpio_cle, 0); + if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) { - ret = gpio_request(gpiomtd->plat.gpio_rdy, "NAND RDY"); + ret = devm_gpio_request(&pdev->dev, gpiomtd->plat.gpio_rdy, + "NAND RDY"); if (ret) - goto err_rdy; + return ret; gpio_direction_input(gpiomtd->plat.gpio_rdy); + chip->dev_ready = gpio_nand_devready; } + chip->IO_ADDR_W = chip->IO_ADDR_R; + chip->ecc.mode = NAND_ECC_SOFT; + chip->options = gpiomtd->plat.options; + chip->chip_delay = gpiomtd->plat.chip_delay; + chip->cmd_ctrl = gpio_nand_cmd_ctrl; - this->IO_ADDR_W = this->IO_ADDR_R; - this->ecc.mode = NAND_ECC_SOFT; - this->options = gpiomtd->plat.options; - this->chip_delay = gpiomtd->plat.chip_delay; - - /* install our routines */ - this->cmd_ctrl = gpio_nand_cmd_ctrl; - this->dev_ready = gpio_nand_devready; + gpiomtd->mtd_info.priv = chip; + gpiomtd->mtd_info.owner = THIS_MODULE; - if (this->options & NAND_BUSWIDTH_16) { - this->read_buf = gpio_nand_readbuf16; - this->write_buf = gpio_nand_writebuf16; - } else { - this->read_buf = gpio_nand_readbuf; - this->write_buf = gpio_nand_writebuf; - } + platform_set_drvdata(pdev, gpiomtd); - /* set the mtd private data for the nand driver */ - gpiomtd->mtd_info.priv = this; - gpiomtd->mtd_info.owner = THIS_MODULE; + if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) + gpio_direction_output(gpiomtd->plat.gpio_nwp, 1); if (nand_scan(&gpiomtd->mtd_info, 1)) { - dev_err(&dev->dev, "no nand chips found?\n"); ret = -ENXIO; goto err_wp; } @@ -377,38 +288,17 @@ static int gpio_nand_probe(struct platform_device *dev) gpiomtd->plat.adjust_parts(&gpiomtd->plat, gpiomtd->mtd_info.size); - ppdata.of_node = dev->dev.of_node; + ppdata.of_node = pdev->dev.of_node; ret = mtd_device_parse_register(&gpiomtd->mtd_info, NULL, &ppdata, gpiomtd->plat.parts, gpiomtd->plat.num_parts); - if (ret) - goto err_wp; - platform_set_drvdata(dev, gpiomtd); - - return 0; + if (!ret) + return 0; err_wp: if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) gpio_set_value(gpiomtd->plat.gpio_nwp, 0); - if (gpio_is_valid(gpiomtd->plat.gpio_rdy)) - gpio_free(gpiomtd->plat.gpio_rdy); -err_rdy: - gpio_free(gpiomtd->plat.gpio_cle); -err_cle: - gpio_free(gpiomtd->plat.gpio_ale); -err_ale: - if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) - gpio_free(gpiomtd->plat.gpio_nwp); -err_nwp: - gpio_free(gpiomtd->plat.gpio_nce); -err_nce: - iounmap(gpiomtd->io_sync); - if (res1) - release_mem_region(res1->start, resource_size(res1)); -err_sync: - iounmap(gpiomtd->nand_chip.IO_ADDR_R); - release_mem_region(res0->start, resource_size(res0)); -err_map: + return ret; } @@ -417,6 +307,7 @@ static struct platform_driver gpio_nand_driver = { .remove = gpio_nand_remove, .driver = { .name = "gpio-nand", + .owner = THIS_MODULE, .of_match_table = of_match_ptr(gpio_nand_id_table), }, }; diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c index 4f8857fa48a7..aaced29727fb 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c @@ -187,6 +187,12 @@ int gpmi_init(struct gpmi_nand_data *this) /* Select BCH ECC. */ writel(BM_GPMI_CTRL1_BCH_MODE, r->gpmi_regs + HW_GPMI_CTRL1_SET); + /* + * Decouple the chip select from dma channel. We use dma0 for all + * the chips. + */ + writel(BM_GPMI_CTRL1_DECOUPLE_CS, r->gpmi_regs + HW_GPMI_CTRL1_SET); + gpmi_disable_clk(this); return 0; err_out: @@ -1073,6 +1079,13 @@ int gpmi_is_ready(struct gpmi_nand_data *this, unsigned chip) mask = MX23_BM_GPMI_DEBUG_READY0 << chip; reg = readl(r->gpmi_regs + HW_GPMI_DEBUG); } else if (GPMI_IS_MX28(this) || GPMI_IS_MX6Q(this)) { + /* + * In the imx6, all the ready/busy pins are bound + * together. So we only need to check chip 0. + */ + if (GPMI_IS_MX6Q(this)) + chip = 0; + /* MX28 shares the same R/B register as MX6Q. */ mask = MX28_BF_GPMI_STAT_READY_BUSY(1 << chip); reg = readl(r->gpmi_regs + HW_GPMI_STAT); diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c index 25ecfa1822a8..dabbc14db563 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c @@ -26,7 +26,6 @@ #include <linux/interrupt.h> #include <linux/module.h> #include <linux/mtd/partitions.h> -#include <linux/pinctrl/consumer.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_mtd.h> @@ -46,7 +45,10 @@ static struct nand_bbt_descr gpmi_bbt_descr = { .pattern = scan_ff_pattern }; -/* We will use all the (page + OOB). */ +/* + * We may change the layout if we can get the ECC info from the datasheet, + * else we will use all the (page + OOB). + */ static struct nand_ecclayout gpmi_hw_ecclayout = { .eccbytes = 0, .eccpos = { 0, }, @@ -112,7 +114,131 @@ static inline bool gpmi_check_ecc(struct gpmi_nand_data *this) return true; } -int common_nfc_set_geometry(struct gpmi_nand_data *this) +/* + * If we can get the ECC information from the nand chip, we do not + * need to calculate them ourselves. + * + * We may have available oob space in this case. + */ +static bool set_geometry_by_ecc_info(struct gpmi_nand_data *this) +{ + struct bch_geometry *geo = &this->bch_geometry; + struct mtd_info *mtd = &this->mtd; + struct nand_chip *chip = mtd->priv; + struct nand_oobfree *of = gpmi_hw_ecclayout.oobfree; + unsigned int block_mark_bit_offset; + + if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) + return false; + + switch (chip->ecc_step_ds) { + case SZ_512: + geo->gf_len = 13; + break; + case SZ_1K: + geo->gf_len = 14; + break; + default: + dev_err(this->dev, + "unsupported nand chip. ecc bits : %d, ecc size : %d\n", + chip->ecc_strength_ds, chip->ecc_step_ds); + return false; + } + geo->ecc_chunk_size = chip->ecc_step_ds; + geo->ecc_strength = round_up(chip->ecc_strength_ds, 2); + if (!gpmi_check_ecc(this)) + return false; + + /* Keep the C >= O */ + if (geo->ecc_chunk_size < mtd->oobsize) { + dev_err(this->dev, + "unsupported nand chip. ecc size: %d, oob size : %d\n", + chip->ecc_step_ds, mtd->oobsize); + return false; + } + + /* The default value, see comment in the legacy_set_geometry(). */ + geo->metadata_size = 10; + + geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; + + /* + * Now, the NAND chip with 2K page(data chunk is 512byte) shows below: + * + * | P | + * |<----------------------------------------------------->| + * | | + * | (Block Mark) | + * | P' | | | | + * |<-------------------------------------------->| D | | O' | + * | |<---->| |<--->| + * V V V V V + * +---+----------+-+----------+-+----------+-+----------+-+-----+ + * | M | data |E| data |E| data |E| data |E| | + * +---+----------+-+----------+-+----------+-+----------+-+-----+ + * ^ ^ + * | O | + * |<------------>| + * | | + * + * P : the page size for BCH module. + * E : The ECC strength. + * G : the length of Galois Field. + * N : The chunk count of per page. + * M : the metasize of per page. + * C : the ecc chunk size, aka the "data" above. + * P': the nand chip's page size. + * O : the nand chip's oob size. + * O': the free oob. + * + * The formula for P is : + * + * E * G * N + * P = ------------ + P' + M + * 8 + * + * The position of block mark moves forward in the ECC-based view + * of page, and the delta is: + * + * E * G * (N - 1) + * D = (---------------- + M) + * 8 + * + * Please see the comment in legacy_set_geometry(). + * With the condition C >= O , we still can get same result. + * So the bit position of the physical block mark within the ECC-based + * view of the page is : + * (P' - D) * 8 + */ + geo->page_size = mtd->writesize + geo->metadata_size + + (geo->gf_len * geo->ecc_strength * geo->ecc_chunk_count) / 8; + + /* The available oob size we have. */ + if (geo->page_size < mtd->writesize + mtd->oobsize) { + of->offset = geo->page_size - mtd->writesize; + of->length = mtd->oobsize - of->offset; + } + + geo->payload_size = mtd->writesize; + + geo->auxiliary_status_offset = ALIGN(geo->metadata_size, 4); + geo->auxiliary_size = ALIGN(geo->metadata_size, 4) + + ALIGN(geo->ecc_chunk_count, 4); + + if (!this->swap_block_mark) + return true; + + /* For bit swap. */ + block_mark_bit_offset = mtd->writesize * 8 - + (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1) + + geo->metadata_size * 8); + + geo->block_mark_byte_offset = block_mark_bit_offset / 8; + geo->block_mark_bit_offset = block_mark_bit_offset % 8; + return true; +} + +static int legacy_set_geometry(struct gpmi_nand_data *this) { struct bch_geometry *geo = &this->bch_geometry; struct mtd_info *mtd = &this->mtd; @@ -224,11 +350,15 @@ int common_nfc_set_geometry(struct gpmi_nand_data *this) return 0; } -struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) +int common_nfc_set_geometry(struct gpmi_nand_data *this) { - int chipnr = this->current_chip; + return legacy_set_geometry(this); +} - return this->dma_chans[chipnr]; +struct dma_chan *get_dma_chan(struct gpmi_nand_data *this) +{ + /* We use the DMA channel 0 to access all the nand chips. */ + return this->dma_chans[0]; } /* Can we use the upper's buffer directly for DMA? */ @@ -264,8 +394,6 @@ static void dma_irq_callback(void *param) struct gpmi_nand_data *this = param; struct completion *dma_c = &this->dma_done; - complete(dma_c); - switch (this->dma_type) { case DMA_FOR_COMMAND: dma_unmap_sg(this->dev, &this->cmd_sgl, 1, DMA_TO_DEVICE); @@ -290,6 +418,8 @@ static void dma_irq_callback(void *param) default: pr_err("in wrong DMA operation.\n"); } + + complete(dma_c); } int start_dma_without_bch_irq(struct gpmi_nand_data *this, @@ -355,7 +485,7 @@ static int acquire_register_block(struct gpmi_nand_data *this, r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name); if (!r) { pr_err("Can't get resource for %s\n", res_name); - return -ENXIO; + return -ENODEV; } p = ioremap(r->start, resource_size(r)); @@ -396,7 +526,7 @@ static int acquire_bch_irq(struct gpmi_nand_data *this, irq_handler_t irq_h) r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, res_name); if (!r) { pr_err("Can't get resource for %s\n", res_name); - return -ENXIO; + return -ENODEV; } err = request_irq(r->start, irq_h, 0, res_name, this); @@ -473,12 +603,14 @@ static int gpmi_get_clks(struct gpmi_nand_data *this) struct resources *r = &this->resources; char **extra_clks = NULL; struct clk *clk; - int i; + int err, i; /* The main clock is stored in the first. */ r->clock[0] = clk_get(this->dev, "gpmi_io"); - if (IS_ERR(r->clock[0])) + if (IS_ERR(r->clock[0])) { + err = PTR_ERR(r->clock[0]); goto err_clock; + } /* Get extra clocks */ if (GPMI_IS_MX6Q(this)) @@ -491,8 +623,10 @@ static int gpmi_get_clks(struct gpmi_nand_data *this) break; clk = clk_get(this->dev, extra_clks[i - 1]); - if (IS_ERR(clk)) + if (IS_ERR(clk)) { + err = PTR_ERR(clk); goto err_clock; + } r->clock[i] = clk; } @@ -511,12 +645,11 @@ static int gpmi_get_clks(struct gpmi_nand_data *this) err_clock: dev_dbg(this->dev, "failed in finding the clocks.\n"); gpmi_put_clks(this); - return -ENOMEM; + return err; } static int acquire_resources(struct gpmi_nand_data *this) { - struct pinctrl *pinctrl; int ret; ret = acquire_register_block(this, GPMI_NAND_GPMI_REGS_ADDR_RES_NAME); @@ -535,19 +668,12 @@ static int acquire_resources(struct gpmi_nand_data *this) if (ret) goto exit_dma_channels; - pinctrl = devm_pinctrl_get_select_default(&this->pdev->dev); - if (IS_ERR(pinctrl)) { - ret = PTR_ERR(pinctrl); - goto exit_pin; - } - ret = gpmi_get_clks(this); if (ret) goto exit_clock; return 0; exit_clock: -exit_pin: release_dma_channels(this); exit_dma_channels: release_bch_irq(this); @@ -1139,57 +1265,53 @@ static int gpmi_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *chip, static int gpmi_ecc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, int page) { - /* - * The BCH will use all the (page + oob). - * Our gpmi_hw_ecclayout can only prohibit the JFFS2 to write the oob. - * But it can not stop some ioctls such MEMWRITEOOB which uses - * MTD_OPS_PLACE_OOB. So We have to implement this function to prohibit - * these ioctls too. - */ - return -EPERM; + struct nand_oobfree *of = mtd->ecclayout->oobfree; + int status = 0; + + /* Do we have available oob area? */ + if (!of->length) + return -EPERM; + + if (!nand_is_slc(chip)) + return -EPERM; + + chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize + of->offset, page); + chip->write_buf(mtd, chip->oob_poi + of->offset, of->length); + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + + status = chip->waitfunc(mtd, chip); + return status & NAND_STATUS_FAIL ? -EIO : 0; } static int gpmi_block_markbad(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *chip = mtd->priv; struct gpmi_nand_data *this = chip->priv; - int block, ret = 0; + int ret = 0; uint8_t *block_mark; int column, page, status, chipnr; - /* Get block number */ - block = (int)(ofs >> chip->bbt_erase_shift); - if (chip->bbt) - chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); + chipnr = (int)(ofs >> chip->chip_shift); + chip->select_chip(mtd, chipnr); - /* Do we have a flash based bad block table ? */ - if (chip->bbt_options & NAND_BBT_USE_FLASH) - ret = nand_update_bbt(mtd, ofs); - else { - chipnr = (int)(ofs >> chip->chip_shift); - chip->select_chip(mtd, chipnr); + column = this->swap_block_mark ? mtd->writesize : 0; - column = this->swap_block_mark ? mtd->writesize : 0; + /* Write the block mark. */ + block_mark = this->data_buffer_dma; + block_mark[0] = 0; /* bad block marker */ - /* Write the block mark. */ - block_mark = this->data_buffer_dma; - block_mark[0] = 0; /* bad block marker */ + /* Shift to get page */ + page = (int)(ofs >> chip->page_shift); - /* Shift to get page */ - page = (int)(ofs >> chip->page_shift); + chip->cmdfunc(mtd, NAND_CMD_SEQIN, column, page); + chip->write_buf(mtd, block_mark, 1); + chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - chip->cmdfunc(mtd, NAND_CMD_SEQIN, column, page); - chip->write_buf(mtd, block_mark, 1); - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); + status = chip->waitfunc(mtd, chip); + if (status & NAND_STATUS_FAIL) + ret = -EIO; - status = chip->waitfunc(mtd, chip); - if (status & NAND_STATUS_FAIL) - ret = -EIO; - - chip->select_chip(mtd, -1); - } - if (!ret) - mtd->ecc_stats.badblocks++; + chip->select_chip(mtd, -1); return ret; } @@ -1456,8 +1578,6 @@ static int gpmi_set_geometry(struct gpmi_nand_data *this) static int gpmi_pre_bbt_scan(struct gpmi_nand_data *this) { - int ret; - /* Set up swap_block_mark, must be set before the gpmi_set_geometry() */ if (GPMI_IS_MX23(this)) this->swap_block_mark = false; @@ -1465,23 +1585,22 @@ static int gpmi_pre_bbt_scan(struct gpmi_nand_data *this) this->swap_block_mark = true; /* Set up the medium geometry */ - ret = gpmi_set_geometry(this); - if (ret) - return ret; + return gpmi_set_geometry(this); - /* Adjust the ECC strength according to the chip. */ - this->nand.ecc.strength = this->bch_geometry.ecc_strength; - this->mtd.ecc_strength = this->bch_geometry.ecc_strength; - this->mtd.bitflip_threshold = this->bch_geometry.ecc_strength; +} - /* NAND boot init, depends on the gpmi_set_geometry(). */ - return nand_boot_init(this); +static void gpmi_nfc_exit(struct gpmi_nand_data *this) +{ + nand_release(&this->mtd); + gpmi_free_dma_buffer(this); } -static int gpmi_scan_bbt(struct mtd_info *mtd) +static int gpmi_init_last(struct gpmi_nand_data *this) { + struct mtd_info *mtd = &this->mtd; struct nand_chip *chip = mtd->priv; - struct gpmi_nand_data *this = chip->priv; + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct bch_geometry *bch_geo = &this->bch_geometry; int ret; /* Prepare for the BBT scan. */ @@ -1489,6 +1608,16 @@ static int gpmi_scan_bbt(struct mtd_info *mtd) if (ret) return ret; + /* Init the nand_ecc_ctrl{} */ + ecc->read_page = gpmi_ecc_read_page; + ecc->write_page = gpmi_ecc_write_page; + ecc->read_oob = gpmi_ecc_read_oob; + ecc->write_oob = gpmi_ecc_write_oob; + ecc->mode = NAND_ECC_HW; + ecc->size = bch_geo->ecc_chunk_size; + ecc->strength = bch_geo->ecc_strength; + ecc->layout = &gpmi_hw_ecclayout; + /* * Can we enable the extra features? such as EDO or Sync mode. * @@ -1497,14 +1626,7 @@ static int gpmi_scan_bbt(struct mtd_info *mtd) */ gpmi_extra_init(this); - /* use the default BBT implementation */ - return nand_default_bbt(mtd); -} - -static void gpmi_nfc_exit(struct gpmi_nand_data *this) -{ - nand_release(&this->mtd); - gpmi_free_dma_buffer(this); + return 0; } static int gpmi_nfc_init(struct gpmi_nand_data *this) @@ -1530,33 +1652,39 @@ static int gpmi_nfc_init(struct gpmi_nand_data *this) chip->read_byte = gpmi_read_byte; chip->read_buf = gpmi_read_buf; chip->write_buf = gpmi_write_buf; - chip->ecc.read_page = gpmi_ecc_read_page; - chip->ecc.write_page = gpmi_ecc_write_page; - chip->ecc.read_oob = gpmi_ecc_read_oob; - chip->ecc.write_oob = gpmi_ecc_write_oob; - chip->scan_bbt = gpmi_scan_bbt; chip->badblock_pattern = &gpmi_bbt_descr; chip->block_markbad = gpmi_block_markbad; chip->options |= NAND_NO_SUBPAGE_WRITE; - chip->ecc.mode = NAND_ECC_HW; - chip->ecc.size = 1; - chip->ecc.strength = 8; - chip->ecc.layout = &gpmi_hw_ecclayout; if (of_get_nand_on_flash_bbt(this->dev->of_node)) chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; - /* Allocate a temporary DMA buffer for reading ID in the nand_scan() */ + /* + * Allocate a temporary DMA buffer for reading ID in the + * nand_scan_ident(). + */ this->bch_geometry.payload_size = 1024; this->bch_geometry.auxiliary_size = 128; ret = gpmi_alloc_dma_buffer(this); if (ret) goto err_out; - ret = nand_scan(mtd, 1); - if (ret) { - pr_err("Chip scan failed\n"); + ret = nand_scan_ident(mtd, GPMI_IS_MX6Q(this) ? 2 : 1, NULL); + if (ret) + goto err_out; + + ret = gpmi_init_last(this); + if (ret) goto err_out; - } + + chip->options |= NAND_SKIP_BBTSCAN; + ret = nand_scan_tail(mtd); + if (ret) + goto err_out; + + ret = nand_boot_init(this); + if (ret) + goto err_out; + chip->scan_bbt(mtd); ppdata.of_node = this->pdev->dev.of_node; ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0); @@ -1573,19 +1701,19 @@ static const struct platform_device_id gpmi_ids[] = { { .name = "imx23-gpmi-nand", .driver_data = IS_MX23, }, { .name = "imx28-gpmi-nand", .driver_data = IS_MX28, }, { .name = "imx6q-gpmi-nand", .driver_data = IS_MX6Q, }, - {}, + {} }; static const struct of_device_id gpmi_nand_id_table[] = { { .compatible = "fsl,imx23-gpmi-nand", - .data = (void *)&gpmi_ids[IS_MX23] + .data = (void *)&gpmi_ids[IS_MX23], }, { .compatible = "fsl,imx28-gpmi-nand", - .data = (void *)&gpmi_ids[IS_MX28] + .data = (void *)&gpmi_ids[IS_MX28], }, { .compatible = "fsl,imx6q-gpmi-nand", - .data = (void *)&gpmi_ids[IS_MX6Q] + .data = (void *)&gpmi_ids[IS_MX6Q], }, {} }; MODULE_DEVICE_TABLE(of, gpmi_nand_id_table); @@ -1601,10 +1729,10 @@ static int gpmi_nand_probe(struct platform_device *pdev) pdev->id_entry = of_id->data; } else { pr_err("Failed to find the right device id.\n"); - return -ENOMEM; + return -ENODEV; } - this = kzalloc(sizeof(*this), GFP_KERNEL); + this = devm_kzalloc(&pdev->dev, sizeof(*this), GFP_KERNEL); if (!this) { pr_err("Failed to allocate per-device memory\n"); return -ENOMEM; @@ -1633,9 +1761,7 @@ static int gpmi_nand_probe(struct platform_device *pdev) exit_nfc_init: release_resources(this); exit_acquire_resources: - platform_set_drvdata(pdev, NULL); dev_err(this->dev, "driver registration failed: %d\n", ret); - kfree(this); return ret; } @@ -1646,8 +1772,6 @@ static int gpmi_nand_remove(struct platform_device *pdev) gpmi_nfc_exit(this); release_resources(this); - platform_set_drvdata(pdev, NULL); - kfree(this); return 0; } diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-regs.h b/drivers/mtd/nand/gpmi-nand/gpmi-regs.h index 53397cc290fc..82114cdc8330 100644 --- a/drivers/mtd/nand/gpmi-nand/gpmi-regs.h +++ b/drivers/mtd/nand/gpmi-nand/gpmi-regs.h @@ -108,6 +108,9 @@ #define HW_GPMI_CTRL1_CLR 0x00000068 #define HW_GPMI_CTRL1_TOG 0x0000006c +#define BP_GPMI_CTRL1_DECOUPLE_CS 24 +#define BM_GPMI_CTRL1_DECOUPLE_CS (1 << BP_GPMI_CTRL1_DECOUPLE_CS) + #define BP_GPMI_CTRL1_WRN_DLY_SEL 22 #define BM_GPMI_CTRL1_WRN_DLY_SEL (0x3 << BP_GPMI_CTRL1_WRN_DLY_SEL) #define BF_GPMI_CTRL1_WRN_DLY_SEL(v) \ diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c index b76460eeaf22..a264b888c66c 100644 --- a/drivers/mtd/nand/jz4740_nand.c +++ b/drivers/mtd/nand/jz4740_nand.c @@ -411,7 +411,7 @@ static int jz_nand_probe(struct platform_device *pdev) struct jz_nand *nand; struct nand_chip *chip; struct mtd_info *mtd; - struct jz_nand_platform_data *pdata = pdev->dev.platform_data; + struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); size_t chipnr, bank_idx; uint8_t nand_maf_id = 0, nand_dev_id = 0; @@ -538,7 +538,6 @@ err_unclaim_banks: err_gpio_busy: if (pdata && gpio_is_valid(pdata->busy_gpio)) gpio_free(pdata->busy_gpio); - platform_set_drvdata(pdev, NULL); err_iounmap_mmio: jz_nand_iounmap_resource(nand->mem, nand->base); err_free: @@ -549,7 +548,7 @@ err_free: static int jz_nand_remove(struct platform_device *pdev) { struct jz_nand *nand = platform_get_drvdata(pdev); - struct jz_nand_platform_data *pdata = pdev->dev.platform_data; + struct jz_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); size_t i; nand_release(&nand->mtd); @@ -570,7 +569,6 @@ static int jz_nand_remove(struct platform_device *pdev) jz_nand_iounmap_resource(nand->mem, nand->base); - platform_set_drvdata(pdev, NULL); kfree(nand); return 0; diff --git a/drivers/mtd/nand/lpc32xx_mlc.c b/drivers/mtd/nand/lpc32xx_mlc.c index fd1df5e13ae4..327d96c03505 100644 --- a/drivers/mtd/nand/lpc32xx_mlc.c +++ b/drivers/mtd/nand/lpc32xx_mlc.c @@ -696,7 +696,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) } lpc32xx_wp_disable(host); - host->pdata = pdev->dev.platform_data; + host->pdata = dev_get_platdata(&pdev->dev); nand_chip->priv = host; /* link the private data structures */ mtd->priv = nand_chip; @@ -828,7 +828,6 @@ err_exit3: err_exit2: clk_disable(host->clk); clk_put(host->clk); - platform_set_drvdata(pdev, NULL); err_exit1: lpc32xx_wp_enable(host); gpio_free(host->ncfg->wp_gpio); @@ -851,7 +850,6 @@ static int lpc32xx_nand_remove(struct platform_device *pdev) clk_disable(host->clk); clk_put(host->clk); - platform_set_drvdata(pdev, NULL); lpc32xx_wp_enable(host); gpio_free(host->ncfg->wp_gpio); @@ -907,7 +905,7 @@ static struct platform_driver lpc32xx_nand_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, - .of_match_table = of_match_ptr(lpc32xx_nand_match), + .of_match_table = lpc32xx_nand_match, }, }; diff --git a/drivers/mtd/nand/lpc32xx_slc.c b/drivers/mtd/nand/lpc32xx_slc.c index be94ed5abefb..23e6974ccd20 100644 --- a/drivers/mtd/nand/lpc32xx_slc.c +++ b/drivers/mtd/nand/lpc32xx_slc.c @@ -798,7 +798,7 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) } lpc32xx_wp_disable(host); - host->pdata = pdev->dev.platform_data; + host->pdata = dev_get_platdata(&pdev->dev); mtd = &host->mtd; chip = &host->nand_chip; @@ -893,7 +893,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) /* Avoid extra scan if using BBT, setup BBT support */ if (host->ncfg->use_bbt) { - chip->options |= NAND_SKIP_BBTSCAN; chip->bbt_options |= NAND_BBT_USE_FLASH; /* @@ -915,13 +914,6 @@ static int lpc32xx_nand_probe(struct platform_device *pdev) goto err_exit3; } - /* Standard layout in FLASH for bad block tables */ - if (host->ncfg->use_bbt) { - if (nand_default_bbt(mtd) < 0) - dev_err(&pdev->dev, - "Error initializing default bad block tables\n"); - } - mtd->name = "nxp_lpc3220_slc"; ppdata.of_node = pdev->dev.of_node; res = mtd_device_parse_register(mtd, NULL, &ppdata, host->ncfg->parts, @@ -936,7 +928,6 @@ err_exit3: err_exit2: clk_disable(host->clk); clk_put(host->clk); - platform_set_drvdata(pdev, NULL); err_exit1: lpc32xx_wp_enable(host); gpio_free(host->ncfg->wp_gpio); @@ -963,7 +954,6 @@ static int lpc32xx_nand_remove(struct platform_device *pdev) clk_disable(host->clk); clk_put(host->clk); - platform_set_drvdata(pdev, NULL); lpc32xx_wp_enable(host); gpio_free(host->ncfg->wp_gpio); @@ -1025,7 +1015,7 @@ static struct platform_driver lpc32xx_nand_driver = { .driver = { .name = LPC32XX_MODNAME, .owner = THIS_MODULE, - .of_match_table = of_match_ptr(lpc32xx_nand_match), + .of_match_table = lpc32xx_nand_match, }, }; diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index 3c9cdcbc4cba..439bc3896418 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -36,7 +36,9 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> +#include <linux/of_address.h> #include <linux/of_device.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <asm/mpc5121.h> @@ -617,10 +619,8 @@ 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->clk) + clk_disable_unprepare(prv->clk); if (prv->csreg) iounmap(prv->csreg); @@ -629,6 +629,7 @@ static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd) static int mpc5121_nfc_probe(struct platform_device *op) { struct device_node *rootnode, *dn = op->dev.of_node; + struct clk *clk; struct device *dev = &op->dev; struct mpc5121_nfc_prv *prv; struct resource res; @@ -730,14 +731,18 @@ static int mpc5121_nfc_probe(struct platform_device *op) of_node_put(rootnode); /* Enable NFC clock */ - prv->clk = clk_get(dev, "nfc_clk"); - if (IS_ERR(prv->clk)) { + clk = devm_clk_get(dev, "nfc_clk"); + if (IS_ERR(clk)) { dev_err(dev, "Unable to acquire NFC clock!\n"); - retval = PTR_ERR(prv->clk); + retval = PTR_ERR(clk); goto error; } - - clk_enable(prv->clk); + retval = clk_prepare_enable(clk); + if (retval) { + dev_err(dev, "Unable to enable NFC clock!\n"); + goto error; + } + prv->clk = clk; /* Reset NAND Flash controller */ nfc_set(mtd, NFC_CONFIG1, NFC_RESET); diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 07e5784e5cd3..9dfdb06c508b 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -32,6 +32,7 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/completion.h> +#include <linux/of.h> #include <linux/of_device.h> #include <linux/of_mtd.h> @@ -266,7 +267,7 @@ static struct nand_ecclayout nandv2_hw_eccoob_4k = { } }; -static const char const *part_probes[] = { +static const char * const part_probes[] = { "cmdlinepart", "RedBoot", "ofpart", NULL }; static void memcpy32_fromio(void *trg, const void __iomem *src, size_t size) @@ -395,7 +396,7 @@ static void wait_op_done(struct mxc_nand_host *host, int useirq) if (useirq) { if (!host->devtype_data->check_int(host)) { - INIT_COMPLETION(host->op_completion); + reinit_completion(&host->op_completion); irq_control(host, 1); wait_for_completion(&host->op_completion); } @@ -1432,7 +1433,8 @@ static int mxcnd_probe(struct platform_device *pdev) err = mxcnd_probe_dt(host); if (err > 0) { - struct mxc_nand_platform_data *pdata = pdev->dev.platform_data; + struct mxc_nand_platform_data *pdata = + dev_get_platdata(&pdev->dev); if (pdata) { host->pdata = *pdata; host->devtype_data = (struct mxc_nand_devtype_data *) @@ -1446,8 +1448,6 @@ static int mxcnd_probe(struct platform_device *pdev) if (host->devtype_data->needs_ip) { res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; host->regs_ip = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->regs_ip)) return PTR_ERR(host->regs_ip); @@ -1457,9 +1457,6 @@ static int mxcnd_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); } - if (!res) - return -ENODEV; - host->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->base)) return PTR_ERR(host->base); @@ -1511,7 +1508,7 @@ static int mxcnd_probe(struct platform_device *pdev) host->devtype_data->irq_control(host, 0); err = devm_request_irq(&pdev->dev, host->irq, mxc_nfc_irq, - IRQF_DISABLED, DRIVER_NAME, host); + 0, DRIVER_NAME, host); if (err) return err; @@ -1578,8 +1575,6 @@ static int mxcnd_remove(struct platform_device *pdev) { struct mxc_nand_host *host = platform_get_drvdata(pdev); - platform_set_drvdata(pdev, NULL); - nand_release(&host->mtd); return 0; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index dfcd0a565c5b..bd39f7b67906 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -108,13 +108,13 @@ static int check_offs_len(struct mtd_info *mtd, int ret = 0; /* Start address must align on block boundary */ - if (ofs & ((1 << chip->phys_erase_shift) - 1)) { + if (ofs & ((1ULL << chip->phys_erase_shift) - 1)) { pr_debug("%s: unaligned address\n", __func__); ret = -EINVAL; } /* Length must align on block boundary */ - if (len & ((1 << chip->phys_erase_shift) - 1)) { + if (len & ((1ULL << chip->phys_erase_shift) - 1)) { pr_debug("%s: length not block aligned\n", __func__); ret = -EINVAL; } @@ -211,11 +211,9 @@ static void nand_select_chip(struct mtd_info *mtd, int chipnr) */ static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) { - int i; struct nand_chip *chip = mtd->priv; - for (i = 0; i < len; i++) - writeb(buf[i], chip->IO_ADDR_W); + iowrite8_rep(chip->IO_ADDR_W, buf, len); } /** @@ -228,11 +226,9 @@ static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) */ static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { - int i; struct nand_chip *chip = mtd->priv; - for (i = 0; i < len; i++) - buf[i] = readb(chip->IO_ADDR_R); + ioread8_rep(chip->IO_ADDR_R, buf, len); } /** @@ -245,14 +241,10 @@ static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) */ static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) { - int i; struct nand_chip *chip = mtd->priv; u16 *p = (u16 *) buf; - len >>= 1; - - for (i = 0; i < len; i++) - writew(p[i], chip->IO_ADDR_W); + iowrite16_rep(chip->IO_ADDR_W, p, len >> 1); } /** @@ -265,13 +257,10 @@ static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len) */ static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len) { - int i; struct nand_chip *chip = mtd->priv; u16 *p = (u16 *) buf; - len >>= 1; - for (i = 0; i < len; i++) - p[i] = readw(chip->IO_ADDR_R); + ioread16_rep(chip->IO_ADDR_R, p, len >> 1); } /** @@ -335,80 +324,88 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) } /** - * nand_default_block_markbad - [DEFAULT] mark a block bad + * nand_default_block_markbad - [DEFAULT] mark a block bad via bad block marker * @mtd: MTD device structure * @ofs: offset from device start * * This is the default implementation, which can be overridden by a hardware - * specific driver. We try operations in the following order, according to our - * bbt_options (NAND_BBT_NO_OOB_BBM and NAND_BBT_USE_FLASH): + * specific driver. It provides the details for writing a bad block marker to a + * block. + */ +static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct nand_chip *chip = mtd->priv; + struct mtd_oob_ops ops; + uint8_t buf[2] = { 0, 0 }; + int ret = 0, res, i = 0; + + ops.datbuf = NULL; + ops.oobbuf = buf; + ops.ooboffs = chip->badblockpos; + if (chip->options & NAND_BUSWIDTH_16) { + ops.ooboffs &= ~0x01; + ops.len = ops.ooblen = 2; + } else { + ops.len = ops.ooblen = 1; + } + ops.mode = MTD_OPS_PLACE_OOB; + + /* Write to first/last page(s) if necessary */ + if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) + ofs += mtd->erasesize - mtd->writesize; + do { + res = nand_do_write_oob(mtd, ofs, &ops); + if (!ret) + ret = res; + + i++; + ofs += mtd->writesize; + } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); + + return ret; +} + +/** + * nand_block_markbad_lowlevel - mark a block bad + * @mtd: MTD device structure + * @ofs: offset from device start + * + * This function performs the generic NAND bad block marking steps (i.e., bad + * block table(s) and/or marker(s)). We only allow the hardware driver to + * specify how to write bad block markers to OOB (chip->block_markbad). + * + * We try operations in the following order: * (1) erase the affected block, to allow OOB marker to be written cleanly - * (2) update in-memory BBT - * (3) write bad block marker to OOB area of affected block - * (4) update flash-based BBT - * Note that we retain the first error encountered in (3) or (4), finish the + * (2) write bad block marker to OOB area of affected block (unless flag + * NAND_BBT_NO_OOB_BBM is present) + * (3) update the BBT + * Note that we retain the first error encountered in (2) or (3), finish the * procedures, and dump the error in the end. */ -static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) +static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) { struct nand_chip *chip = mtd->priv; - uint8_t buf[2] = { 0, 0 }; - int block, res, ret = 0, i = 0; - int write_oob = !(chip->bbt_options & NAND_BBT_NO_OOB_BBM); + int res, ret = 0; - if (write_oob) { + if (!(chip->bbt_options & NAND_BBT_NO_OOB_BBM)) { struct erase_info einfo; /* Attempt erase before marking OOB */ memset(&einfo, 0, sizeof(einfo)); einfo.mtd = mtd; einfo.addr = ofs; - einfo.len = 1 << chip->phys_erase_shift; + einfo.len = 1ULL << chip->phys_erase_shift; nand_erase_nand(mtd, &einfo, 0); - } - - /* Get block number */ - block = (int)(ofs >> chip->bbt_erase_shift); - /* Mark block bad in memory-based BBT */ - if (chip->bbt) - chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); - - /* Write bad block marker to OOB */ - if (write_oob) { - struct mtd_oob_ops ops; - loff_t wr_ofs = ofs; + /* Write bad block marker to OOB */ nand_get_device(mtd, FL_WRITING); - - ops.datbuf = NULL; - ops.oobbuf = buf; - ops.ooboffs = chip->badblockpos; - if (chip->options & NAND_BUSWIDTH_16) { - ops.ooboffs &= ~0x01; - ops.len = ops.ooblen = 2; - } else { - ops.len = ops.ooblen = 1; - } - ops.mode = MTD_OPS_PLACE_OOB; - - /* Write to first/last page(s) if necessary */ - if (chip->bbt_options & NAND_BBT_SCANLASTPAGE) - wr_ofs += mtd->erasesize - mtd->writesize; - do { - res = nand_do_write_oob(mtd, wr_ofs, &ops); - if (!ret) - ret = res; - - i++; - wr_ofs += mtd->writesize; - } while ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) && i < 2); - + ret = chip->block_markbad(mtd, ofs); nand_release_device(mtd); } - /* Update flash-based bad block table */ - if (chip->bbt_options & NAND_BBT_USE_FLASH) { - res = nand_update_bbt(mtd, ofs); + /* Mark block bad in BBT */ + if (chip->bbt) { + res = nand_markbad_bbt(mtd, ofs); if (!ret) ret = res; } @@ -1983,13 +1980,14 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, * nand_write_subpage_hwecc - [REPLACABLE] hardware ECC based subpage write * @mtd: mtd info structure * @chip: nand chip info structure - * @column: column address of subpage within the page + * @offset: column address of subpage within the page * @data_len: data length + * @buf: data buffer * @oob_required: must write chip->oob_poi to OOB */ static int nand_write_subpage_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint32_t offset, - uint32_t data_len, const uint8_t *data_buf, + uint32_t data_len, const uint8_t *buf, int oob_required) { uint8_t *oob_buf = chip->oob_poi; @@ -2008,20 +2006,20 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, chip->ecc.hwctl(mtd, NAND_ECC_WRITE); /* write data (untouched subpages already masked by 0xFF) */ - chip->write_buf(mtd, data_buf, ecc_size); + chip->write_buf(mtd, buf, ecc_size); /* mask ECC of un-touched subpages by padding 0xFF */ if ((step < start_step) || (step > end_step)) memset(ecc_calc, 0xff, ecc_bytes); else - chip->ecc.calculate(mtd, data_buf, ecc_calc); + chip->ecc.calculate(mtd, buf, ecc_calc); /* mask OOB of un-touched subpages by padding 0xFF */ /* if oob_required, preserve OOB metadata of written subpage */ if (!oob_required || (step < start_step) || (step > end_step)) memset(oob_buf, 0xff, oob_bytes); - data_buf += ecc_size; + buf += ecc_size; ecc_calc += ecc_bytes; oob_buf += oob_bytes; } @@ -2633,7 +2631,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, } /* Increment page address and decrement length */ - len -= (1 << chip->phys_erase_shift); + len -= (1ULL << chip->phys_erase_shift); page += pages_per_block; /* Check, if we cross a chip boundary */ @@ -2694,7 +2692,6 @@ static int nand_block_isbad(struct mtd_info *mtd, loff_t offs) */ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) { - struct nand_chip *chip = mtd->priv; int ret; ret = nand_block_isbad(mtd, ofs); @@ -2705,7 +2702,7 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) return ret; } - return chip->block_markbad(mtd, ofs); + return nand_block_markbad_lowlevel(mtd, ofs); } /** @@ -2720,7 +2717,9 @@ static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, { int status; - if (!chip->onfi_version) + if (!chip->onfi_version || + !(le16_to_cpu(chip->onfi_params.opt_cmd) + & ONFI_OPT_CMD_SET_GET_FEATURES)) return -EINVAL; chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1); @@ -2741,7 +2740,9 @@ static int nand_onfi_set_features(struct mtd_info *mtd, struct nand_chip *chip, static int nand_onfi_get_features(struct mtd_info *mtd, struct nand_chip *chip, int addr, uint8_t *subfeature_param) { - if (!chip->onfi_version) + if (!chip->onfi_version || + !(le16_to_cpu(chip->onfi_params.opt_cmd) + & ONFI_OPT_CMD_SET_GET_FEATURES)) return -EINVAL; /* clear the sub feature parameters */ @@ -2793,7 +2794,15 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) if (!chip->select_chip) chip->select_chip = nand_select_chip; - if (!chip->read_byte) + + /* set for ONFI nand */ + if (!chip->onfi_set_features) + chip->onfi_set_features = nand_onfi_set_features; + if (!chip->onfi_get_features) + chip->onfi_get_features = nand_onfi_get_features; + + /* If called twice, pointers that depend on busw may need to be reset */ + if (!chip->read_byte || chip->read_byte == nand_read_byte) chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; if (!chip->read_word) chip->read_word = nand_read_word; @@ -2801,9 +2810,9 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) chip->block_bad = nand_block_bad; if (!chip->block_markbad) chip->block_markbad = nand_default_block_markbad; - if (!chip->write_buf) + if (!chip->write_buf || chip->write_buf == nand_write_buf) chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; - if (!chip->read_buf) + if (!chip->read_buf || chip->read_buf == nand_read_buf) chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; if (!chip->scan_bbt) chip->scan_bbt = nand_default_bbt; @@ -2846,6 +2855,77 @@ static u16 onfi_crc16(u16 crc, u8 const *p, size_t len) return crc; } +/* Parse the Extended Parameter Page. */ +static int nand_flash_detect_ext_param_page(struct mtd_info *mtd, + struct nand_chip *chip, struct nand_onfi_params *p) +{ + struct onfi_ext_param_page *ep; + struct onfi_ext_section *s; + struct onfi_ext_ecc_info *ecc; + uint8_t *cursor; + int ret = -EINVAL; + int len; + int i; + + len = le16_to_cpu(p->ext_param_page_length) * 16; + ep = kmalloc(len, GFP_KERNEL); + if (!ep) + return -ENOMEM; + + /* Send our own NAND_CMD_PARAM. */ + chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); + + /* Use the Change Read Column command to skip the ONFI param pages. */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, + sizeof(*p) * p->num_of_param_pages , -1); + + /* Read out the Extended Parameter Page. */ + chip->read_buf(mtd, (uint8_t *)ep, len); + if ((onfi_crc16(ONFI_CRC_BASE, ((uint8_t *)ep) + 2, len - 2) + != le16_to_cpu(ep->crc))) { + pr_debug("fail in the CRC.\n"); + goto ext_out; + } + + /* + * Check the signature. + * Do not strictly follow the ONFI spec, maybe changed in future. + */ + if (strncmp(ep->sig, "EPPS", 4)) { + pr_debug("The signature is invalid.\n"); + goto ext_out; + } + + /* find the ECC section. */ + cursor = (uint8_t *)(ep + 1); + for (i = 0; i < ONFI_EXT_SECTION_MAX; i++) { + s = ep->sections + i; + if (s->type == ONFI_SECTION_TYPE_2) + break; + cursor += s->length * 16; + } + if (i == ONFI_EXT_SECTION_MAX) { + pr_debug("We can not find the ECC section.\n"); + goto ext_out; + } + + /* get the info we want. */ + ecc = (struct onfi_ext_ecc_info *)cursor; + + if (!ecc->codeword_size) { + pr_debug("Invalid codeword size\n"); + goto ext_out; + } + + chip->ecc_strength_ds = ecc->ecc_bits; + chip->ecc_step_ds = 1 << ecc->codeword_size; + ret = 0; + +ext_out: + kfree(ep); + return ret; +} + /* * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise. */ @@ -2856,29 +2936,34 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, int i; int val; - /* ONFI need to be probed in 8 bits mode, and 16 bits should be selected with NAND_BUSWIDTH_AUTO */ - if (chip->options & NAND_BUSWIDTH_16) { - pr_err("Trying ONFI probe in 16 bits mode, aborting !\n"); - return 0; - } /* Try ONFI for unknown chip or LP */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1); if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' || chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I') return 0; + /* + * ONFI must be probed in 8-bit mode or with NAND_BUSWIDTH_AUTO, not + * with NAND_BUSWIDTH_16 + */ + if (chip->options & NAND_BUSWIDTH_16) { + pr_err("ONFI cannot be probed in 16-bit mode; aborting\n"); + return 0; + } + chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1); for (i = 0; i < 3; i++) { chip->read_buf(mtd, (uint8_t *)p, sizeof(*p)); if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) == le16_to_cpu(p->crc)) { - pr_info("ONFI param page %d valid\n", i); break; } } - if (i == 3) + if (i == 3) { + pr_err("Could not find valid ONFI parameter page; aborting\n"); return 0; + } /* Check version */ val = le16_to_cpu(p->revision); @@ -2902,16 +2987,51 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, sanitize_string(p->model, sizeof(p->model)); if (!mtd->name) mtd->name = p->model; + mtd->writesize = le32_to_cpu(p->byte_per_page); - mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize; + + /* + * pages_per_block and blocks_per_lun may not be a power-of-2 size + * (don't ask me who thought of this...). MTD assumes that these + * dimensions will be power-of-2, so just truncate the remaining area. + */ + mtd->erasesize = 1 << (fls(le32_to_cpu(p->pages_per_block)) - 1); + mtd->erasesize *= mtd->writesize; + mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page); - chip->chipsize = le32_to_cpu(p->blocks_per_lun); + + /* See erasesize comment */ + chip->chipsize = 1 << (fls(le32_to_cpu(p->blocks_per_lun)) - 1); chip->chipsize *= (uint64_t)mtd->erasesize * p->lun_count; - *busw = 0; - if (le16_to_cpu(p->features) & 1) + chip->bits_per_cell = p->bits_per_cell; + + if (onfi_feature(chip) & ONFI_FEATURE_16_BIT_BUS) *busw = NAND_BUSWIDTH_16; + else + *busw = 0; + + if (p->ecc_bits != 0xff) { + chip->ecc_strength_ds = p->ecc_bits; + chip->ecc_step_ds = 512; + } else if (chip->onfi_version >= 21 && + (onfi_feature(chip) & ONFI_FEATURE_EXT_PARAM_PAGE)) { + + /* + * The nand_flash_detect_ext_param_page() uses the + * Change Read Column command which maybe not supported + * by the chip->cmdfunc. So try to update the chip->cmdfunc + * now. We do not replace user supplied command function. + */ + if (mtd->writesize > 512 && chip->cmdfunc == nand_command) + chip->cmdfunc = nand_command_lp; + + /* The Extended Parameter Page is supported since ONFI 2.1. */ + if (nand_flash_detect_ext_param_page(mtd, chip, p)) + pr_warn("Failed to detect ONFI extended param page\n"); + } else { + pr_warn("Could not retrieve ONFI ECC requirements\n"); + } - pr_info("ONFI flash detected\n"); return 1; } @@ -2974,6 +3094,16 @@ static int nand_id_len(u8 *id_data, int arrlen) return arrlen; } +/* Extract the bits of per cell from the 3rd byte of the extended ID */ +static int nand_get_bits_per_cell(u8 cellinfo) +{ + int bits; + + bits = cellinfo & NAND_CI_CELLTYPE_MSK; + bits >>= NAND_CI_CELLTYPE_SHIFT; + return bits + 1; +} + /* * Many new NAND share similar device ID codes, which represent the size of the * chip. The rest of the parameters must be decoded according to generic or @@ -2984,7 +3114,7 @@ static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip, { int extid, id_len; /* The 3rd id byte holds MLC / multichip data */ - chip->cellinfo = id_data[2]; + chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]); /* The 4th id byte is the important one */ extid = id_data[3]; @@ -3000,8 +3130,7 @@ static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip, * ID to decide what to do. */ if (id_len == 6 && id_data[0] == NAND_MFR_SAMSUNG && - (chip->cellinfo & NAND_CI_CELLTYPE_MSK) && - id_data[5] != 0x00) { + !nand_is_slc(chip) && id_data[5] != 0x00) { /* Calc pagesize */ mtd->writesize = 2048 << (extid & 0x03); extid >>= 2; @@ -3033,7 +3162,7 @@ static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip, (((extid >> 1) & 0x04) | (extid & 0x03)); *busw = 0; } else if (id_len == 6 && id_data[0] == NAND_MFR_HYNIX && - (chip->cellinfo & NAND_CI_CELLTYPE_MSK)) { + !nand_is_slc(chip)) { unsigned int tmp; /* Calc pagesize */ @@ -3086,6 +3215,22 @@ static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip, extid >>= 2; /* Get buswidth information */ *busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + + /* + * Toshiba 24nm raw SLC (i.e., not BENAND) have 32B OOB per + * 512B page. For Toshiba SLC, we decode the 5th/6th byte as + * follows: + * - ID byte 6, bits[2:0]: 100b -> 43nm, 101b -> 32nm, + * 110b -> 24nm + * - ID byte 5, bit[7]: 1 -> BENAND, 0 -> raw SLC + */ + if (id_len >= 6 && id_data[0] == NAND_MFR_TOSHIBA && + nand_is_slc(chip) && + (id_data[5] & 0x7) == 0x6 /* 24nm */ && + !(id_data[4] & 0x80) /* !BENAND */) { + mtd->oobsize = 32 * mtd->writesize >> 9; + } + } } @@ -3105,6 +3250,9 @@ static void nand_decode_id(struct mtd_info *mtd, struct nand_chip *chip, mtd->oobsize = mtd->writesize / 32; *busw = type->options & NAND_BUSWIDTH_16; + /* All legacy ID NAND are small-page, SLC */ + chip->bits_per_cell = 1; + /* * Check for Spansion/AMD ID + repeating 5th, 6th byte since * some Spansion chips have erasesize that conflicts with size @@ -3141,11 +3289,11 @@ static void nand_decode_bbm_options(struct mtd_info *mtd, * Micron devices with 2KiB pages and on SLC Samsung, Hynix, Toshiba, * AMD/Spansion, and Macronix. All others scan only the first page. */ - if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) && + if (!nand_is_slc(chip) && (maf_id == NAND_MFR_SAMSUNG || maf_id == NAND_MFR_HYNIX)) chip->bbt_options |= NAND_BBT_SCANLASTPAGE; - else if ((!(chip->cellinfo & NAND_CI_CELLTYPE_MSK) && + else if ((nand_is_slc(chip) && (maf_id == NAND_MFR_SAMSUNG || maf_id == NAND_MFR_HYNIX || maf_id == NAND_MFR_TOSHIBA || @@ -3169,9 +3317,11 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip, mtd->erasesize = type->erasesize; mtd->oobsize = type->oobsize; - chip->cellinfo = id_data[2]; + chip->bits_per_cell = nand_get_bits_per_cell(id_data[2]); chip->chipsize = (uint64_t)type->chipsize << 20; chip->options |= type->options; + chip->ecc_strength_ds = NAND_ECC_STRENGTH(type); + chip->ecc_step_ds = NAND_ECC_STEP(type); *busw = type->options & NAND_BUSWIDTH_16; @@ -3322,11 +3472,13 @@ ident_done: if (mtd->writesize > 512 && chip->cmdfunc == nand_command) chip->cmdfunc = nand_command_lp; - pr_info("NAND device: Manufacturer ID: 0x%02x, Chip ID: 0x%02x (%s %s)," - " %dMiB, page size: %d, OOB size: %d\n", + pr_info("NAND device: Manufacturer ID: 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id, nand_manuf_ids[maf_idx].name, - chip->onfi_version ? chip->onfi_params.model : type->name, - (int)(chip->chipsize >> 20), mtd->writesize, mtd->oobsize); + chip->onfi_version ? chip->onfi_params.model : type->name); + + pr_info("NAND device: %dMiB, %s, page size: %d, OOB size: %d\n", + (int)(chip->chipsize >> 20), nand_is_slc(chip) ? "SLC" : "MLC", + mtd->writesize, mtd->oobsize); return type; } @@ -3406,6 +3558,7 @@ int nand_scan_tail(struct mtd_info *mtd) { int i; struct nand_chip *chip = mtd->priv; + struct nand_ecc_ctrl *ecc = &chip->ecc; /* New bad blocks should be marked in OOB, flash-based BBT, or both */ BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && @@ -3422,19 +3575,19 @@ int nand_scan_tail(struct mtd_info *mtd) /* * If no default placement scheme is given, select an appropriate one. */ - if (!chip->ecc.layout && (chip->ecc.mode != NAND_ECC_SOFT_BCH)) { + if (!ecc->layout && (ecc->mode != NAND_ECC_SOFT_BCH)) { switch (mtd->oobsize) { case 8: - chip->ecc.layout = &nand_oob_8; + ecc->layout = &nand_oob_8; break; case 16: - chip->ecc.layout = &nand_oob_16; + ecc->layout = &nand_oob_16; break; case 64: - chip->ecc.layout = &nand_oob_64; + ecc->layout = &nand_oob_64; break; case 128: - chip->ecc.layout = &nand_oob_128; + ecc->layout = &nand_oob_128; break; default: pr_warn("No oob scheme defined for oobsize %d\n", @@ -3446,75 +3599,67 @@ int nand_scan_tail(struct mtd_info *mtd) if (!chip->write_page) chip->write_page = nand_write_page; - /* set for ONFI nand */ - if (!chip->onfi_set_features) - chip->onfi_set_features = nand_onfi_set_features; - if (!chip->onfi_get_features) - chip->onfi_get_features = nand_onfi_get_features; - /* * Check ECC mode, default to software if 3byte/512byte hardware ECC is * selected and we have 256 byte pagesize fallback to software ECC */ - switch (chip->ecc.mode) { + switch (ecc->mode) { case NAND_ECC_HW_OOB_FIRST: /* Similar to NAND_ECC_HW, but a separate read_page handle */ - if (!chip->ecc.calculate || !chip->ecc.correct || - !chip->ecc.hwctl) { + if (!ecc->calculate || !ecc->correct || !ecc->hwctl) { pr_warn("No ECC functions supplied; " "hardware ECC not possible\n"); BUG(); } - if (!chip->ecc.read_page) - chip->ecc.read_page = nand_read_page_hwecc_oob_first; + if (!ecc->read_page) + ecc->read_page = nand_read_page_hwecc_oob_first; case NAND_ECC_HW: /* Use standard hwecc read page function? */ - if (!chip->ecc.read_page) - chip->ecc.read_page = nand_read_page_hwecc; - if (!chip->ecc.write_page) - chip->ecc.write_page = nand_write_page_hwecc; - if (!chip->ecc.read_page_raw) - chip->ecc.read_page_raw = nand_read_page_raw; - if (!chip->ecc.write_page_raw) - chip->ecc.write_page_raw = nand_write_page_raw; - if (!chip->ecc.read_oob) - chip->ecc.read_oob = nand_read_oob_std; - if (!chip->ecc.write_oob) - chip->ecc.write_oob = nand_write_oob_std; - if (!chip->ecc.read_subpage) - chip->ecc.read_subpage = nand_read_subpage; - if (!chip->ecc.write_subpage) - chip->ecc.write_subpage = nand_write_subpage_hwecc; + if (!ecc->read_page) + ecc->read_page = nand_read_page_hwecc; + if (!ecc->write_page) + ecc->write_page = nand_write_page_hwecc; + if (!ecc->read_page_raw) + ecc->read_page_raw = nand_read_page_raw; + if (!ecc->write_page_raw) + ecc->write_page_raw = nand_write_page_raw; + if (!ecc->read_oob) + ecc->read_oob = nand_read_oob_std; + if (!ecc->write_oob) + ecc->write_oob = nand_write_oob_std; + if (!ecc->read_subpage) + ecc->read_subpage = nand_read_subpage; + if (!ecc->write_subpage) + ecc->write_subpage = nand_write_subpage_hwecc; case NAND_ECC_HW_SYNDROME: - if ((!chip->ecc.calculate || !chip->ecc.correct || - !chip->ecc.hwctl) && - (!chip->ecc.read_page || - chip->ecc.read_page == nand_read_page_hwecc || - !chip->ecc.write_page || - chip->ecc.write_page == nand_write_page_hwecc)) { + if ((!ecc->calculate || !ecc->correct || !ecc->hwctl) && + (!ecc->read_page || + ecc->read_page == nand_read_page_hwecc || + !ecc->write_page || + ecc->write_page == nand_write_page_hwecc)) { pr_warn("No ECC functions supplied; " "hardware ECC not possible\n"); BUG(); } /* Use standard syndrome read/write page function? */ - if (!chip->ecc.read_page) - chip->ecc.read_page = nand_read_page_syndrome; - if (!chip->ecc.write_page) - chip->ecc.write_page = nand_write_page_syndrome; - if (!chip->ecc.read_page_raw) - chip->ecc.read_page_raw = nand_read_page_raw_syndrome; - if (!chip->ecc.write_page_raw) - chip->ecc.write_page_raw = nand_write_page_raw_syndrome; - if (!chip->ecc.read_oob) - chip->ecc.read_oob = nand_read_oob_syndrome; - if (!chip->ecc.write_oob) - chip->ecc.write_oob = nand_write_oob_syndrome; - - if (mtd->writesize >= chip->ecc.size) { - if (!chip->ecc.strength) { + if (!ecc->read_page) + ecc->read_page = nand_read_page_syndrome; + if (!ecc->write_page) + ecc->write_page = nand_write_page_syndrome; + if (!ecc->read_page_raw) + ecc->read_page_raw = nand_read_page_raw_syndrome; + if (!ecc->write_page_raw) + ecc->write_page_raw = nand_write_page_raw_syndrome; + if (!ecc->read_oob) + ecc->read_oob = nand_read_oob_syndrome; + if (!ecc->write_oob) + ecc->write_oob = nand_write_oob_syndrome; + + if (mtd->writesize >= ecc->size) { + if (!ecc->strength) { pr_warn("Driver must set ecc.strength when using hardware ECC\n"); BUG(); } @@ -3522,23 +3667,23 @@ int nand_scan_tail(struct mtd_info *mtd) } pr_warn("%d byte HW ECC not possible on " "%d byte page size, fallback to SW ECC\n", - chip->ecc.size, mtd->writesize); - chip->ecc.mode = NAND_ECC_SOFT; + ecc->size, mtd->writesize); + ecc->mode = NAND_ECC_SOFT; case NAND_ECC_SOFT: - chip->ecc.calculate = nand_calculate_ecc; - chip->ecc.correct = nand_correct_data; - chip->ecc.read_page = nand_read_page_swecc; - chip->ecc.read_subpage = nand_read_subpage; - chip->ecc.write_page = nand_write_page_swecc; - chip->ecc.read_page_raw = nand_read_page_raw; - chip->ecc.write_page_raw = nand_write_page_raw; - chip->ecc.read_oob = nand_read_oob_std; - chip->ecc.write_oob = nand_write_oob_std; - if (!chip->ecc.size) - chip->ecc.size = 256; - chip->ecc.bytes = 3; - chip->ecc.strength = 1; + ecc->calculate = nand_calculate_ecc; + ecc->correct = nand_correct_data; + ecc->read_page = nand_read_page_swecc; + ecc->read_subpage = nand_read_subpage; + ecc->write_page = nand_write_page_swecc; + ecc->read_page_raw = nand_read_page_raw; + ecc->write_page_raw = nand_write_page_raw; + ecc->read_oob = nand_read_oob_std; + ecc->write_oob = nand_write_oob_std; + if (!ecc->size) + ecc->size = 256; + ecc->bytes = 3; + ecc->strength = 1; break; case NAND_ECC_SOFT_BCH: @@ -3546,88 +3691,83 @@ int nand_scan_tail(struct mtd_info *mtd) pr_warn("CONFIG_MTD_ECC_BCH not enabled\n"); BUG(); } - chip->ecc.calculate = nand_bch_calculate_ecc; - chip->ecc.correct = nand_bch_correct_data; - chip->ecc.read_page = nand_read_page_swecc; - chip->ecc.read_subpage = nand_read_subpage; - chip->ecc.write_page = nand_write_page_swecc; - chip->ecc.read_page_raw = nand_read_page_raw; - chip->ecc.write_page_raw = nand_write_page_raw; - chip->ecc.read_oob = nand_read_oob_std; - chip->ecc.write_oob = nand_write_oob_std; + ecc->calculate = nand_bch_calculate_ecc; + ecc->correct = nand_bch_correct_data; + ecc->read_page = nand_read_page_swecc; + ecc->read_subpage = nand_read_subpage; + ecc->write_page = nand_write_page_swecc; + ecc->read_page_raw = nand_read_page_raw; + ecc->write_page_raw = nand_write_page_raw; + ecc->read_oob = nand_read_oob_std; + ecc->write_oob = nand_write_oob_std; /* * Board driver should supply ecc.size and ecc.bytes values to * select how many bits are correctable; see nand_bch_init() * for details. Otherwise, default to 4 bits for large page * devices. */ - if (!chip->ecc.size && (mtd->oobsize >= 64)) { - chip->ecc.size = 512; - chip->ecc.bytes = 7; + if (!ecc->size && (mtd->oobsize >= 64)) { + ecc->size = 512; + ecc->bytes = 7; } - chip->ecc.priv = nand_bch_init(mtd, - chip->ecc.size, - chip->ecc.bytes, - &chip->ecc.layout); - if (!chip->ecc.priv) { + ecc->priv = nand_bch_init(mtd, ecc->size, ecc->bytes, + &ecc->layout); + if (!ecc->priv) { pr_warn("BCH ECC initialization failed!\n"); BUG(); } - chip->ecc.strength = - chip->ecc.bytes * 8 / fls(8 * chip->ecc.size); + ecc->strength = ecc->bytes * 8 / fls(8 * ecc->size); break; case NAND_ECC_NONE: pr_warn("NAND_ECC_NONE selected by board driver. " "This is not recommended!\n"); - chip->ecc.read_page = nand_read_page_raw; - chip->ecc.write_page = nand_write_page_raw; - chip->ecc.read_oob = nand_read_oob_std; - chip->ecc.read_page_raw = nand_read_page_raw; - chip->ecc.write_page_raw = nand_write_page_raw; - chip->ecc.write_oob = nand_write_oob_std; - chip->ecc.size = mtd->writesize; - chip->ecc.bytes = 0; - chip->ecc.strength = 0; + ecc->read_page = nand_read_page_raw; + ecc->write_page = nand_write_page_raw; + ecc->read_oob = nand_read_oob_std; + ecc->read_page_raw = nand_read_page_raw; + ecc->write_page_raw = nand_write_page_raw; + ecc->write_oob = nand_write_oob_std; + ecc->size = mtd->writesize; + ecc->bytes = 0; + ecc->strength = 0; break; default: - pr_warn("Invalid NAND_ECC_MODE %d\n", chip->ecc.mode); + pr_warn("Invalid NAND_ECC_MODE %d\n", ecc->mode); BUG(); } /* For many systems, the standard OOB write also works for raw */ - if (!chip->ecc.read_oob_raw) - chip->ecc.read_oob_raw = chip->ecc.read_oob; - if (!chip->ecc.write_oob_raw) - chip->ecc.write_oob_raw = chip->ecc.write_oob; + if (!ecc->read_oob_raw) + ecc->read_oob_raw = ecc->read_oob; + if (!ecc->write_oob_raw) + ecc->write_oob_raw = ecc->write_oob; /* * The number of bytes available for a client to place data into * the out of band area. */ - chip->ecc.layout->oobavail = 0; - for (i = 0; chip->ecc.layout->oobfree[i].length - && i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++) - chip->ecc.layout->oobavail += - chip->ecc.layout->oobfree[i].length; - mtd->oobavail = chip->ecc.layout->oobavail; + ecc->layout->oobavail = 0; + for (i = 0; ecc->layout->oobfree[i].length + && i < ARRAY_SIZE(ecc->layout->oobfree); i++) + ecc->layout->oobavail += ecc->layout->oobfree[i].length; + mtd->oobavail = ecc->layout->oobavail; /* * Set the number of read / write steps for one page depending on ECC * mode. */ - chip->ecc.steps = mtd->writesize / chip->ecc.size; - if (chip->ecc.steps * chip->ecc.size != mtd->writesize) { + ecc->steps = mtd->writesize / ecc->size; + if (ecc->steps * ecc->size != mtd->writesize) { pr_warn("Invalid ECC parameters\n"); BUG(); } - chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; + ecc->total = ecc->steps * ecc->bytes; /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ - if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && - !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) { - switch (chip->ecc.steps) { + if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) { + switch (ecc->steps) { case 2: mtd->subpage_sft = 1; break; @@ -3647,11 +3787,11 @@ int nand_scan_tail(struct mtd_info *mtd) chip->pagebuf = -1; /* Large page NAND with SOFT_ECC should support subpage reads */ - if ((chip->ecc.mode == NAND_ECC_SOFT) && (chip->page_shift > 9)) + if ((ecc->mode == NAND_ECC_SOFT) && (chip->page_shift > 9)) chip->options |= NAND_SUBPAGE_READ; /* Fill in remaining MTD driver data */ - mtd->type = MTD_NANDFLASH; + mtd->type = nand_is_slc(chip) ? MTD_NANDFLASH : MTD_MLCNANDFLASH; mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : MTD_CAP_NANDFLASH; mtd->_erase = nand_erase; @@ -3672,8 +3812,9 @@ int nand_scan_tail(struct mtd_info *mtd) mtd->writebufsize = mtd->writesize; /* propagate ecc info to mtd_info */ - mtd->ecclayout = chip->ecc.layout; - mtd->ecc_strength = chip->ecc.strength; + mtd->ecclayout = ecc->layout; + mtd->ecc_strength = ecc->strength; + mtd->ecc_step_size = ecc->size; /* * Initialize bitflip_threshold to its default prior scan_bbt() call. * scan_bbt() might invoke mtd_read(), thus bitflip_threshold must be diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 267264320e06..c0615d1526f9 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -71,6 +71,30 @@ #include <linux/export.h> #include <linux/string.h> +#define BBT_BLOCK_GOOD 0x00 +#define BBT_BLOCK_WORN 0x01 +#define BBT_BLOCK_RESERVED 0x02 +#define BBT_BLOCK_FACTORY_BAD 0x03 + +#define BBT_ENTRY_MASK 0x03 +#define BBT_ENTRY_SHIFT 2 + +static int nand_update_bbt(struct mtd_info *mtd, loff_t offs); + +static inline uint8_t bbt_get_entry(struct nand_chip *chip, int block) +{ + uint8_t entry = chip->bbt[block >> BBT_ENTRY_SHIFT]; + entry >>= (block & BBT_ENTRY_MASK) * 2; + return entry & BBT_ENTRY_MASK; +} + +static inline void bbt_mark_entry(struct nand_chip *chip, int block, + uint8_t mark) +{ + uint8_t msk = (mark & BBT_ENTRY_MASK) << ((block & BBT_ENTRY_MASK) * 2); + chip->bbt[block >> BBT_ENTRY_SHIFT] |= msk; +} + static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) { if (memcmp(buf, td->pattern, td->len)) @@ -86,33 +110,17 @@ static int check_pattern_no_oob(uint8_t *buf, struct nand_bbt_descr *td) * @td: search pattern descriptor * * Check for a pattern at the given place. Used to search bad block tables and - * good / bad block identifiers. If the SCAN_EMPTY option is set then check, if - * all bytes except the pattern area contain 0xff. + * good / bad block identifiers. */ static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) { - int end = 0; - uint8_t *p = buf; - if (td->options & NAND_BBT_NO_OOB) return check_pattern_no_oob(buf, td); - end = paglen + td->offs; - if (td->options & NAND_BBT_SCANEMPTY) - if (memchr_inv(p, 0xff, end)) - return -1; - p += end; - /* Compare the pattern */ - if (memcmp(p, td->pattern, td->len)) + if (memcmp(buf + paglen + td->offs, td->pattern, td->len)) return -1; - if (td->options & NAND_BBT_SCANEMPTY) { - p += td->len; - end += td->len; - if (memchr_inv(p, 0xff, len - end)) - return -1; - } return 0; } @@ -159,7 +167,7 @@ static u32 add_marker_len(struct nand_bbt_descr *td) * @page: the starting page * @num: the number of bbt descriptors to read * @td: the bbt describtion table - * @offs: offset in the memory table + * @offs: block number offset in the table * * Read the bad block table starting from page. */ @@ -209,14 +217,16 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, /* Analyse data */ for (i = 0; i < len; i++) { uint8_t dat = buf[i]; - for (j = 0; j < 8; j += bits, act += 2) { + for (j = 0; j < 8; j += bits, act++) { uint8_t tmp = (dat >> j) & msk; if (tmp == msk) continue; if (reserved_block_code && (tmp == reserved_block_code)) { pr_info("nand_read_bbt: reserved block at 0x%012llx\n", - (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift); - this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); + (loff_t)(offs + act) << + this->bbt_erase_shift); + bbt_mark_entry(this, offs + act, + BBT_BLOCK_RESERVED); mtd->ecc_stats.bbtblocks++; continue; } @@ -225,12 +235,15 @@ static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, * move this message to pr_debug. */ pr_info("nand_read_bbt: bad block at 0x%012llx\n", - (loff_t)((offs << 2) + (act >> 1)) << this->bbt_erase_shift); + (loff_t)(offs + act) << + this->bbt_erase_shift); /* Factory marked bad or worn out? */ if (tmp == 0) - this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); + bbt_mark_entry(this, offs + act, + BBT_BLOCK_FACTORY_BAD); else - this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06); + bbt_mark_entry(this, offs + act, + BBT_BLOCK_WORN); mtd->ecc_stats.badblocks++; } } @@ -265,7 +278,7 @@ static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc td, offs); if (res) return res; - offs += this->chipsize >> (this->bbt_erase_shift + 2); + offs += this->chipsize >> this->bbt_erase_shift; } } else { res = read_bbt(mtd, buf, td->pages[0], @@ -399,25 +412,6 @@ static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, } } -/* Scan a given block full */ -static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd, - loff_t offs, uint8_t *buf, size_t readlen, - int scanlen, int numpages) -{ - int ret, j; - - ret = scan_read_oob(mtd, buf, offs, readlen); - /* Ignore ECC errors when checking for BBM */ - if (ret && !mtd_is_bitflip_or_eccerr(ret)) - return ret; - - for (j = 0; j < numpages; j++, buf += scanlen) { - if (check_pattern(buf, scanlen, mtd->writesize, bd)) - return 1; - } - return 0; -} - /* Scan a given block partially */ static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, loff_t offs, uint8_t *buf, int numpages) @@ -464,36 +458,19 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) { struct nand_chip *this = mtd->priv; - int i, numblocks, numpages, scanlen; + int i, numblocks, numpages; int startblock; loff_t from; - size_t readlen; pr_info("Scanning device for bad blocks\n"); - if (bd->options & NAND_BBT_SCANALLPAGES) - numpages = 1 << (this->bbt_erase_shift - this->page_shift); - else if (bd->options & NAND_BBT_SCAN2NDPAGE) + if (bd->options & NAND_BBT_SCAN2NDPAGE) numpages = 2; else numpages = 1; - if (!(bd->options & NAND_BBT_SCANEMPTY)) { - /* We need only read few bytes from the OOB area */ - scanlen = 0; - readlen = bd->len; - } else { - /* Full page content should be read */ - scanlen = mtd->writesize + mtd->oobsize; - readlen = numpages * mtd->writesize; - } - if (chip == -1) { - /* - * Note that numblocks is 2 * (real numblocks) here, see i+=2 - * below as it makes shifting and masking less painful - */ - numblocks = mtd->size >> (this->bbt_erase_shift - 1); + numblocks = mtd->size >> this->bbt_erase_shift; startblock = 0; from = 0; } else { @@ -502,37 +479,31 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, chip + 1, this->numchips); return -EINVAL; } - numblocks = this->chipsize >> (this->bbt_erase_shift - 1); + numblocks = this->chipsize >> this->bbt_erase_shift; startblock = chip * numblocks; numblocks += startblock; - from = (loff_t)startblock << (this->bbt_erase_shift - 1); + from = (loff_t)startblock << this->bbt_erase_shift; } if (this->bbt_options & NAND_BBT_SCANLASTPAGE) from += mtd->erasesize - (mtd->writesize * numpages); - for (i = startblock; i < numblocks;) { + for (i = startblock; i < numblocks; i++) { int ret; BUG_ON(bd->options & NAND_BBT_NO_OOB); - if (bd->options & NAND_BBT_SCANALLPAGES) - ret = scan_block_full(mtd, bd, from, buf, readlen, - scanlen, numpages); - else - ret = scan_block_fast(mtd, bd, from, buf, numpages); - + ret = scan_block_fast(mtd, bd, from, buf, numpages); if (ret < 0) return ret; if (ret) { - this->bbt[i >> 3] |= 0x03 << (i & 0x6); + bbt_mark_entry(this, i, BBT_BLOCK_FACTORY_BAD); pr_warn("Bad eraseblock %d at 0x%012llx\n", - i >> 1, (unsigned long long)from); + i, (unsigned long long)from); mtd->ecc_stats.badblocks++; } - i += 2; from += (1 << this->bbt_erase_shift); } return 0; @@ -655,9 +626,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, { struct nand_chip *this = mtd->priv; struct erase_info einfo; - int i, j, res, chip = 0; + int i, res, chip = 0; int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; - int nrchips, bbtoffs, pageoffs, ooboffs; + int nrchips, pageoffs, ooboffs; uint8_t msk[4]; uint8_t rcode = td->reserved_block_code; size_t retlen, len = 0; @@ -713,10 +684,9 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, for (i = 0; i < td->maxblocks; i++) { int block = startblock + dir * i; /* Check, if the block is bad */ - switch ((this->bbt[block >> 2] >> - (2 * (block & 0x03))) & 0x03) { - case 0x01: - case 0x03: + switch (bbt_get_entry(this, block)) { + case BBT_BLOCK_WORN: + case BBT_BLOCK_FACTORY_BAD: continue; } page = block << @@ -748,8 +718,6 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, default: return -EINVAL; } - bbtoffs = chip * (numblocks >> 2); - to = ((loff_t)page) << this->page_shift; /* Must we save the block contents? */ @@ -814,16 +782,12 @@ static int write_bbt(struct mtd_info *mtd, uint8_t *buf, buf[ooboffs + td->veroffs] = td->version[chip]; /* Walk through the memory table */ - for (i = 0; i < numblocks;) { + for (i = 0; i < numblocks; i++) { uint8_t dat; - dat = this->bbt[bbtoffs + (i >> 2)]; - for (j = 0; j < 4; j++, i++) { - int sftcnt = (i << (3 - sft)) & sftmsk; - /* Do not store the reserved bbt blocks! */ - buf[offs + (i >> sft)] &= - ~(msk[dat & 0x03] << sftcnt); - dat >>= 2; - } + int sftcnt = (i << (3 - sft)) & sftmsk; + dat = bbt_get_entry(this, chip * numblocks + i); + /* Do not store the reserved bbt blocks! */ + buf[offs + (i >> sft)] &= ~(msk[dat] << sftcnt); } memset(&einfo, 0, sizeof(einfo)); @@ -865,7 +829,6 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b { struct nand_chip *this = mtd->priv; - bd->options &= ~NAND_BBT_SCANEMPTY; return create_bbt(mtd, this->buffers->databuf, bd, -1); } @@ -1009,7 +972,7 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) { struct nand_chip *this = mtd->priv; int i, j, chips, block, nrblocks, update; - uint8_t oldval, newval; + uint8_t oldval; /* Do we have a bbt per chip? */ if (td->options & NAND_BBT_PERCHIP) { @@ -1026,12 +989,12 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) if (td->pages[i] == -1) continue; block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); - block <<= 1; - oldval = this->bbt[(block >> 3)]; - newval = oldval | (0x2 << (block & 0x06)); - this->bbt[(block >> 3)] = newval; - if ((oldval != newval) && td->reserved_block_code) - nand_update_bbt(mtd, (loff_t)block << (this->bbt_erase_shift - 1)); + oldval = bbt_get_entry(this, block); + bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); + if ((oldval != BBT_BLOCK_RESERVED) && + td->reserved_block_code) + nand_update_bbt(mtd, (loff_t)block << + this->bbt_erase_shift); continue; } update = 0; @@ -1039,14 +1002,12 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) block = ((i + 1) * nrblocks) - td->maxblocks; else block = i * nrblocks; - block <<= 1; for (j = 0; j < td->maxblocks; j++) { - oldval = this->bbt[(block >> 3)]; - newval = oldval | (0x2 << (block & 0x06)); - this->bbt[(block >> 3)] = newval; - if (oldval != newval) + oldval = bbt_get_entry(this, block); + bbt_mark_entry(this, block, BBT_BLOCK_RESERVED); + if (oldval != BBT_BLOCK_RESERVED) update = 1; - block += 2; + block++; } /* * If we want reserved blocks to be recorded to flash, and some @@ -1054,7 +1015,8 @@ static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) * bbts. This should only happen once. */ if (update && td->reserved_block_code) - nand_update_bbt(mtd, (loff_t)(block - 2) << (this->bbt_erase_shift - 1)); + nand_update_bbt(mtd, (loff_t)(block - 1) << + this->bbt_erase_shift); } } @@ -1180,13 +1142,13 @@ int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) } /** - * nand_update_bbt - [NAND Interface] update bad block table(s) + * nand_update_bbt - update bad block table(s) * @mtd: MTD device structure * @offs: the offset of the newly marked block * * The function updates the bad block table(s). */ -int nand_update_bbt(struct mtd_info *mtd, loff_t offs) +static int nand_update_bbt(struct mtd_info *mtd, loff_t offs) { struct nand_chip *this = mtd->priv; int len, res = 0; @@ -1356,28 +1318,46 @@ int nand_default_bbt(struct mtd_info *mtd) int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) { struct nand_chip *this = mtd->priv; - int block; - uint8_t res; + int block, res; - /* Get block number * 2 */ - block = (int)(offs >> (this->bbt_erase_shift - 1)); - res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; + block = (int)(offs >> this->bbt_erase_shift); + res = bbt_get_entry(this, block); pr_debug("nand_isbad_bbt(): bbt info for offs 0x%08x: " "(block %d) 0x%02x\n", - (unsigned int)offs, block >> 1, res); + (unsigned int)offs, block, res); - switch ((int)res) { - case 0x00: + switch (res) { + case BBT_BLOCK_GOOD: return 0; - case 0x01: + case BBT_BLOCK_WORN: return 1; - case 0x02: + case BBT_BLOCK_RESERVED: return allowbbt ? 0 : 1; } return 1; } +/** + * nand_markbad_bbt - [NAND Interface] Mark a block bad in the BBT + * @mtd: MTD device structure + * @offs: offset of the bad block + */ +int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs) +{ + struct nand_chip *this = mtd->priv; + int block, ret = 0; + + block = (int)(offs >> this->bbt_erase_shift); + + /* Mark bad block in memory */ + bbt_mark_entry(this, block, BBT_BLOCK_WORN); + + /* Update flash-based bad block table */ + if (this->bbt_options & NAND_BBT_USE_FLASH) + ret = nand_update_bbt(mtd, offs); + + return ret; +} + EXPORT_SYMBOL(nand_scan_bbt); -EXPORT_SYMBOL(nand_default_bbt); -EXPORT_SYMBOL_GPL(nand_update_bbt); diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 683813a46a90..a87b0a3afa35 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -33,16 +33,16 @@ struct nand_flash_dev nand_flash_ids[] = { */ {"TC58NVG2S0F 4G 3.3V 8-bit", { .id = {0x98, 0xdc, 0x90, 0x26, 0x76, 0x15, 0x01, 0x08} }, - SZ_4K, SZ_512, SZ_256K, 0, 8, 224}, + SZ_4K, SZ_512, SZ_256K, 0, 8, 224, NAND_ECC_INFO(4, SZ_512) }, {"TC58NVG3S0F 8G 3.3V 8-bit", { .id = {0x98, 0xd3, 0x90, 0x26, 0x76, 0x15, 0x02, 0x08} }, - SZ_4K, SZ_1K, SZ_256K, 0, 8, 232}, + SZ_4K, SZ_1K, SZ_256K, 0, 8, 232, NAND_ECC_INFO(4, SZ_512) }, {"TC58NVG5D2 32G 3.3V 8-bit", { .id = {0x98, 0xd7, 0x94, 0x32, 0x76, 0x56, 0x09, 0x00} }, - SZ_8K, SZ_4K, SZ_1M, 0, 8, 640}, + SZ_8K, SZ_4K, SZ_1M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, {"TC58NVG6D2 64G 3.3V 8-bit", { .id = {0x98, 0xde, 0x94, 0x82, 0x76, 0x56, 0x04, 0x20} }, - SZ_8K, SZ_8K, SZ_2M, 0, 8, 640}, + SZ_8K, SZ_8K, SZ_2M, 0, 8, 640, NAND_ECC_INFO(40, SZ_1K) }, LEGACY_ID_NAND("NAND 4MiB 5V 8-bit", 0x6B, 4, SZ_8K, SP_OPTIONS), LEGACY_ID_NAND("NAND 4MiB 3,3V 8-bit", 0xE3, 4, SZ_8K, SP_OPTIONS), diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index cb38f3d94218..42e8a770e631 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -205,7 +205,7 @@ MODULE_PARM_DESC(bch, "Enable BCH ecc and set how many bits should " /* Calculate the page offset in flash RAM image by (row, column) address */ #define NS_RAW_OFFSET(ns) \ - (((ns)->regs.row << (ns)->geom.pgshift) + ((ns)->regs.row * (ns)->geom.oobsz) + (ns)->regs.column) + (((ns)->regs.row * (ns)->geom.pgszoob) + (ns)->regs.column) /* Calculate the OOB offset in flash RAM image by (row, column) address */ #define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz) @@ -336,7 +336,6 @@ struct nandsim { uint pgsec; /* number of pages per sector */ uint secshift; /* bits number in sector size */ uint pgshift; /* bits number in page size */ - uint oobshift; /* bits number in OOB size */ uint pgaddrbytes; /* bytes per page address */ uint secaddrbytes; /* bytes per sector address */ uint idbytes; /* the number ID bytes that this chip outputs */ @@ -363,7 +362,7 @@ struct nandsim { /* Fields needed when using a cache file */ struct file *cfile; /* Open file */ - unsigned char *pages_written; /* Which pages have been written */ + unsigned long *pages_written; /* Which pages have been written */ void *file_buf; struct page *held_pages[NS_MAX_HELD_PAGES]; int held_cnt; @@ -576,7 +575,7 @@ static int alloc_device(struct nandsim *ns) cfile = filp_open(cache_file, O_CREAT | O_RDWR | O_LARGEFILE, 0600); if (IS_ERR(cfile)) return PTR_ERR(cfile); - if (!cfile->f_op || (!cfile->f_op->read && !cfile->f_op->aio_read)) { + if (!cfile->f_op->read && !cfile->f_op->aio_read) { NS_ERR("alloc_device: cache file not readable\n"); err = -EINVAL; goto err_close; @@ -586,7 +585,8 @@ static int alloc_device(struct nandsim *ns) err = -EINVAL; goto err_close; } - ns->pages_written = vzalloc(ns->geom.pgnum); + ns->pages_written = vzalloc(BITS_TO_LONGS(ns->geom.pgnum) * + sizeof(unsigned long)); if (!ns->pages_written) { NS_ERR("alloc_device: unable to allocate pages written array\n"); err = -ENOMEM; @@ -653,9 +653,7 @@ static void free_device(struct nandsim *ns) static char *get_partition_name(int i) { - char buf[64]; - sprintf(buf, "NAND simulator partition %d", i); - return kstrdup(buf, GFP_KERNEL); + return kasprintf(GFP_KERNEL, "NAND simulator partition %d", i); } /* @@ -690,7 +688,6 @@ static int init_nandsim(struct mtd_info *mtd) ns->geom.totszoob = ns->geom.totsz + (uint64_t)ns->geom.pgnum * ns->geom.oobsz; ns->geom.secshift = ffs(ns->geom.secsz) - 1; ns->geom.pgshift = chip->page_shift; - ns->geom.oobshift = ffs(ns->geom.oobsz) - 1; ns->geom.pgsec = ns->geom.secsz / ns->geom.pgsz; ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec; ns->options = 0; @@ -761,12 +758,6 @@ static int init_nandsim(struct mtd_info *mtd) ns->nbparts += 1; } - /* Detect how many ID bytes the NAND chip outputs */ - for (i = 0; nand_flash_ids[i].name != NULL; i++) { - if (second_id_byte != nand_flash_ids[i].dev_id) - continue; - } - if (ns->busw == 16) NS_WARN("16-bit flashes support wasn't tested\n"); @@ -780,7 +771,7 @@ static int init_nandsim(struct mtd_info *mtd) printk("bus width: %u\n", ns->busw); printk("bits in sector size: %u\n", ns->geom.secshift); printk("bits in page size: %u\n", ns->geom.pgshift); - printk("bits in OOB size: %u\n", ns->geom.oobshift); + printk("bits in OOB size: %u\n", ffs(ns->geom.oobsz) - 1); printk("flash size with OOB: %llu KiB\n", (unsigned long long)ns->geom.totszoob >> 10); printk("page address bytes: %u\n", ns->geom.pgaddrbytes); @@ -1442,7 +1433,7 @@ static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns) return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off; } -int do_read_error(struct nandsim *ns, int num) +static int do_read_error(struct nandsim *ns, int num) { unsigned int page_no = ns->regs.row; @@ -1454,7 +1445,7 @@ int do_read_error(struct nandsim *ns, int num) return 0; } -void do_bit_flips(struct nandsim *ns, int num) +static void do_bit_flips(struct nandsim *ns, int num) { if (bitflips && prandom_u32() < (1 << 22)) { int flips = 1; @@ -1479,7 +1470,7 @@ static void read_page(struct nandsim *ns, int num) union ns_mem *mypage; if (ns->cfile) { - if (!ns->pages_written[ns->regs.row]) { + if (!test_bit(ns->regs.row, ns->pages_written)) { NS_DBG("read_page: page %d not written\n", ns->regs.row); memset(ns->buf.byte, 0xFF, num); } else { @@ -1490,7 +1481,7 @@ static void read_page(struct nandsim *ns, int num) ns->regs.row, ns->regs.column + ns->regs.off); if (do_read_error(ns, num)) return; - pos = (loff_t)ns->regs.row * ns->geom.pgszoob + ns->regs.column + ns->regs.off; + pos = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off; tx = read_file(ns, ns->cfile, ns->buf.byte, num, pos); if (tx != num) { NS_ERR("read_page: read error for page %d ret %ld\n", ns->regs.row, (long)tx); @@ -1525,9 +1516,9 @@ static void erase_sector(struct nandsim *ns) if (ns->cfile) { for (i = 0; i < ns->geom.pgsec; i++) - if (ns->pages_written[ns->regs.row + i]) { + if (__test_and_clear_bit(ns->regs.row + i, + ns->pages_written)) { NS_DBG("erase_sector: freeing page %d\n", ns->regs.row + i); - ns->pages_written[ns->regs.row + i] = 0; } return; } @@ -1559,8 +1550,8 @@ static int prog_page(struct nandsim *ns, int num) NS_DBG("prog_page: writing page %d\n", ns->regs.row); pg_off = ns->file_buf + ns->regs.column + ns->regs.off; - off = (loff_t)ns->regs.row * ns->geom.pgszoob + ns->regs.column + ns->regs.off; - if (!ns->pages_written[ns->regs.row]) { + off = (loff_t)NS_RAW_OFFSET(ns) + ns->regs.off; + if (!test_bit(ns->regs.row, ns->pages_written)) { all = 1; memset(ns->file_buf, 0xff, ns->geom.pgszoob); } else { @@ -1580,7 +1571,7 @@ static int prog_page(struct nandsim *ns, int num) NS_ERR("prog_page: write error for page %d ret %ld\n", ns->regs.row, (long)tx); return -1; } - ns->pages_written[ns->regs.row] = 1; + __set_bit(ns->regs.row, ns->pages_written); } else { tx = write_file(ns, ns->cfile, pg_off, num, off); if (tx != num) { @@ -2381,7 +2372,7 @@ static int __init ns_init_module(void) if ((retval = init_nandsim(nsmtd)) != 0) goto err_exit; - if ((retval = nand_default_bbt(nsmtd)) != 0) + if ((retval = chip->scan_bbt(nsmtd)) != 0) goto err_exit; if ((retval = parse_badblocks(nand, nsmtd)) != 0) diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 8e148f1478fd..69eaba690a99 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -30,6 +30,7 @@ #include <linux/mtd/ndfc.h> #include <linux/slab.h> #include <linux/mtd/mtd.h> +#include <linux/of_address.h> #include <linux/of_platform.h> #include <asm/io.h> diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c index cd6be2ed53a8..52115151e4a7 100644 --- a/drivers/mtd/nand/nuc900_nand.c +++ b/drivers/mtd/nand/nuc900_nand.c @@ -324,8 +324,6 @@ static int nuc900_nand_remove(struct platform_device *pdev) kfree(nuc900_nand); - platform_set_drvdata(pdev, NULL); - return 0; } diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 81b80af55872..f77725009907 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -25,10 +25,8 @@ #include <linux/of.h> #include <linux/of_device.h> -#ifdef CONFIG_MTD_NAND_OMAP_BCH -#include <linux/bch.h> +#include <linux/mtd/nand_bch.h> #include <linux/platform_data/elm.h> -#endif #include <linux/platform_data/mtd-nand-omap2.h> @@ -141,6 +139,8 @@ #define BCH_ECC_SIZE0 0x0 /* ecc_size0 = 0, no oob protection */ #define BCH_ECC_SIZE1 0x20 /* ecc_size1 = 32 */ +#define BADBLOCK_MARKER_LENGTH 2 + #ifdef CONFIG_MTD_NAND_OMAP_BCH static u_char bch8_vector[] = {0xf3, 0xdb, 0x14, 0x16, 0x8b, 0xd2, 0xbe, 0xcc, 0xac, 0x6b, 0xff, 0x99, 0x7b}; @@ -149,17 +149,6 @@ static u_char bch4_vector[] = {0x00, 0x6b, 0x31, 0xdd, 0x41, 0xbc, 0x10}; /* oob info generated runtime depending on ecc algorithm and layout selected */ static struct nand_ecclayout omap_oobinfo; -/* Define some generic bad / good block scan pattern which are used - * while scanning a device for factory marked good / bad blocks - */ -static uint8_t scan_ff_pattern[] = { 0xff }; -static struct nand_bbt_descr bb_descrip_flashbased = { - .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, - .offs = 0, - .len = 1, - .pattern = scan_ff_pattern, -}; - struct omap_nand_info { struct nand_hw_control controller; @@ -182,14 +171,10 @@ struct omap_nand_info { u_char *buf; int buf_len; struct gpmc_nand_regs reg; - -#ifdef CONFIG_MTD_NAND_OMAP_BCH - struct bch_control *bch; - struct nand_ecclayout ecclayout; + /* fields specific for BCHx_HW ECC scheme */ bool is_elm_used; struct device *elm_dev; struct device_node *of_node; -#endif }; /** @@ -1058,8 +1043,7 @@ static int omap_dev_ready(struct mtd_info *mtd) } } -#ifdef CONFIG_MTD_NAND_OMAP_BCH - +#if defined(CONFIG_MTD_NAND_ECC_BCH) || defined(CONFIG_MTD_NAND_OMAP_BCH) /** * omap3_enable_hwecc_bch - Program OMAP3 GPMC to perform BCH ECC correction * @mtd: MTD device structure @@ -1140,7 +1124,9 @@ static void omap3_enable_hwecc_bch(struct mtd_info *mtd, int mode) /* Clear ecc and enable bits */ writel(ECCCLEAR | ECC1, info->reg.gpmc_ecc_control); } +#endif +#ifdef CONFIG_MTD_NAND_ECC_BCH /** * omap3_calculate_ecc_bch4 - Generate 7 bytes of ECC bytes * @mtd: MTD device structure @@ -1225,7 +1211,9 @@ static int omap3_calculate_ecc_bch8(struct mtd_info *mtd, const u_char *dat, return 0; } +#endif /* CONFIG_MTD_NAND_ECC_BCH */ +#ifdef CONFIG_MTD_NAND_OMAP_BCH /** * omap3_calculate_ecc_bch - Generate bytes of ECC bytes * @mtd: MTD device structure @@ -1519,38 +1507,6 @@ static int omap_elm_correct_data(struct mtd_info *mtd, u_char *data, } /** - * omap3_correct_data_bch - Decode received data and correct errors - * @mtd: MTD device structure - * @data: page data - * @read_ecc: ecc read from nand flash - * @calc_ecc: ecc read from HW ECC registers - */ -static int omap3_correct_data_bch(struct mtd_info *mtd, u_char *data, - u_char *read_ecc, u_char *calc_ecc) -{ - int i, count; - /* cannot correct more than 8 errors */ - unsigned int errloc[8]; - struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, - mtd); - - count = decode_bch(info->bch, NULL, 512, read_ecc, calc_ecc, NULL, - errloc); - if (count > 0) { - /* correct errors */ - for (i = 0; i < count; i++) { - /* correct data only, not ecc bytes */ - if (errloc[i] < 8*512) - data[errloc[i]/8] ^= 1 << (errloc[i] & 7); - pr_debug("corrected bitflip %u\n", errloc[i]); - } - } else if (count < 0) { - pr_err("ecc unrecoverable error\n"); - } - return count; -} - -/** * omap_write_page_bch - BCH ecc based write page function for entire page * @mtd: mtd info structure * @chip: nand chip info structure @@ -1637,207 +1593,57 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, } /** - * omap3_free_bch - Release BCH ecc resources - * @mtd: MTD device structure - */ -static void omap3_free_bch(struct mtd_info *mtd) -{ - struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, - mtd); - if (info->bch) { - free_bch(info->bch); - info->bch = NULL; - } -} - -/** - * omap3_init_bch - Initialize BCH ECC - * @mtd: MTD device structure - * @ecc_opt: OMAP ECC mode (OMAP_ECC_BCH4_CODE_HW or OMAP_ECC_BCH8_CODE_HW) - */ -static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt) -{ - int max_errors; - struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, - mtd); -#ifdef CONFIG_MTD_NAND_OMAP_BCH8 - const int hw_errors = BCH8_MAX_ERROR; -#else - const int hw_errors = BCH4_MAX_ERROR; -#endif - enum bch_ecc bch_type; - const __be32 *parp; - int lenp; - struct device_node *elm_node; - - info->bch = NULL; - - max_errors = (ecc_opt == OMAP_ECC_BCH8_CODE_HW) ? - BCH8_MAX_ERROR : BCH4_MAX_ERROR; - if (max_errors != hw_errors) { - pr_err("cannot configure %d-bit BCH ecc, only %d-bit supported", - max_errors, hw_errors); - goto fail; - } - - info->nand.ecc.size = 512; - info->nand.ecc.hwctl = omap3_enable_hwecc_bch; - info->nand.ecc.mode = NAND_ECC_HW; - info->nand.ecc.strength = max_errors; - - if (hw_errors == BCH8_MAX_ERROR) - bch_type = BCH8_ECC; - else - bch_type = BCH4_ECC; - - /* Detect availability of ELM module */ - parp = of_get_property(info->of_node, "elm_id", &lenp); - if ((parp == NULL) && (lenp != (sizeof(void *) * 2))) { - pr_err("Missing elm_id property, fall back to Software BCH\n"); - info->is_elm_used = false; - } else { - struct platform_device *pdev; - - elm_node = of_find_node_by_phandle(be32_to_cpup(parp)); - pdev = of_find_device_by_node(elm_node); - info->elm_dev = &pdev->dev; - - if (elm_config(info->elm_dev, bch_type) == 0) - info->is_elm_used = true; - } - - if (info->is_elm_used && (mtd->writesize <= 4096)) { - - if (hw_errors == BCH8_MAX_ERROR) - info->nand.ecc.bytes = BCH8_SIZE; - else - info->nand.ecc.bytes = BCH4_SIZE; - - info->nand.ecc.correct = omap_elm_correct_data; - info->nand.ecc.calculate = omap3_calculate_ecc_bch; - info->nand.ecc.read_page = omap_read_page_bch; - info->nand.ecc.write_page = omap_write_page_bch; - } else { - /* - * software bch library is only used to detect and - * locate errors - */ - info->bch = init_bch(13, max_errors, - 0x201b /* hw polynomial */); - if (!info->bch) - goto fail; - - info->nand.ecc.correct = omap3_correct_data_bch; - - /* - * The number of corrected errors in an ecc block that will - * trigger block scrubbing defaults to the ecc strength (4 or 8) - * Set mtd->bitflip_threshold here to define a custom threshold. - */ - - if (max_errors == 8) { - info->nand.ecc.bytes = 13; - info->nand.ecc.calculate = omap3_calculate_ecc_bch8; - } else { - info->nand.ecc.bytes = 7; - info->nand.ecc.calculate = omap3_calculate_ecc_bch4; - } - } - - pr_info("enabling NAND BCH ecc with %d-bit correction\n", max_errors); - return 0; -fail: - omap3_free_bch(mtd); - return -1; -} - -/** - * omap3_init_bch_tail - Build an oob layout for BCH ECC correction. - * @mtd: MTD device structure + * is_elm_present - checks for presence of ELM module by scanning DT nodes + * @omap_nand_info: NAND device structure containing platform data + * @bch_type: 0x0=BCH4, 0x1=BCH8, 0x2=BCH16 */ -static int omap3_init_bch_tail(struct mtd_info *mtd) +static int is_elm_present(struct omap_nand_info *info, + struct device_node *elm_node, enum bch_ecc bch_type) { - int i, steps, offset; - struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, - mtd); - struct nand_ecclayout *layout = &info->ecclayout; - - /* build oob layout */ - steps = mtd->writesize/info->nand.ecc.size; - layout->eccbytes = steps*info->nand.ecc.bytes; - - /* do not bother creating special oob layouts for small page devices */ - if (mtd->oobsize < 64) { - pr_err("BCH ecc is not supported on small page devices\n"); - goto fail; + struct platform_device *pdev; + info->is_elm_used = false; + /* check whether elm-id is passed via DT */ + if (!elm_node) { + pr_err("nand: error: ELM DT node not found\n"); + return -ENODEV; } - - /* reserve 2 bytes for bad block marker */ - if (layout->eccbytes+2 > mtd->oobsize) { - pr_err("no oob layout available for oobsize %d eccbytes %u\n", - mtd->oobsize, layout->eccbytes); - goto fail; + pdev = of_find_device_by_node(elm_node); + /* check whether ELM device is registered */ + if (!pdev) { + pr_err("nand: error: ELM device not found\n"); + return -ENODEV; } - - /* ECC layout compatible with RBL for BCH8 */ - if (info->is_elm_used && (info->nand.ecc.bytes == BCH8_SIZE)) - offset = 2; - else - offset = mtd->oobsize - layout->eccbytes; - - /* put ecc bytes at oob tail */ - for (i = 0; i < layout->eccbytes; i++) - layout->eccpos[i] = offset + i; - - if (info->is_elm_used && (info->nand.ecc.bytes == BCH8_SIZE)) - layout->oobfree[0].offset = 2 + layout->eccbytes * steps; - else - layout->oobfree[0].offset = 2; - - layout->oobfree[0].length = mtd->oobsize-2-layout->eccbytes; - info->nand.ecc.layout = layout; - - if (!(info->nand.options & NAND_BUSWIDTH_16)) - info->nand.badblock_pattern = &bb_descrip_flashbased; + /* ELM module available, now configure it */ + info->elm_dev = &pdev->dev; + if (elm_config(info->elm_dev, bch_type)) + return -ENODEV; + info->is_elm_used = true; return 0; -fail: - omap3_free_bch(mtd); - return -1; -} - -#else -static int omap3_init_bch(struct mtd_info *mtd, int ecc_opt) -{ - pr_err("CONFIG_MTD_NAND_OMAP_BCH is not enabled\n"); - return -1; -} -static int omap3_init_bch_tail(struct mtd_info *mtd) -{ - return -1; -} -static void omap3_free_bch(struct mtd_info *mtd) -{ } -#endif /* CONFIG_MTD_NAND_OMAP_BCH */ +#endif /* CONFIG_MTD_NAND_ECC_BCH */ static int omap_nand_probe(struct platform_device *pdev) { struct omap_nand_info *info; struct omap_nand_platform_data *pdata; + struct mtd_info *mtd; + struct nand_chip *nand_chip; + struct nand_ecclayout *ecclayout; int err; - int i, offset; - dma_cap_mask_t mask; - unsigned sig; + int i; + dma_cap_mask_t mask; + unsigned sig; struct resource *res; struct mtd_part_parser_data ppdata = {}; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (pdata == NULL) { dev_err(&pdev->dev, "platform data missing\n"); return -ENODEV; } - info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL); + info = devm_kzalloc(&pdev->dev, sizeof(struct omap_nand_info), + GFP_KERNEL); if (!info) return -ENOMEM; @@ -1846,47 +1652,45 @@ static int omap_nand_probe(struct platform_device *pdev) spin_lock_init(&info->controller.lock); init_waitqueue_head(&info->controller.wq); - info->pdev = pdev; - + info->pdev = pdev; info->gpmc_cs = pdata->cs; info->reg = pdata->reg; - - info->mtd.priv = &info->nand; - info->mtd.name = dev_name(&pdev->dev); - info->mtd.owner = THIS_MODULE; - - info->nand.options = pdata->devsize; - info->nand.options |= NAND_SKIP_BBTSCAN; -#ifdef CONFIG_MTD_NAND_OMAP_BCH info->of_node = pdata->of_node; -#endif + mtd = &info->mtd; + mtd->priv = &info->nand; + mtd->name = dev_name(&pdev->dev); + mtd->owner = THIS_MODULE; + nand_chip = &info->nand; + nand_chip->ecc.priv = NULL; + nand_chip->options |= NAND_SKIP_BBTSCAN; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) { err = -EINVAL; dev_err(&pdev->dev, "error getting memory resource\n"); - goto out_free_info; + goto return_error; } info->phys_base = res->start; info->mem_size = resource_size(res); - if (!request_mem_region(info->phys_base, info->mem_size, - pdev->dev.driver->name)) { + if (!devm_request_mem_region(&pdev->dev, info->phys_base, + info->mem_size, pdev->dev.driver->name)) { err = -EBUSY; - goto out_free_info; + goto return_error; } - info->nand.IO_ADDR_R = ioremap(info->phys_base, info->mem_size); - if (!info->nand.IO_ADDR_R) { + nand_chip->IO_ADDR_R = devm_ioremap(&pdev->dev, info->phys_base, + info->mem_size); + if (!nand_chip->IO_ADDR_R) { err = -ENOMEM; - goto out_release_mem_region; + goto return_error; } - info->nand.controller = &info->controller; + nand_chip->controller = &info->controller; - info->nand.IO_ADDR_W = info->nand.IO_ADDR_R; - info->nand.cmd_ctrl = omap_hwcontrol; + nand_chip->IO_ADDR_W = nand_chip->IO_ADDR_R; + nand_chip->cmd_ctrl = omap_hwcontrol; /* * If RDY/BSY line is connected to OMAP then use the omap ready @@ -1896,26 +1700,42 @@ static int omap_nand_probe(struct platform_device *pdev) * device and read status register until you get a failure or success */ if (pdata->dev_ready) { - info->nand.dev_ready = omap_dev_ready; - info->nand.chip_delay = 0; + nand_chip->dev_ready = omap_dev_ready; + nand_chip->chip_delay = 0; } else { - info->nand.waitfunc = omap_wait; - info->nand.chip_delay = 50; + nand_chip->waitfunc = omap_wait; + nand_chip->chip_delay = 50; } + /* scan NAND device connected to chip controller */ + nand_chip->options |= pdata->devsize & NAND_BUSWIDTH_16; + if (nand_scan_ident(mtd, 1, NULL)) { + pr_err("nand device scan failed, may be bus-width mismatch\n"); + err = -ENXIO; + goto return_error; + } + + /* check for small page devices */ + if ((mtd->oobsize < 64) && (pdata->ecc_opt != OMAP_ECC_HAM1_CODE_HW)) { + pr_err("small page devices are not supported\n"); + err = -EINVAL; + goto return_error; + } + + /* re-populate low-level callbacks based on xfer modes */ switch (pdata->xfer_type) { case NAND_OMAP_PREFETCH_POLLED: - info->nand.read_buf = omap_read_buf_pref; - info->nand.write_buf = omap_write_buf_pref; + nand_chip->read_buf = omap_read_buf_pref; + nand_chip->write_buf = omap_write_buf_pref; break; case NAND_OMAP_POLLED: - if (info->nand.options & NAND_BUSWIDTH_16) { - info->nand.read_buf = omap_read_buf16; - info->nand.write_buf = omap_write_buf16; + if (nand_chip->options & NAND_BUSWIDTH_16) { + nand_chip->read_buf = omap_read_buf16; + nand_chip->write_buf = omap_write_buf16; } else { - info->nand.read_buf = omap_read_buf8; - info->nand.write_buf = omap_write_buf8; + nand_chip->read_buf = omap_read_buf8; + nand_chip->write_buf = omap_write_buf8; } break; @@ -1927,7 +1747,7 @@ static int omap_nand_probe(struct platform_device *pdev) if (!info->dma) { dev_err(&pdev->dev, "DMA engine request failed\n"); err = -ENXIO; - goto out_release_mem_region; + goto return_error; } else { struct dma_slave_config cfg; @@ -1942,10 +1762,10 @@ static int omap_nand_probe(struct platform_device *pdev) if (err) { dev_err(&pdev->dev, "DMA engine slave config failed: %d\n", err); - goto out_release_mem_region; + goto return_error; } - info->nand.read_buf = omap_read_buf_dma_pref; - info->nand.write_buf = omap_write_buf_dma_pref; + nand_chip->read_buf = omap_read_buf_dma_pref; + nand_chip->write_buf = omap_write_buf_dma_pref; } break; @@ -1954,34 +1774,36 @@ static int omap_nand_probe(struct platform_device *pdev) if (info->gpmc_irq_fifo <= 0) { dev_err(&pdev->dev, "error getting fifo irq\n"); err = -ENODEV; - goto out_release_mem_region; + goto return_error; } - err = request_irq(info->gpmc_irq_fifo, omap_nand_irq, - IRQF_SHARED, "gpmc-nand-fifo", info); + err = devm_request_irq(&pdev->dev, info->gpmc_irq_fifo, + omap_nand_irq, IRQF_SHARED, + "gpmc-nand-fifo", info); if (err) { dev_err(&pdev->dev, "requesting irq(%d) error:%d", info->gpmc_irq_fifo, err); info->gpmc_irq_fifo = 0; - goto out_release_mem_region; + goto return_error; } info->gpmc_irq_count = platform_get_irq(pdev, 1); if (info->gpmc_irq_count <= 0) { dev_err(&pdev->dev, "error getting count irq\n"); err = -ENODEV; - goto out_release_mem_region; + goto return_error; } - err = request_irq(info->gpmc_irq_count, omap_nand_irq, - IRQF_SHARED, "gpmc-nand-count", info); + err = devm_request_irq(&pdev->dev, info->gpmc_irq_count, + omap_nand_irq, IRQF_SHARED, + "gpmc-nand-count", info); if (err) { dev_err(&pdev->dev, "requesting irq(%d) error:%d", info->gpmc_irq_count, err); info->gpmc_irq_count = 0; - goto out_release_mem_region; + goto return_error; } - info->nand.read_buf = omap_read_buf_irq_pref; - info->nand.write_buf = omap_write_buf_irq_pref; + nand_chip->read_buf = omap_read_buf_irq_pref; + nand_chip->write_buf = omap_write_buf_irq_pref; break; @@ -1989,118 +1811,223 @@ static int omap_nand_probe(struct platform_device *pdev) dev_err(&pdev->dev, "xfer_type(%d) not supported!\n", pdata->xfer_type); err = -EINVAL; - goto out_release_mem_region; + goto return_error; } - /* select the ecc type */ - if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_DEFAULT) - info->nand.ecc.mode = NAND_ECC_SOFT; - else if ((pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW) || - (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE)) { - info->nand.ecc.bytes = 3; - info->nand.ecc.size = 512; - info->nand.ecc.strength = 1; - info->nand.ecc.calculate = omap_calculate_ecc; - info->nand.ecc.hwctl = omap_enable_hwecc; - info->nand.ecc.correct = omap_correct_data; - info->nand.ecc.mode = NAND_ECC_HW; - } else if ((pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) || - (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW)) { - err = omap3_init_bch(&info->mtd, pdata->ecc_opt); - if (err) { + /* populate MTD interface based on ECC scheme */ + nand_chip->ecc.layout = &omap_oobinfo; + ecclayout = &omap_oobinfo; + switch (pdata->ecc_opt) { + case OMAP_ECC_HAM1_CODE_HW: + pr_info("nand: using OMAP_ECC_HAM1_CODE_HW\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.bytes = 3; + nand_chip->ecc.size = 512; + nand_chip->ecc.strength = 1; + nand_chip->ecc.calculate = omap_calculate_ecc; + nand_chip->ecc.hwctl = omap_enable_hwecc; + nand_chip->ecc.correct = omap_correct_data; + /* define ECC layout */ + ecclayout->eccbytes = nand_chip->ecc.bytes * + (mtd->writesize / + nand_chip->ecc.size); + if (nand_chip->options & NAND_BUSWIDTH_16) + ecclayout->eccpos[0] = BADBLOCK_MARKER_LENGTH; + else + ecclayout->eccpos[0] = 1; + ecclayout->oobfree->offset = ecclayout->eccpos[0] + + ecclayout->eccbytes; + break; + + case OMAP_ECC_BCH4_CODE_HW_DETECTION_SW: +#ifdef CONFIG_MTD_NAND_ECC_BCH + pr_info("nand: using OMAP_ECC_BCH4_CODE_HW_DETECTION_SW\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + nand_chip->ecc.bytes = 7; + nand_chip->ecc.strength = 4; + nand_chip->ecc.hwctl = omap3_enable_hwecc_bch; + nand_chip->ecc.correct = nand_bch_correct_data; + nand_chip->ecc.calculate = omap3_calculate_ecc_bch4; + /* define ECC layout */ + ecclayout->eccbytes = nand_chip->ecc.bytes * + (mtd->writesize / + nand_chip->ecc.size); + ecclayout->eccpos[0] = BADBLOCK_MARKER_LENGTH; + ecclayout->oobfree->offset = ecclayout->eccpos[0] + + ecclayout->eccbytes; + /* software bch library is used for locating errors */ + nand_chip->ecc.priv = nand_bch_init(mtd, + nand_chip->ecc.size, + nand_chip->ecc.bytes, + &nand_chip->ecc.layout); + if (!nand_chip->ecc.priv) { + pr_err("nand: error: unable to use s/w BCH library\n"); err = -EINVAL; - goto out_release_mem_region; } - } + break; +#else + pr_err("nand: error: CONFIG_MTD_NAND_ECC_BCH not enabled\n"); + err = -EINVAL; + goto return_error; +#endif - /* DIP switches on some boards change between 8 and 16 bit - * bus widths for flash. Try the other width if the first try fails. - */ - if (nand_scan_ident(&info->mtd, 1, NULL)) { - info->nand.options ^= NAND_BUSWIDTH_16; - if (nand_scan_ident(&info->mtd, 1, NULL)) { - err = -ENXIO; - goto out_release_mem_region; + case OMAP_ECC_BCH4_CODE_HW: +#ifdef CONFIG_MTD_NAND_OMAP_BCH + pr_info("nand: using OMAP_ECC_BCH4_CODE_HW ECC scheme\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + /* 14th bit is kept reserved for ROM-code compatibility */ + nand_chip->ecc.bytes = 7 + 1; + nand_chip->ecc.strength = 4; + nand_chip->ecc.hwctl = omap3_enable_hwecc_bch; + nand_chip->ecc.correct = omap_elm_correct_data; + nand_chip->ecc.calculate = omap3_calculate_ecc_bch; + nand_chip->ecc.read_page = omap_read_page_bch; + nand_chip->ecc.write_page = omap_write_page_bch; + /* define ECC layout */ + ecclayout->eccbytes = nand_chip->ecc.bytes * + (mtd->writesize / + nand_chip->ecc.size); + ecclayout->eccpos[0] = BADBLOCK_MARKER_LENGTH; + ecclayout->oobfree->offset = ecclayout->eccpos[0] + + ecclayout->eccbytes; + /* This ECC scheme requires ELM H/W block */ + if (is_elm_present(info, pdata->elm_of_node, BCH4_ECC) < 0) { + pr_err("nand: error: could not initialize ELM\n"); + err = -ENODEV; + goto return_error; } - } - - /* rom code layout */ - if (pdata->ecc_opt == OMAP_ECC_HAMMING_CODE_HW_ROMCODE) { + break; +#else + pr_err("nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled\n"); + err = -EINVAL; + goto return_error; +#endif - if (info->nand.options & NAND_BUSWIDTH_16) - offset = 2; - else { - offset = 1; - info->nand.badblock_pattern = &bb_descrip_flashbased; - } - omap_oobinfo.eccbytes = 3 * (info->mtd.oobsize/16); - for (i = 0; i < omap_oobinfo.eccbytes; i++) - omap_oobinfo.eccpos[i] = i+offset; - - omap_oobinfo.oobfree->offset = offset + omap_oobinfo.eccbytes; - omap_oobinfo.oobfree->length = info->mtd.oobsize - - (offset + omap_oobinfo.eccbytes); - - info->nand.ecc.layout = &omap_oobinfo; - } else if ((pdata->ecc_opt == OMAP_ECC_BCH4_CODE_HW) || - (pdata->ecc_opt == OMAP_ECC_BCH8_CODE_HW)) { - /* build OOB layout for BCH ECC correction */ - err = omap3_init_bch_tail(&info->mtd); - if (err) { + case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW: +#ifdef CONFIG_MTD_NAND_ECC_BCH + pr_info("nand: using OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + nand_chip->ecc.bytes = 13; + nand_chip->ecc.strength = 8; + nand_chip->ecc.hwctl = omap3_enable_hwecc_bch; + nand_chip->ecc.correct = nand_bch_correct_data; + nand_chip->ecc.calculate = omap3_calculate_ecc_bch8; + /* define ECC layout */ + ecclayout->eccbytes = nand_chip->ecc.bytes * + (mtd->writesize / + nand_chip->ecc.size); + ecclayout->eccpos[0] = BADBLOCK_MARKER_LENGTH; + ecclayout->oobfree->offset = ecclayout->eccpos[0] + + ecclayout->eccbytes; + /* software bch library is used for locating errors */ + nand_chip->ecc.priv = nand_bch_init(mtd, + nand_chip->ecc.size, + nand_chip->ecc.bytes, + &nand_chip->ecc.layout); + if (!nand_chip->ecc.priv) { + pr_err("nand: error: unable to use s/w BCH library\n"); err = -EINVAL; - goto out_release_mem_region; + goto return_error; + } + break; +#else + pr_err("nand: error: CONFIG_MTD_NAND_ECC_BCH not enabled\n"); + err = -EINVAL; + goto return_error; +#endif + + case OMAP_ECC_BCH8_CODE_HW: +#ifdef CONFIG_MTD_NAND_OMAP_BCH + pr_info("nand: using OMAP_ECC_BCH8_CODE_HW ECC scheme\n"); + nand_chip->ecc.mode = NAND_ECC_HW; + nand_chip->ecc.size = 512; + /* 14th bit is kept reserved for ROM-code compatibility */ + nand_chip->ecc.bytes = 13 + 1; + nand_chip->ecc.strength = 8; + nand_chip->ecc.hwctl = omap3_enable_hwecc_bch; + nand_chip->ecc.correct = omap_elm_correct_data; + nand_chip->ecc.calculate = omap3_calculate_ecc_bch; + nand_chip->ecc.read_page = omap_read_page_bch; + nand_chip->ecc.write_page = omap_write_page_bch; + /* This ECC scheme requires ELM H/W block */ + err = is_elm_present(info, pdata->elm_of_node, BCH8_ECC); + if (err < 0) { + pr_err("nand: error: could not initialize ELM\n"); + goto return_error; } + /* define ECC layout */ + ecclayout->eccbytes = nand_chip->ecc.bytes * + (mtd->writesize / + nand_chip->ecc.size); + ecclayout->eccpos[0] = BADBLOCK_MARKER_LENGTH; + ecclayout->oobfree->offset = ecclayout->eccpos[0] + + ecclayout->eccbytes; + break; +#else + pr_err("nand: error: CONFIG_MTD_NAND_OMAP_BCH not enabled\n"); + err = -EINVAL; + goto return_error; +#endif + + default: + pr_err("nand: error: invalid or unsupported ECC scheme\n"); + err = -EINVAL; + goto return_error; + } + + /* populate remaining ECC layout data */ + ecclayout->oobfree->length = mtd->oobsize - (BADBLOCK_MARKER_LENGTH + + ecclayout->eccbytes); + for (i = 1; i < ecclayout->eccbytes; i++) + ecclayout->eccpos[i] = ecclayout->eccpos[0] + i; + /* check if NAND device's OOB is enough to store ECC signatures */ + if (mtd->oobsize < (ecclayout->eccbytes + BADBLOCK_MARKER_LENGTH)) { + pr_err("not enough OOB bytes required = %d, available=%d\n", + ecclayout->eccbytes, mtd->oobsize); + err = -EINVAL; + goto return_error; } /* second phase scan */ - if (nand_scan_tail(&info->mtd)) { + if (nand_scan_tail(mtd)) { err = -ENXIO; - goto out_release_mem_region; + goto return_error; } ppdata.of_node = pdata->of_node; - mtd_device_parse_register(&info->mtd, NULL, &ppdata, pdata->parts, + mtd_device_parse_register(mtd, NULL, &ppdata, pdata->parts, pdata->nr_parts); - platform_set_drvdata(pdev, &info->mtd); + platform_set_drvdata(pdev, mtd); return 0; -out_release_mem_region: +return_error: if (info->dma) dma_release_channel(info->dma); - if (info->gpmc_irq_count > 0) - free_irq(info->gpmc_irq_count, info); - if (info->gpmc_irq_fifo > 0) - free_irq(info->gpmc_irq_fifo, info); - release_mem_region(info->phys_base, info->mem_size); -out_free_info: - kfree(info); - + if (nand_chip->ecc.priv) { + nand_bch_free(nand_chip->ecc.priv); + nand_chip->ecc.priv = NULL; + } return err; } static int omap_nand_remove(struct platform_device *pdev) { struct mtd_info *mtd = platform_get_drvdata(pdev); + struct nand_chip *nand_chip = mtd->priv; struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); - omap3_free_bch(&info->mtd); - - platform_set_drvdata(pdev, NULL); + if (nand_chip->ecc.priv) { + nand_bch_free(nand_chip->ecc.priv); + nand_chip->ecc.priv = NULL; + } if (info->dma) dma_release_channel(info->dma); - - if (info->gpmc_irq_count > 0) - free_irq(info->gpmc_irq_count, info); - if (info->gpmc_irq_fifo > 0) - free_irq(info->gpmc_irq_fifo, info); - - /* Release NAND device, its internal structures and partitions */ - nand_release(&info->mtd); - iounmap(info->nand.IO_ADDR_R); - release_mem_region(info->phys_base, info->mem_size); - kfree(info); + nand_release(mtd); return 0; } diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index 8fbd00208610..a393a5b6ce1e 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -130,8 +130,9 @@ static int __init orion_nand_probe(struct platform_device *pdev) if (!of_property_read_u32(pdev->dev.of_node, "chip-delay", &val)) board->chip_delay = (u8)val; - } else - board = pdev->dev.platform_data; + } else { + board = dev_get_platdata(&pdev->dev); + } mtd->priv = nc; mtd->owner = THIS_MODULE; @@ -186,7 +187,6 @@ no_dev: clk_disable_unprepare(clk); clk_put(clk); } - platform_set_drvdata(pdev, NULL); iounmap(io_base); no_res: kfree(nc); diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c index 5a67082c07ee..4d174366a0f0 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/pasemi_nand.c @@ -28,6 +28,8 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/pci.h> diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index c004566a9ad2..cad4cdc9df39 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -30,7 +30,7 @@ static const char *part_probe_types[] = { "cmdlinepart", NULL }; */ static int plat_nand_probe(struct platform_device *pdev) { - struct platform_nand_data *pdata = pdev->dev.platform_data; + struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); struct mtd_part_parser_data ppdata; struct plat_nand_data *data; struct resource *res; @@ -122,7 +122,6 @@ static int plat_nand_probe(struct platform_device *pdev) out: if (pdata->ctrl.remove) pdata->ctrl.remove(pdev); - platform_set_drvdata(pdev, NULL); iounmap(data->io_base); out_release_io: release_mem_region(res->start, resource_size(res)); @@ -137,7 +136,7 @@ out_free: static int plat_nand_remove(struct platform_device *pdev) { struct plat_nand_data *data = platform_get_drvdata(pdev); - struct platform_nand_data *pdata = pdev->dev.platform_data; + struct platform_nand_data *pdata = dev_get_platdata(&pdev->dev); struct resource *res; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index dec80ca6a5ce..4cabdc9fda90 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -25,13 +25,27 @@ #include <linux/of.h> #include <linux/of_device.h> +#if defined(CONFIG_ARCH_PXA) || defined(CONFIG_ARCH_MMP) +#define ARCH_HAS_DMA +#endif + +#ifdef ARCH_HAS_DMA #include <mach/dma.h> +#endif + #include <linux/platform_data/mtd-nand-pxa3xx.h> #define CHIP_DELAY_TIMEOUT (2 * HZ/10) #define NAND_STOP_DELAY (2 * HZ/50) #define PAGE_CHUNK_SIZE (2048) +/* + * Define a buffer size for the initial command that detects the flash device: + * STATUS, READID and PARAM. The largest of these is the PARAM command, + * needing 256 bytes. + */ +#define INIT_BUFFER_SIZE 256 + /* registers and bit definitions */ #define NDCR (0x00) /* Control register */ #define NDTR0CS0 (0x04) /* Timing Parameter 0 for CS0 */ @@ -80,6 +94,7 @@ #define NDSR_RDDREQ (0x1 << 1) #define NDSR_WRCMDREQ (0x1) +#define NDCB0_LEN_OVRD (0x1 << 28) #define NDCB0_ST_ROW_EN (0x1 << 26) #define NDCB0_AUTO_RS (0x1 << 25) #define NDCB0_CSEL (0x1 << 24) @@ -123,9 +138,13 @@ enum { STATE_READY, }; +enum pxa3xx_nand_variant { + PXA3XX_NAND_VARIANT_PXA, + PXA3XX_NAND_VARIANT_ARMADA370, +}; + struct pxa3xx_nand_host { struct nand_chip chip; - struct pxa3xx_nand_cmdset *cmdset; struct mtd_info *mtd; void *info_data; @@ -139,10 +158,6 @@ struct pxa3xx_nand_host { unsigned int row_addr_cycles; size_t read_id_bytes; - /* cached register value */ - uint32_t reg_ndcr; - uint32_t ndtr0cs0; - uint32_t ndtr1cs0; }; struct pxa3xx_nand_info { @@ -156,6 +171,7 @@ struct pxa3xx_nand_info { unsigned int buf_start; unsigned int buf_count; + unsigned int buf_size; /* DMA information */ int drcmr_dat; @@ -171,9 +187,16 @@ struct pxa3xx_nand_info { struct pxa3xx_nand_host *host[NUM_CHIP_SELECT]; unsigned int state; + /* + * This driver supports NFCv1 (as found in PXA SoC) + * and NFCv2 (as found in Armada 370/XP SoC). + */ + enum pxa3xx_nand_variant variant; + int cs; int use_ecc; /* use HW ECC ? */ int use_dma; /* use DMA ? */ + int use_spare; /* use spare ? */ int is_ready; unsigned int page_size; /* page size of attached chip */ @@ -181,33 +204,22 @@ struct pxa3xx_nand_info { unsigned int oob_size; int retcode; + /* cached register value */ + uint32_t reg_ndcr; + uint32_t ndtr0cs0; + uint32_t ndtr1cs0; + /* generated NDCBx register values */ uint32_t ndcb0; uint32_t ndcb1; uint32_t ndcb2; + uint32_t ndcb3; }; static bool use_dma = 1; module_param(use_dma, bool, 0444); MODULE_PARM_DESC(use_dma, "enable DMA for data transferring to/from NAND HW"); -/* - * Default NAND flash controller configuration setup by the - * bootloader. This configuration is used only when pdata->keep_config is set - */ -static struct pxa3xx_nand_cmdset default_cmdset = { - .read1 = 0x3000, - .read2 = 0x0050, - .program = 0x1080, - .read_status = 0x0070, - .read_id = 0x0090, - .erase = 0xD060, - .reset = 0x00FF, - .lock = 0x002A, - .unlock = 0x2423, - .lock_status = 0x007A, -}; - static struct pxa3xx_nand_timing timing[] = { { 40, 80, 60, 100, 80, 100, 90000, 400, 40, }, { 10, 0, 20, 40, 30, 40, 11123, 110, 10, }, @@ -230,8 +242,6 @@ static struct pxa3xx_nand_flash builtin_flash_types[] = { /* Define a default flash type setting serve as flash detecting only */ #define DEFAULT_FLASH_TYPE (&builtin_flash_types[0]) -const char *mtd_names[] = {"pxa3xx_nand-0", "pxa3xx_nand-1", NULL}; - #define NDTR0_tCH(c) (min((c), 7) << 19) #define NDTR0_tCS(c) (min((c), 7) << 16) #define NDTR0_tWH(c) (min((c), 7) << 11) @@ -264,8 +274,8 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host, NDTR1_tWHR(ns2cycle(t->tWHR, nand_clk)) | NDTR1_tAR(ns2cycle(t->tAR, nand_clk)); - host->ndtr0cs0 = ndtr0; - host->ndtr1cs0 = ndtr1; + info->ndtr0cs0 = ndtr0; + info->ndtr1cs0 = ndtr1; nand_writel(info, NDTR0CS0, ndtr0); nand_writel(info, NDTR1CS0, ndtr1); } @@ -273,7 +283,7 @@ static void pxa3xx_nand_set_timing(struct pxa3xx_nand_host *host, static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info) { struct pxa3xx_nand_host *host = info->host[info->cs]; - int oob_enable = host->reg_ndcr & NDCR_SPARE_EN; + int oob_enable = info->reg_ndcr & NDCR_SPARE_EN; info->data_size = host->page_size; if (!oob_enable) { @@ -299,12 +309,25 @@ static void pxa3xx_set_datasize(struct pxa3xx_nand_info *info) */ static void pxa3xx_nand_start(struct pxa3xx_nand_info *info) { - struct pxa3xx_nand_host *host = info->host[info->cs]; uint32_t ndcr; - ndcr = host->reg_ndcr; - ndcr |= info->use_ecc ? NDCR_ECC_EN : 0; - ndcr |= info->use_dma ? NDCR_DMA_EN : 0; + ndcr = info->reg_ndcr; + + if (info->use_ecc) + ndcr |= NDCR_ECC_EN; + else + ndcr &= ~NDCR_ECC_EN; + + if (info->use_dma) + ndcr |= NDCR_DMA_EN; + else + ndcr &= ~NDCR_DMA_EN; + + if (info->use_spare) + ndcr |= NDCR_SPARE_EN; + else + ndcr &= ~NDCR_SPARE_EN; + ndcr |= NDCR_ND_RUN; /* clear status bits and run */ @@ -333,7 +356,8 @@ static void pxa3xx_nand_stop(struct pxa3xx_nand_info *info) nand_writel(info, NDSR, NDSR_MASK); } -static void enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) +static void __maybe_unused +enable_int(struct pxa3xx_nand_info *info, uint32_t int_mask) { uint32_t ndcr; @@ -373,6 +397,7 @@ static void handle_data_pio(struct pxa3xx_nand_info *info) } } +#ifdef ARCH_HAS_DMA static void start_data_dma(struct pxa3xx_nand_info *info) { struct pxa_dma_desc *desc = info->data_desc; @@ -419,6 +444,10 @@ static void pxa3xx_nand_data_dma_irq(int channel, void *data) enable_int(info, NDCR_INT_MASK); nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ); } +#else +static void start_data_dma(struct pxa3xx_nand_info *info) +{} +#endif static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) { @@ -467,9 +496,22 @@ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid) nand_writel(info, NDSR, NDSR_WRCMDREQ); status &= ~NDSR_WRCMDREQ; info->state = STATE_CMD_HANDLE; + + /* + * Command buffer registers NDCB{0-2} (and optionally NDCB3) + * must be loaded by writing directly either 12 or 16 + * bytes directly to NDCB0, four bytes at a time. + * + * Direct write access to NDCB1, NDCB2 and NDCB3 is ignored + * but each NDCBx register can be read. + */ nand_writel(info, NDCB0, info->ndcb0); nand_writel(info, NDCB0, info->ndcb1); nand_writel(info, NDCB0, info->ndcb2); + + /* NDCB3 register is available in NFCv2 (Armada 370/XP SoC) */ + if (info->variant == PXA3XX_NAND_VARIANT_ARMADA370) + nand_writel(info, NDCB0, info->ndcb3); } /* clear NDSR to let the controller exit the IRQ */ @@ -491,7 +533,6 @@ static inline int is_buf_blank(uint8_t *buf, size_t len) static int prepare_command_pool(struct pxa3xx_nand_info *info, int command, uint16_t column, int page_addr) { - uint16_t cmd; int addr_cycle, exec_cmd; struct pxa3xx_nand_host *host; struct mtd_info *mtd; @@ -506,6 +547,7 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command, info->buf_count = 0; info->oob_size = 0; info->use_ecc = 0; + info->use_spare = 1; info->is_ready = 0; info->retcode = ERR_NONE; if (info->cs != 0) @@ -520,12 +562,16 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command, case NAND_CMD_READOOB: pxa3xx_set_datasize(info); break; + case NAND_CMD_PARAM: + info->use_spare = 0; + break; case NAND_CMD_SEQIN: exec_cmd = 0; break; default: info->ndcb1 = 0; info->ndcb2 = 0; + info->ndcb3 = 0; break; } @@ -535,21 +581,17 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command, switch (command) { case NAND_CMD_READOOB: case NAND_CMD_READ0: - cmd = host->cmdset->read1; + info->buf_start = column; + info->ndcb0 |= NDCB0_CMD_TYPE(0) + | addr_cycle + | NAND_CMD_READ0; + if (command == NAND_CMD_READOOB) - info->buf_start = mtd->writesize + column; - else - info->buf_start = column; + info->buf_start += mtd->writesize; - if (unlikely(host->page_size < PAGE_CHUNK_SIZE)) - info->ndcb0 |= NDCB0_CMD_TYPE(0) - | addr_cycle - | (cmd & NDCB0_CMD1_MASK); - else - info->ndcb0 |= NDCB0_CMD_TYPE(0) - | NDCB0_DBC - | addr_cycle - | cmd; + /* Second command setting for large pages */ + if (host->page_size >= PAGE_CHUNK_SIZE) + info->ndcb0 |= NDCB0_DBC | (NAND_CMD_READSTART << 8); case NAND_CMD_SEQIN: /* small page addr setting */ @@ -580,49 +622,58 @@ static int prepare_command_pool(struct pxa3xx_nand_info *info, int command, break; } - cmd = host->cmdset->program; info->ndcb0 |= NDCB0_CMD_TYPE(0x1) | NDCB0_AUTO_RS | NDCB0_ST_ROW_EN | NDCB0_DBC - | cmd + | (NAND_CMD_PAGEPROG << 8) + | NAND_CMD_SEQIN | addr_cycle; break; + case NAND_CMD_PARAM: + info->buf_count = 256; + info->ndcb0 |= NDCB0_CMD_TYPE(0) + | NDCB0_ADDR_CYC(1) + | NDCB0_LEN_OVRD + | command; + info->ndcb1 = (column & 0xFF); + info->ndcb3 = 256; + info->data_size = 256; + break; + case NAND_CMD_READID: - cmd = host->cmdset->read_id; info->buf_count = host->read_id_bytes; info->ndcb0 |= NDCB0_CMD_TYPE(3) | NDCB0_ADDR_CYC(1) - | cmd; + | command; + info->ndcb1 = (column & 0xFF); info->data_size = 8; break; case NAND_CMD_STATUS: - cmd = host->cmdset->read_status; info->buf_count = 1; info->ndcb0 |= NDCB0_CMD_TYPE(4) | NDCB0_ADDR_CYC(1) - | cmd; + | command; info->data_size = 8; break; case NAND_CMD_ERASE1: - cmd = host->cmdset->erase; info->ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS | NDCB0_ADDR_CYC(3) | NDCB0_DBC - | cmd; + | (NAND_CMD_ERASE2 << 8) + | NAND_CMD_ERASE1; info->ndcb1 = page_addr; info->ndcb2 = 0; break; case NAND_CMD_RESET: - cmd = host->cmdset->reset; info->ndcb0 |= NDCB0_CMD_TYPE(5) - | cmd; + | command; break; @@ -652,7 +703,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, * "byte" address into a "word" address appropriate * for indexing a word-oriented device */ - if (host->reg_ndcr & NDCR_DWIDTH_M) + if (info->reg_ndcr & NDCR_DWIDTH_M) column /= 2; /* @@ -662,8 +713,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, */ if (info->cs != host->cs) { info->cs = host->cs; - nand_writel(info, NDTR0CS0, host->ndtr0cs0); - nand_writel(info, NDTR1CS0, host->ndtr1cs0); + nand_writel(info, NDTR0CS0, info->ndtr0cs0); + nand_writel(info, NDTR1CS0, info->ndtr1cs0); } info->state = STATE_PREPARED; @@ -803,7 +854,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, const struct pxa3xx_nand_flash *f) { struct platform_device *pdev = info->pdev; - struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data; + struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); struct pxa3xx_nand_host *host = info->host[info->cs]; uint32_t ndcr = 0x0; /* enable all interrupts */ @@ -818,7 +869,6 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, } /* calculate flash information */ - host->cmdset = &default_cmdset; host->page_size = f->page_size; host->read_id_bytes = (f->page_size == 2048) ? 4 : 2; @@ -840,7 +890,7 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, ndcr |= NDCR_RD_ID_CNT(host->read_id_bytes); ndcr |= NDCR_SPARE_EN; /* enable spare by default */ - host->reg_ndcr = ndcr; + info->reg_ndcr = ndcr; pxa3xx_nand_set_timing(host, f->timing); return 0; @@ -863,34 +913,26 @@ static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) host->read_id_bytes = 2; } - host->reg_ndcr = ndcr & ~NDCR_INT_MASK; - host->cmdset = &default_cmdset; - - host->ndtr0cs0 = nand_readl(info, NDTR0CS0); - host->ndtr1cs0 = nand_readl(info, NDTR1CS0); - + info->reg_ndcr = ndcr & ~NDCR_INT_MASK; + info->ndtr0cs0 = nand_readl(info, NDTR0CS0); + info->ndtr1cs0 = nand_readl(info, NDTR1CS0); return 0; } -/* the maximum possible buffer size for large page with OOB data - * is: 2048 + 64 = 2112 bytes, allocate a page here for both the - * data buffer and the DMA descriptor - */ -#define MAX_BUFF_SIZE PAGE_SIZE - +#ifdef ARCH_HAS_DMA static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) { struct platform_device *pdev = info->pdev; - int data_desc_offset = MAX_BUFF_SIZE - sizeof(struct pxa_dma_desc); + int data_desc_offset = info->buf_size - sizeof(struct pxa_dma_desc); if (use_dma == 0) { - info->data_buff = kmalloc(MAX_BUFF_SIZE, GFP_KERNEL); + info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); if (info->data_buff == NULL) return -ENOMEM; return 0; } - info->data_buff = dma_alloc_coherent(&pdev->dev, MAX_BUFF_SIZE, + info->data_buff = dma_alloc_coherent(&pdev->dev, info->buf_size, &info->data_buff_phys, GFP_KERNEL); if (info->data_buff == NULL) { dev_err(&pdev->dev, "failed to allocate dma buffer\n"); @@ -904,14 +946,45 @@ static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) pxa3xx_nand_data_dma_irq, info); if (info->data_dma_ch < 0) { dev_err(&pdev->dev, "failed to request data dma\n"); - dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE, + dma_free_coherent(&pdev->dev, info->buf_size, info->data_buff, info->data_buff_phys); return info->data_dma_ch; } + /* + * Now that DMA buffers are allocated we turn on + * DMA proper for I/O operations. + */ + info->use_dma = 1; + return 0; +} + +static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info) +{ + struct platform_device *pdev = info->pdev; + if (use_dma) { + pxa_free_dma(info->data_dma_ch); + dma_free_coherent(&pdev->dev, info->buf_size, + info->data_buff, info->data_buff_phys); + } else { + kfree(info->data_buff); + } +} +#else +static int pxa3xx_nand_init_buff(struct pxa3xx_nand_info *info) +{ + info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); + if (info->data_buff == NULL) + return -ENOMEM; return 0; } +static void pxa3xx_nand_free_buff(struct pxa3xx_nand_info *info) +{ + kfree(info->data_buff); +} +#endif + static int pxa3xx_nand_sensing(struct pxa3xx_nand_info *info) { struct mtd_info *mtd; @@ -934,7 +1007,7 @@ static int pxa3xx_nand_scan(struct mtd_info *mtd) struct pxa3xx_nand_host *host = mtd->priv; struct pxa3xx_nand_info *info = host->info_data; struct platform_device *pdev = info->pdev; - struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data; + struct pxa3xx_nand_platform_data *pdata = dev_get_platdata(&pdev->dev); struct nand_flash_dev pxa3xx_flash_ids[2], *def = NULL; const struct pxa3xx_nand_flash *f = NULL; struct nand_chip *chip = mtd->priv; @@ -1003,7 +1076,7 @@ KEEP_CONFIG: chip->ecc.size = host->page_size; chip->ecc.strength = 1; - if (host->reg_ndcr & NDCR_DWIDTH_M) + if (info->reg_ndcr & NDCR_DWIDTH_M) chip->options |= NAND_BUSWIDTH_16; if (nand_scan_ident(mtd, 1, def)) @@ -1014,13 +1087,20 @@ KEEP_CONFIG: else host->col_addr_cycles = 1; + /* release the initial buffer */ + kfree(info->data_buff); + + /* allocate the real data + oob buffer */ + info->buf_size = mtd->writesize + mtd->oobsize; + ret = pxa3xx_nand_init_buff(info); + if (ret) + return ret; info->oob_buff = info->data_buff + mtd->writesize; + if ((mtd->size >> chip->page_shift) > 65536) host->row_addr_cycles = 3; else host->row_addr_cycles = 2; - - mtd->name = mtd_names[0]; return nand_scan_tail(mtd); } @@ -1034,13 +1114,11 @@ static int alloc_nand_resource(struct platform_device *pdev) struct resource *r; int ret, irq, cs; - pdata = pdev->dev.platform_data; - info = kzalloc(sizeof(*info) + (sizeof(*mtd) + - sizeof(*host)) * pdata->num_cs, GFP_KERNEL); - if (!info) { - dev_err(&pdev->dev, "failed to allocate memory\n"); + pdata = dev_get_platdata(&pdev->dev); + info = devm_kzalloc(&pdev->dev, sizeof(*info) + (sizeof(*mtd) + + sizeof(*host)) * pdata->num_cs, GFP_KERNEL); + if (!info) return -ENOMEM; - } info->pdev = pdev; for (cs = 0; cs < pdata->num_cs; cs++) { @@ -1069,78 +1147,73 @@ static int alloc_nand_resource(struct platform_device *pdev) spin_lock_init(&chip->controller->lock); init_waitqueue_head(&chip->controller->wq); - info->clk = clk_get(&pdev->dev, NULL); + info->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(info->clk)) { dev_err(&pdev->dev, "failed to get nand clock\n"); - ret = PTR_ERR(info->clk); - goto fail_free_mtd; + return PTR_ERR(info->clk); } - clk_enable(info->clk); - - /* - * This is a dirty hack to make this driver work from devicetree - * bindings. It can be removed once we have a prober DMA controller - * framework for DT. - */ - if (pdev->dev.of_node && cpu_is_pxa3xx()) { - info->drcmr_dat = 97; - info->drcmr_cmd = 99; - } else { - r = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (r == NULL) { - dev_err(&pdev->dev, "no resource defined for data DMA\n"); - ret = -ENXIO; - goto fail_put_clk; - } - info->drcmr_dat = r->start; + ret = clk_prepare_enable(info->clk); + if (ret < 0) + return ret; - r = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (r == NULL) { - dev_err(&pdev->dev, "no resource defined for command DMA\n"); - ret = -ENXIO; - goto fail_put_clk; + if (use_dma) { + /* + * This is a dirty hack to make this driver work from + * devicetree bindings. It can be removed once we have + * a prober DMA controller framework for DT. + */ + if (pdev->dev.of_node && + of_machine_is_compatible("marvell,pxa3xx")) { + info->drcmr_dat = 97; + info->drcmr_cmd = 99; + } else { + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (r == NULL) { + dev_err(&pdev->dev, + "no resource defined for data DMA\n"); + ret = -ENXIO; + goto fail_disable_clk; + } + info->drcmr_dat = r->start; + + r = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (r == NULL) { + dev_err(&pdev->dev, + "no resource defined for cmd DMA\n"); + ret = -ENXIO; + goto fail_disable_clk; + } + info->drcmr_cmd = r->start; } - info->drcmr_cmd = r->start; } irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "no IRQ resource defined\n"); ret = -ENXIO; - goto fail_put_clk; + goto fail_disable_clk; } r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (r == NULL) { - dev_err(&pdev->dev, "no IO memory resource defined\n"); - ret = -ENODEV; - goto fail_put_clk; - } - - r = request_mem_region(r->start, resource_size(r), pdev->name); - if (r == NULL) { - dev_err(&pdev->dev, "failed to request memory resource\n"); - ret = -EBUSY; - goto fail_put_clk; - } - - info->mmio_base = ioremap(r->start, resource_size(r)); - if (info->mmio_base == NULL) { - dev_err(&pdev->dev, "ioremap() failed\n"); - ret = -ENODEV; - goto fail_free_res; + info->mmio_base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(info->mmio_base)) { + ret = PTR_ERR(info->mmio_base); + goto fail_disable_clk; } info->mmio_phys = r->start; - ret = pxa3xx_nand_init_buff(info); - if (ret) - goto fail_free_io; + /* Allocate a buffer to allow flash detection */ + info->buf_size = INIT_BUFFER_SIZE; + info->data_buff = kmalloc(info->buf_size, GFP_KERNEL); + if (info->data_buff == NULL) { + ret = -ENOMEM; + goto fail_disable_clk; + } /* initialize all interrupts to be disabled */ disable_int(info, NDSR_MASK); - ret = request_irq(irq, pxa3xx_nand_irq, IRQF_DISABLED, - pdev->name, info); + ret = request_irq(irq, pxa3xx_nand_irq, 0, pdev->name, info); if (ret < 0) { dev_err(&pdev->dev, "failed to request IRQ\n"); goto fail_free_buf; @@ -1152,21 +1225,9 @@ static int alloc_nand_resource(struct platform_device *pdev) fail_free_buf: free_irq(irq, info); - if (use_dma) { - pxa_free_dma(info->data_dma_ch); - dma_free_coherent(&pdev->dev, MAX_BUFF_SIZE, - info->data_buff, info->data_buff_phys); - } else - kfree(info->data_buff); -fail_free_io: - iounmap(info->mmio_base); -fail_free_res: - release_mem_region(r->start, resource_size(r)); -fail_put_clk: - clk_disable(info->clk); - clk_put(info->clk); -fail_free_mtd: - kfree(info); + kfree(info->data_buff); +fail_disable_clk: + clk_disable_unprepare(info->clk); return ret; } @@ -1174,44 +1235,47 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) { struct pxa3xx_nand_info *info = platform_get_drvdata(pdev); struct pxa3xx_nand_platform_data *pdata; - struct resource *r; int irq, cs; if (!info) return 0; - pdata = pdev->dev.platform_data; - platform_set_drvdata(pdev, NULL); + pdata = dev_get_platdata(&pdev->dev); irq = platform_get_irq(pdev, 0); if (irq >= 0) free_irq(irq, info); - if (use_dma) { - pxa_free_dma(info->data_dma_ch); - dma_free_writecombine(&pdev->dev, MAX_BUFF_SIZE, - info->data_buff, info->data_buff_phys); - } else - kfree(info->data_buff); - - iounmap(info->mmio_base); - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(r->start, resource_size(r)); + pxa3xx_nand_free_buff(info); - clk_disable(info->clk); - clk_put(info->clk); + clk_disable_unprepare(info->clk); for (cs = 0; cs < pdata->num_cs; cs++) nand_release(info->host[cs]->mtd); - kfree(info); return 0; } -#ifdef CONFIG_OF static struct of_device_id pxa3xx_nand_dt_ids[] = { - { .compatible = "marvell,pxa3xx-nand" }, + { + .compatible = "marvell,pxa3xx-nand", + .data = (void *)PXA3XX_NAND_VARIANT_PXA, + }, + { + .compatible = "marvell,armada370-nand", + .data = (void *)PXA3XX_NAND_VARIANT_ARMADA370, + }, {} }; -MODULE_DEVICE_TABLE(of, i2c_pxa_dt_ids); +MODULE_DEVICE_TABLE(of, pxa3xx_nand_dt_ids); + +static enum pxa3xx_nand_variant +pxa3xx_nand_get_variant(struct platform_device *pdev) +{ + const struct of_device_id *of_id = + of_match_device(pxa3xx_nand_dt_ids, &pdev->dev); + if (!of_id) + return PXA3XX_NAND_VARIANT_PXA; + return (enum pxa3xx_nand_variant)of_id->data; +} static int pxa3xx_nand_probe_dt(struct platform_device *pdev) { @@ -1237,12 +1301,6 @@ static int pxa3xx_nand_probe_dt(struct platform_device *pdev) return 0; } -#else -static inline int pxa3xx_nand_probe_dt(struct platform_device *pdev) -{ - return 0; -} -#endif static int pxa3xx_nand_probe(struct platform_device *pdev) { @@ -1251,11 +1309,18 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) struct pxa3xx_nand_info *info; int ret, cs, probe_success; +#ifndef ARCH_HAS_DMA + if (use_dma) { + use_dma = 0; + dev_warn(&pdev->dev, + "This platform can't do DMA on this device\n"); + } +#endif ret = pxa3xx_nand_probe_dt(pdev); if (ret) return ret; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "no platform data defined\n"); return -ENODEV; @@ -1268,10 +1333,19 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) } info = platform_get_drvdata(pdev); + info->variant = pxa3xx_nand_get_variant(pdev); probe_success = 0; for (cs = 0; cs < pdata->num_cs; cs++) { + struct mtd_info *mtd = info->host[cs]->mtd; + + /* + * The mtd name matches the one used in 'mtdparts' kernel + * parameter. This name cannot be changed or otherwise + * user's mtd partitions configuration would get broken. + */ + mtd->name = "pxa3xx_nand-0"; info->cs = cs; - ret = pxa3xx_nand_scan(info->host[cs]->mtd); + ret = pxa3xx_nand_scan(mtd); if (ret) { dev_warn(&pdev->dev, "failed to scan nand at cs %d\n", cs); @@ -1279,7 +1353,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) } ppdata.of_node = pdev->dev.of_node; - ret = mtd_device_parse_register(info->host[cs]->mtd, NULL, + ret = mtd_device_parse_register(mtd, NULL, &ppdata, pdata->parts[cs], pdata->nr_parts[cs]); if (!ret) @@ -1302,7 +1376,7 @@ static int pxa3xx_nand_suspend(struct platform_device *pdev, pm_message_t state) struct mtd_info *mtd; int cs; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (info->state) { dev_err(&pdev->dev, "driver busy, state = %d\n", info->state); return -EAGAIN; @@ -1323,7 +1397,7 @@ static int pxa3xx_nand_resume(struct platform_device *pdev) struct mtd_info *mtd; int cs; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); /* We don't want to handle interrupt without calling mtd routine */ disable_int(info, NDCR_INT_MASK); @@ -1356,7 +1430,7 @@ static int pxa3xx_nand_resume(struct platform_device *pdev) static struct platform_driver pxa3xx_nand_driver = { .driver = { .name = "pxa3xx-nand", - .of_match_table = of_match_ptr(pxa3xx_nand_dt_ids), + .of_match_table = pxa3xx_nand_dt_ids, }, .probe = pxa3xx_nand_probe, .remove = pxa3xx_nand_remove, diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index 4495f8551fa0..325930db3f04 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -181,7 +181,7 @@ static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read) /* Set dma direction */ dev->dma_dir = do_read; dev->dma_stage = 1; - INIT_COMPLETION(dev->dma_done); + reinit_completion(&dev->dma_done); dbg_verbose("doing dma %s ", do_read ? "read" : "write"); @@ -229,7 +229,7 @@ static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read) /* * 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) +static 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; @@ -261,7 +261,7 @@ void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) /* * Read data lines of the nand chip to retrieve data */ -void r852_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +static void r852_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { struct r852_device *dev = r852_get_dev(mtd); uint32_t reg; @@ -312,7 +312,7 @@ static uint8_t r852_read_byte(struct mtd_info *mtd) /* * Control several chip lines & send commands */ -void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl) +static void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl) { struct r852_device *dev = r852_get_dev(mtd); @@ -357,7 +357,7 @@ void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl) * 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) +static int r852_wait(struct mtd_info *mtd, struct nand_chip *chip) { struct r852_device *dev = chip->priv; @@ -386,7 +386,7 @@ int r852_wait(struct mtd_info *mtd, struct nand_chip *chip) * Check if card is ready */ -int r852_ready(struct mtd_info *mtd) +static 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); @@ -397,7 +397,7 @@ int r852_ready(struct mtd_info *mtd) * Set ECC engine mode */ -void r852_ecc_hwctl(struct mtd_info *mtd, int mode) +static void r852_ecc_hwctl(struct mtd_info *mtd, int mode) { struct r852_device *dev = r852_get_dev(mtd); @@ -429,7 +429,7 @@ void r852_ecc_hwctl(struct mtd_info *mtd, int mode) * Calculate ECC, only used for writes */ -int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, +static int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, uint8_t *ecc_code) { struct r852_device *dev = r852_get_dev(mtd); @@ -461,7 +461,7 @@ int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, * Correct the data using ECC, hw did almost everything for us */ -int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat, +static int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc, uint8_t *calc_ecc) { uint16_t ecc_reg; @@ -529,7 +529,7 @@ static int r852_read_oob(struct mtd_info *mtd, struct nand_chip *chip, * Start the nand engine */ -void r852_engine_enable(struct r852_device *dev) +static 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); @@ -547,7 +547,7 @@ void r852_engine_enable(struct r852_device *dev) * Stop the nand engine */ -void r852_engine_disable(struct r852_device *dev) +static 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); @@ -557,7 +557,7 @@ void r852_engine_disable(struct r852_device *dev) * Test if card is present */ -void r852_card_update_present(struct r852_device *dev) +static void r852_card_update_present(struct r852_device *dev) { unsigned long flags; uint8_t reg; @@ -572,7 +572,7 @@ void r852_card_update_present(struct r852_device *dev) * 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) +static 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; @@ -586,8 +586,8 @@ void r852_update_card_detect(struct r852_device *dev) 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) +static 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); @@ -597,11 +597,11 @@ ssize_t r852_media_type_show(struct device *sys_dev, return strlen(data); } -DEVICE_ATTR(media_type, S_IRUGO, r852_media_type_show, NULL); +static 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) +static void r852_update_media_status(struct r852_device *dev) { uint8_t reg; unsigned long flags; @@ -630,7 +630,7 @@ void r852_update_media_status(struct r852_device *dev) * Register the nand device * Called when the card is detected */ -int r852_register_nand_device(struct r852_device *dev) +static int r852_register_nand_device(struct r852_device *dev) { dev->mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); @@ -668,7 +668,7 @@ error1: * Unregister the card */ -void r852_unregister_nand_device(struct r852_device *dev) +static void r852_unregister_nand_device(struct r852_device *dev) { if (!dev->card_registred) return; @@ -682,7 +682,7 @@ void r852_unregister_nand_device(struct r852_device *dev) } /* Card state updater */ -void r852_card_detect_work(struct work_struct *work) +static void r852_card_detect_work(struct work_struct *work) { struct r852_device *dev = container_of(work, struct r852_device, card_detect_work.work); @@ -821,7 +821,7 @@ out: return ret; } -int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) +static int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) { int error; struct nand_chip *chip; @@ -961,7 +961,7 @@ error1: return error; } -void r852_remove(struct pci_dev *pci_dev) +static void r852_remove(struct pci_dev *pci_dev) { struct r852_device *dev = pci_get_drvdata(pci_dev); @@ -992,7 +992,7 @@ void r852_remove(struct pci_dev *pci_dev) pci_disable_device(pci_dev); } -void r852_shutdown(struct pci_dev *pci_dev) +static void r852_shutdown(struct pci_dev *pci_dev) { struct r852_device *dev = pci_get_drvdata(pci_dev); @@ -1002,7 +1002,7 @@ void r852_shutdown(struct pci_dev *pci_dev) pci_disable_device(pci_dev); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int r852_suspend(struct device *device) { struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); @@ -1055,9 +1055,6 @@ static int r852_resume(struct device *device) 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[] = { diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index d65afd23e171..d65cbe903d40 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -150,7 +150,7 @@ static struct s3c2410_nand_info *to_nand_info(struct platform_device *dev) static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev) { - return dev->dev.platform_data; + return dev_get_platdata(&dev->dev); } static inline int allow_clk_suspend(struct s3c2410_nand_info *info) @@ -697,8 +697,6 @@ static int s3c24xx_nand_remove(struct platform_device *pdev) { struct s3c2410_nand_info *info = to_nand_info(pdev); - platform_set_drvdata(pdev, NULL); - if (info == NULL) return 0; diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index e57e18e8c289..a3c84ebbe392 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -137,7 +137,7 @@ static void flctl_setup_dma(struct sh_flctl *flctl) dma_cap_mask_t mask; struct dma_slave_config cfg; struct platform_device *pdev = flctl->pdev; - struct sh_flctl_platform_data *pdata = pdev->dev.platform_data; + struct sh_flctl_platform_data *pdata = dev_get_platdata(&pdev->dev); int ret; if (!pdata) @@ -1131,7 +1131,7 @@ static int flctl_probe(struct platform_device *pdev) if (pdev->dev.of_node) pdata = flctl_parse_dt(&pdev->dev); else - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "no setup data defined\n"); diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 127bc4271821..87908d760feb 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -112,7 +112,7 @@ static int sharpsl_nand_probe(struct platform_device *pdev) struct resource *r; int err = 0; struct sharpsl_nand *sharpsl; - struct sharpsl_nand_platform_data *data = pdev->dev.platform_data; + struct sharpsl_nand_platform_data *data = dev_get_platdata(&pdev->dev); if (!data) { dev_err(&pdev->dev, "no platform data!\n"); @@ -194,7 +194,6 @@ err_add: nand_release(&sharpsl->mtd); err_scan: - platform_set_drvdata(pdev, NULL); iounmap(sharpsl->io); err_ioremap: err_get_res: @@ -212,8 +211,6 @@ static int sharpsl_nand_remove(struct platform_device *pdev) /* Release resources, unregister device */ nand_release(&sharpsl->mtd); - platform_set_drvdata(pdev, NULL); - iounmap(sharpsl->io); /* Free the MTD device structure */ diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c index e8181edebddd..e06b5e5d3287 100644 --- a/drivers/mtd/nand/sm_common.c +++ b/drivers/mtd/nand/sm_common.c @@ -42,7 +42,7 @@ 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; + int ret; memset(&oob, -1, SM_OOB_SIZE); oob.block_status = 0x0F; @@ -61,11 +61,10 @@ static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs) printk(KERN_NOTICE "sm_common: can't mark sector at %i as bad\n", (int)ofs); - error = -EIO; - } else - mtd->ecc_stats.badblocks++; + return -EIO; + } - return error; + return 0; } static struct nand_flash_dev nand_smartmedia_flash_ids[] = { diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c index 09dde7d27178..fe8058a45054 100644 --- a/drivers/mtd/nand/socrates_nand.c +++ b/drivers/mtd/nand/socrates_nand.c @@ -15,6 +15,7 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> +#include <linux/of_address.h> #include <linux/of_platform.h> #include <linux/io.h> @@ -149,17 +150,13 @@ static int socrates_nand_probe(struct platform_device *ofdev) struct mtd_part_parser_data ppdata; /* Allocate memory for the device structure (and zero it) */ - host = kzalloc(sizeof(struct socrates_nand_host), GFP_KERNEL); - if (!host) { - printk(KERN_ERR - "socrates_nand: failed to allocate device structure.\n"); + host = devm_kzalloc(&ofdev->dev, sizeof(*host), GFP_KERNEL); + if (!host) return -ENOMEM; - } host->io_base = of_iomap(ofdev->dev.of_node, 0); if (host->io_base == NULL) { - printk(KERN_ERR "socrates_nand: ioremap failed\n"); - kfree(host); + dev_err(&ofdev->dev, "ioremap failed\n"); return -EIO; } @@ -211,9 +208,7 @@ static int socrates_nand_probe(struct platform_device *ofdev) nand_release(mtd); out: - dev_set_drvdata(&ofdev->dev, NULL); iounmap(host->io_base); - kfree(host); return res; } @@ -227,9 +222,7 @@ static int socrates_nand_remove(struct platform_device *ofdev) nand_release(mtd); - dev_set_drvdata(&ofdev->dev, NULL); iounmap(host->io_base); - kfree(host); return 0; } diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index 508e9e04b092..a3747c914d57 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -357,7 +357,7 @@ static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) static int tmio_probe(struct platform_device *dev) { - struct tmio_nand_data *data = dev->dev.platform_data; + struct tmio_nand_data *data = dev_get_platdata(&dev->dev); struct resource *fcr = platform_get_resource(dev, IORESOURCE_MEM, 0); struct resource *ccr = platform_get_resource(dev, @@ -428,8 +428,7 @@ static int tmio_probe(struct platform_device *dev) /* 15 us command delay time */ nand_chip->chip_delay = 15; - retval = request_irq(irq, &tmio_irq, - IRQF_DISABLED, dev_name(&dev->dev), tmio); + retval = request_irq(irq, &tmio_irq, 0, dev_name(&dev->dev), tmio); if (retval) { dev_err(&dev->dev, "request_irq error %d\n", retval); goto err_irq; diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 7ed654c68b08..235714a421dd 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -87,7 +87,7 @@ static struct platform_device *mtd_to_platdev(struct mtd_info *mtd) static void __iomem *ndregaddr(struct platform_device *dev, unsigned int reg) { struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); - struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); return drvdata->base + (reg << plat->shift); } @@ -138,7 +138,7 @@ static void txx9ndfmc_cmd_ctrl(struct mtd_info *mtd, int cmd, struct nand_chip *chip = mtd->priv; struct txx9ndfmc_priv *txx9_priv = chip->priv; struct platform_device *dev = txx9_priv->dev; - struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); if (ctrl & NAND_CTRL_CHANGE) { u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); @@ -225,7 +225,7 @@ static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) static void txx9ndfmc_initialize(struct platform_device *dev) { - struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); int tmout = 100; @@ -274,19 +274,17 @@ static int txx9ndfmc_nand_scan(struct mtd_info *mtd) static int __init txx9ndfmc_probe(struct platform_device *dev) { - struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; + struct txx9ndfmc_platform_data *plat = dev_get_platdata(&dev->dev); int hold, spw; int i; struct txx9ndfmc_drvdata *drvdata; unsigned long gbusclk = plat->gbus_clock; struct resource *res; - res = platform_get_resource(dev, IORESOURCE_MEM, 0); - if (!res) - return -ENODEV; drvdata = devm_kzalloc(&dev->dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; + res = platform_get_resource(dev, IORESOURCE_MEM, 0); drvdata->base = devm_ioremap_resource(&dev->dev, res); if (IS_ERR(drvdata->base)) return PTR_ERR(drvdata->base); @@ -387,7 +385,6 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev) struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); int i; - platform_set_drvdata(dev, NULL); if (!drvdata) return 0; for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) { diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index c5f4ebf4b384..46f27de018c3 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -50,7 +50,7 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) struct NFTLrecord *nftl; unsigned long temp; - if (mtd->type != MTD_NANDFLASH || mtd->size > UINT_MAX) + if (!mtd_type_is_nand(mtd) || mtd->size > UINT_MAX) return; /* OK, this is moderately ugly. But probably safe. Alternatives? */ if (memcmp(mtd->name, "DiskOnChip", 10)) diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c index 553d6d6d5603..d64f8c30945f 100644 --- a/drivers/mtd/ofpart.c +++ b/drivers/mtd/ofpart.c @@ -20,6 +20,11 @@ #include <linux/slab.h> #include <linux/mtd/partitions.h> +static bool node_has_compatible(struct device_node *pp) +{ + return of_get_property(pp, "compatible", NULL); +} + static int parse_ofpart_partitions(struct mtd_info *master, struct mtd_partition **pparts, struct mtd_part_parser_data *data) @@ -38,10 +43,13 @@ static int parse_ofpart_partitions(struct mtd_info *master, return 0; /* First count the subnodes */ - pp = NULL; nr_parts = 0; - while ((pp = of_get_next_child(node, pp))) + for_each_child_of_node(node, pp) { + if (node_has_compatible(pp)) + continue; + nr_parts++; + } if (nr_parts == 0) return 0; @@ -50,13 +58,15 @@ static int parse_ofpart_partitions(struct mtd_info *master, if (!*pparts) return -ENOMEM; - pp = NULL; i = 0; - while ((pp = of_get_next_child(node, pp))) { + for_each_child_of_node(node, pp) { const __be32 *reg; int len; int a_cells, s_cells; + if (node_has_compatible(pp)) + continue; + reg = of_get_property(pp, "reg", &len); if (!reg) { nr_parts--; diff --git a/drivers/mtd/onenand/generic.c b/drivers/mtd/onenand/generic.c index 9f11562f849d..63699fffc96d 100644 --- a/drivers/mtd/onenand/generic.c +++ b/drivers/mtd/onenand/generic.c @@ -38,7 +38,7 @@ struct onenand_info { static int generic_onenand_probe(struct platform_device *pdev) { struct onenand_info *info; - struct onenand_platform_data *pdata = pdev->dev.platform_data; + struct onenand_platform_data *pdata = dev_get_platdata(&pdev->dev); struct resource *res = pdev->resource; unsigned long size = resource_size(res); int err; @@ -94,8 +94,6 @@ static int generic_onenand_remove(struct platform_device *pdev) struct resource *res = pdev->resource; unsigned long size = resource_size(res); - platform_set_drvdata(pdev, NULL); - if (info) { onenand_release(&info->mtd); release_mem_region(res->start, size); diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index d98b198edd53..6547c84afc3a 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -159,7 +159,7 @@ static int omap2_onenand_wait(struct mtd_info *mtd, int state) syscfg = read_reg(c, ONENAND_REG_SYS_CFG1); } - INIT_COMPLETION(c->irq_done); + reinit_completion(&c->irq_done); if (c->gpio_irq) { result = gpio_get_value(c->gpio_irq); if (result == -1) { @@ -349,7 +349,7 @@ static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area, omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, dma_dst, 0, 0); - INIT_COMPLETION(c->dma_done); + reinit_completion(&c->dma_done); omap_start_dma(c->dma_channel); timeout = jiffies + msecs_to_jiffies(20); @@ -420,7 +420,7 @@ static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, dma_dst, 0, 0); - INIT_COMPLETION(c->dma_done); + reinit_completion(&c->dma_done); omap_start_dma(c->dma_channel); timeout = jiffies + msecs_to_jiffies(20); @@ -499,7 +499,7 @@ static int omap2_onenand_read_bufferram(struct mtd_info *mtd, int area, omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, dma_dst, 0, 0); - INIT_COMPLETION(c->dma_done); + reinit_completion(&c->dma_done); omap_start_dma(c->dma_channel); wait_for_completion(&c->dma_done); @@ -544,7 +544,7 @@ static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, omap_set_dma_dest_params(c->dma_channel, 0, OMAP_DMA_AMODE_POST_INC, dma_dst, 0, 0); - INIT_COMPLETION(c->dma_done); + reinit_completion(&c->dma_done); omap_start_dma(c->dma_channel); wait_for_completion(&c->dma_done); @@ -573,28 +573,6 @@ static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, static struct platform_driver omap2_onenand_driver; -static int __adjust_timing(struct device *dev, void *data) -{ - int ret = 0; - struct omap2_onenand *c; - - c = dev_get_drvdata(dev); - - BUG_ON(c->setup == NULL); - - /* DMA is not in use so this is all that is needed */ - /* Revisit for OMAP3! */ - ret = c->setup(c->onenand.base, &c->freq); - - return ret; -} - -int omap2_onenand_rephase(void) -{ - return driver_for_each_device(&omap2_onenand_driver.driver, NULL, - NULL, __adjust_timing); -} - static void omap2_onenand_shutdown(struct platform_device *pdev) { struct omap2_onenand *c = dev_get_drvdata(&pdev->dev); @@ -639,7 +617,7 @@ static int omap2_onenand_probe(struct platform_device *pdev) struct resource *res; struct mtd_part_parser_data ppdata = {}; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (pdata == NULL) { dev_err(&pdev->dev, "platform data missing\n"); return -ENODEV; @@ -810,7 +788,6 @@ static int omap2_onenand_remove(struct platform_device *pdev) if (c->dma_channel != -1) omap_free_dma(c->dma_channel); omap2_onenand_shutdown(pdev); - platform_set_drvdata(pdev, NULL); if (c->gpio_irq) { free_irq(gpio_to_irq(c->gpio_irq), c); gpio_free(c->gpio_irq); diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index b3f41f200622..1de33b5d3903 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -2556,10 +2556,6 @@ static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs) { int ret; - /* Check for invalid offset */ - if (ofs > mtd->size) - return -EINVAL; - onenand_get_device(mtd, FL_READING); ret = onenand_block_isbad_nolock(mtd, ofs, 0); onenand_release_device(mtd); @@ -3529,7 +3525,7 @@ static int flexonenand_get_boundary(struct mtd_info *mtd) { struct onenand_chip *this = mtd->priv; unsigned die, bdry; - int ret, syscfg, locked; + int syscfg, locked; /* Disable ECC */ syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); @@ -3540,7 +3536,7 @@ static int flexonenand_get_boundary(struct mtd_info *mtd) this->wait(mtd, FL_SYNCING); this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); - ret = this->wait(mtd, FL_READING); + this->wait(mtd, FL_READING); bdry = this->read_word(this->base + ONENAND_DATARAM); if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3) @@ -3550,7 +3546,7 @@ static int flexonenand_get_boundary(struct mtd_info *mtd) this->boundary[die] = bdry & FLEXONENAND_PI_MASK; this->command(mtd, ONENAND_CMD_RESET, 0, 0); - ret = this->wait(mtd, FL_RESETING); + this->wait(mtd, FL_RESETING); printk(KERN_INFO "Die %d boundary: %d%s\n", die, this->boundary[die], locked ? "(Locked)" : "(Unlocked)"); @@ -3734,7 +3730,7 @@ static int flexonenand_set_boundary(struct mtd_info *mtd, int die, /* Check is boundary is locked */ this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); - ret = this->wait(mtd, FL_READING); + this->wait(mtd, FL_READING); thisboundary = this->read_word(this->base + ONENAND_DATARAM); if ((thisboundary >> FLEXONENAND_PI_UNLOCK_SHIFT) != 3) { @@ -3835,7 +3831,7 @@ static int onenand_chip_probe(struct mtd_info *mtd) static int onenand_probe(struct mtd_info *mtd) { struct onenand_chip *this = mtd->priv; - int maf_id, dev_id, ver_id; + int dev_id, ver_id; int density; int ret; @@ -3843,8 +3839,7 @@ static int onenand_probe(struct mtd_info *mtd) if (ret) return ret; - /* Read manufacturer and device IDs from Register */ - maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); + /* Device and version IDs from Register */ dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY); diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 66fe3b7e7851..08d0085f3e93 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -133,7 +133,6 @@ static inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_desc { struct onenand_chip *this = mtd->priv; - bd->options &= ~NAND_BBT_SCANEMPTY; return create_bbt(mtd, this->page_buf, bd, -1); } diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c index 2cf74085f935..df7400dd4df8 100644 --- a/drivers/mtd/onenand/samsung.c +++ b/drivers/mtd/onenand/samsung.c @@ -867,7 +867,7 @@ static int s3c_onenand_probe(struct platform_device *pdev) struct resource *r; int size, err; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); /* No need to check pdata. the platform data is optional */ size = sizeof(struct mtd_info) + sizeof(struct onenand_chip); @@ -1073,7 +1073,6 @@ static int s3c_onenand_remove(struct platform_device *pdev) release_mem_region(onenand->base_res->start, resource_size(onenand->base_res)); - platform_set_drvdata(pdev, NULL); kfree(onenand->oob_buf); kfree(onenand->page_buf); kfree(onenand); diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index f9d5615c5727..4b8e89583f2a 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -22,7 +22,7 @@ -struct workqueue_struct *cache_flush_workqueue; +static struct workqueue_struct *cache_flush_workqueue; static int cache_timeout = 1000; module_param(cache_timeout, int, S_IRUGO); @@ -41,7 +41,7 @@ struct sm_sysfs_attribute { int len; }; -ssize_t sm_attr_show(struct device *dev, struct device_attribute *attr, +static ssize_t sm_attr_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sm_sysfs_attribute *sm_attr = @@ -54,7 +54,7 @@ ssize_t sm_attr_show(struct device *dev, struct device_attribute *attr, #define NUM_ATTRIBUTES 1 #define SM_CIS_VENDOR_OFFSET 0x59 -struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl) +static struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl) { struct attribute_group *attr_group; struct attribute **attributes; @@ -107,7 +107,7 @@ error1: return NULL; } -void sm_delete_sysfs_attributes(struct sm_ftl *ftl) +static void sm_delete_sysfs_attributes(struct sm_ftl *ftl) { struct attribute **attributes = ftl->disk_attributes->attrs; int i; @@ -571,7 +571,7 @@ static const uint8_t cis_signature[] = { }; /* 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) +static int sm_get_media_info(struct sm_ftl *ftl, struct mtd_info *mtd) { int i; int size_in_megs = mtd->size / (1024 * 1024); @@ -878,7 +878,7 @@ static int sm_init_zone(struct sm_ftl *ftl, int zone_num) } /* Get and automatically initialize an FTL mapping for one zone */ -struct ftl_zone *sm_get_zone(struct sm_ftl *ftl, int zone_num) +static struct ftl_zone *sm_get_zone(struct sm_ftl *ftl, int zone_num) { struct ftl_zone *zone; int error; @@ -899,7 +899,7 @@ struct ftl_zone *sm_get_zone(struct sm_ftl *ftl, int zone_num) /* ----------------- cache handling ------------------------------------------*/ /* Initialize the one block cache */ -void sm_cache_init(struct sm_ftl *ftl) +static void sm_cache_init(struct sm_ftl *ftl) { ftl->cache_data_invalid_bitmap = 0xFFFFFFFF; ftl->cache_clean = 1; @@ -909,7 +909,7 @@ void sm_cache_init(struct sm_ftl *ftl) } /* Put sector in one block cache */ -void sm_cache_put(struct sm_ftl *ftl, char *buffer, int boffset) +static 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); @@ -917,7 +917,7 @@ void sm_cache_put(struct sm_ftl *ftl, char *buffer, int boffset) } /* Read a sector from the cache */ -int sm_cache_get(struct sm_ftl *ftl, char *buffer, int boffset) +static int sm_cache_get(struct sm_ftl *ftl, char *buffer, int boffset) { if (test_bit(boffset / SM_SECTOR_SIZE, &ftl->cache_data_invalid_bitmap)) @@ -928,7 +928,7 @@ int sm_cache_get(struct sm_ftl *ftl, char *buffer, int boffset) } /* Write the cache to hardware */ -int sm_cache_flush(struct sm_ftl *ftl) +static int sm_cache_flush(struct sm_ftl *ftl) { struct ftl_zone *zone; @@ -1274,10 +1274,10 @@ static struct mtd_blktrans_ops sm_ftl_ops = { static __init int sm_module_init(void) { int error = 0; - cache_flush_workqueue = create_freezable_workqueue("smflush"); - if (IS_ERR(cache_flush_workqueue)) - return PTR_ERR(cache_flush_workqueue); + cache_flush_workqueue = create_freezable_workqueue("smflush"); + if (!cache_flush_workqueue) + return -ENOMEM; error = register_mtd_blktrans(&sm_ftl_ops); if (error) diff --git a/drivers/mtd/ssfdc.c b/drivers/mtd/ssfdc.c index ab2a52a039c3..daf82ba7aba0 100644 --- a/drivers/mtd/ssfdc.c +++ b/drivers/mtd/ssfdc.c @@ -290,7 +290,7 @@ static void ssfdcr_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) int cis_sector; /* Check for small page NAND flash */ - if (mtd->type != MTD_NANDFLASH || mtd->oobsize != OOB_SIZE || + if (!mtd_type_is_nand(mtd) || mtd->oobsize != OOB_SIZE || mtd->size > UINT_MAX) return; diff --git a/drivers/mtd/tests/Makefile b/drivers/mtd/tests/Makefile index bd0065c0d359..937a829bb701 100644 --- a/drivers/mtd/tests/Makefile +++ b/drivers/mtd/tests/Makefile @@ -7,3 +7,12 @@ obj-$(CONFIG_MTD_TESTS) += mtd_subpagetest.o obj-$(CONFIG_MTD_TESTS) += mtd_torturetest.o obj-$(CONFIG_MTD_TESTS) += mtd_nandecctest.o obj-$(CONFIG_MTD_TESTS) += mtd_nandbiterrs.o + +mtd_oobtest-objs := oobtest.o mtd_test.o +mtd_pagetest-objs := pagetest.o mtd_test.o +mtd_readtest-objs := readtest.o mtd_test.o +mtd_speedtest-objs := speedtest.o mtd_test.o +mtd_stresstest-objs := stresstest.o mtd_test.o +mtd_subpagetest-objs := subpagetest.o mtd_test.o +mtd_torturetest-objs := torturetest.o mtd_test.o +mtd_nandbiterrs-objs := nandbiterrs.o mtd_test.o diff --git a/drivers/mtd/tests/mtd_test.c b/drivers/mtd/tests/mtd_test.c new file mode 100644 index 000000000000..c818a63532e7 --- /dev/null +++ b/drivers/mtd/tests/mtd_test.c @@ -0,0 +1,114 @@ +#define pr_fmt(fmt) "mtd_test: " fmt + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/printk.h> + +#include "mtd_test.h" + +int mtdtest_erase_eraseblock(struct mtd_info *mtd, unsigned int ebnum) +{ + int err; + struct erase_info ei; + loff_t addr = ebnum * mtd->erasesize; + + memset(&ei, 0, sizeof(struct erase_info)); + ei.mtd = mtd; + ei.addr = addr; + ei.len = mtd->erasesize; + + err = mtd_erase(mtd, &ei); + if (err) { + pr_info("error %d while erasing EB %d\n", err, ebnum); + return err; + } + + if (ei.state == MTD_ERASE_FAILED) { + pr_info("some erase error occurred at EB %d\n", ebnum); + return -EIO; + } + return 0; +} + +static int is_block_bad(struct mtd_info *mtd, unsigned int ebnum) +{ + int ret; + loff_t addr = ebnum * mtd->erasesize; + + ret = mtd_block_isbad(mtd, addr); + if (ret) + pr_info("block %d is bad\n", ebnum); + + return ret; +} + +int mtdtest_scan_for_bad_eraseblocks(struct mtd_info *mtd, unsigned char *bbt, + unsigned int eb, int ebcnt) +{ + int i, bad = 0; + + if (!mtd_can_have_bb(mtd)) + return 0; + + pr_info("scanning for bad eraseblocks\n"); + for (i = 0; i < ebcnt; ++i) { + bbt[i] = is_block_bad(mtd, eb + i) ? 1 : 0; + if (bbt[i]) + bad += 1; + cond_resched(); + } + pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); + + return 0; +} + +int mtdtest_erase_good_eraseblocks(struct mtd_info *mtd, unsigned char *bbt, + unsigned int eb, int ebcnt) +{ + int err; + unsigned int i; + + for (i = 0; i < ebcnt; ++i) { + if (bbt[i]) + continue; + err = mtdtest_erase_eraseblock(mtd, eb + i); + if (err) + return err; + cond_resched(); + } + + return 0; +} + +int mtdtest_read(struct mtd_info *mtd, loff_t addr, size_t size, void *buf) +{ + size_t read; + int err; + + err = mtd_read(mtd, addr, size, &read, buf); + /* Ignore corrected ECC errors */ + if (mtd_is_bitflip(err)) + err = 0; + if (!err && read != size) + err = -EIO; + if (err) + pr_err("error: read failed at %#llx\n", addr); + + return err; +} + +int mtdtest_write(struct mtd_info *mtd, loff_t addr, size_t size, + const void *buf) +{ + size_t written; + int err; + + err = mtd_write(mtd, addr, size, &written, buf); + if (!err && written != size) + err = -EIO; + if (err) + pr_err("error: write failed at %#llx\n", addr); + + return err; +} diff --git a/drivers/mtd/tests/mtd_test.h b/drivers/mtd/tests/mtd_test.h new file mode 100644 index 000000000000..f437c776c54f --- /dev/null +++ b/drivers/mtd/tests/mtd_test.h @@ -0,0 +1,11 @@ +#include <linux/mtd/mtd.h> + +int mtdtest_erase_eraseblock(struct mtd_info *mtd, unsigned int ebnum); +int mtdtest_scan_for_bad_eraseblocks(struct mtd_info *mtd, unsigned char *bbt, + unsigned int eb, int ebcnt); +int mtdtest_erase_good_eraseblocks(struct mtd_info *mtd, unsigned char *bbt, + unsigned int eb, int ebcnt); + +int mtdtest_read(struct mtd_info *mtd, loff_t addr, size_t size, void *buf); +int mtdtest_write(struct mtd_info *mtd, loff_t addr, size_t size, + const void *buf); diff --git a/drivers/mtd/tests/mtd_nandbiterrs.c b/drivers/mtd/tests/nandbiterrs.c index 207bf9a9972f..6f976159611f 100644 --- a/drivers/mtd/tests/mtd_nandbiterrs.c +++ b/drivers/mtd/tests/nandbiterrs.c @@ -49,6 +49,7 @@ #include <linux/err.h> #include <linux/mtd/nand.h> #include <linux/slab.h> +#include "mtd_test.h" static int dev; module_param(dev, int, S_IRUGO); @@ -98,47 +99,13 @@ static uint8_t hash(unsigned offset) return c; } -static int erase_block(void) -{ - int err; - struct erase_info ei; - loff_t addr = eraseblock * mtd->erasesize; - - pr_info("erase_block\n"); - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (err || ei.state == MTD_ERASE_FAILED) { - pr_err("error %d while erasing\n", err); - if (!err) - err = -EIO; - return err; - } - - return 0; -} - /* Writes wbuffer to page */ static int write_page(int log) { - int err = 0; - size_t written; - if (log) pr_info("write_page\n"); - err = mtd_write(mtd, offset, mtd->writesize, &written, wbuffer); - if (err || written != mtd->writesize) { - pr_err("error: write failed at %#llx\n", (long long)offset); - if (!err) - err = -EIO; - } - - return err; + return mtdtest_write(mtd, offset, mtd->writesize, wbuffer); } /* Re-writes the data area while leaving the OOB alone. */ @@ -382,7 +349,7 @@ static int __init mtd_nandbiterrs_init(void) goto exit_mtddev; } - if (mtd->type != MTD_NANDFLASH) { + if (!mtd_type_is_nand(mtd)) { pr_info("this test requires NAND flash\n"); err = -ENODEV; goto exit_nand; @@ -415,7 +382,7 @@ static int __init mtd_nandbiterrs_init(void) goto exit_rbuffer; } - err = erase_block(); + err = mtdtest_erase_eraseblock(mtd, eraseblock); if (err) goto exit_error; @@ -428,7 +395,7 @@ static int __init mtd_nandbiterrs_init(void) goto exit_error; /* We leave the block un-erased in case of test failure. */ - err = erase_block(); + err = mtdtest_erase_eraseblock(mtd, eraseblock); if (err) goto exit_error; diff --git a/drivers/mtd/tests/mtd_oobtest.c b/drivers/mtd/tests/oobtest.c index 3e24b379ffa4..2e9e2d11f204 100644 --- a/drivers/mtd/tests/mtd_oobtest.c +++ b/drivers/mtd/tests/oobtest.c @@ -31,6 +31,8 @@ #include <linux/sched.h> #include <linux/random.h> +#include "mtd_test.h" + static int dev = -EINVAL; module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); @@ -49,49 +51,6 @@ static int use_len_max; static int vary_offset; static struct rnd_state rnd_state; -static int erase_eraseblock(int ebnum) -{ - int err; - struct erase_info ei; - loff_t addr = ebnum * mtd->erasesize; - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (err) { - pr_err("error %d while erasing EB %d\n", err, ebnum); - return err; - } - - if (ei.state == MTD_ERASE_FAILED) { - pr_err("some erase error occurred at EB %d\n", ebnum); - return -EIO; - } - - return 0; -} - -static int erase_whole_device(void) -{ - int err; - unsigned int i; - - pr_info("erasing whole device\n"); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = erase_eraseblock(i); - if (err) - return err; - cond_resched(); - } - pr_info("erased %u eraseblocks\n", i); - return 0; -} - static void do_vary_offset(void) { use_len -= 1; @@ -304,38 +263,6 @@ static int verify_all_eraseblocks(void) return 0; } -static int is_block_bad(int ebnum) -{ - int ret; - loff_t addr = ebnum * mtd->erasesize; - - ret = mtd_block_isbad(mtd, addr); - if (ret) - pr_info("block %d is bad\n", ebnum); - return ret; -} - -static int scan_for_bad_eraseblocks(void) -{ - int i, bad = 0; - - bbt = kmalloc(ebcnt, GFP_KERNEL); - if (!bbt) { - pr_err("error: cannot allocate memory\n"); - return -ENOMEM; - } - - pr_info("scanning for bad eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - bbt[i] = is_block_bad(i) ? 1 : 0; - if (bbt[i]) - bad += 1; - cond_resched(); - } - pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); - return 0; -} - static int __init mtd_oobtest_init(void) { int err = 0; @@ -362,7 +289,7 @@ static int __init mtd_oobtest_init(void) return err; } - if (mtd->type != MTD_NANDFLASH) { + if (!mtd_type_is_nand(mtd)) { pr_info("this test requires NAND flash\n"); goto out; } @@ -380,17 +307,16 @@ static int __init mtd_oobtest_init(void) err = -ENOMEM; readbuf = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!readbuf) { - pr_err("error: cannot allocate memory\n"); + if (!readbuf) goto out; - } writebuf = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!writebuf) { - pr_err("error: cannot allocate memory\n"); + if (!writebuf) + goto out; + bbt = kzalloc(ebcnt, GFP_KERNEL); + if (!bbt) goto out; - } - err = scan_for_bad_eraseblocks(); + err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -402,7 +328,7 @@ static int __init mtd_oobtest_init(void) /* First test: write all OOB, read it back and verify */ pr_info("test 1 of 5\n"); - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -422,7 +348,7 @@ static int __init mtd_oobtest_init(void) */ pr_info("test 2 of 5\n"); - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -452,7 +378,7 @@ static int __init mtd_oobtest_init(void) */ pr_info("test 3 of 5\n"); - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -485,7 +411,7 @@ static int __init mtd_oobtest_init(void) /* Fourth test: try to write off end of device */ pr_info("test 4 of 5\n"); - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -577,7 +503,7 @@ static int __init mtd_oobtest_init(void) errcnt += 1; } - err = erase_eraseblock(ebcnt - 1); + err = mtdtest_erase_eraseblock(mtd, ebcnt - 1); if (err) goto out; @@ -626,7 +552,7 @@ static int __init mtd_oobtest_init(void) pr_info("test 5 of 5\n"); /* Erase all eraseblocks */ - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; diff --git a/drivers/mtd/tests/mtd_pagetest.c b/drivers/mtd/tests/pagetest.c index 0c1140b6c286..ed2d3f656fd2 100644 --- a/drivers/mtd/tests/mtd_pagetest.c +++ b/drivers/mtd/tests/pagetest.c @@ -31,6 +31,8 @@ #include <linux/sched.h> #include <linux/random.h> +#include "mtd_test.h" + static int dev = -EINVAL; module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); @@ -48,52 +50,18 @@ static int pgcnt; static int errcnt; static struct rnd_state rnd_state; -static int erase_eraseblock(int ebnum) -{ - int err; - struct erase_info ei; - loff_t addr = ebnum * mtd->erasesize; - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (err) { - pr_err("error %d while erasing EB %d\n", err, ebnum); - return err; - } - - if (ei.state == MTD_ERASE_FAILED) { - pr_err("some erase error occurred at EB %d\n", - ebnum); - return -EIO; - } - - return 0; -} - static int write_eraseblock(int ebnum) { - int err = 0; - size_t written; loff_t addr = ebnum * mtd->erasesize; prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize); cond_resched(); - err = mtd_write(mtd, addr, mtd->erasesize, &written, writebuf); - if (err || written != mtd->erasesize) - pr_err("error: write failed at %#llx\n", - (long long)addr); - - return err; + return mtdtest_write(mtd, addr, mtd->erasesize, writebuf); } static int verify_eraseblock(int ebnum) { uint32_t j; - size_t read; int err = 0, i; loff_t addr0, addrn; loff_t addr = ebnum * mtd->erasesize; @@ -109,31 +77,16 @@ static int verify_eraseblock(int ebnum) prandom_bytes_state(&rnd_state, writebuf, mtd->erasesize); for (j = 0; j < pgcnt - 1; ++j, addr += pgsize) { /* Do a read to set the internal dataRAMs to different data */ - err = mtd_read(mtd, addr0, bufsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != bufsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr0); + err = mtdtest_read(mtd, addr0, bufsize, twopages); + if (err) return err; - } - err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != bufsize) { - pr_err("error: read failed at %#llx\n", - (long long)(addrn - bufsize)); + err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages); + if (err) return err; - } memset(twopages, 0, bufsize); - err = mtd_read(mtd, addr, bufsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != bufsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr); + err = mtdtest_read(mtd, addr, bufsize, twopages); + if (err) break; - } if (memcmp(twopages, writebuf + (j * pgsize), bufsize)) { pr_err("error: verify failed at %#llx\n", (long long)addr); @@ -145,31 +98,16 @@ static int verify_eraseblock(int ebnum) struct rnd_state old_state = rnd_state; /* Do a read to set the internal dataRAMs to different data */ - err = mtd_read(mtd, addr0, bufsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != bufsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr0); + err = mtdtest_read(mtd, addr0, bufsize, twopages); + if (err) return err; - } - err = mtd_read(mtd, addrn - bufsize, bufsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != bufsize) { - pr_err("error: read failed at %#llx\n", - (long long)(addrn - bufsize)); + err = mtdtest_read(mtd, addrn - bufsize, bufsize, twopages); + if (err) return err; - } memset(twopages, 0, bufsize); - err = mtd_read(mtd, addr, bufsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != bufsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr); + err = mtdtest_read(mtd, addr, bufsize, twopages); + if (err) return err; - } memcpy(boundary, writebuf + mtd->erasesize - pgsize, pgsize); prandom_bytes_state(&rnd_state, boundary + pgsize, pgsize); if (memcmp(twopages, boundary, bufsize)) { @@ -184,17 +122,14 @@ static int verify_eraseblock(int ebnum) static int crosstest(void) { - size_t read; int err = 0, i; loff_t addr, addr0, addrn; unsigned char *pp1, *pp2, *pp3, *pp4; pr_info("crosstest\n"); pp1 = kmalloc(pgsize * 4, GFP_KERNEL); - if (!pp1) { - pr_err("error: cannot allocate memory\n"); + if (!pp1) return -ENOMEM; - } pp2 = pp1 + pgsize; pp3 = pp2 + pgsize; pp4 = pp3 + pgsize; @@ -210,24 +145,16 @@ static int crosstest(void) /* Read 2nd-to-last page to pp1 */ addr = addrn - pgsize - pgsize; - err = mtd_read(mtd, addr, pgsize, &read, pp1); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr); + err = mtdtest_read(mtd, addr, pgsize, pp1); + if (err) { kfree(pp1); return err; } /* Read 3rd-to-last page to pp1 */ addr = addrn - pgsize - pgsize - pgsize; - err = mtd_read(mtd, addr, pgsize, &read, pp1); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr); + err = mtdtest_read(mtd, addr, pgsize, pp1); + if (err) { kfree(pp1); return err; } @@ -235,12 +162,8 @@ static int crosstest(void) /* Read first page to pp2 */ addr = addr0; pr_info("reading page at %#llx\n", (long long)addr); - err = mtd_read(mtd, addr, pgsize, &read, pp2); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr); + err = mtdtest_read(mtd, addr, pgsize, pp2); + if (err) { kfree(pp1); return err; } @@ -248,12 +171,8 @@ static int crosstest(void) /* Read last page to pp3 */ addr = addrn - pgsize; pr_info("reading page at %#llx\n", (long long)addr); - err = mtd_read(mtd, addr, pgsize, &read, pp3); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr); + err = mtdtest_read(mtd, addr, pgsize, pp3); + if (err) { kfree(pp1); return err; } @@ -261,12 +180,8 @@ static int crosstest(void) /* Read first page again to pp4 */ addr = addr0; pr_info("reading page at %#llx\n", (long long)addr); - err = mtd_read(mtd, addr, pgsize, &read, pp4); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr); + err = mtdtest_read(mtd, addr, pgsize, pp4); + if (err) { kfree(pp1); return err; } @@ -285,7 +200,6 @@ static int crosstest(void) static int erasecrosstest(void) { - size_t read, written; int err = 0, i, ebnum, ebnum2; loff_t addr0; char *readbuf = twopages; @@ -304,30 +218,22 @@ static int erasecrosstest(void) ebnum2 -= 1; pr_info("erasing block %d\n", ebnum); - err = erase_eraseblock(ebnum); + err = mtdtest_erase_eraseblock(mtd, ebnum); if (err) return err; pr_info("writing 1st page of block %d\n", ebnum); prandom_bytes_state(&rnd_state, writebuf, pgsize); strcpy(writebuf, "There is no data like this!"); - err = mtd_write(mtd, addr0, pgsize, &written, writebuf); - if (err || written != pgsize) { - pr_info("error: write failed at %#llx\n", - (long long)addr0); - return err ? err : -1; - } + err = mtdtest_write(mtd, addr0, pgsize, writebuf); + if (err) + return err; pr_info("reading 1st page of block %d\n", ebnum); memset(readbuf, 0, pgsize); - err = mtd_read(mtd, addr0, pgsize, &read, readbuf); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr0); - return err ? err : -1; - } + err = mtdtest_read(mtd, addr0, pgsize, readbuf); + if (err) + return err; pr_info("verifying 1st page of block %d\n", ebnum); if (memcmp(writebuf, readbuf, pgsize)) { @@ -337,35 +243,27 @@ static int erasecrosstest(void) } pr_info("erasing block %d\n", ebnum); - err = erase_eraseblock(ebnum); + err = mtdtest_erase_eraseblock(mtd, ebnum); if (err) return err; pr_info("writing 1st page of block %d\n", ebnum); prandom_bytes_state(&rnd_state, writebuf, pgsize); strcpy(writebuf, "There is no data like this!"); - err = mtd_write(mtd, addr0, pgsize, &written, writebuf); - if (err || written != pgsize) { - pr_err("error: write failed at %#llx\n", - (long long)addr0); - return err ? err : -1; - } + err = mtdtest_write(mtd, addr0, pgsize, writebuf); + if (err) + return err; pr_info("erasing block %d\n", ebnum2); - err = erase_eraseblock(ebnum2); + err = mtdtest_erase_eraseblock(mtd, ebnum2); if (err) return err; pr_info("reading 1st page of block %d\n", ebnum); memset(readbuf, 0, pgsize); - err = mtd_read(mtd, addr0, pgsize, &read, readbuf); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr0); - return err ? err : -1; - } + err = mtdtest_read(mtd, addr0, pgsize, readbuf); + if (err) + return err; pr_info("verifying 1st page of block %d\n", ebnum); if (memcmp(writebuf, readbuf, pgsize)) { @@ -381,7 +279,6 @@ static int erasecrosstest(void) static int erasetest(void) { - size_t read, written; int err = 0, i, ebnum, ok = 1; loff_t addr0; @@ -395,33 +292,25 @@ static int erasetest(void) } pr_info("erasing block %d\n", ebnum); - err = erase_eraseblock(ebnum); + err = mtdtest_erase_eraseblock(mtd, ebnum); if (err) return err; pr_info("writing 1st page of block %d\n", ebnum); prandom_bytes_state(&rnd_state, writebuf, pgsize); - err = mtd_write(mtd, addr0, pgsize, &written, writebuf); - if (err || written != pgsize) { - pr_err("error: write failed at %#llx\n", - (long long)addr0); - return err ? err : -1; - } + err = mtdtest_write(mtd, addr0, pgsize, writebuf); + if (err) + return err; pr_info("erasing block %d\n", ebnum); - err = erase_eraseblock(ebnum); + err = mtdtest_erase_eraseblock(mtd, ebnum); if (err) return err; pr_info("reading 1st page of block %d\n", ebnum); - err = mtd_read(mtd, addr0, pgsize, &read, twopages); - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr0); - return err ? err : -1; - } + err = mtdtest_read(mtd, addr0, pgsize, twopages); + if (err) + return err; pr_info("verifying 1st page of block %d is all 0xff\n", ebnum); @@ -440,38 +329,6 @@ static int erasetest(void) return err; } -static int is_block_bad(int ebnum) -{ - loff_t addr = ebnum * mtd->erasesize; - int ret; - - ret = mtd_block_isbad(mtd, addr); - if (ret) - pr_info("block %d is bad\n", ebnum); - return ret; -} - -static int scan_for_bad_eraseblocks(void) -{ - int i, bad = 0; - - bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) { - pr_err("error: cannot allocate memory\n"); - return -ENOMEM; - } - - pr_info("scanning for bad eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - bbt[i] = is_block_bad(i) ? 1 : 0; - if (bbt[i]) - bad += 1; - cond_resched(); - } - pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); - return 0; -} - static int __init mtd_pagetest_init(void) { int err = 0; @@ -496,7 +353,7 @@ static int __init mtd_pagetest_init(void) return err; } - if (mtd->type != MTD_NANDFLASH) { + if (!mtd_type_is_nand(mtd)) { pr_info("this test requires NAND flash\n"); goto out; } @@ -516,36 +373,28 @@ static int __init mtd_pagetest_init(void) err = -ENOMEM; bufsize = pgsize * 2; writebuf = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!writebuf) { - pr_err("error: cannot allocate memory\n"); + if (!writebuf) goto out; - } twopages = kmalloc(bufsize, GFP_KERNEL); - if (!twopages) { - pr_err("error: cannot allocate memory\n"); + if (!twopages) goto out; - } boundary = kmalloc(bufsize, GFP_KERNEL); - if (!boundary) { - pr_err("error: cannot allocate memory\n"); + if (!boundary) goto out; - } - err = scan_for_bad_eraseblocks(); + bbt = kzalloc(ebcnt, GFP_KERNEL); + if (!bbt) + goto out; + err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; /* Erase all eraseblocks */ pr_info("erasing whole device\n"); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = erase_eraseblock(i); - if (err) - goto out; - cond_resched(); - } - pr_info("erased %u eraseblocks\n", i); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); + if (err) + goto out; + pr_info("erased %u eraseblocks\n", ebcnt); /* Write all eraseblocks */ prandom_seed_state(&rnd_state, 1); diff --git a/drivers/mtd/tests/mtd_readtest.c b/drivers/mtd/tests/readtest.c index 266de04b6d29..626e66d0f7e7 100644 --- a/drivers/mtd/tests/mtd_readtest.c +++ b/drivers/mtd/tests/readtest.c @@ -29,6 +29,8 @@ #include <linux/slab.h> #include <linux/sched.h> +#include "mtd_test.h" + static int dev = -EINVAL; module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); @@ -44,7 +46,6 @@ static int pgcnt; static int read_eraseblock_by_page(int ebnum) { - size_t read; int i, ret, err = 0; loff_t addr = ebnum * mtd->erasesize; void *buf = iobuf; @@ -52,16 +53,10 @@ static int read_eraseblock_by_page(int ebnum) for (i = 0; i < pgcnt; i++) { memset(buf, 0 , pgsize); - ret = mtd_read(mtd, addr, pgsize, &read, buf); - if (ret == -EUCLEAN) - ret = 0; - if (ret || read != pgsize) { - pr_err("error: read failed at %#llx\n", - (long long)addr); + ret = mtdtest_read(mtd, addr, pgsize, buf); + if (ret) { if (!err) err = ret; - if (!err) - err = -EINVAL; } if (mtd->oobsize) { struct mtd_oob_ops ops; @@ -127,41 +122,6 @@ static void dump_eraseblock(int ebnum) } } -static int is_block_bad(int ebnum) -{ - loff_t addr = ebnum * mtd->erasesize; - int ret; - - ret = mtd_block_isbad(mtd, addr); - if (ret) - pr_info("block %d is bad\n", ebnum); - return ret; -} - -static int scan_for_bad_eraseblocks(void) -{ - int i, bad = 0; - - bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) { - pr_err("error: cannot allocate memory\n"); - return -ENOMEM; - } - - if (!mtd_can_have_bb(mtd)) - return 0; - - pr_info("scanning for bad eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - bbt[i] = is_block_bad(i) ? 1 : 0; - if (bbt[i]) - bad += 1; - cond_resched(); - } - pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); - return 0; -} - static int __init mtd_readtest_init(void) { uint64_t tmp; @@ -204,17 +164,16 @@ static int __init mtd_readtest_init(void) err = -ENOMEM; iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!iobuf) { - pr_err("error: cannot allocate memory\n"); + if (!iobuf) goto out; - } iobuf1 = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!iobuf1) { - pr_err("error: cannot allocate memory\n"); + if (!iobuf1) goto out; - } - err = scan_for_bad_eraseblocks(); + bbt = kzalloc(ebcnt, GFP_KERNEL); + if (!bbt) + goto out; + err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; diff --git a/drivers/mtd/tests/mtd_speedtest.c b/drivers/mtd/tests/speedtest.c index a6ce9c1fa6c5..87ff6a29f84e 100644 --- a/drivers/mtd/tests/mtd_speedtest.c +++ b/drivers/mtd/tests/speedtest.c @@ -30,6 +30,8 @@ #include <linux/sched.h> #include <linux/random.h> +#include "mtd_test.h" + static int dev = -EINVAL; module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); @@ -49,33 +51,6 @@ static int pgcnt; static int goodebcnt; static struct timeval start, finish; - -static int erase_eraseblock(int ebnum) -{ - int err; - struct erase_info ei; - loff_t addr = ebnum * mtd->erasesize; - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (err) { - pr_err("error %d while erasing EB %d\n", err, ebnum); - return err; - } - - if (ei.state == MTD_ERASE_FAILED) { - pr_err("some erase error occurred at EB %d\n", - ebnum); - return -EIO; - } - - return 0; -} - static int multiblock_erase(int ebnum, int blocks) { int err; @@ -103,54 +78,23 @@ static int multiblock_erase(int ebnum, int blocks) return 0; } -static int erase_whole_device(void) -{ - int err; - unsigned int i; - - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = erase_eraseblock(i); - if (err) - return err; - cond_resched(); - } - return 0; -} - static int write_eraseblock(int ebnum) { - size_t written; - int err = 0; loff_t addr = ebnum * mtd->erasesize; - err = mtd_write(mtd, addr, mtd->erasesize, &written, iobuf); - if (err || written != mtd->erasesize) { - pr_err("error: write failed at %#llx\n", addr); - if (!err) - err = -EINVAL; - } - - return err; + return mtdtest_write(mtd, addr, mtd->erasesize, iobuf); } static int write_eraseblock_by_page(int ebnum) { - size_t written; int i, err = 0; loff_t addr = ebnum * mtd->erasesize; void *buf = iobuf; for (i = 0; i < pgcnt; i++) { - err = mtd_write(mtd, addr, pgsize, &written, buf); - if (err || written != pgsize) { - pr_err("error: write failed at %#llx\n", - addr); - if (!err) - err = -EINVAL; + err = mtdtest_write(mtd, addr, pgsize, buf); + if (err) break; - } addr += pgsize; buf += pgsize; } @@ -160,74 +104,41 @@ static int write_eraseblock_by_page(int ebnum) static int write_eraseblock_by_2pages(int ebnum) { - size_t written, sz = pgsize * 2; + size_t sz = pgsize * 2; int i, n = pgcnt / 2, err = 0; loff_t addr = ebnum * mtd->erasesize; void *buf = iobuf; for (i = 0; i < n; i++) { - err = mtd_write(mtd, addr, sz, &written, buf); - if (err || written != sz) { - pr_err("error: write failed at %#llx\n", - addr); - if (!err) - err = -EINVAL; + err = mtdtest_write(mtd, addr, sz, buf); + if (err) return err; - } addr += sz; buf += sz; } - if (pgcnt % 2) { - err = mtd_write(mtd, addr, pgsize, &written, buf); - if (err || written != pgsize) { - pr_err("error: write failed at %#llx\n", - addr); - if (!err) - err = -EINVAL; - } - } + if (pgcnt % 2) + err = mtdtest_write(mtd, addr, pgsize, buf); return err; } static int read_eraseblock(int ebnum) { - size_t read; - int err = 0; loff_t addr = ebnum * mtd->erasesize; - err = mtd_read(mtd, addr, mtd->erasesize, &read, iobuf); - /* Ignore corrected ECC errors */ - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != mtd->erasesize) { - pr_err("error: read failed at %#llx\n", addr); - if (!err) - err = -EINVAL; - } - - return err; + return mtdtest_read(mtd, addr, mtd->erasesize, iobuf); } static int read_eraseblock_by_page(int ebnum) { - size_t read; int i, err = 0; loff_t addr = ebnum * mtd->erasesize; void *buf = iobuf; for (i = 0; i < pgcnt; i++) { - err = mtd_read(mtd, addr, pgsize, &read, buf); - /* Ignore corrected ECC errors */ - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - addr); - if (!err) - err = -EINVAL; + err = mtdtest_read(mtd, addr, pgsize, buf); + if (err) break; - } addr += pgsize; buf += pgsize; } @@ -237,53 +148,24 @@ static int read_eraseblock_by_page(int ebnum) static int read_eraseblock_by_2pages(int ebnum) { - size_t read, sz = pgsize * 2; + size_t sz = pgsize * 2; int i, n = pgcnt / 2, err = 0; loff_t addr = ebnum * mtd->erasesize; void *buf = iobuf; for (i = 0; i < n; i++) { - err = mtd_read(mtd, addr, sz, &read, buf); - /* Ignore corrected ECC errors */ - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != sz) { - pr_err("error: read failed at %#llx\n", - addr); - if (!err) - err = -EINVAL; + err = mtdtest_read(mtd, addr, sz, buf); + if (err) return err; - } addr += sz; buf += sz; } - if (pgcnt % 2) { - err = mtd_read(mtd, addr, pgsize, &read, buf); - /* Ignore corrected ECC errors */ - if (mtd_is_bitflip(err)) - err = 0; - if (err || read != pgsize) { - pr_err("error: read failed at %#llx\n", - addr); - if (!err) - err = -EINVAL; - } - } + if (pgcnt % 2) + err = mtdtest_read(mtd, addr, pgsize, buf); return err; } -static int is_block_bad(int ebnum) -{ - loff_t addr = ebnum * mtd->erasesize; - int ret; - - ret = mtd_block_isbad(mtd, addr); - if (ret) - pr_info("block %d is bad\n", ebnum); - return ret; -} - static inline void start_timing(void) { do_gettimeofday(&start); @@ -308,32 +190,6 @@ static long calc_speed(void) return k; } -static int scan_for_bad_eraseblocks(void) -{ - int i, bad = 0; - - bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) { - pr_err("error: cannot allocate memory\n"); - return -ENOMEM; - } - - if (!mtd_can_have_bb(mtd)) - goto out; - - pr_info("scanning for bad eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - bbt[i] = is_block_bad(i) ? 1 : 0; - if (bbt[i]) - bad += 1; - cond_resched(); - } - pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); -out: - goodebcnt = ebcnt - bad; - return 0; -} - static int __init mtd_speedtest_init(void) { int err, i, blocks, j, k; @@ -384,18 +240,23 @@ static int __init mtd_speedtest_init(void) err = -ENOMEM; iobuf = kmalloc(mtd->erasesize, GFP_KERNEL); - if (!iobuf) { - pr_err("error: cannot allocate memory\n"); + if (!iobuf) goto out; - } prandom_bytes(iobuf, mtd->erasesize); - err = scan_for_bad_eraseblocks(); + bbt = kzalloc(ebcnt, GFP_KERNEL); + if (!bbt) + goto out; + err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; + for (i = 0; i < ebcnt; i++) { + if (!bbt[i]) + goodebcnt++; + } - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -429,7 +290,7 @@ static int __init mtd_speedtest_init(void) speed = calc_speed(); pr_info("eraseblock read speed is %ld KiB/s\n", speed); - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -463,7 +324,7 @@ static int __init mtd_speedtest_init(void) speed = calc_speed(); pr_info("page read speed is %ld KiB/s\n", speed); - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -500,14 +361,9 @@ static int __init mtd_speedtest_init(void) /* Erase all eraseblocks */ pr_info("Testing erase speed\n"); start_timing(); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = erase_eraseblock(i); - if (err) - goto out; - cond_resched(); - } + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); + if (err) + goto out; stop_timing(); speed = calc_speed(); pr_info("erase speed is %ld KiB/s\n", speed); diff --git a/drivers/mtd/tests/mtd_stresstest.c b/drivers/mtd/tests/stresstest.c index 787f539d16ca..c9d42cc2df1b 100644 --- a/drivers/mtd/tests/mtd_stresstest.c +++ b/drivers/mtd/tests/stresstest.c @@ -31,6 +31,8 @@ #include <linux/vmalloc.h> #include <linux/random.h> +#include "mtd_test.h" + static int dev = -EINVAL; module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); @@ -81,49 +83,11 @@ static int rand_len(int offs) return len; } -static int erase_eraseblock(int ebnum) -{ - int err; - struct erase_info ei; - loff_t addr = ebnum * mtd->erasesize; - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (unlikely(err)) { - pr_err("error %d while erasing EB %d\n", err, ebnum); - return err; - } - - if (unlikely(ei.state == MTD_ERASE_FAILED)) { - pr_err("some erase error occurred at EB %d\n", - ebnum); - return -EIO; - } - - return 0; -} - -static int is_block_bad(int ebnum) -{ - loff_t addr = ebnum * mtd->erasesize; - int ret; - - ret = mtd_block_isbad(mtd, addr); - if (ret) - pr_info("block %d is bad\n", ebnum); - return ret; -} - static int do_read(void) { - size_t read; int eb = rand_eb(); int offs = rand_offs(); - int len = rand_len(offs), err; + int len = rand_len(offs); loff_t addr; if (bbt[eb + 1]) { @@ -133,28 +97,17 @@ static int do_read(void) len = mtd->erasesize - offs; } addr = eb * mtd->erasesize + offs; - err = mtd_read(mtd, addr, len, &read, readbuf); - if (mtd_is_bitflip(err)) - err = 0; - if (unlikely(err || read != len)) { - pr_err("error: read failed at 0x%llx\n", - (long long)addr); - if (!err) - err = -EINVAL; - return err; - } - return 0; + return mtdtest_read(mtd, addr, len, readbuf); } static int do_write(void) { int eb = rand_eb(), offs, err, len; - size_t written; loff_t addr; offs = offsets[eb]; if (offs >= mtd->erasesize) { - err = erase_eraseblock(eb); + err = mtdtest_erase_eraseblock(mtd, eb); if (err) return err; offs = offsets[eb] = 0; @@ -165,21 +118,16 @@ static int do_write(void) if (bbt[eb + 1]) len = mtd->erasesize - offs; else { - err = erase_eraseblock(eb + 1); + err = mtdtest_erase_eraseblock(mtd, eb + 1); if (err) return err; offsets[eb + 1] = 0; } } addr = eb * mtd->erasesize + offs; - err = mtd_write(mtd, addr, len, &written, writebuf); - if (unlikely(err || written != len)) { - pr_err("error: write failed at 0x%llx\n", - (long long)addr); - if (!err) - err = -EINVAL; + err = mtdtest_write(mtd, addr, len, writebuf); + if (unlikely(err)) return err; - } offs += len; while (offs > mtd->erasesize) { offsets[eb++] = mtd->erasesize; @@ -197,30 +145,6 @@ static int do_operation(void) return do_write(); } -static int scan_for_bad_eraseblocks(void) -{ - int i, bad = 0; - - bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) { - pr_err("error: cannot allocate memory\n"); - return -ENOMEM; - } - - if (!mtd_can_have_bb(mtd)) - return 0; - - pr_info("scanning for bad eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - bbt[i] = is_block_bad(i) ? 1 : 0; - if (bbt[i]) - bad += 1; - cond_resched(); - } - pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); - return 0; -} - static int __init mtd_stresstest_init(void) { int err; @@ -276,15 +200,16 @@ static int __init mtd_stresstest_init(void) readbuf = vmalloc(bufsize); writebuf = vmalloc(bufsize); offsets = kmalloc(ebcnt * sizeof(int), GFP_KERNEL); - if (!readbuf || !writebuf || !offsets) { - pr_err("error: cannot allocate memory\n"); + if (!readbuf || !writebuf || !offsets) goto out; - } for (i = 0; i < ebcnt; i++) offsets[i] = mtd->erasesize; prandom_bytes(writebuf, bufsize); - err = scan_for_bad_eraseblocks(); + bbt = kzalloc(ebcnt, GFP_KERNEL); + if (!bbt) + goto out; + err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; diff --git a/drivers/mtd/tests/mtd_subpagetest.c b/drivers/mtd/tests/subpagetest.c index aade56f27945..a876371ad410 100644 --- a/drivers/mtd/tests/mtd_subpagetest.c +++ b/drivers/mtd/tests/subpagetest.c @@ -30,6 +30,8 @@ #include <linux/sched.h> #include <linux/random.h> +#include "mtd_test.h" + static int dev = -EINVAL; module_param(dev, int, S_IRUGO); MODULE_PARM_DESC(dev, "MTD device number to use"); @@ -51,50 +53,6 @@ static inline void clear_data(unsigned char *buf, size_t len) memset(buf, 0, len); } -static int erase_eraseblock(int ebnum) -{ - int err; - struct erase_info ei; - loff_t addr = ebnum * mtd->erasesize; - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (err) { - pr_err("error %d while erasing EB %d\n", err, ebnum); - return err; - } - - if (ei.state == MTD_ERASE_FAILED) { - pr_err("some erase error occurred at EB %d\n", - ebnum); - return -EIO; - } - - return 0; -} - -static int erase_whole_device(void) -{ - int err; - unsigned int i; - - pr_info("erasing whole device\n"); - for (i = 0; i < ebcnt; ++i) { - if (bbt[i]) - continue; - err = erase_eraseblock(i); - if (err) - return err; - cond_resched(); - } - pr_info("erased %u eraseblocks\n", i); - return 0; -} - static int write_eraseblock(int ebnum) { size_t written; @@ -317,38 +275,6 @@ static int verify_all_eraseblocks_ff(void) return 0; } -static int is_block_bad(int ebnum) -{ - loff_t addr = ebnum * mtd->erasesize; - int ret; - - ret = mtd_block_isbad(mtd, addr); - if (ret) - pr_info("block %d is bad\n", ebnum); - return ret; -} - -static int scan_for_bad_eraseblocks(void) -{ - int i, bad = 0; - - bbt = kzalloc(ebcnt, GFP_KERNEL); - if (!bbt) { - pr_err("error: cannot allocate memory\n"); - return -ENOMEM; - } - - pr_info("scanning for bad eraseblocks\n"); - for (i = 0; i < ebcnt; ++i) { - bbt[i] = is_block_bad(i) ? 1 : 0; - if (bbt[i]) - bad += 1; - cond_resched(); - } - pr_info("scanned %d eraseblocks, %d are bad\n", i, bad); - return 0; -} - static int __init mtd_subpagetest_init(void) { int err = 0; @@ -373,7 +299,7 @@ static int __init mtd_subpagetest_init(void) return err; } - if (mtd->type != MTD_NANDFLASH) { + if (!mtd_type_is_nand(mtd)) { pr_info("this test requires NAND flash\n"); goto out; } @@ -393,21 +319,20 @@ static int __init mtd_subpagetest_init(void) err = -ENOMEM; bufsize = subpgsize * 32; writebuf = kmalloc(bufsize, GFP_KERNEL); - if (!writebuf) { - pr_info("error: cannot allocate memory\n"); + if (!writebuf) goto out; - } readbuf = kmalloc(bufsize, GFP_KERNEL); - if (!readbuf) { - pr_info("error: cannot allocate memory\n"); + if (!readbuf) + goto out; + bbt = kzalloc(ebcnt, GFP_KERNEL); + if (!bbt) goto out; - } - err = scan_for_bad_eraseblocks(); + err = mtdtest_scan_for_bad_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -439,7 +364,7 @@ static int __init mtd_subpagetest_init(void) } pr_info("verified %u eraseblocks\n", i); - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; @@ -477,7 +402,7 @@ static int __init mtd_subpagetest_init(void) } pr_info("verified %u eraseblocks\n", i); - err = erase_whole_device(); + err = mtdtest_erase_good_eraseblocks(mtd, bbt, 0, ebcnt); if (err) goto out; diff --git a/drivers/mtd/tests/mtd_torturetest.c b/drivers/mtd/tests/torturetest.c index 3a9f6a6a79f9..eeab96973cf0 100644 --- a/drivers/mtd/tests/mtd_torturetest.c +++ b/drivers/mtd/tests/torturetest.c @@ -32,6 +32,7 @@ #include <linux/mtd/mtd.h> #include <linux/slab.h> #include <linux/sched.h> +#include "mtd_test.h" #define RETRIES 3 @@ -93,35 +94,6 @@ static inline void stop_timing(void) } /* - * Erase eraseblock number @ebnum. - */ -static inline int erase_eraseblock(int ebnum) -{ - int err; - struct erase_info ei; - loff_t addr = ebnum * mtd->erasesize; - - memset(&ei, 0, sizeof(struct erase_info)); - ei.mtd = mtd; - ei.addr = addr; - ei.len = mtd->erasesize; - - err = mtd_erase(mtd, &ei); - if (err) { - pr_err("error %d while erasing EB %d\n", err, ebnum); - return err; - } - - if (ei.state == MTD_ERASE_FAILED) { - pr_err("some erase error occurred at EB %d\n", - ebnum); - return -EIO; - } - - return 0; -} - -/* * Check that the contents of eraseblock number @enbum is equivalent to the * @buf buffer. */ @@ -208,7 +180,7 @@ static inline int write_pattern(int ebnum, void *buf) static int __init tort_init(void) { int err = 0, i, infinite = !cycles_count; - int *bad_ebs; + unsigned char *bad_ebs; printk(KERN_INFO "\n"); printk(KERN_INFO "=================================================\n"); @@ -265,7 +237,7 @@ static int __init tort_init(void) if (!check_buf) goto out_patt_FF; - bad_ebs = kcalloc(ebcnt, sizeof(*bad_ebs), GFP_KERNEL); + bad_ebs = kzalloc(ebcnt, GFP_KERNEL); if (!bad_ebs) goto out_check_buf; @@ -283,40 +255,16 @@ static int __init tort_init(void) } } - /* - * Check if there is a bad eraseblock among those we are going to test. - */ - if (mtd_can_have_bb(mtd)) { - for (i = eb; i < eb + ebcnt; i++) { - err = mtd_block_isbad(mtd, (loff_t)i * mtd->erasesize); - - if (err < 0) { - pr_info("block_isbad() returned %d " - "for EB %d\n", err, i); - goto out; - } - - if (err) { - pr_err("EB %d is bad. Skip it.\n", i); - bad_ebs[i - eb] = 1; - } - } - } + err = mtdtest_scan_for_bad_eraseblocks(mtd, bad_ebs, eb, ebcnt); + if (err) + goto out; start_timing(); while (1) { int i; void *patt; - /* Erase all eraseblocks */ - for (i = eb; i < eb + ebcnt; i++) { - if (bad_ebs[i - eb]) - continue; - err = erase_eraseblock(i); - if (err) - goto out; - cond_resched(); - } + mtdtest_erase_good_eraseblocks(mtd, bad_ebs, eb, ebcnt); /* Check if the eraseblocks contain only 0xFF bytes */ if (check) { diff --git a/drivers/mtd/ubi/attach.c b/drivers/mtd/ubi/attach.c index c071d410488f..33bb1f2b63e4 100644 --- a/drivers/mtd/ubi/attach.c +++ b/drivers/mtd/ubi/attach.c @@ -900,10 +900,9 @@ static int scan_peb(struct ubi_device *ubi, struct ubi_attach_info *ai, * number. */ image_seq = be32_to_cpu(ech->image_seq); - if (!ubi->image_seq && image_seq) + if (!ubi->image_seq) ubi->image_seq = image_seq; - if (ubi->image_seq && image_seq && - ubi->image_seq != image_seq) { + if (image_seq && ubi->image_seq != image_seq) { ubi_err("bad image sequence number %d in PEB %d, expected %d", image_seq, pnum, ubi->image_seq); ubi_dump_ec_hdr(ech); @@ -1417,9 +1416,11 @@ int ubi_attach(struct ubi_device *ubi, int force_scan) ai = alloc_ai("ubi_aeb_slab_cache2"); if (!ai) return -ENOMEM; - } - err = scan_all(ubi, ai, UBI_FM_MAX_START); + err = scan_all(ubi, ai, 0); + } else { + err = scan_all(ubi, ai, UBI_FM_MAX_START); + } } } #else diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 315dcc6ec1f5..e05dc6298c1d 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -41,6 +41,7 @@ #include <linux/kthread.h> #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/major.h> #include "ubi.h" /* Maximum length of the 'mtd=' parameter */ diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c index 154275182b4b..ead861307b3c 100644 --- a/drivers/mtd/ubi/fastmap.c +++ b/drivers/mtd/ubi/fastmap.c @@ -407,6 +407,7 @@ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai, */ for (i = 0; i < pool_size; i++) { int scrub = 0; + int image_seq; pnum = be32_to_cpu(pebs[i]); @@ -425,10 +426,16 @@ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai, } else if (ret == UBI_IO_BITFLIPS) scrub = 1; - if (be32_to_cpu(ech->image_seq) != ubi->image_seq) { + /* + * Older UBI implementations have image_seq set to zero, so + * we shouldn't fail if image_seq == 0. + */ + image_seq = be32_to_cpu(ech->image_seq); + + if (image_seq && (image_seq != ubi->image_seq)) { ubi_err("bad image seq: 0x%x, expected: 0x%x", be32_to_cpu(ech->image_seq), ubi->image_seq); - err = UBI_BAD_FASTMAP; + ret = UBI_BAD_FASTMAP; goto out; } @@ -819,6 +826,10 @@ static int ubi_attach_fastmap(struct ubi_device *ubi, list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &free, u.list) list_move_tail(&tmp_aeb->u.list, &ai->free); + ubi_assert(list_empty(&used)); + ubi_assert(list_empty(&eba_orphans)); + ubi_assert(list_empty(&free)); + /* * If fastmap is leaking PEBs (must not happen), raise a * fat warning and fall back to scanning mode. @@ -834,6 +845,19 @@ static int ubi_attach_fastmap(struct ubi_device *ubi, fail_bad: ret = UBI_BAD_FASTMAP; fail: + list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &used, u.list) { + kmem_cache_free(ai->aeb_slab_cache, tmp_aeb); + list_del(&tmp_aeb->u.list); + } + list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &eba_orphans, u.list) { + kmem_cache_free(ai->aeb_slab_cache, tmp_aeb); + list_del(&tmp_aeb->u.list); + } + list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &free, u.list) { + kmem_cache_free(ai->aeb_slab_cache, tmp_aeb); + list_del(&tmp_aeb->u.list); + } + return ret; } @@ -923,6 +947,8 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, } for (i = 0; i < used_blocks; i++) { + int image_seq; + pnum = be32_to_cpu(fmsb->block_loc[i]); if (ubi_io_is_bad(ubi, pnum)) { @@ -940,10 +966,17 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, } else if (ret == UBI_IO_BITFLIPS) fm->to_be_tortured[i] = 1; + image_seq = be32_to_cpu(ech->image_seq); if (!ubi->image_seq) - ubi->image_seq = be32_to_cpu(ech->image_seq); + ubi->image_seq = image_seq; - if (be32_to_cpu(ech->image_seq) != ubi->image_seq) { + /* + * Older UBI implementations have image_seq set to zero, so + * we shouldn't fail if image_seq == 0. + */ + if (image_seq && (image_seq != ubi->image_seq)) { + ubi_err("wrong image seq:%d instead of %d", + be32_to_cpu(ech->image_seq), ubi->image_seq); ret = UBI_BAD_FASTMAP; goto free_hdr; } @@ -1343,7 +1376,7 @@ out: static int invalidate_fastmap(struct ubi_device *ubi, struct ubi_fastmap_layout *fm) { - int ret, i; + int ret; struct ubi_vid_hdr *vh; ret = erase_block(ubi, fm->e[0]->pnum); @@ -1360,9 +1393,6 @@ static int invalidate_fastmap(struct ubi_device *ubi, vh->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); ret = ubi_io_write_vid_hdr(ubi, fm->e[0]->pnum, vh); - for (i = 0; i < fm->used_blocks; i++) - ubi_wl_put_fm_peb(ubi, fm->e[i], i, fm->to_be_tortured[i]); - return ret; } diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 5df49d3cb5c7..02317c1c0238 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -599,10 +599,6 @@ static void refill_wl_user_pool(struct ubi_device *ubi) return_unused_pool_pebs(ubi, pool); for (pool->size = 0; pool->size < pool->max_size; pool->size++) { - if (!ubi->free.rb_node || - (ubi->free_count - ubi->beb_rsvd_pebs < 1)) - break; - pool->pebs[pool->size] = __wl_get_peb(ubi); if (pool->pebs[pool->size] < 0) break; @@ -1069,6 +1065,9 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) { dbg_wl("no WL needed: min used EC %d, max free EC %d", e1->ec, e2->ec); + + /* Give the unused PEB back */ + wl_tree_add(e2, &ubi->free); goto out_cancel; } self_check_in_wl_tree(ubi, e1, &ubi->used); |