diff options
author | Grant Likely <grant.likely@secretlab.ca> | 2010-05-22 06:36:56 (GMT) |
---|---|---|
committer | Grant Likely <grant.likely@secretlab.ca> | 2010-05-22 06:36:56 (GMT) |
commit | cf9b59e9d3e008591d1f54830f570982bb307a0d (patch) | |
tree | 113478ce8fd8c832ba726ffdf59b82cb46356476 /drivers/mtd | |
parent | 44504b2bebf8b5823c59484e73096a7d6574471d (diff) | |
parent | f4b87dee923342505e1ddba8d34ce9de33e75050 (diff) | |
download | linux-cf9b59e9d3e008591d1f54830f570982bb307a0d.tar.xz |
Merge remote branch 'origin' into secretlab/next-devicetree
Merging in current state of Linus' tree to deal with merge conflicts and
build failures in vio.c after merge.
Conflicts:
drivers/i2c/busses/i2c-cpm.c
drivers/i2c/busses/i2c-mpc.c
drivers/net/gianfar.c
Also fixed up one line in arch/powerpc/kernel/vio.c to use the
correct node pointer.
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Diffstat (limited to 'drivers/mtd')
100 files changed, 9981 insertions, 1631 deletions
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index ecf90f5..f8210bf 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -304,6 +304,19 @@ config SSFDC This enables read only access to SmartMedia formatted NAND flash. You can mount it with FAT file system. + +config SM_FTL + tristate "SmartMedia/xD new translation layer" + depends on EXPERIMENTAL && BLOCK + select MTD_BLKDEVS + select MTD_NAND_ECC + help + This enables new and very EXPERMENTAL support for SmartMedia/xD + FTL (Flash translation layer). + Write support isn't yet well tested, therefore this code IS likely to + eat your card, so please don't use it together with valuable data. + Use readonly driver (CONFIG_SSFDC) instead. + config MTD_OOPS tristate "Log panic/oops to an MTD buffer" depends on MTD diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 82d1e4d..760abc5 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -4,7 +4,7 @@ # Core functionality. obj-$(CONFIG_MTD) += mtd.o -mtd-y := mtdcore.o mtdsuper.o mtdbdi.o +mtd-y := mtdcore.o mtdsuper.o mtd-$(CONFIG_MTD_PARTITIONS) += mtdpart.o obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o @@ -24,6 +24,7 @@ obj-$(CONFIG_NFTL) += nftl.o obj-$(CONFIG_INFTL) += inftl.o obj-$(CONFIG_RFD_FTL) += rfd_ftl.o obj-$(CONFIG_SSFDC) += ssfdc.o +obj-$(CONFIG_SM_FTL) += sm_ftl.o obj-$(CONFIG_MTD_OOPS) += mtdoops.o nftl-objs := nftlcore.o nftlmount.o diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 5fbf29e..62f3ea9 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -615,10 +615,8 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) return mtd; setup_err: - if(mtd) { - kfree(mtd->eraseregions); - kfree(mtd); - } + kfree(mtd->eraseregions); + kfree(mtd); kfree(cfi->cmdset_priv); return NULL; } @@ -727,8 +725,7 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, /* those should be reset too since they create memory references. */ init_waitqueue_head(&chip->wq); - spin_lock_init(&chip->_spinlock); - chip->mutex = &chip->_spinlock; + mutex_init(&chip->mutex); chip++; } } @@ -774,9 +771,9 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long if (chip->priv && map_word_andequal(map, status, status_PWS, status_PWS)) break; - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); /* Someone else might have been playing with it. */ return -EAGAIN; } @@ -823,9 +820,9 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long return -EIO; } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. So we can just loop here. */ } @@ -852,10 +849,10 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long sleep: set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); return -EAGAIN; } } @@ -901,20 +898,20 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr * it'll happily send us to sleep. In any case, when * get_chip returns success we're clear to go ahead. */ - ret = spin_trylock(contender->mutex); + ret = mutex_trylock(&contender->mutex); spin_unlock(&shared->lock); if (!ret) goto retry; - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); ret = chip_ready(map, contender, contender->start, mode); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (ret == -EAGAIN) { - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); goto retry; } if (ret) { - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); return ret; } spin_lock(&shared->lock); @@ -923,10 +920,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr * in FL_SYNCING state. Put contender and retry. */ if (chip->state == FL_SYNCING) { put_chip(map, contender, contender->start); - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); goto retry; } - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); } /* Check if we already have suspended erase @@ -936,10 +933,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr spin_unlock(&shared->lock); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); goto retry; } @@ -969,12 +966,12 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad if (shared->writing && shared->writing != chip) { /* give back ownership to who we loaned it from */ struct flchip *loaner = shared->writing; - spin_lock(loaner->mutex); + mutex_lock(&loaner->mutex); spin_unlock(&shared->lock); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); put_chip(map, loaner, loaner->start); - spin_lock(chip->mutex); - spin_unlock(loaner->mutex); + mutex_lock(&chip->mutex); + mutex_unlock(&loaner->mutex); wake_up(&chip->wq); return; } @@ -1144,7 +1141,7 @@ static int __xipram xip_wait_for_operation( (void) map_read(map, adr); xip_iprefetch(); local_irq_enable(); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); xip_iprefetch(); cond_resched(); @@ -1154,15 +1151,15 @@ static int __xipram xip_wait_for_operation( * a suspended erase state. If so let's wait * until it's done. */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); while (chip->state != newstate) { DECLARE_WAITQUEUE(wait, current); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); } /* Disallow XIP again */ local_irq_disable(); @@ -1218,10 +1215,10 @@ static int inval_cache_and_wait_for_operation( int chip_state = chip->state; unsigned int timeo, sleep_time, reset_timeo; - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); if (inval_len) INVALIDATE_CACHED_RANGE(map, inval_adr, inval_len); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); timeo = chip_op_time_max; if (!timeo) @@ -1241,7 +1238,7 @@ static int inval_cache_and_wait_for_operation( } /* OK Still waiting. Drop the lock, wait a while and retry. */ - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); if (sleep_time >= 1000000/HZ) { /* * Half of the normal delay still remaining @@ -1256,17 +1253,17 @@ static int inval_cache_and_wait_for_operation( cond_resched(); timeo--; } - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); while (chip->state != chip_state) { /* Someone's suspended the operation: sleep */ DECLARE_WAITQUEUE(wait, current); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); } if (chip->erase_suspended && chip_state == FL_ERASING) { /* Erase suspend occured while sleep: reset timeout */ @@ -1302,7 +1299,7 @@ static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t a /* Ensure cmd read/writes are aligned. */ cmd_addr = adr & ~(map_bankwidth(map)-1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, cmd_addr, FL_POINT); @@ -1313,7 +1310,7 @@ static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t a chip->state = FL_POINT; chip->ref_point_counter++; } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1398,7 +1395,7 @@ static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len) else thislen = len; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_POINT) { chip->ref_point_counter--; if(chip->ref_point_counter == 0) @@ -1407,7 +1404,7 @@ static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len) printk(KERN_ERR "%s: Warning: unpoint called on non pointed region\n", map->name); /* Should this give an error? */ put_chip(map, chip, chip->start); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); len -= thislen; ofs = 0; @@ -1426,10 +1423,10 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof /* Ensure cmd read/writes are aligned. */ cmd_addr = adr & ~(map_bankwidth(map)-1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, cmd_addr, FL_READY); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1443,7 +1440,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof put_chip(map, chip, cmd_addr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } @@ -1506,10 +1503,10 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, return -EINVAL; } - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, mode); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1555,7 +1552,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, xip_enable(map, chip, adr); out: put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1664,10 +1661,10 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, /* Let's determine this according to the interleave only once */ write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0xe8) : CMD(0xe9); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, cmd_adr, FL_WRITING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1798,7 +1795,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, xip_enable(map, chip, cmd_adr); out: put_chip(map, chip, cmd_adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1877,10 +1874,10 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, adr += chip->start; retry: - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_ERASING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1936,7 +1933,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, } else if (chipstatus & 0x20 && retries--) { printk(KERN_DEBUG "block erase failed at 0x%08lx: status 0x%lx. Retrying...\n", adr, chipstatus); put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); goto retry; } else { printk(KERN_ERR "%s: block erase failed at 0x%08lx (status 0x%lx)\n", map->name, adr, chipstatus); @@ -1948,7 +1945,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, xip_enable(map, chip, adr); out: put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1981,7 +1978,7 @@ static void cfi_intelext_sync (struct mtd_info *mtd) for (i=0; !ret && i<cfi->numchips; i++) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, chip->start, FL_SYNCING); if (!ret) { @@ -1992,7 +1989,7 @@ static void cfi_intelext_sync (struct mtd_info *mtd) * with the chip now anyway. */ } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } /* Unlock the chips again */ @@ -2000,14 +1997,14 @@ static void cfi_intelext_sync (struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_SYNCING) { chip->state = chip->oldstate; chip->oldstate = FL_READY; wake_up(&chip->wq); } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } } @@ -2053,10 +2050,10 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip adr += chip->start; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_LOCKING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -2090,7 +2087,7 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip xip_enable(map, chip, adr); out: put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -2155,10 +2152,10 @@ do_otp_read(struct map_info *map, struct flchip *chip, u_long offset, struct cfi_private *cfi = map->fldrv_priv; int ret; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -2177,7 +2174,7 @@ do_otp_read(struct map_info *map, struct flchip *chip, u_long offset, INVALIDATE_CACHED_RANGE(map, chip->start + offset, size); put_chip(map, chip, chip->start); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } @@ -2452,7 +2449,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) for (i=0; !ret && i<cfi->numchips; i++) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); switch (chip->state) { case FL_READY: @@ -2484,7 +2481,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) case FL_PM_SUSPENDED: break; } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } /* Unlock the chips again */ @@ -2493,7 +2490,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_PM_SUSPENDED) { /* No need to force it into a known state here, @@ -2503,7 +2500,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) chip->oldstate = FL_READY; wake_up(&chip->wq); } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } } @@ -2544,7 +2541,7 @@ static void cfi_intelext_resume(struct mtd_info *mtd) chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); /* Go to known state. Chip may have been power cycled */ if (chip->state == FL_PM_SUSPENDED) { @@ -2553,7 +2550,7 @@ static void cfi_intelext_resume(struct mtd_info *mtd) wake_up(&chip->wq); } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } if ((mtd->flags & MTD_POWERUP_LOCK) @@ -2573,14 +2570,14 @@ static int cfi_intelext_reset(struct mtd_info *mtd) /* force the completion of any ongoing operation and switch to array mode so any bootloader in flash is accessible for soft reboot. */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, chip->start, FL_SHUTDOWN); if (!ret) { map_write(map, CMD(0xff), chip->start); chip->state = FL_SHUTDOWN; put_chip(map, chip, chip->start); } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } return 0; diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index f3600e8..d81079e 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -32,6 +32,7 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/reboot.h> #include <linux/mtd/compatmac.h> #include <linux/mtd/map.h> #include <linux/mtd/mtd.h> @@ -43,10 +44,6 @@ #define MAX_WORD_RETRIES 3 -#define MANUFACTURER_AMD 0x0001 -#define MANUFACTURER_ATMEL 0x001F -#define MANUFACTURER_MACRONIX 0x00C2 -#define MANUFACTURER_SST 0x00BF #define SST49LF004B 0x0060 #define SST49LF040B 0x0050 #define SST49LF008A 0x005a @@ -60,6 +57,7 @@ static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *); static void cfi_amdstd_sync (struct mtd_info *); static int cfi_amdstd_suspend (struct mtd_info *); static void cfi_amdstd_resume (struct mtd_info *); +static int cfi_amdstd_reboot(struct notifier_block *, unsigned long, void *); static int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static void cfi_amdstd_destroy(struct mtd_info *); @@ -168,7 +166,7 @@ static void fixup_amd_bootblock(struct mtd_info *mtd, void* param) * This reduces the risk of false detection due to * the 8-bit device ID. */ - (cfi->mfr == MANUFACTURER_MACRONIX)) { + (cfi->mfr == CFI_MFR_MACRONIX)) { DEBUG(MTD_DEBUG_LEVEL1, "%s: Macronix MX29LV400C with bottom boot block" " detected\n", map->name); @@ -260,6 +258,42 @@ static void fixup_use_atmel_lock(struct mtd_info *mtd, void *param) mtd->flags |= MTD_POWERUP_LOCK; } +static void fixup_old_sst_eraseregion(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + + /* + * These flashes report two seperate eraseblock regions based on the + * sector_erase-size and block_erase-size, although they both operate on the + * same memory. This is not allowed according to CFI, so we just pick the + * sector_erase-size. + */ + cfi->cfiq->NumEraseRegions = 1; +} + +static void fixup_sst39vf(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + + fixup_old_sst_eraseregion(mtd); + + cfi->addr_unlock1 = 0x5555; + cfi->addr_unlock2 = 0x2AAA; +} + +static void fixup_sst39vf_rev_b(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + + fixup_old_sst_eraseregion(mtd); + + cfi->addr_unlock1 = 0x555; + cfi->addr_unlock2 = 0x2AA; +} + static void fixup_s29gl064n_sectors(struct mtd_info *mtd, void *param) { struct map_info *map = mtd->priv; @@ -282,11 +316,24 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param) } } +/* Used to fix CFI-Tables of chips without Extended Query Tables */ +static struct cfi_fixup cfi_nopri_fixup_table[] = { + { CFI_MFR_SST, 0x234A, fixup_sst39vf, NULL, }, // SST39VF1602 + { CFI_MFR_SST, 0x234B, fixup_sst39vf, NULL, }, // SST39VF1601 + { CFI_MFR_SST, 0x235A, fixup_sst39vf, NULL, }, // SST39VF3202 + { CFI_MFR_SST, 0x235B, fixup_sst39vf, NULL, }, // SST39VF3201 + { CFI_MFR_SST, 0x235C, fixup_sst39vf_rev_b, NULL, }, // SST39VF3202B + { CFI_MFR_SST, 0x235D, fixup_sst39vf_rev_b, NULL, }, // SST39VF3201B + { CFI_MFR_SST, 0x236C, fixup_sst39vf_rev_b, NULL, }, // SST39VF6402B + { CFI_MFR_SST, 0x236D, fixup_sst39vf_rev_b, NULL, }, // SST39VF6401B + { 0, 0, NULL, NULL } +}; + static struct cfi_fixup cfi_fixup_table[] = { { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, #ifdef AMD_BOOTLOC_BUG { CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock, NULL }, - { MANUFACTURER_MACRONIX, CFI_ID_ANY, fixup_amd_bootblock, NULL }, + { CFI_MFR_MACRONIX, CFI_ID_ANY, fixup_amd_bootblock, NULL }, #endif { CFI_MFR_AMD, 0x0050, fixup_use_secsi, NULL, }, { CFI_MFR_AMD, 0x0053, fixup_use_secsi, NULL, }, @@ -304,9 +351,9 @@ static struct cfi_fixup cfi_fixup_table[] = { { 0, 0, NULL, NULL } }; static struct cfi_fixup jedec_fixup_table[] = { - { MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, }, - { MANUFACTURER_SST, SST49LF040B, fixup_use_fwh_lock, NULL, }, - { MANUFACTURER_SST, SST49LF008A, fixup_use_fwh_lock, NULL, }, + { CFI_MFR_SST, SST49LF004B, fixup_use_fwh_lock, NULL, }, + { CFI_MFR_SST, SST49LF040B, fixup_use_fwh_lock, NULL, }, + { CFI_MFR_SST, SST49LF008A, fixup_use_fwh_lock, NULL, }, { 0, 0, NULL, NULL } }; @@ -355,67 +402,72 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) mtd->name = map->name; mtd->writesize = 1; + mtd->reboot_notifier.notifier_call = cfi_amdstd_reboot; + if (cfi->cfi_mode==CFI_MODE_CFI){ unsigned char bootloc; - /* - * It's a real CFI chip, not one for which the probe - * routine faked a CFI structure. So we read the feature - * table from it. - */ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; struct cfi_pri_amdstd *extp; extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu"); - if (!extp) { - kfree(mtd); - return NULL; - } - - cfi_fixup_major_minor(cfi, extp); - - if (extp->MajorVersion != '1' || - (extp->MinorVersion < '0' || extp->MinorVersion > '4')) { - printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query " - "version %c.%c.\n", extp->MajorVersion, - extp->MinorVersion); - kfree(extp); - kfree(mtd); - return NULL; - } + if (extp) { + /* + * It's a real CFI chip, not one for which the probe + * routine faked a CFI structure. + */ + cfi_fixup_major_minor(cfi, extp); + + if (extp->MajorVersion != '1' || + (extp->MinorVersion < '0' || extp->MinorVersion > '4')) { + printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query " + "version %c.%c.\n", extp->MajorVersion, + extp->MinorVersion); + kfree(extp); + kfree(mtd); + return NULL; + } - /* Install our own private info structure */ - cfi->cmdset_priv = extp; + /* Install our own private info structure */ + cfi->cmdset_priv = extp; - /* Apply cfi device specific fixups */ - cfi_fixup(mtd, cfi_fixup_table); + /* Apply cfi device specific fixups */ + cfi_fixup(mtd, cfi_fixup_table); #ifdef DEBUG_CFI_FEATURES - /* Tell the user about it in lots of lovely detail */ - cfi_tell_features(extp); + /* Tell the user about it in lots of lovely detail */ + cfi_tell_features(extp); #endif - bootloc = extp->TopBottom; - if ((bootloc != 2) && (bootloc != 3)) { - printk(KERN_WARNING "%s: CFI does not contain boot " - "bank location. Assuming top.\n", map->name); - bootloc = 2; - } + bootloc = extp->TopBottom; + if ((bootloc < 2) || (bootloc > 5)) { + printk(KERN_WARNING "%s: CFI contains unrecognised boot " + "bank location (%d). Assuming bottom.\n", + map->name, bootloc); + bootloc = 2; + } - if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) { - printk(KERN_WARNING "%s: Swapping erase regions for broken CFI table.\n", map->name); + if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) { + printk(KERN_WARNING "%s: Swapping erase regions for top-boot CFI table.\n", map->name); - for (i=0; i<cfi->cfiq->NumEraseRegions / 2; i++) { - int j = (cfi->cfiq->NumEraseRegions-1)-i; - __u32 swap; + for (i=0; i<cfi->cfiq->NumEraseRegions / 2; i++) { + int j = (cfi->cfiq->NumEraseRegions-1)-i; + __u32 swap; - swap = cfi->cfiq->EraseRegionInfo[i]; - cfi->cfiq->EraseRegionInfo[i] = cfi->cfiq->EraseRegionInfo[j]; - cfi->cfiq->EraseRegionInfo[j] = swap; + swap = cfi->cfiq->EraseRegionInfo[i]; + cfi->cfiq->EraseRegionInfo[i] = cfi->cfiq->EraseRegionInfo[j]; + cfi->cfiq->EraseRegionInfo[j] = swap; + } } + /* Set the default CFI lock/unlock addresses */ + cfi->addr_unlock1 = 0x555; + cfi->addr_unlock2 = 0x2aa; + } + cfi_fixup(mtd, cfi_nopri_fixup_table); + + if (!cfi->addr_unlock1 || !cfi->addr_unlock2) { + kfree(mtd); + return NULL; } - /* Set the default CFI lock/unlock addresses */ - cfi->addr_unlock1 = 0x555; - cfi->addr_unlock2 = 0x2aa; } /* CFI mode */ else if (cfi->cfi_mode == CFI_MODE_JEDEC) { @@ -437,7 +489,11 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) return cfi_amdstd_setup(mtd); } +struct mtd_info *cfi_cmdset_0006(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0002"))); +struct mtd_info *cfi_cmdset_0701(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0002"))); EXPORT_SYMBOL_GPL(cfi_cmdset_0002); +EXPORT_SYMBOL_GPL(cfi_cmdset_0006); +EXPORT_SYMBOL_GPL(cfi_cmdset_0701); static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) { @@ -491,13 +547,12 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) #endif __module_get(THIS_MODULE); + register_reboot_notifier(&mtd->reboot_notifier); return mtd; setup_err: - if(mtd) { - kfree(mtd->eraseregions); - kfree(mtd); - } + kfree(mtd->eraseregions); + kfree(mtd); kfree(cfi->cmdset_priv); kfree(cfi->cfiq); return NULL; @@ -571,9 +626,9 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr printk(KERN_ERR "Waiting for chip to be ready timed out.\n"); return -EIO; } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); /* Someone else might have been playing with it. */ goto retry; } @@ -617,9 +672,9 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr return -EIO; } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. So we can just loop here. */ } @@ -634,6 +689,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr chip->state = FL_READY; return 0; + case FL_SHUTDOWN: + /* The machine is rebooting */ + return -EIO; + case FL_POINT: /* Only if there's no operation suspended... */ if (mode == FL_READY && chip->oldstate == FL_READY) @@ -643,10 +702,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr sleep: set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); goto resettime; } } @@ -778,7 +837,7 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, (void) map_read(map, adr); xip_iprefetch(); local_irq_enable(); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); xip_iprefetch(); cond_resched(); @@ -788,15 +847,15 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, * a suspended erase state. If so let's wait * until it's done. */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); while (chip->state != FL_XIP_WHILE_ERASING) { DECLARE_WAITQUEUE(wait, current); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); } /* Disallow XIP again */ local_irq_disable(); @@ -858,17 +917,17 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, #define UDELAY(map, chip, adr, usec) \ do { \ - spin_unlock(chip->mutex); \ + mutex_unlock(&chip->mutex); \ cfi_udelay(usec); \ - spin_lock(chip->mutex); \ + mutex_lock(&chip->mutex); \ } while (0) #define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \ do { \ - spin_unlock(chip->mutex); \ + mutex_unlock(&chip->mutex); \ INVALIDATE_CACHED_RANGE(map, adr, len); \ cfi_udelay(usec); \ - spin_lock(chip->mutex); \ + mutex_lock(&chip->mutex); \ } while (0) #endif @@ -884,10 +943,10 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof /* Ensure cmd read/writes are aligned. */ cmd_addr = adr & ~(map_bankwidth(map)-1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, cmd_addr, FL_READY); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -900,7 +959,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof put_chip(map, chip, cmd_addr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } @@ -954,7 +1013,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi struct cfi_private *cfi = map->fldrv_priv; retry: - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state != FL_READY){ #if 0 @@ -963,7 +1022,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); @@ -992,7 +1051,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi cfi_send_gen_cmd(0x00, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); wake_up(&chip->wq); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } @@ -1061,10 +1120,10 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, adr += chip->start; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_WRITING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1107,11 +1166,11 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + (HZ / 2); /* FIXME */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); continue; } @@ -1143,7 +1202,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, op_done: chip->state = FL_READY; put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1175,7 +1234,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, map_word tmp_buf; retry: - spin_lock(cfi->chips[chipnum].mutex); + mutex_lock(&cfi->chips[chipnum].mutex); if (cfi->chips[chipnum].state != FL_READY) { #if 0 @@ -1184,7 +1243,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&cfi->chips[chipnum].wq, &wait); - spin_unlock(cfi->chips[chipnum].mutex); + mutex_unlock(&cfi->chips[chipnum].mutex); schedule(); remove_wait_queue(&cfi->chips[chipnum].wq, &wait); @@ -1198,7 +1257,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, /* Load 'tmp_buf' with old contents of flash */ tmp_buf = map_read(map, bus_ofs+chipstart); - spin_unlock(cfi->chips[chipnum].mutex); + mutex_unlock(&cfi->chips[chipnum].mutex); /* Number of bytes to copy from buffer */ n = min_t(int, len, map_bankwidth(map)-i); @@ -1253,7 +1312,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, map_word tmp_buf; retry1: - spin_lock(cfi->chips[chipnum].mutex); + mutex_lock(&cfi->chips[chipnum].mutex); if (cfi->chips[chipnum].state != FL_READY) { #if 0 @@ -1262,7 +1321,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&cfi->chips[chipnum].wq, &wait); - spin_unlock(cfi->chips[chipnum].mutex); + mutex_unlock(&cfi->chips[chipnum].mutex); schedule(); remove_wait_queue(&cfi->chips[chipnum].wq, &wait); @@ -1275,7 +1334,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, tmp_buf = map_read(map, ofs + chipstart); - spin_unlock(cfi->chips[chipnum].mutex); + mutex_unlock(&cfi->chips[chipnum].mutex); tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len); @@ -1310,10 +1369,10 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, adr += chip->start; cmd_adr = adr; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_WRITING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1368,11 +1427,11 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + (HZ / 2); /* FIXME */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); continue; } @@ -1400,7 +1459,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, op_done: chip->state = FL_READY; put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1500,10 +1559,10 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) adr = cfi->addr_unlock1; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_WRITING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1536,10 +1595,10 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) /* Someone's suspended the erase. Sleep */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); continue; } if (chip->erase_suspended) { @@ -1573,7 +1632,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) chip->state = FL_READY; xip_enable(map, chip, adr); put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1588,10 +1647,10 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, adr += chip->start; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_ERASING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1624,10 +1683,10 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, /* Someone's suspended the erase. Sleep */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); continue; } if (chip->erase_suspended) { @@ -1663,7 +1722,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, chip->state = FL_READY; put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1715,7 +1774,7 @@ static int do_atmel_lock(struct map_info *map, struct flchip *chip, struct cfi_private *cfi = map->fldrv_priv; int ret; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr + chip->start, FL_LOCKING); if (ret) goto out_unlock; @@ -1741,7 +1800,7 @@ static int do_atmel_lock(struct map_info *map, struct flchip *chip, ret = 0; out_unlock: - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1751,7 +1810,7 @@ static int do_atmel_unlock(struct map_info *map, struct flchip *chip, struct cfi_private *cfi = map->fldrv_priv; int ret; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr + chip->start, FL_UNLOCKING); if (ret) goto out_unlock; @@ -1769,7 +1828,7 @@ static int do_atmel_unlock(struct map_info *map, struct flchip *chip, ret = 0; out_unlock: - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1797,7 +1856,7 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) chip = &cfi->chips[i]; retry: - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); switch(chip->state) { case FL_READY: @@ -1811,7 +1870,7 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) * with the chip now anyway. */ case FL_SYNCING: - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); break; default: @@ -1819,7 +1878,7 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); @@ -1834,13 +1893,13 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_SYNCING) { chip->state = chip->oldstate; wake_up(&chip->wq); } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } } @@ -1856,7 +1915,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd) for (i=0; !ret && i<cfi->numchips; i++) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); switch(chip->state) { case FL_READY: @@ -1876,7 +1935,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd) ret = -EAGAIN; break; } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } /* Unlock the chips again */ @@ -1885,13 +1944,13 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_PM_SUSPENDED) { chip->state = chip->oldstate; wake_up(&chip->wq); } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } } @@ -1910,7 +1969,7 @@ static void cfi_amdstd_resume(struct mtd_info *mtd) chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_PM_SUSPENDED) { chip->state = FL_READY; @@ -1920,15 +1979,62 @@ static void cfi_amdstd_resume(struct mtd_info *mtd) else printk(KERN_ERR "Argh. Chip not in PM_SUSPENDED state upon resume()\n"); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } } + +/* + * Ensure that the flash device is put back into read array mode before + * unloading the driver or rebooting. On some systems, rebooting while + * the flash is in query/program/erase mode will prevent the CPU from + * fetching the bootloader code, requiring a hard reset or power cycle. + */ +static int cfi_amdstd_reset(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int i, ret; + struct flchip *chip; + + for (i = 0; i < cfi->numchips; i++) { + + chip = &cfi->chips[i]; + + mutex_lock(&chip->mutex); + + ret = get_chip(map, chip, chip->start, FL_SHUTDOWN); + if (!ret) { + map_write(map, CMD(0xF0), chip->start); + chip->state = FL_SHUTDOWN; + put_chip(map, chip, chip->start); + } + + mutex_unlock(&chip->mutex); + } + + return 0; +} + + +static int cfi_amdstd_reboot(struct notifier_block *nb, unsigned long val, + void *v) +{ + struct mtd_info *mtd; + + mtd = container_of(nb, struct mtd_info, reboot_notifier); + cfi_amdstd_reset(mtd); + return NOTIFY_DONE; +} + + static void cfi_amdstd_destroy(struct mtd_info *mtd) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; + cfi_amdstd_reset(mtd); + unregister_reboot_notifier(&mtd->reboot_notifier); kfree(cfi->cmdset_priv); kfree(cfi->cfiq); kfree(cfi); @@ -1938,3 +2044,5 @@ static void cfi_amdstd_destroy(struct mtd_info *mtd) MODULE_LICENSE("GPL"); MODULE_AUTHOR("Crossnet Co. <info@crossnet.co.jp> et al."); MODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips"); +MODULE_ALIAS("cfi_cmdset_0006"); +MODULE_ALIAS("cfi_cmdset_0701"); diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index 0667a67..e54e8c1 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -265,7 +265,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof timeo = jiffies + HZ; retry: - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* Check that the chip's ready to talk to us. * If it's in FL_ERASING state, suspend it and make it talk now. @@ -296,15 +296,15 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof /* make sure we're in 'read status' mode */ map_write(map, CMD(0x70), cmd_addr); chip->state = FL_ERASING; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "Chip not ready after erase " "suspended: status = 0x%lx\n", status.x[0]); return -EIO; } - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); } suspended = 1; @@ -335,13 +335,13 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %lx\n", status.x[0]); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); goto retry; @@ -351,7 +351,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof someone changes the status */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + HZ; @@ -376,7 +376,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof } wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } @@ -445,7 +445,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, #ifdef DEBUG_CFI_FEATURES printk("%s: chip->state[%d]\n", __func__, chip->state); #endif - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* Check that the chip's ready to talk to us. * Later, we can actually think about interrupting it @@ -470,14 +470,14 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, break; /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in buffer write Xstatus = %lx, status = %lx\n", status.x[0], map_read(map, cmd_adr).x[0]); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); goto retry; @@ -486,7 +486,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, someone changes the status */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + HZ; @@ -503,16 +503,16 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, if (map_word_andequal(map, status, status_OK, status_OK)) break; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); if (++z > 100) { /* Argh. Not ready for write to buffer */ DISABLE_VPP(map); map_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %lx\n", status.x[0]); return -EIO; } @@ -532,9 +532,9 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, map_write(map, CMD(0xd0), cmd_adr); chip->state = FL_WRITING; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(chip->buffer_write_time); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); timeo = jiffies + (HZ/2); z = 0; @@ -543,11 +543,11 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, /* Someone's suspended the write. Sleep */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + (HZ / 2); /* FIXME */ - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); continue; } @@ -563,16 +563,16 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; DISABLE_VPP(map); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n"); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); z++; - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); } if (!z) { chip->buffer_write_time--; @@ -596,11 +596,11 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, /* put back into read status register mode */ map_write(map, CMD(0x70), adr); wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return map_word_bitsset(map, status, CMD(0x02)) ? -EROFS : -EIO; } wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } @@ -749,7 +749,7 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u timeo = jiffies + HZ; retry: - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* Check that the chip's ready to talk to us. */ switch (chip->state) { @@ -766,13 +766,13 @@ retry: /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in erase\n"); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); goto retry; @@ -781,7 +781,7 @@ retry: someone changes the status */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + HZ; @@ -797,9 +797,9 @@ retry: map_write(map, CMD(0xD0), adr); chip->state = FL_ERASING; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); msleep(1000); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ /* Once the state machine's known to be working I'll do that */ @@ -810,11 +810,11 @@ retry: /* Someone's suspended the erase. Sleep */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + (HZ*20); /* FIXME */ - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); continue; } @@ -828,14 +828,14 @@ retry: chip->state = FL_STATUS; printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); DISABLE_VPP(map); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); } DISABLE_VPP(map); @@ -878,7 +878,7 @@ retry: printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus); timeo = jiffies + HZ; chip->state = FL_STATUS; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); goto retry; } printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus); @@ -887,7 +887,7 @@ retry: } wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -995,7 +995,7 @@ static void cfi_staa_sync (struct mtd_info *mtd) chip = &cfi->chips[i]; retry: - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); switch(chip->state) { case FL_READY: @@ -1009,7 +1009,7 @@ static void cfi_staa_sync (struct mtd_info *mtd) * with the chip now anyway. */ case FL_SYNCING: - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); break; default: @@ -1017,7 +1017,7 @@ static void cfi_staa_sync (struct mtd_info *mtd) set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); @@ -1030,13 +1030,13 @@ static void cfi_staa_sync (struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_SYNCING) { chip->state = chip->oldstate; wake_up(&chip->wq); } - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); } } @@ -1054,7 +1054,7 @@ static inline int do_lock_oneblock(struct map_info *map, struct flchip *chip, un timeo = jiffies + HZ; retry: - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* Check that the chip's ready to talk to us. */ switch (chip->state) { @@ -1071,13 +1071,13 @@ retry: /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in lock\n"); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); goto retry; @@ -1086,7 +1086,7 @@ retry: someone changes the status */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + HZ; @@ -1098,9 +1098,9 @@ retry: map_write(map, CMD(0x01), adr); chip->state = FL_LOCKING; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); msleep(1000); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ /* Once the state machine's known to be working I'll do that */ @@ -1118,21 +1118,21 @@ retry: chip->state = FL_STATUS; printk(KERN_ERR "waiting for lock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); DISABLE_VPP(map); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); } /* Done and happy. */ chip->state = FL_STATUS; DISABLE_VPP(map); wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) @@ -1203,7 +1203,7 @@ static inline int do_unlock_oneblock(struct map_info *map, struct flchip *chip, timeo = jiffies + HZ; retry: - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* Check that the chip's ready to talk to us. */ switch (chip->state) { @@ -1220,13 +1220,13 @@ retry: /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in unlock\n"); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); goto retry; @@ -1235,7 +1235,7 @@ retry: someone changes the status */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + HZ; @@ -1247,9 +1247,9 @@ retry: map_write(map, CMD(0xD0), adr); chip->state = FL_UNLOCKING; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); msleep(1000); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ /* Once the state machine's known to be working I'll do that */ @@ -1267,21 +1267,21 @@ retry: chip->state = FL_STATUS; printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); DISABLE_VPP(map); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return -EIO; } /* Latency issues. Drop the unlock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); } /* Done and happy. */ chip->state = FL_STATUS; DISABLE_VPP(map); wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } static int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) @@ -1334,7 +1334,7 @@ static int cfi_staa_suspend(struct mtd_info *mtd) for (i=0; !ret && i<cfi->numchips; i++) { chip = &cfi->chips[i]; - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); switch(chip->state) { case FL_READY: @@ -1354,7 +1354,7 @@ static int cfi_staa_suspend(struct mtd_info *mtd) ret = -EAGAIN; break; } - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); } /* Unlock the chips again */ @@ -1363,7 +1363,7 @@ static int cfi_staa_suspend(struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_PM_SUSPENDED) { /* No need to force it into a known state here, @@ -1372,7 +1372,7 @@ static int cfi_staa_suspend(struct mtd_info *mtd) chip->state = chip->oldstate; wake_up(&chip->wq); } - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); } } @@ -1390,7 +1390,7 @@ static void cfi_staa_resume(struct mtd_info *mtd) chip = &cfi->chips[i]; - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* Go to known state. Chip may have been power cycled */ if (chip->state == FL_PM_SUSPENDED) { @@ -1399,7 +1399,7 @@ static void cfi_staa_resume(struct mtd_info *mtd) wake_up(&chip->wq); } - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); } } diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index e63e674..b2acd32f 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -158,6 +158,7 @@ static int __xipram cfi_chip_setup(struct map_info *map, __u32 base = 0; int num_erase_regions = cfi_read_query(map, base + (0x10 + 28)*ofs_factor); int i; + int addr_unlock1 = 0x555, addr_unlock2 = 0x2AA; xip_enable(base, map, cfi); #ifdef DEBUG_CFI @@ -181,29 +182,6 @@ static int __xipram cfi_chip_setup(struct map_info *map, for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) ((unsigned char *)cfi->cfiq)[i] = cfi_read_query(map,base + (0x10 + i)*ofs_factor); - /* Note we put the device back into Read Mode BEFORE going into Auto - * Select Mode, as some devices support nesting of modes, others - * don't. This way should always work. - * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and - * so should be treated as nops or illegal (and so put the device - * back into Read Mode, which is a nop in this case). - */ - cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); - cfi->mfr = cfi_read_query16(map, base); - cfi->id = cfi_read_query16(map, base + ofs_factor); - - /* Get AMD/Spansion extended JEDEC ID */ - if (cfi->mfr == CFI_MFR_AMD && (cfi->id & 0xff) == 0x7e) - cfi->id = cfi_read_query(map, base + 0xe * ofs_factor) << 8 | - cfi_read_query(map, base + 0xf * ofs_factor); - - /* Put it back into Read Mode */ - cfi_qry_mode_off(base, map, cfi); - xip_allowed(base, map); - /* Do any necessary byteswapping */ cfi->cfiq->P_ID = le16_to_cpu(cfi->cfiq->P_ID); @@ -228,6 +206,35 @@ static int __xipram cfi_chip_setup(struct map_info *map, #endif } + if (cfi->cfiq->P_ID == P_ID_SST_OLD) { + addr_unlock1 = 0x5555; + addr_unlock2 = 0x2AAA; + } + + /* + * Note we put the device back into Read Mode BEFORE going into Auto + * Select Mode, as some devices support nesting of modes, others + * don't. This way should always work. + * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and + * so should be treated as nops or illegal (and so put the device + * back into Read Mode, which is a nop in this case). + */ + cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xaa, addr_unlock1, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, addr_unlock2, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x90, addr_unlock1, base, map, cfi, cfi->device_type, NULL); + cfi->mfr = cfi_read_query16(map, base); + cfi->id = cfi_read_query16(map, base + ofs_factor); + + /* Get AMD/Spansion extended JEDEC ID */ + if (cfi->mfr == CFI_MFR_AMD && (cfi->id & 0xff) == 0x7e) + cfi->id = cfi_read_query(map, base + 0xe * ofs_factor) << 8 | + cfi_read_query(map, base + 0xf * ofs_factor); + + /* Put it back into Read Mode */ + cfi_qry_mode_off(base, map, cfi); + xip_allowed(base, map); + printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n", map->name, cfi->interleave, cfi->device_type*8, base, map->bankwidth*8); @@ -269,6 +276,9 @@ static char *vendorname(__u16 vendor) case P_ID_SST_PAGE: return "SST Page Write"; + case P_ID_SST_OLD: + return "SST 39VF160x/39VF320x"; + case P_ID_INTEL_PERFORMANCE: return "Intel Performance Code"; diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index ca584d0..d7c2c67 100644 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -104,10 +104,11 @@ __xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* n int i; struct cfi_extquery *extp = NULL; - printk(" %s Extended Query Table at 0x%4.4X\n", name, adr); if (!adr) goto out; + printk(KERN_INFO "%s Extended Query Table at 0x%4.4X\n", name, adr); + extp = kmalloc(size, GFP_KERNEL); if (!extp) { printk(KERN_ERR "Failed to allocate memory\n"); diff --git a/drivers/mtd/chips/fwh_lock.h b/drivers/mtd/chips/fwh_lock.h index 57e0e4e..d180649 100644 --- a/drivers/mtd/chips/fwh_lock.h +++ b/drivers/mtd/chips/fwh_lock.h @@ -58,10 +58,10 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, * to flash memory - that means that we don't have to check status * and timeout. */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_LOCKING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -72,7 +72,7 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, /* Done and happy. */ chip->state = chip->oldstate; put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index e2dc964..3b9a284 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -155,8 +155,7 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi pchip->start = (i << cfi.chipshift); pchip->state = FL_READY; init_waitqueue_head(&pchip->wq); - spin_lock_init(&pchip->_spinlock); - pchip->mutex = &pchip->_spinlock; + mutex_init(&pchip->mutex); } } @@ -242,17 +241,19 @@ static struct mtd_info *check_cmd_set(struct map_info *map, int primary) /* We need these for the !CONFIG_MODULES case, because symbol_get() doesn't work there */ #ifdef CONFIG_MTD_CFI_INTELEXT - case 0x0001: - case 0x0003: - case 0x0200: + case P_ID_INTEL_EXT: + case P_ID_INTEL_STD: + case P_ID_INTEL_PERFORMANCE: return cfi_cmdset_0001(map, primary); #endif #ifdef CONFIG_MTD_CFI_AMDSTD - case 0x0002: + case P_ID_AMD_STD: + case P_ID_SST_OLD: + case P_ID_WINBOND: return cfi_cmdset_0002(map, primary); #endif #ifdef CONFIG_MTD_CFI_STAA - case 0x0020: + case P_ID_ST_ADV: return cfi_cmdset_0020(map, primary); #endif default: diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 8db1148..d72a5fb 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -22,24 +22,6 @@ #include <linux/mtd/cfi.h> #include <linux/mtd/gen_probe.h> -/* Manufacturers */ -#define MANUFACTURER_AMD 0x0001 -#define MANUFACTURER_ATMEL 0x001f -#define MANUFACTURER_EON 0x001c -#define MANUFACTURER_FUJITSU 0x0004 -#define MANUFACTURER_HYUNDAI 0x00AD -#define MANUFACTURER_INTEL 0x0089 -#define MANUFACTURER_MACRONIX 0x00C2 -#define MANUFACTURER_NEC 0x0010 -#define MANUFACTURER_PMC 0x009D -#define MANUFACTURER_SHARP 0x00b0 -#define MANUFACTURER_SST 0x00BF -#define MANUFACTURER_ST 0x0020 -#define MANUFACTURER_TOSHIBA 0x0098 -#define MANUFACTURER_WINBOND 0x00da -#define CONTINUATION_CODE 0x007f - - /* AMD */ #define AM29DL800BB 0x22CB #define AM29DL800BT 0x224A @@ -166,6 +148,8 @@ #define SST39LF160 0x2782 #define SST39VF1601 0x234b #define SST39VF3201 0x235b +#define SST39WF1601 0x274b +#define SST39WF1602 0x274a #define SST39LF512 0x00D4 #define SST39LF010 0x00D5 #define SST39LF020 0x00D6 @@ -309,7 +293,7 @@ struct amd_flash_info { */ static const struct amd_flash_info jedec_table[] = { { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F032B, .name = "AMD AM29F032B", .uaddr = MTD_UADDR_0x0555_0x02AA, @@ -321,7 +305,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,64) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV160DT, .name = "AMD AM29LV160DT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -336,7 +320,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV160DB, .name = "AMD AM29LV160DB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -351,7 +335,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,31) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV400BB, .name = "AMD AM29LV400BB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -366,7 +350,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,7) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV400BT, .name = "AMD AM29LV400BT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -381,7 +365,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV800BB, .name = "AMD AM29LV800BB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -397,7 +381,7 @@ static const struct amd_flash_info jedec_table[] = { } }, { /* add DL */ - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29DL800BB, .name = "AMD AM29DL800BB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -414,7 +398,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,14) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29DL800BT, .name = "AMD AM29DL800BT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -431,7 +415,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F800BB, .name = "AMD AM29F800BB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -446,7 +430,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV800BT, .name = "AMD AM29LV800BT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -461,7 +445,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F800BT, .name = "AMD AM29F800BT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -476,7 +460,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F017D, .name = "AMD AM29F017D", .devtypes = CFI_DEVICETYPE_X8, @@ -488,7 +472,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,32), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F016D, .name = "AMD AM29F016D", .devtypes = CFI_DEVICETYPE_X8, @@ -500,7 +484,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,32), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F080, .name = "AMD AM29F080", .devtypes = CFI_DEVICETYPE_X8, @@ -512,7 +496,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,16), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F040, .name = "AMD AM29F040", .devtypes = CFI_DEVICETYPE_X8, @@ -524,7 +508,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV040B, .name = "AMD AM29LV040B", .devtypes = CFI_DEVICETYPE_X8, @@ -536,7 +520,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F002T, .name = "AMD AM29F002T", .devtypes = CFI_DEVICETYPE_X8, @@ -551,7 +535,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29SL800DT, .name = "AMD AM29SL800DT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -566,7 +550,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29SL800DB, .name = "AMD AM29SL800DB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -581,7 +565,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15), } }, { - .mfr_id = MANUFACTURER_ATMEL, + .mfr_id = CFI_MFR_ATMEL, .dev_id = AT49BV512, .name = "Atmel AT49BV512", .devtypes = CFI_DEVICETYPE_X8, @@ -593,7 +577,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,1) } }, { - .mfr_id = MANUFACTURER_ATMEL, + .mfr_id = CFI_MFR_ATMEL, .dev_id = AT29LV512, .name = "Atmel AT29LV512", .devtypes = CFI_DEVICETYPE_X8, @@ -606,7 +590,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x80,256) } }, { - .mfr_id = MANUFACTURER_ATMEL, + .mfr_id = CFI_MFR_ATMEL, .dev_id = AT49BV16X, .name = "Atmel AT49BV16X", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -619,7 +603,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,31) } }, { - .mfr_id = MANUFACTURER_ATMEL, + .mfr_id = CFI_MFR_ATMEL, .dev_id = AT49BV16XT, .name = "Atmel AT49BV16XT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -632,7 +616,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000,8) } }, { - .mfr_id = MANUFACTURER_ATMEL, + .mfr_id = CFI_MFR_ATMEL, .dev_id = AT49BV32X, .name = "Atmel AT49BV32X", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -645,7 +629,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,63) } }, { - .mfr_id = MANUFACTURER_ATMEL, + .mfr_id = CFI_MFR_ATMEL, .dev_id = AT49BV32XT, .name = "Atmel AT49BV32XT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -658,7 +642,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000,8) } }, { - .mfr_id = MANUFACTURER_EON, + .mfr_id = CFI_MFR_EON, .dev_id = EN29SL800BT, .name = "Eon EN29SL800BT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -673,7 +657,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1), } }, { - .mfr_id = MANUFACTURER_EON, + .mfr_id = CFI_MFR_EON, .dev_id = EN29SL800BB, .name = "Eon EN29SL800BB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -688,7 +672,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15), } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29F040C, .name = "Fujitsu MBM29F040C", .devtypes = CFI_DEVICETYPE_X8, @@ -700,7 +684,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29F800BA, .name = "Fujitsu MBM29F800BA", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -715,7 +699,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15), } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV650UE, .name = "Fujitsu MBM29LV650UE", .devtypes = CFI_DEVICETYPE_X8, @@ -727,7 +711,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,128) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV320TE, .name = "Fujitsu MBM29LV320TE", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -740,7 +724,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000,8) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV320BE, .name = "Fujitsu MBM29LV320BE", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -753,7 +737,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,63) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV160TE, .name = "Fujitsu MBM29LV160TE", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -768,7 +752,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV160BE, .name = "Fujitsu MBM29LV160BE", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -783,7 +767,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,31) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV800BA, .name = "Fujitsu MBM29LV800BA", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -798,7 +782,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV800TA, .name = "Fujitsu MBM29LV800TA", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -813,7 +797,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV400BC, .name = "Fujitsu MBM29LV400BC", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -828,7 +812,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,7) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV400TC, .name = "Fujitsu MBM29LV400TC", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -843,7 +827,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_HYUNDAI, + .mfr_id = CFI_MFR_HYUNDAI, .dev_id = HY29F002T, .name = "Hyundai HY29F002T", .devtypes = CFI_DEVICETYPE_X8, @@ -858,7 +842,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F004B3B, .name = "Intel 28F004B3B", .devtypes = CFI_DEVICETYPE_X8, @@ -871,7 +855,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 7), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F004B3T, .name = "Intel 28F004B3T", .devtypes = CFI_DEVICETYPE_X8, @@ -884,7 +868,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F400B3B, .name = "Intel 28F400B3B", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -897,7 +881,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 7), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F400B3T, .name = "Intel 28F400B3T", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -910,7 +894,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F008B3B, .name = "Intel 28F008B3B", .devtypes = CFI_DEVICETYPE_X8, @@ -923,7 +907,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 15), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F008B3T, .name = "Intel 28F008B3T", .devtypes = CFI_DEVICETYPE_X8, @@ -936,7 +920,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F008S5, .name = "Intel 28F008S5", .devtypes = CFI_DEVICETYPE_X8, @@ -948,7 +932,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,16), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F016S5, .name = "Intel 28F016S5", .devtypes = CFI_DEVICETYPE_X8, @@ -960,7 +944,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,32), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F008SA, .name = "Intel 28F008SA", .devtypes = CFI_DEVICETYPE_X8, @@ -972,7 +956,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 16), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F800B3B, .name = "Intel 28F800B3B", .devtypes = CFI_DEVICETYPE_X16, @@ -985,7 +969,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 15), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F800B3T, .name = "Intel 28F800B3T", .devtypes = CFI_DEVICETYPE_X16, @@ -998,7 +982,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F016B3B, .name = "Intel 28F016B3B", .devtypes = CFI_DEVICETYPE_X8, @@ -1011,7 +995,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 31), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F016S3, .name = "Intel I28F016S3", .devtypes = CFI_DEVICETYPE_X8, @@ -1023,7 +1007,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 32), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F016B3T, .name = "Intel 28F016B3T", .devtypes = CFI_DEVICETYPE_X8, @@ -1036,7 +1020,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F160B3B, .name = "Intel 28F160B3B", .devtypes = CFI_DEVICETYPE_X16, @@ -1049,7 +1033,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 31), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F160B3T, .name = "Intel 28F160B3T", .devtypes = CFI_DEVICETYPE_X16, @@ -1062,7 +1046,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F320B3B, .name = "Intel 28F320B3B", .devtypes = CFI_DEVICETYPE_X16, @@ -1075,7 +1059,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 63), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F320B3T, .name = "Intel 28F320B3T", .devtypes = CFI_DEVICETYPE_X16, @@ -1088,7 +1072,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F640B3B, .name = "Intel 28F640B3B", .devtypes = CFI_DEVICETYPE_X16, @@ -1101,7 +1085,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 127), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F640B3T, .name = "Intel 28F640B3T", .devtypes = CFI_DEVICETYPE_X16, @@ -1114,7 +1098,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F640C3B, .name = "Intel 28F640C3B", .devtypes = CFI_DEVICETYPE_X16, @@ -1127,7 +1111,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 127), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I82802AB, .name = "Intel 82802AB", .devtypes = CFI_DEVICETYPE_X8, @@ -1139,7 +1123,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I82802AC, .name = "Intel 82802AC", .devtypes = CFI_DEVICETYPE_X8, @@ -1151,7 +1135,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,16), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29LV040C, .name = "Macronix MX29LV040C", .devtypes = CFI_DEVICETYPE_X8, @@ -1163,7 +1147,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29LV160T, .name = "MXIC MX29LV160T", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1178,7 +1162,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_NEC, + .mfr_id = CFI_MFR_NEC, .dev_id = UPD29F064115, .name = "NEC uPD29F064115", .devtypes = CFI_DEVICETYPE_X16, @@ -1192,7 +1176,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x2000,8), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29LV160B, .name = "MXIC MX29LV160B", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1207,7 +1191,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,31) } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29F040, .name = "Macronix MX29F040", .devtypes = CFI_DEVICETYPE_X8, @@ -1219,7 +1203,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29F016, .name = "Macronix MX29F016", .devtypes = CFI_DEVICETYPE_X8, @@ -1231,7 +1215,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,32), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29F004T, .name = "Macronix MX29F004T", .devtypes = CFI_DEVICETYPE_X8, @@ -1246,7 +1230,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29F004B, .name = "Macronix MX29F004B", .devtypes = CFI_DEVICETYPE_X8, @@ -1261,7 +1245,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,7), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29F002T, .name = "Macronix MX29F002T", .devtypes = CFI_DEVICETYPE_X8, @@ -1276,7 +1260,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1), } }, { - .mfr_id = MANUFACTURER_PMC, + .mfr_id = CFI_MFR_PMC, .dev_id = PM49FL002, .name = "PMC Pm49FL002", .devtypes = CFI_DEVICETYPE_X8, @@ -1288,7 +1272,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO( 0x01000, 64 ) } }, { - .mfr_id = MANUFACTURER_PMC, + .mfr_id = CFI_MFR_PMC, .dev_id = PM49FL004, .name = "PMC Pm49FL004", .devtypes = CFI_DEVICETYPE_X8, @@ -1300,7 +1284,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO( 0x01000, 128 ) } }, { - .mfr_id = MANUFACTURER_PMC, + .mfr_id = CFI_MFR_PMC, .dev_id = PM49FL008, .name = "PMC Pm49FL008", .devtypes = CFI_DEVICETYPE_X8, @@ -1312,7 +1296,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO( 0x01000, 256 ) } }, { - .mfr_id = MANUFACTURER_SHARP, + .mfr_id = CFI_MFR_SHARP, .dev_id = LH28F640BF, .name = "LH28F640BF", .devtypes = CFI_DEVICETYPE_X8, @@ -1324,7 +1308,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x40000,16), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39LF512, .name = "SST 39LF512", .devtypes = CFI_DEVICETYPE_X8, @@ -1336,7 +1320,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,16), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39LF010, .name = "SST 39LF010", .devtypes = CFI_DEVICETYPE_X8, @@ -1348,8 +1332,8 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,32), } }, { - .mfr_id = MANUFACTURER_SST, - .dev_id = SST29EE020, + .mfr_id = CFI_MFR_SST, + .dev_id = SST29EE020, .name = "SST 29EE020", .devtypes = CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x5555_0x2AAA, @@ -1359,9 +1343,9 @@ static const struct amd_flash_info jedec_table[] = { .regions = {ERASEINFO(0x01000,64), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST29LE020, - .name = "SST 29LE020", + .name = "SST 29LE020", .devtypes = CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x5555_0x2AAA, .dev_size = SIZE_256KiB, @@ -1370,7 +1354,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = {ERASEINFO(0x01000,64), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39LF020, .name = "SST 39LF020", .devtypes = CFI_DEVICETYPE_X8, @@ -1382,7 +1366,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,64), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39LF040, .name = "SST 39LF040", .devtypes = CFI_DEVICETYPE_X8, @@ -1394,7 +1378,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,128), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39SF010A, .name = "SST 39SF010A", .devtypes = CFI_DEVICETYPE_X8, @@ -1406,7 +1390,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,32), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39SF020A, .name = "SST 39SF020A", .devtypes = CFI_DEVICETYPE_X8, @@ -1418,7 +1402,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,64), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39SF040, .name = "SST 39SF040", .devtypes = CFI_DEVICETYPE_X8, @@ -1430,7 +1414,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,128), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST49LF040B, .name = "SST 49LF040B", .devtypes = CFI_DEVICETYPE_X8, @@ -1443,7 +1427,7 @@ static const struct amd_flash_info jedec_table[] = { } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST49LF004B, .name = "SST 49LF004B", .devtypes = CFI_DEVICETYPE_X8, @@ -1455,7 +1439,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,128), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST49LF008A, .name = "SST 49LF008A", .devtypes = CFI_DEVICETYPE_X8, @@ -1467,7 +1451,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,256), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST49LF030A, .name = "SST 49LF030A", .devtypes = CFI_DEVICETYPE_X8, @@ -1479,7 +1463,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,96), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST49LF040A, .name = "SST 49LF040A", .devtypes = CFI_DEVICETYPE_X8, @@ -1491,7 +1475,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,128), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST49LF080A, .name = "SST 49LF080A", .devtypes = CFI_DEVICETYPE_X8, @@ -1503,7 +1487,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,256), } }, { - .mfr_id = MANUFACTURER_SST, /* should be CFI */ + .mfr_id = CFI_MFR_SST, /* should be CFI */ .dev_id = SST39LF160, .name = "SST 39LF160", .devtypes = CFI_DEVICETYPE_X16, @@ -1516,7 +1500,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,256) } }, { - .mfr_id = MANUFACTURER_SST, /* should be CFI */ + .mfr_id = CFI_MFR_SST, /* should be CFI */ .dev_id = SST39VF1601, .name = "SST 39VF1601", .devtypes = CFI_DEVICETYPE_X16, @@ -1529,7 +1513,35 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,256) } }, { - .mfr_id = MANUFACTURER_SST, /* should be CFI */ + /* CFI is broken: reports AMD_STD, but needs custom uaddr */ + .mfr_id = CFI_MFR_SST, + .dev_id = SST39WF1601, + .name = "SST 39WF1601", + .devtypes = CFI_DEVICETYPE_X16, + .uaddr = MTD_UADDR_0xAAAA_0x5555, + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 2, + .regions = { + ERASEINFO(0x1000,256), + ERASEINFO(0x1000,256) + } + }, { + /* CFI is broken: reports AMD_STD, but needs custom uaddr */ + .mfr_id = CFI_MFR_SST, + .dev_id = SST39WF1602, + .name = "SST 39WF1602", + .devtypes = CFI_DEVICETYPE_X16, + .uaddr = MTD_UADDR_0xAAAA_0x5555, + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 2, + .regions = { + ERASEINFO(0x1000,256), + ERASEINFO(0x1000,256) + } + }, { + .mfr_id = CFI_MFR_SST, /* should be CFI */ .dev_id = SST39VF3201, .name = "SST 39VF3201", .devtypes = CFI_DEVICETYPE_X16, @@ -1544,7 +1556,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,256) } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST36VF3203, .name = "SST 36VF3203", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1556,7 +1568,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,64), } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M29F800AB, .name = "ST M29F800AB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1571,7 +1583,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15), } }, { - .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ + .mfr_id = CFI_MFR_ST, /* FIXME - CFI device? */ .dev_id = M29W800DT, .name = "ST M29W800DT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1586,7 +1598,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ + .mfr_id = CFI_MFR_ST, /* FIXME - CFI device? */ .dev_id = M29W800DB, .name = "ST M29W800DB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1601,7 +1613,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15) } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M29W400DT, .name = "ST M29W400DT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1616,7 +1628,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,1) } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M29W400DB, .name = "ST M29W400DB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1631,7 +1643,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,7) } }, { - .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ + .mfr_id = CFI_MFR_ST, /* FIXME - CFI device? */ .dev_id = M29W160DT, .name = "ST M29W160DT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1646,7 +1658,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ + .mfr_id = CFI_MFR_ST, /* FIXME - CFI device? */ .dev_id = M29W160DB, .name = "ST M29W160DB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1661,7 +1673,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,31) } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M29W040B, .name = "ST M29W040B", .devtypes = CFI_DEVICETYPE_X8, @@ -1673,7 +1685,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M50FW040, .name = "ST M50FW040", .devtypes = CFI_DEVICETYPE_X8, @@ -1685,7 +1697,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M50FW080, .name = "ST M50FW080", .devtypes = CFI_DEVICETYPE_X8, @@ -1697,7 +1709,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,16), } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M50FW016, .name = "ST M50FW016", .devtypes = CFI_DEVICETYPE_X8, @@ -1709,7 +1721,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,32), } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M50LPW080, .name = "ST M50LPW080", .devtypes = CFI_DEVICETYPE_X8, @@ -1721,7 +1733,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,16), }, }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M50FLW080A, .name = "ST M50FLW080A", .devtypes = CFI_DEVICETYPE_X8, @@ -1736,7 +1748,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,16), } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M50FLW080B, .name = "ST M50FLW080B", .devtypes = CFI_DEVICETYPE_X8, @@ -1751,7 +1763,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,16), } }, { - .mfr_id = 0xff00 | MANUFACTURER_ST, + .mfr_id = 0xff00 | CFI_MFR_ST, .dev_id = 0xff00 | PSD4256G6V, .name = "ST PSD4256G6V", .devtypes = CFI_DEVICETYPE_X16, @@ -1763,7 +1775,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,16), } }, { - .mfr_id = MANUFACTURER_TOSHIBA, + .mfr_id = CFI_MFR_TOSHIBA, .dev_id = TC58FVT160, .name = "Toshiba TC58FVT160", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1778,7 +1790,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_TOSHIBA, + .mfr_id = CFI_MFR_TOSHIBA, .dev_id = TC58FVB160, .name = "Toshiba TC58FVB160", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1793,7 +1805,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,31) } }, { - .mfr_id = MANUFACTURER_TOSHIBA, + .mfr_id = CFI_MFR_TOSHIBA, .dev_id = TC58FVB321, .name = "Toshiba TC58FVB321", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1806,7 +1818,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,63) } }, { - .mfr_id = MANUFACTURER_TOSHIBA, + .mfr_id = CFI_MFR_TOSHIBA, .dev_id = TC58FVT321, .name = "Toshiba TC58FVT321", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1819,7 +1831,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000,8) } }, { - .mfr_id = MANUFACTURER_TOSHIBA, + .mfr_id = CFI_MFR_TOSHIBA, .dev_id = TC58FVB641, .name = "Toshiba TC58FVB641", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1832,7 +1844,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,127) } }, { - .mfr_id = MANUFACTURER_TOSHIBA, + .mfr_id = CFI_MFR_TOSHIBA, .dev_id = TC58FVT641, .name = "Toshiba TC58FVT641", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1845,7 +1857,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000,8) } }, { - .mfr_id = MANUFACTURER_WINBOND, + .mfr_id = CFI_MFR_WINBOND, .dev_id = W49V002A, .name = "Winbond W49V002A", .devtypes = CFI_DEVICETYPE_X8, @@ -1878,7 +1890,7 @@ static inline u32 jedec_read_mfr(struct map_info *map, uint32_t base, mask = (1 << (cfi->device_type * 8)) - 1; result = map_read(map, base + ofs); bank++; - } while ((result.x[0] & mask) == CONTINUATION_CODE); + } while ((result.x[0] & mask) == CFI_MFR_CONTINUATION); return result.x[0] & mask; } @@ -1969,7 +1981,7 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1 / p_cfi->device_type; p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2 / p_cfi->device_type; - return 1; /* ok */ + return 1; /* ok */ } diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index ab5c9b9..f3226b1 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -1,5 +1,5 @@ # -# linux/drivers/devices/Makefile +# linux/drivers/mtd/devices/Makefile # obj-$(CONFIG_MTD_DOC2000) += doc2000.o diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index ce64240..9365186 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -276,12 +276,10 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) /* Setup the MTD structure */ /* make the name contain the block device in */ - name = kmalloc(sizeof("block2mtd: ") + strlen(devname) + 1, - GFP_KERNEL); + name = kasprintf(GFP_KERNEL, "block2mtd: %s", devname); if (!name) goto devinit_err; - sprintf(name, "block2mtd: %s", devname); dev->mtd.name = name; dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index d2fd550..fc8ea0a 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -668,7 +668,7 @@ static int __init init_pmc551(void) { struct pci_dev *PCI_Device = NULL; struct mypriv *priv; - int count, found = 0; + int found = 0; struct mtd_info *mtd; u32 length = 0; @@ -695,7 +695,7 @@ static int __init init_pmc551(void) /* * PCU-bus chipset probe. */ - for (count = 0; count < MAX_MTD_DEVICES; count++) { + for (;;) { if ((PCI_Device = pci_get_device(PCI_VENDOR_ID_V3_SEMI, PCI_DEVICE_ID_V3_SEMI_V370PDC, diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index fe17054..ab5d8cd02 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -73,15 +73,25 @@ static struct flash_info __initdata sst25l_flash_info[] = { static int sst25l_status(struct sst25l_flash *flash, int *status) { - unsigned char command, response; + struct spi_message m; + struct spi_transfer t; + unsigned char cmd_resp[2]; int err; - command = SST25L_CMD_RDSR; - err = spi_write_then_read(flash->spi, &command, 1, &response, 1); + spi_message_init(&m); + memset(&t, 0, sizeof(struct spi_transfer)); + + cmd_resp[0] = SST25L_CMD_RDSR; + cmd_resp[1] = 0xff; + t.tx_buf = cmd_resp; + t.rx_buf = cmd_resp; + t.len = sizeof(cmd_resp); + spi_message_add_tail(&t, &m); + err = spi_sync(flash->spi, &m); if (err < 0) return err; - *status = response; + *status = cmd_resp[1]; return 0; } @@ -328,33 +338,32 @@ out: static struct flash_info *__init sst25l_match_device(struct spi_device *spi) { struct flash_info *flash_info = NULL; - unsigned char command[4], response; + struct spi_message m; + struct spi_transfer t; + unsigned char cmd_resp[6]; int i, err; uint16_t id; - command[0] = SST25L_CMD_READ_ID; - command[1] = 0; - command[2] = 0; - command[3] = 0; - err = spi_write_then_read(spi, command, sizeof(command), &response, 1); + spi_message_init(&m); + memset(&t, 0, sizeof(struct spi_transfer)); + + cmd_resp[0] = SST25L_CMD_READ_ID; + cmd_resp[1] = 0; + cmd_resp[2] = 0; + cmd_resp[3] = 0; + cmd_resp[4] = 0xff; + cmd_resp[5] = 0xff; + t.tx_buf = cmd_resp; + t.rx_buf = cmd_resp; + t.len = sizeof(cmd_resp); + spi_message_add_tail(&t, &m); + err = spi_sync(spi, &m); if (err < 0) { - dev_err(&spi->dev, "error reading device id msb\n"); + dev_err(&spi->dev, "error reading device id\n"); return NULL; } - id = response << 8; - - command[0] = SST25L_CMD_READ_ID; - command[1] = 0; - command[2] = 0; - command[3] = 1; - err = spi_write_then_read(spi, command, sizeof(command), &response, 1); - if (err < 0) { - dev_err(&spi->dev, "error reading device id lsb\n"); - return NULL; - } - - id |= response; + id = (cmd_resp[4] << 8) | cmd_resp[5]; for (i = 0; i < ARRAY_SIZE(sst25l_flash_info); i++) if (sst25l_flash_info[i].device_id == id) @@ -411,17 +420,6 @@ static int __init sst25l_probe(struct spi_device *spi) flash->mtd.erasesize, flash->mtd.erasesize / 1024, flash->mtd.numeraseregions); - if (flash->mtd.numeraseregions) - for (i = 0; i < flash->mtd.numeraseregions; i++) - DEBUG(MTD_DEBUG_LEVEL2, - "mtd.eraseregions[%d] = { .offset = 0x%llx, " - ".erasesize = 0x%.8x (%uKiB), " - ".numblocks = %d }\n", - i, (long long)flash->mtd.eraseregions[i].offset, - flash->mtd.eraseregions[i].erasesize, - flash->mtd.eraseregions[i].erasesize / 1024, - flash->mtd.eraseregions[i].numblocks); - if (mtd_has_partitions()) { struct mtd_partition *parts = NULL; int nr_parts = 0; diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index e56d6b4..62da9eb 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -1082,7 +1082,6 @@ static void ftl_remove_dev(struct mtd_blktrans_dev *dev) { del_mtd_blktrans_dev(dev); ftl_freepart((partition_t *)dev); - kfree(dev); } static struct mtd_blktrans_ops ftl_tr = { diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index 8aca552..015a7fe 100644 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -139,7 +139,6 @@ static void inftl_remove_dev(struct mtd_blktrans_dev *dev) kfree(inftl->PUtable); kfree(inftl->VUtable); - kfree(inftl); } /* diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index 32e82ae..8f988d7 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -100,9 +100,10 @@ static int find_boot_record(struct INFTLrecord *inftl) } /* To be safer with BIOS, also use erase mark as discriminant */ - if ((ret = inftl_read_oob(mtd, block * inftl->EraseSize + - SECTORSIZE + 8, 8, &retlen, - (char *)&h1) < 0)) { + ret = inftl_read_oob(mtd, + block * inftl->EraseSize + SECTORSIZE + 8, + 8, &retlen,(char *)&h1); + if (ret < 0) { printk(KERN_WARNING "INFTL: ANAND header found at " "0x%x in mtd%d, but OOB data read failed " "(err %d)\n", block * inftl->EraseSize, diff --git a/drivers/mtd/internal.h b/drivers/mtd/internal.h deleted file mode 100644 index c658fe7..0000000 --- a/drivers/mtd/internal.h +++ /dev/null @@ -1,17 +0,0 @@ -/* Internal MTD definitions - * - * Copyright © 2006 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -/* - * mtdbdi.c - */ -extern struct backing_dev_info mtd_bdi_unmappable; -extern struct backing_dev_info mtd_bdi_ro_mappable; -extern struct backing_dev_info mtd_bdi_rw_mappable; diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c index a73ee12..fece5be 100644 --- a/drivers/mtd/lpddr/lpddr_cmds.c +++ b/drivers/mtd/lpddr/lpddr_cmds.c @@ -107,8 +107,7 @@ struct mtd_info *lpddr_cmdset(struct map_info *map) /* those should be reset too since they create memory references. */ init_waitqueue_head(&chip->wq); - spin_lock_init(&chip->_spinlock); - chip->mutex = &chip->_spinlock; + mutex_init(&chip->mutex); chip++; } } @@ -144,7 +143,7 @@ static int wait_for_ready(struct map_info *map, struct flchip *chip, } /* OK Still waiting. Drop the lock, wait a while and retry. */ - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); if (sleep_time >= 1000000/HZ) { /* * Half of the normal delay still remaining @@ -159,17 +158,17 @@ static int wait_for_ready(struct map_info *map, struct flchip *chip, cond_resched(); timeo--; } - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); while (chip->state != chip_state) { /* Someone's suspended the operation: sleep */ DECLARE_WAITQUEUE(wait, current); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); } if (chip->erase_suspended || chip->write_suspended) { /* Suspend has occured while sleep: reset timeout */ @@ -230,20 +229,20 @@ static int get_chip(struct map_info *map, struct flchip *chip, int mode) * it'll happily send us to sleep. In any case, when * get_chip returns success we're clear to go ahead. */ - ret = spin_trylock(contender->mutex); + ret = mutex_trylock(&contender->mutex); spin_unlock(&shared->lock); if (!ret) goto retry; - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); ret = chip_ready(map, contender, mode); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (ret == -EAGAIN) { - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); goto retry; } if (ret) { - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); return ret; } spin_lock(&shared->lock); @@ -252,10 +251,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, int mode) * state. Put contender and retry. */ if (chip->state == FL_SYNCING) { put_chip(map, contender); - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); goto retry; } - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); } /* Check if we have suspended erase on this chip. @@ -265,10 +264,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, int mode) spin_unlock(&shared->lock); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); goto retry; } @@ -337,10 +336,10 @@ static int chip_ready(struct map_info *map, struct flchip *chip, int mode) sleep: set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); return -EAGAIN; } } @@ -356,12 +355,12 @@ static void put_chip(struct map_info *map, struct flchip *chip) if (shared->writing && shared->writing != chip) { /* give back the ownership */ struct flchip *loaner = shared->writing; - spin_lock(loaner->mutex); + mutex_lock(&loaner->mutex); spin_unlock(&shared->lock); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); put_chip(map, loaner); - spin_lock(chip->mutex); - spin_unlock(loaner->mutex); + mutex_lock(&chip->mutex); + mutex_unlock(&loaner->mutex); wake_up(&chip->wq); return; } @@ -414,10 +413,10 @@ int do_write_buffer(struct map_info *map, struct flchip *chip, wbufsize = 1 << lpddr->qinfo->BufSizeShift; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, FL_WRITING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } /* Figure out the number of words to write */ @@ -478,7 +477,7 @@ int do_write_buffer(struct map_info *map, struct flchip *chip, } out: put_chip(map, chip); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -490,10 +489,10 @@ int do_erase_oneblock(struct mtd_info *mtd, loff_t adr) struct flchip *chip = &lpddr->chips[chipnum]; int ret; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, FL_ERASING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } send_pfow_command(map, LPDDR_BLOCK_ERASE, adr, 0, NULL); @@ -505,7 +504,7 @@ int do_erase_oneblock(struct mtd_info *mtd, loff_t adr) goto out; } out: put_chip(map, chip); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -518,10 +517,10 @@ static int lpddr_read(struct mtd_info *mtd, loff_t adr, size_t len, struct flchip *chip = &lpddr->chips[chipnum]; int ret = 0; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, FL_READY); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -529,7 +528,7 @@ static int lpddr_read(struct mtd_info *mtd, loff_t adr, size_t len, *retlen = len; put_chip(map, chip); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -569,9 +568,9 @@ static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len, else thislen = len; /* get the chip */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, FL_POINT); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); if (ret) break; @@ -611,7 +610,7 @@ static void lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len) else thislen = len; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_POINT) { chip->ref_point_counter--; if (chip->ref_point_counter == 0) @@ -621,7 +620,7 @@ static void lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len) "pointed region\n", map->name); put_chip(map, chip); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); len -= thislen; ofs = 0; @@ -727,10 +726,10 @@ int do_xxlock(struct mtd_info *mtd, loff_t adr, uint32_t len, int thunk) int chipnum = adr >> lpddr->chipshift; struct flchip *chip = &lpddr->chips[chipnum]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, FL_LOCKING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -750,7 +749,7 @@ int do_xxlock(struct mtd_info *mtd, loff_t adr, uint32_t len, int thunk) goto out; } out: put_chip(map, chip); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -771,10 +770,10 @@ int word_program(struct map_info *map, loff_t adr, uint32_t curval) int chipnum = adr >> lpddr->chipshift; struct flchip *chip = &lpddr->chips[chipnum]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, FL_WRITING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -788,7 +787,7 @@ int word_program(struct map_info *map, loff_t adr, uint32_t curval) } out: put_chip(map, chip); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } diff --git a/drivers/mtd/lpddr/qinfo_probe.c b/drivers/mtd/lpddr/qinfo_probe.c index 79bf40f..dbfe17b 100644 --- a/drivers/mtd/lpddr/qinfo_probe.c +++ b/drivers/mtd/lpddr/qinfo_probe.c @@ -134,13 +134,12 @@ out: static int lpddr_chip_setup(struct map_info *map, struct lpddr_private *lpddr) { - lpddr->qinfo = kmalloc(sizeof(struct qinfo_chip), GFP_KERNEL); + lpddr->qinfo = kzalloc(sizeof(struct qinfo_chip), GFP_KERNEL); if (!lpddr->qinfo) { printk(KERN_WARNING "%s: no memory for LPDDR qinfo structure\n", map->name); return 0; } - memset(lpddr->qinfo, 0, sizeof(struct qinfo_chip)); /* Get the ManuID */ lpddr->ManufactId = CMDVAL(map_read(map, map->pfow_base + PFOW_MANUFACTURER_ID)); @@ -185,13 +184,11 @@ static struct lpddr_private *lpddr_probe_chip(struct map_info *map) lpddr.numchips = 1; numvirtchips = lpddr.numchips * lpddr.qinfo->HWPartsNum; - retlpddr = kmalloc(sizeof(struct lpddr_private) + + retlpddr = kzalloc(sizeof(struct lpddr_private) + numvirtchips * sizeof(struct flchip), GFP_KERNEL); if (!retlpddr) return NULL; - memset(retlpddr, 0, sizeof(struct lpddr_private) + - numvirtchips * sizeof(struct flchip)); memcpy(retlpddr, &lpddr, sizeof(struct lpddr_private)); retlpddr->numchips = numvirtchips; diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index aa2807d..f22bc9f 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -435,7 +435,7 @@ config MTD_PCI config MTD_PCMCIA tristate "PCMCIA MTD driver" - depends on PCMCIA && MTD_COMPLEX_MAPPINGS && BROKEN + depends on PCMCIA && MTD_COMPLEX_MAPPINGS help Map driver for accessing PCMCIA linear flash memory cards. These cards are usually around 4-16MiB in size. This does not include diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c index c0fd99b..85dd181 100644 --- a/drivers/mtd/maps/bfin-async-flash.c +++ b/drivers/mtd/maps/bfin-async-flash.c @@ -70,7 +70,7 @@ static void switch_back(struct async_state *state) local_irq_restore(state->irq_flags); } -static map_word bfin_read(struct map_info *map, unsigned long ofs) +static map_word bfin_flash_read(struct map_info *map, unsigned long ofs) { struct async_state *state = (struct async_state *)map->map_priv_1; uint16_t word; @@ -86,7 +86,7 @@ static map_word bfin_read(struct map_info *map, unsigned long ofs) return test; } -static void bfin_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +static void bfin_flash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) { struct async_state *state = (struct async_state *)map->map_priv_1; @@ -97,7 +97,7 @@ static void bfin_copy_from(struct map_info *map, void *to, unsigned long from, s switch_back(state); } -static void bfin_write(struct map_info *map, map_word d1, unsigned long ofs) +static void bfin_flash_write(struct map_info *map, map_word d1, unsigned long ofs) { struct async_state *state = (struct async_state *)map->map_priv_1; uint16_t d; @@ -112,7 +112,7 @@ static void bfin_write(struct map_info *map, map_word d1, unsigned long ofs) switch_back(state); } -static void bfin_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +static void bfin_flash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) { struct async_state *state = (struct async_state *)map->map_priv_1; @@ -141,10 +141,10 @@ static int __devinit bfin_flash_probe(struct platform_device *pdev) return -ENOMEM; state->map.name = DRIVER_NAME; - state->map.read = bfin_read; - state->map.copy_from = bfin_copy_from; - state->map.write = bfin_write; - state->map.copy_to = bfin_copy_to; + state->map.read = bfin_flash_read; + state->map.copy_from = bfin_flash_copy_from; + state->map.write = bfin_flash_write; + state->map.copy_to = bfin_flash_copy_to; state->map.bankwidth = pdata->width; state->map.size = memory->end - memory->start + 1; state->map.virt = (void __iomem *)memory->start; diff --git a/drivers/mtd/maps/ceiva.c b/drivers/mtd/maps/ceiva.c index d41f347..c09f4f5 100644 --- a/drivers/mtd/maps/ceiva.c +++ b/drivers/mtd/maps/ceiva.c @@ -253,7 +253,7 @@ static void __exit clps_destroy_mtd(struct clps_info *clps, struct mtd_info *mtd static int __init clps_setup_flash(void) { - int nr; + int nr = 0; #ifdef CONFIG_ARCH_CEIVA if (machine_is_ceiva()) { diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c index 1bdf0ee..9639d83 100644 --- a/drivers/mtd/maps/ixp2000.c +++ b/drivers/mtd/maps/ixp2000.c @@ -165,12 +165,11 @@ static int ixp2000_flash_probe(struct platform_device *dev) return -EIO; } - info = kmalloc(sizeof(struct ixp2000_flash_info), GFP_KERNEL); + info = kzalloc(sizeof(struct ixp2000_flash_info), GFP_KERNEL); if(!info) { err = -ENOMEM; goto Error; } - memset(info, 0, sizeof(struct ixp2000_flash_info)); platform_set_drvdata(dev, info); diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index 7b05152..e0a5e04 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -107,8 +107,8 @@ static void ixp4xx_copy_from(struct map_info *map, void *to, return; if (from & 1) { - *dest++ = BYTE1(flash_read16(src)); - src++; + *dest++ = BYTE1(flash_read16(src-1)); + src++; --len; } @@ -196,12 +196,11 @@ static int ixp4xx_flash_probe(struct platform_device *dev) return err; } - info = kmalloc(sizeof(struct ixp4xx_flash_info), GFP_KERNEL); + info = kzalloc(sizeof(struct ixp4xx_flash_info), GFP_KERNEL); if(!info) { err = -ENOMEM; goto Error; } - memset(info, 0, sizeof(struct ixp4xx_flash_info)); platform_set_drvdata(dev, info); diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index 689d6a7..e699e6a 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -40,10 +40,7 @@ MODULE_PARM_DESC(debug, "Set Debug Level 0=quiet, 5=noisy"); static const int debug = 0; #endif -#define err(format, arg...) printk(KERN_ERR "pcmciamtd: " format "\n" , ## arg) #define info(format, arg...) printk(KERN_INFO "pcmciamtd: " format "\n" , ## arg) -#define warn(format, arg...) printk(KERN_WARNING "pcmciamtd: " format "\n" , ## arg) - #define DRIVER_DESC "PCMCIA Flash memory card driver" @@ -52,7 +49,6 @@ static const int debug = 0; struct pcmciamtd_dev { struct pcmcia_device *p_dev; - dev_node_t node; /* device node */ caddr_t win_base; /* ioremapped address of PCMCIA window */ unsigned int win_size; /* size of window */ unsigned int offset; /* offset into card the window currently points at */ @@ -100,7 +96,9 @@ module_param(mem_type, int, 0); MODULE_PARM_DESC(mem_type, "Set Memory type (0=Flash, 1=RAM, 2=ROM, default=0)"); -/* read/write{8,16} copy_{from,to} routines with window remapping to access whole card */ +/* read/write{8,16} copy_{from,to} routines with window remapping + * to access whole card + */ static caddr_t remap_window(struct map_info *map, unsigned long to) { struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; @@ -137,7 +135,7 @@ static map_word pcmcia_read8_remap(struct map_info *map, unsigned long ofs) return d; d.x[0] = readb(addr); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, addr, d.x[0]); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02lx", ofs, addr, d.x[0]); return d; } @@ -152,7 +150,7 @@ static map_word pcmcia_read16_remap(struct map_info *map, unsigned long ofs) return d; d.x[0] = readw(addr); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, addr, d.x[0]); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04lx", ofs, addr, d.x[0]); return d; } @@ -162,7 +160,7 @@ static void pcmcia_copy_from_remap(struct map_info *map, void *to, unsigned long struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; unsigned long win_size = dev->win_size; - DEBUG(3, "to = %p from = %lu len = %u", to, from, len); + DEBUG(3, "to = %p from = %lu len = %zd", to, from, len); while(len) { int toread = win_size - (from & (win_size-1)); caddr_t addr; @@ -190,7 +188,7 @@ static void pcmcia_write8_remap(struct map_info *map, map_word d, unsigned long if(!addr) return; - DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02x", adr, addr, d.x[0]); + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02lx", adr, addr, d.x[0]); writeb(d.x[0], addr); } @@ -201,7 +199,7 @@ static void pcmcia_write16_remap(struct map_info *map, map_word d, unsigned long if(!addr) return; - DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04x", adr, addr, d.x[0]); + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04lx", adr, addr, d.x[0]); writew(d.x[0], addr); } @@ -211,7 +209,7 @@ static void pcmcia_copy_to_remap(struct map_info *map, unsigned long to, const v struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; unsigned long win_size = dev->win_size; - DEBUG(3, "to = %lu from = %p len = %u", to, from, len); + DEBUG(3, "to = %lu from = %p len = %zd", to, from, len); while(len) { int towrite = win_size - (to & (win_size-1)); caddr_t addr; @@ -245,7 +243,8 @@ static map_word pcmcia_read8(struct map_info *map, unsigned long ofs) return d; d.x[0] = readb(win_base + ofs); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, win_base + ofs, d.x[0]); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02lx", + ofs, win_base + ofs, d.x[0]); return d; } @@ -259,7 +258,8 @@ static map_word pcmcia_read16(struct map_info *map, unsigned long ofs) return d; d.x[0] = readw(win_base + ofs); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, win_base + ofs, d.x[0]); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04lx", + ofs, win_base + ofs, d.x[0]); return d; } @@ -271,32 +271,34 @@ static void pcmcia_copy_from(struct map_info *map, void *to, unsigned long from, if(DEV_REMOVED(map)) return; - DEBUG(3, "to = %p from = %lu len = %u", to, from, len); + DEBUG(3, "to = %p from = %lu len = %zd", to, from, len); memcpy_fromio(to, win_base + from, len); } -static void pcmcia_write8(struct map_info *map, u8 d, unsigned long adr) +static void pcmcia_write8(struct map_info *map, map_word d, unsigned long adr) { caddr_t win_base = (caddr_t)map->map_priv_2; if(DEV_REMOVED(map)) return; - DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02x", adr, win_base + adr, d); - writeb(d, win_base + adr); + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02lx", + adr, win_base + adr, d.x[0]); + writeb(d.x[0], win_base + adr); } -static void pcmcia_write16(struct map_info *map, u16 d, unsigned long adr) +static void pcmcia_write16(struct map_info *map, map_word d, unsigned long adr) { caddr_t win_base = (caddr_t)map->map_priv_2; if(DEV_REMOVED(map)) return; - DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04x", adr, win_base + adr, d); - writew(d, win_base + adr); + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04lx", + adr, win_base + adr, d.x[0]); + writew(d.x[0], win_base + adr); } @@ -307,7 +309,7 @@ static void pcmcia_copy_to(struct map_info *map, unsigned long to, const void *f if(DEV_REMOVED(map)) return; - DEBUG(3, "to = %lu from = %p len = %u", to, from, len); + DEBUG(3, "to = %lu from = %p len = %zd", to, from, len); memcpy_toio(win_base + to, from, len); } @@ -376,7 +378,8 @@ static int pcmciamtd_cistpl_jedec(struct pcmcia_device *p_dev, if (!pcmcia_parse_tuple(tuple, &parse)) { cistpl_jedec_t *t = &parse.jedec; for (i = 0; i < t->nid; i++) - DEBUG(2, "JEDEC: 0x%02x 0x%02x", t->id[i].mfr, t->id[i].info); + DEBUG(2, "JEDEC: 0x%02x 0x%02x", + t->id[i].mfr, t->id[i].info); } return -ENOSPC; } @@ -432,7 +435,7 @@ static int pcmciamtd_cistpl_geo(struct pcmcia_device *p_dev, } -static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *link, int *new_name) +static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *p_dev, int *new_name) { int i; @@ -477,7 +480,8 @@ static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *link, } DEBUG(1, "Device: Size: %lu Width:%d Name: %s", - dev->pcmcia_map.size, dev->pcmcia_map.bankwidth << 3, dev->mtd_name); + dev->pcmcia_map.size, + dev->pcmcia_map.bankwidth << 3, dev->mtd_name); } @@ -490,7 +494,6 @@ static int pcmciamtd_config(struct pcmcia_device *link) { struct pcmciamtd_dev *dev = link->priv; struct mtd_info *mtd = NULL; - cs_status_t status; win_req_t req; int ret; int i; @@ -514,9 +517,11 @@ static int pcmciamtd_config(struct pcmcia_device *link) if(setvpp == 1) dev->pcmcia_map.set_vpp = pcmciamtd_set_vpp; - /* Request a memory window for PCMCIA. Some architeures can map windows upto the maximum - that PCMCIA can support (64MiB) - this is ideal and we aim for a window the size of the - whole card - otherwise we try smaller windows until we succeed */ + /* Request a memory window for PCMCIA. Some architeures can map windows + * upto the maximum that PCMCIA can support (64MiB) - this is ideal and + * we aim for a window the size of the whole card - otherwise we try + * smaller windows until we succeed + */ req.Attributes = WIN_MEMORY_TYPE_CM | WIN_ENABLE; req.Attributes |= (dev->pcmcia_map.bankwidth == 1) ? WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16; @@ -544,7 +549,7 @@ static int pcmciamtd_config(struct pcmcia_device *link) DEBUG(2, "dev->win_size = %d", dev->win_size); if(!dev->win_size) { - err("Cant allocate memory window"); + dev_err(&dev->p_dev->dev, "Cannot allocate memory window\n"); pcmciamtd_release(link); return -ENODEV; } @@ -554,7 +559,8 @@ static int pcmciamtd_config(struct pcmcia_device *link) DEBUG(2, "window handle = 0x%8.8lx", (unsigned long)link->win); dev->win_base = ioremap(req.Base, req.Size); if(!dev->win_base) { - err("ioremap(%lu, %u) failed", req.Base, req.Size); + dev_err(&dev->p_dev->dev, "ioremap(%lu, %u) failed\n", + req.Base, req.Size); pcmciamtd_release(link); return -ENODEV; } @@ -565,7 +571,7 @@ static int pcmciamtd_config(struct pcmcia_device *link) dev->pcmcia_map.map_priv_1 = (unsigned long)dev; dev->pcmcia_map.map_priv_2 = (unsigned long)link->win; - dev->vpp = (vpp) ? vpp : link->socket.socket.Vpp; + dev->vpp = (vpp) ? vpp : link->socket->socket.Vpp; link->conf.Attributes = 0; if(setvpp == 2) { link->conf.Vpp = dev->vpp; @@ -601,7 +607,7 @@ static int pcmciamtd_config(struct pcmcia_device *link) } if(!mtd) { - DEBUG(1, "Cant find an MTD"); + DEBUG(1, "Can not find an MTD"); pcmciamtd_release(link); return -ENODEV; } @@ -612,8 +618,9 @@ static int pcmciamtd_config(struct pcmcia_device *link) if(new_name) { int size = 0; char unit = ' '; - /* Since we are using a default name, make it better by adding in the - size */ + /* Since we are using a default name, make it better by adding + * in the size + */ if(mtd->size < 1048576) { /* <1MiB in size, show size in KiB */ size = mtd->size >> 10; unit = 'K'; @@ -643,17 +650,15 @@ static int pcmciamtd_config(struct pcmcia_device *link) if(add_mtd_device(mtd)) { map_destroy(mtd); dev->mtd_info = NULL; - err("Couldnt register MTD device"); + dev_err(&dev->p_dev->dev, + "Could not register the MTD device\n"); pcmciamtd_release(link); return -ENODEV; } - snprintf(dev->node.dev_name, sizeof(dev->node.dev_name), "mtd%d", mtd->index); - info("mtd%d: %s", mtd->index, mtd->name); - link->dev_node = &dev->node; + dev_info(&dev->p_dev->dev, "mtd%d: %s\n", mtd->index, mtd->name); return 0; - failed: - err("CS Error, exiting"); + dev_err(&dev->p_dev->dev, "CS Error, exiting\n"); pcmciamtd_release(link); return -ENODEV; } @@ -692,8 +697,9 @@ static void pcmciamtd_detach(struct pcmcia_device *link) if(dev->mtd_info) { del_mtd_device(dev->mtd_info); + dev_info(&dev->p_dev->dev, "mtd%d: Removing\n", + dev->mtd_info->index); map_destroy(dev->mtd_info); - info("mtd%d: Removed", dev->mtd_info->index); } pcmciamtd_release(link); @@ -737,8 +743,11 @@ static struct pcmcia_device_id pcmciamtd_ids[] = { PCMCIA_DEVICE_PROD_ID12("intel", "VALUE SERIES 100 ", 0x40ade711, 0xdf8506d8), PCMCIA_DEVICE_PROD_ID12("KINGMAX TECHNOLOGY INC.", "SRAM 256K Bytes", 0x54d0c69c, 0xad12c29c), PCMCIA_DEVICE_PROD_ID12("Maxtor", "MAXFL MobileMax Flash Memory Card", 0xb68968c8, 0x2dfb47b0), + PCMCIA_DEVICE_PROD_ID123("M-Systems", "M-SYS Flash Memory Card", "(c) M-Systems", 0x7ed2ad87, 0x675dc3fb, 0x7aef3965), + PCMCIA_DEVICE_PROD_ID12("PRETEC", " 2MB SRAM CARD", 0xebf91155, 0x805360ca), PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB101EN20", 0xf9876baf, 0xad0b207b), PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB513EN20", 0xf9876baf, 0xe8d884ad), + PCMCIA_DEVICE_PROD_ID12("SMART Modular Technologies", " 4MB FLASH Card", 0x96fd8277, 0x737a5b05), PCMCIA_DEVICE_PROD_ID12("Starfish, Inc.", "REX-3000", 0x05ddca47, 0xe7d67bca), PCMCIA_DEVICE_PROD_ID12("Starfish, Inc.", "REX-4100", 0x05ddca47, 0x7bc32944), /* the following was commented out in pcmcia-cs-3.2.7 */ diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index d9603f7..426461a 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -264,8 +264,11 @@ static int __init physmap_init(void) err = platform_driver_register(&physmap_flash_driver); #ifdef CONFIG_MTD_PHYSMAP_COMPAT - if (err == 0) - platform_device_register(&physmap_flash); + if (err == 0) { + err = platform_device_register(&physmap_flash); + if (err) + platform_driver_unregister(&physmap_flash_driver); + } #endif return err; diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 4147fe3..ba124ba 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -173,12 +173,53 @@ static struct mtd_info * __devinit obsolete_probe(struct of_device *dev, } } +#ifdef CONFIG_MTD_PARTITIONS +/* When partitions are set we look for a linux,part-probe property which + specifies the list of partition probers to use. If none is given then the + default is use. These take precedence over other device tree + information. */ +static const char *part_probe_types_def[] = { "cmdlinepart", "RedBoot", NULL }; +static const char ** __devinit of_get_probes(struct device_node *dp) +{ + const char *cp; + int cplen; + unsigned int l; + unsigned int count; + const char **res; + + cp = of_get_property(dp, "linux,part-probe", &cplen); + if (cp == NULL) + return part_probe_types_def; + + count = 0; + for (l = 0; l != cplen; l++) + if (cp[l] == 0) + count++; + + res = kzalloc((count + 1)*sizeof(*res), GFP_KERNEL); + count = 0; + while (cplen > 0) { + res[count] = cp; + l = strlen(cp) + 1; + cp += l; + cplen -= l; + count++; + } + return res; +} + +static void __devinit of_free_probes(const char **probes) +{ + if (probes != part_probe_types_def) + kfree(probes); +} +#endif + static int __devinit of_flash_probe(struct of_device *dev, const struct of_device_id *match) { #ifdef CONFIG_MTD_PARTITIONS - static const char *part_probe_types[] - = { "cmdlinepart", "RedBoot", NULL }; + const char **part_probe_types; #endif struct device_node *dp = dev->dev.of_node; struct resource res; @@ -218,7 +259,7 @@ static int __devinit of_flash_probe(struct of_device *dev, dev_set_drvdata(&dev->dev, info); - mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL); + mtd_list = kzalloc(sizeof(*mtd_list) * count, GFP_KERNEL); if (!mtd_list) goto err_flash_remove; @@ -307,12 +348,14 @@ static int __devinit of_flash_probe(struct of_device *dev, goto err_out; #ifdef CONFIG_MTD_PARTITIONS - /* First look for RedBoot table or partitions on the command - * line, these take precedence over device tree information */ + part_probe_types = of_get_probes(dp); err = parse_mtd_partitions(info->cmtd, part_probe_types, &info->parts, 0); - if (err < 0) + if (err < 0) { + of_free_probes(part_probe_types); return err; + } + of_free_probes(part_probe_types); #ifdef CONFIG_MTD_OF_PARTS if (err == 0) { diff --git a/drivers/mtd/maps/pismo.c b/drivers/mtd/maps/pismo.c index 60c068d..eb476b7 100644 --- a/drivers/mtd/maps/pismo.c +++ b/drivers/mtd/maps/pismo.c @@ -234,6 +234,7 @@ static int __devexit pismo_remove(struct i2c_client *client) /* FIXME: set_vpp needs saner arguments */ pismo_setvpp_remove_fix(pismo); + i2c_set_clientdata(client, NULL); kfree(pismo); return 0; @@ -272,7 +273,7 @@ static int __devinit pismo_probe(struct i2c_client *client, ret = pismo_eeprom_read(client, &eeprom, 0, sizeof(eeprom)); if (ret < 0) { dev_err(&client->dev, "error reading EEPROM: %d\n", ret); - return ret; + goto exit_free; } dev_info(&client->dev, "%.15s board found\n", eeprom.board); @@ -283,6 +284,11 @@ static int __devinit pismo_probe(struct i2c_client *client, pdata->cs_addrs[i]); return 0; + + exit_free: + i2c_set_clientdata(client, NULL); + kfree(pismo); + return ret; } static const struct i2c_device_id pismo_id[] = { diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c index 91dc633..dd90880 100644 --- a/drivers/mtd/maps/pxa2xx-flash.c +++ b/drivers/mtd/maps/pxa2xx-flash.c @@ -63,11 +63,10 @@ static int __init pxa2xx_flash_probe(struct platform_device *pdev) if (!res) return -ENODEV; - info = kmalloc(sizeof(struct pxa2xx_flash_info), GFP_KERNEL); + info = kzalloc(sizeof(struct pxa2xx_flash_info), GFP_KERNEL); if (!info) return -ENOMEM; - memset(info, 0, sizeof(struct pxa2xx_flash_info)); info->map.name = (char *) flash->name; info->map.bankwidth = flash->width; info->map.phys = res->start; diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index c82e09b..03e19c1 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -14,7 +14,6 @@ #include <linux/mtd/mtd.h> #include <linux/blkdev.h> #include <linux/blkpg.h> -#include <linux/freezer.h> #include <linux/spinlock.h> #include <linux/hdreg.h> #include <linux/init.h> @@ -25,12 +24,42 @@ #include "mtdcore.h" static LIST_HEAD(blktrans_majors); +static DEFINE_MUTEX(blktrans_ref_mutex); + +void blktrans_dev_release(struct kref *kref) +{ + struct mtd_blktrans_dev *dev = + container_of(kref, struct mtd_blktrans_dev, ref); + + dev->disk->private_data = NULL; + blk_cleanup_queue(dev->rq); + put_disk(dev->disk); + list_del(&dev->list); + kfree(dev); +} + +static struct mtd_blktrans_dev *blktrans_dev_get(struct gendisk *disk) +{ + struct mtd_blktrans_dev *dev; + + mutex_lock(&blktrans_ref_mutex); + dev = disk->private_data; + + if (!dev) + goto unlock; + kref_get(&dev->ref); +unlock: + mutex_unlock(&blktrans_ref_mutex); + return dev; +} + +void blktrans_dev_put(struct mtd_blktrans_dev *dev) +{ + mutex_lock(&blktrans_ref_mutex); + kref_put(&dev->ref, blktrans_dev_release); + mutex_unlock(&blktrans_ref_mutex); +} -struct mtd_blkcore_priv { - struct task_struct *thread; - struct request_queue *rq; - spinlock_t queue_lock; -}; static int do_blktrans_request(struct mtd_blktrans_ops *tr, struct mtd_blktrans_dev *dev, @@ -61,7 +90,6 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, return -EIO; rq_flush_dcache_pages(req); return 0; - case WRITE: if (!tr->writesect) return -EIO; @@ -71,7 +99,6 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, if (tr->writesect(dev, block, buf)) return -EIO; return 0; - default: printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req)); return -EIO; @@ -80,14 +107,13 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, static int mtd_blktrans_thread(void *arg) { - struct mtd_blktrans_ops *tr = arg; - struct request_queue *rq = tr->blkcore_priv->rq; + struct mtd_blktrans_dev *dev = arg; + struct request_queue *rq = dev->rq; struct request *req = NULL; spin_lock_irq(rq->queue_lock); while (!kthread_should_stop()) { - struct mtd_blktrans_dev *dev; int res; if (!req && !(req = blk_fetch_request(rq))) { @@ -98,13 +124,10 @@ static int mtd_blktrans_thread(void *arg) continue; } - dev = req->rq_disk->private_data; - tr = dev->tr; - spin_unlock_irq(rq->queue_lock); mutex_lock(&dev->lock); - res = do_blktrans_request(tr, dev, req); + res = do_blktrans_request(dev->tr, dev, req); mutex_unlock(&dev->lock); spin_lock_irq(rq->queue_lock); @@ -123,81 +146,112 @@ static int mtd_blktrans_thread(void *arg) static void mtd_blktrans_request(struct request_queue *rq) { - struct mtd_blktrans_ops *tr = rq->queuedata; - wake_up_process(tr->blkcore_priv->thread); -} + struct mtd_blktrans_dev *dev; + struct request *req = NULL; + + dev = rq->queuedata; + if (!dev) + while ((req = blk_fetch_request(rq)) != NULL) + __blk_end_request_all(req, -ENODEV); + else + wake_up_process(dev->thread); +} static int blktrans_open(struct block_device *bdev, fmode_t mode) { - struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data; - struct mtd_blktrans_ops *tr = dev->tr; - int ret = -ENODEV; - - if (!get_mtd_device(NULL, dev->mtd->index)) - goto out; - - if (!try_module_get(tr->owner)) - goto out_tr; - - /* FIXME: Locking. A hot pluggable device can go away - (del_mtd_device can be called for it) without its module - being unloaded. */ - dev->mtd->usecount++; - - ret = 0; - if (tr->open && (ret = tr->open(dev))) { - dev->mtd->usecount--; - put_mtd_device(dev->mtd); - out_tr: - module_put(tr->owner); + struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); + int ret; + + if (!dev) + return -ERESTARTSYS; + + mutex_lock(&dev->lock); + + if (!dev->mtd) { + ret = -ENXIO; + goto unlock; } - out: + + ret = !dev->open++ && dev->tr->open ? dev->tr->open(dev) : 0; + + /* Take another reference on the device so it won't go away till + last release */ + if (!ret) + kref_get(&dev->ref); +unlock: + mutex_unlock(&dev->lock); + blktrans_dev_put(dev); return ret; } static int blktrans_release(struct gendisk *disk, fmode_t mode) { - struct mtd_blktrans_dev *dev = disk->private_data; - struct mtd_blktrans_ops *tr = dev->tr; - int ret = 0; + struct mtd_blktrans_dev *dev = blktrans_dev_get(disk); + int ret = -ENXIO; - if (tr->release) - ret = tr->release(dev); + if (!dev) + return ret; - if (!ret) { - dev->mtd->usecount--; - put_mtd_device(dev->mtd); - module_put(tr->owner); - } + mutex_lock(&dev->lock); + + /* Release one reference, we sure its not the last one here*/ + kref_put(&dev->ref, blktrans_dev_release); + if (!dev->mtd) + goto unlock; + + ret = !--dev->open && dev->tr->release ? dev->tr->release(dev) : 0; +unlock: + mutex_unlock(&dev->lock); + blktrans_dev_put(dev); return ret; } static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo) { - struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data; + struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); + int ret = -ENXIO; + + if (!dev) + return ret; + + mutex_lock(&dev->lock); + + if (!dev->mtd) + goto unlock; - if (dev->tr->getgeo) - return dev->tr->getgeo(dev, geo); - return -ENOTTY; + ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : 0; +unlock: + mutex_unlock(&dev->lock); + blktrans_dev_put(dev); + return ret; } static int blktrans_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { - struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data; - struct mtd_blktrans_ops *tr = dev->tr; + struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); + int ret = -ENXIO; + + if (!dev) + return ret; + + mutex_lock(&dev->lock); + + if (!dev->mtd) + goto unlock; switch (cmd) { case BLKFLSBUF: - if (tr->flush) - return tr->flush(dev); - /* The core code did the work, we had nothing to do. */ - return 0; + ret = dev->tr->flush ? dev->tr->flush(dev) : 0; default: - return -ENOTTY; + ret = -ENOTTY; } +unlock: + mutex_unlock(&dev->lock); + blktrans_dev_put(dev); + return ret; } static const struct block_device_operations mtd_blktrans_ops = { @@ -214,12 +268,14 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) struct mtd_blktrans_dev *d; int last_devnum = -1; struct gendisk *gd; + int ret; if (mutex_trylock(&mtd_table_mutex)) { mutex_unlock(&mtd_table_mutex); BUG(); } + mutex_lock(&blktrans_ref_mutex); list_for_each_entry(d, &tr->devs, list) { if (new->devnum == -1) { /* Use first free number */ @@ -231,6 +287,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) } } else if (d->devnum == new->devnum) { /* Required number taken */ + mutex_unlock(&blktrans_ref_mutex); return -EBUSY; } else if (d->devnum > new->devnum) { /* Required number was free */ @@ -239,24 +296,38 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) } last_devnum = d->devnum; } + + ret = -EBUSY; if (new->devnum == -1) new->devnum = last_devnum+1; - if ((new->devnum << tr->part_bits) > 256) { - return -EBUSY; + /* Check that the device and any partitions will get valid + * minor numbers and that the disk naming code below can cope + * with this number. */ + if (new->devnum > (MINORMASK >> tr->part_bits) || + (tr->part_bits && new->devnum >= 27 * 26)) { + mutex_unlock(&blktrans_ref_mutex); + goto error1; } list_add_tail(&new->list, &tr->devs); added: + mutex_unlock(&blktrans_ref_mutex); + mutex_init(&new->lock); + kref_init(&new->ref); if (!tr->writesect) new->readonly = 1; + /* Create gendisk */ + ret = -ENOMEM; gd = alloc_disk(1 << tr->part_bits); - if (!gd) { - list_del(&new->list); - return -ENOMEM; - } + + if (!gd) + goto error2; + + new->disk = gd; + gd->private_data = new; gd->major = tr->major; gd->first_minor = (new->devnum) << tr->part_bits; gd->fops = &mtd_blktrans_ops; @@ -274,13 +345,35 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) snprintf(gd->disk_name, sizeof(gd->disk_name), "%s%d", tr->name, new->devnum); - /* 2.5 has capacity in units of 512 bytes while still - having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */ set_capacity(gd, (new->size * tr->blksize) >> 9); - gd->private_data = new; - new->blkcore_priv = gd; - gd->queue = tr->blkcore_priv->rq; + /* Create the request queue */ + spin_lock_init(&new->queue_lock); + new->rq = blk_init_queue(mtd_blktrans_request, &new->queue_lock); + + if (!new->rq) + goto error3; + + new->rq->queuedata = new; + blk_queue_logical_block_size(new->rq, tr->blksize); + + if (tr->discard) + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, + new->rq); + + gd->queue = new->rq; + + __get_mtd_device(new->mtd); + __module_get(tr->owner); + + /* Create processing thread */ + /* TODO: workqueue ? */ + new->thread = kthread_run(mtd_blktrans_thread, new, + "%s%d", tr->name, new->mtd->index); + if (IS_ERR(new->thread)) { + ret = PTR_ERR(new->thread); + goto error4; + } gd->driverfs_dev = &new->mtd->dev; if (new->readonly) @@ -288,21 +381,65 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) add_disk(gd); + if (new->disk_attributes) { + ret = sysfs_create_group(&disk_to_dev(gd)->kobj, + new->disk_attributes); + WARN_ON(ret); + } return 0; +error4: + module_put(tr->owner); + __put_mtd_device(new->mtd); + blk_cleanup_queue(new->rq); +error3: + put_disk(new->disk); +error2: + list_del(&new->list); +error1: + kfree(new); + return ret; } int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) { + unsigned long flags; + if (mutex_trylock(&mtd_table_mutex)) { mutex_unlock(&mtd_table_mutex); BUG(); } - list_del(&old->list); + /* Stop new requests to arrive */ + del_gendisk(old->disk); + + if (old->disk_attributes) + sysfs_remove_group(&disk_to_dev(old->disk)->kobj, + old->disk_attributes); + + /* Stop the thread */ + kthread_stop(old->thread); + + /* Kill current requests */ + spin_lock_irqsave(&old->queue_lock, flags); + old->rq->queuedata = NULL; + blk_start_queue(old->rq); + spin_unlock_irqrestore(&old->queue_lock, flags); + + /* Ask trans driver for release to the mtd device */ + mutex_lock(&old->lock); + if (old->open && old->tr->release) { + old->tr->release(old); + old->open = 0; + } + + __put_mtd_device(old->mtd); + module_put(old->tr->owner); - del_gendisk(old->blkcore_priv); - put_disk(old->blkcore_priv); + /* At that point, we don't touch the mtd anymore */ + old->mtd = NULL; + mutex_unlock(&old->lock); + blktrans_dev_put(old); return 0; } @@ -335,7 +472,8 @@ static struct mtd_notifier blktrans_notifier = { int register_mtd_blktrans(struct mtd_blktrans_ops *tr) { - int ret, i; + struct mtd_info *mtd; + int ret; /* Register the notifier if/when the first device type is registered, to prevent the link/init ordering from fucking @@ -343,9 +481,6 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) if (!blktrans_notifier.list.next) register_mtd_user(&blktrans_notifier); - tr->blkcore_priv = kzalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL); - if (!tr->blkcore_priv) - return -ENOMEM; mutex_lock(&mtd_table_mutex); @@ -353,49 +488,20 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) if (ret) { printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", tr->name, tr->major, ret); - kfree(tr->blkcore_priv); mutex_unlock(&mtd_table_mutex); return ret; } - spin_lock_init(&tr->blkcore_priv->queue_lock); - - tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock); - if (!tr->blkcore_priv->rq) { - unregister_blkdev(tr->major, tr->name); - kfree(tr->blkcore_priv); - mutex_unlock(&mtd_table_mutex); - return -ENOMEM; - } - - tr->blkcore_priv->rq->queuedata = tr; - blk_queue_logical_block_size(tr->blkcore_priv->rq, tr->blksize); - if (tr->discard) - queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, - tr->blkcore_priv->rq); tr->blkshift = ffs(tr->blksize) - 1; - tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr, - "%sd", tr->name); - if (IS_ERR(tr->blkcore_priv->thread)) { - ret = PTR_ERR(tr->blkcore_priv->thread); - blk_cleanup_queue(tr->blkcore_priv->rq); - unregister_blkdev(tr->major, tr->name); - kfree(tr->blkcore_priv); - mutex_unlock(&mtd_table_mutex); - return ret; - } - INIT_LIST_HEAD(&tr->devs); list_add(&tr->list, &blktrans_majors); - for (i=0; i<MAX_MTD_DEVICES; i++) { - if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT) - tr->add_mtd(tr, mtd_table[i]); - } + mtd_for_each_device(mtd) + if (mtd->type != MTD_ABSENT) + tr->add_mtd(tr, mtd); mutex_unlock(&mtd_table_mutex); - return 0; } @@ -405,22 +511,15 @@ int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) mutex_lock(&mtd_table_mutex); - /* Clean up the kernel thread */ - kthread_stop(tr->blkcore_priv->thread); - /* Remove it from the list of active majors */ list_del(&tr->list); list_for_each_entry_safe(dev, next, &tr->devs, list) tr->remove_dev(dev); - blk_cleanup_queue(tr->blkcore_priv->rq); unregister_blkdev(tr->major, tr->name); - mutex_unlock(&mtd_table_mutex); - kfree(tr->blkcore_priv); - BUG_ON(!list_empty(&tr->devs)); return 0; } diff --git a/drivers/mtd/mtdbdi.c b/drivers/mtd/mtdbdi.c deleted file mode 100644 index 5ca5aed..0000000 --- a/drivers/mtd/mtdbdi.c +++ /dev/null @@ -1,43 +0,0 @@ -/* MTD backing device capabilities - * - * Copyright © 2006 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include <linux/backing-dev.h> -#include <linux/mtd/mtd.h> -#include "internal.h" - -/* - * backing device capabilities for non-mappable devices (such as NAND flash) - * - permits private mappings, copies are taken of the data - */ -struct backing_dev_info mtd_bdi_unmappable = { - .capabilities = BDI_CAP_MAP_COPY, -}; - -/* - * backing device capabilities for R/O mappable devices (such as ROM) - * - permits private mappings, copies are taken of the data - * - permits non-writable shared mappings - */ -struct backing_dev_info mtd_bdi_ro_mappable = { - .capabilities = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT | - BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP), -}; - -/* - * backing device capabilities for writable mappable devices (such as RAM) - * - permits private mappings, copies are taken of the data - * - permits non-writable shared mappings - */ -struct backing_dev_info mtd_bdi_rw_mappable = { - .capabilities = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT | - BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP | - BDI_CAP_WRITE_MAP), -}; diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 9f41b1a..e6edbec 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -19,15 +19,15 @@ #include <linux/mutex.h> -static struct mtdblk_dev { - struct mtd_info *mtd; +struct mtdblk_dev { + struct mtd_blktrans_dev mbd; int count; struct mutex cache_mutex; unsigned char *cache_data; unsigned long cache_offset; unsigned int cache_size; enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state; -} *mtdblks[MAX_MTD_DEVICES]; +}; static struct mutex mtdblks_lock; @@ -98,7 +98,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, static int write_cached_data (struct mtdblk_dev *mtdblk) { - struct mtd_info *mtd = mtdblk->mtd; + struct mtd_info *mtd = mtdblk->mbd.mtd; int ret; if (mtdblk->cache_state != STATE_DIRTY) @@ -128,7 +128,7 @@ static int write_cached_data (struct mtdblk_dev *mtdblk) static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos, int len, const char *buf) { - struct mtd_info *mtd = mtdblk->mtd; + struct mtd_info *mtd = mtdblk->mbd.mtd; unsigned int sect_size = mtdblk->cache_size; size_t retlen; int ret; @@ -198,7 +198,7 @@ static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos, static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos, int len, char *buf) { - struct mtd_info *mtd = mtdblk->mtd; + struct mtd_info *mtd = mtdblk->mbd.mtd; unsigned int sect_size = mtdblk->cache_size; size_t retlen; int ret; @@ -244,16 +244,16 @@ static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos, static int mtdblock_readsect(struct mtd_blktrans_dev *dev, unsigned long block, char *buf) { - struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; + struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd); return do_cached_read(mtdblk, block<<9, 512, buf); } static int mtdblock_writesect(struct mtd_blktrans_dev *dev, unsigned long block, char *buf) { - struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; + struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd); if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) { - mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize); + mtdblk->cache_data = vmalloc(mtdblk->mbd.mtd->erasesize); if (!mtdblk->cache_data) return -EINTR; /* -EINTR is not really correct, but it is the best match @@ -266,37 +266,26 @@ static int mtdblock_writesect(struct mtd_blktrans_dev *dev, static int mtdblock_open(struct mtd_blktrans_dev *mbd) { - struct mtdblk_dev *mtdblk; - struct mtd_info *mtd = mbd->mtd; - int dev = mbd->devnum; + struct mtdblk_dev *mtdblk = container_of(mbd, struct mtdblk_dev, mbd); DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n"); mutex_lock(&mtdblks_lock); - if (mtdblks[dev]) { - mtdblks[dev]->count++; + if (mtdblk->count) { + mtdblk->count++; mutex_unlock(&mtdblks_lock); return 0; } /* OK, it's not open. Create cache info for it */ - mtdblk = kzalloc(sizeof(struct mtdblk_dev), GFP_KERNEL); - if (!mtdblk) { - mutex_unlock(&mtdblks_lock); - return -ENOMEM; - } - mtdblk->count = 1; - mtdblk->mtd = mtd; - mutex_init(&mtdblk->cache_mutex); mtdblk->cache_state = STATE_EMPTY; - if ( !(mtdblk->mtd->flags & MTD_NO_ERASE) && mtdblk->mtd->erasesize) { - mtdblk->cache_size = mtdblk->mtd->erasesize; + if (!(mbd->mtd->flags & MTD_NO_ERASE) && mbd->mtd->erasesize) { + mtdblk->cache_size = mbd->mtd->erasesize; mtdblk->cache_data = NULL; } - mtdblks[dev] = mtdblk; mutex_unlock(&mtdblks_lock); DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); @@ -306,8 +295,7 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd) static int mtdblock_release(struct mtd_blktrans_dev *mbd) { - int dev = mbd->devnum; - struct mtdblk_dev *mtdblk = mtdblks[dev]; + struct mtdblk_dev *mtdblk = container_of(mbd, struct mtdblk_dev, mbd); DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n"); @@ -318,12 +306,10 @@ static int mtdblock_release(struct mtd_blktrans_dev *mbd) mutex_unlock(&mtdblk->cache_mutex); if (!--mtdblk->count) { - /* It was the last usage. Free the device */ - mtdblks[dev] = NULL; - if (mtdblk->mtd->sync) - mtdblk->mtd->sync(mtdblk->mtd); + /* It was the last usage. Free the cache */ + if (mbd->mtd->sync) + mbd->mtd->sync(mbd->mtd); vfree(mtdblk->cache_data); - kfree(mtdblk); } mutex_unlock(&mtdblks_lock); @@ -335,40 +321,40 @@ static int mtdblock_release(struct mtd_blktrans_dev *mbd) static int mtdblock_flush(struct mtd_blktrans_dev *dev) { - struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; + struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd); mutex_lock(&mtdblk->cache_mutex); write_cached_data(mtdblk); mutex_unlock(&mtdblk->cache_mutex); - if (mtdblk->mtd->sync) - mtdblk->mtd->sync(mtdblk->mtd); + if (dev->mtd->sync) + dev->mtd->sync(dev->mtd); return 0; } static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) { - struct mtd_blktrans_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); + struct mtdblk_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return; - dev->mtd = mtd; - dev->devnum = mtd->index; + dev->mbd.mtd = mtd; + dev->mbd.devnum = mtd->index; - dev->size = mtd->size >> 9; - dev->tr = tr; + dev->mbd.size = mtd->size >> 9; + dev->mbd.tr = tr; if (!(mtd->flags & MTD_WRITEABLE)) - dev->readonly = 1; + dev->mbd.readonly = 1; - add_mtd_blktrans_dev(dev); + if (add_mtd_blktrans_dev(&dev->mbd)) + kfree(dev); } static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) { del_mtd_blktrans_dev(dev); - kfree(dev); } static struct mtd_blktrans_ops mtdblock_tr = { diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c index 852165f..d0d3f79 100644 --- a/drivers/mtd/mtdblock_ro.c +++ b/drivers/mtd/mtdblock_ro.c @@ -43,13 +43,13 @@ static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) dev->tr = tr; dev->readonly = 1; - add_mtd_blktrans_dev(dev); + if (add_mtd_blktrans_dev(dev)) + kfree(dev); } static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) { del_mtd_blktrans_dev(dev); - kfree(dev); } static struct mtd_blktrans_ops mtdblock_tr = { diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 5b081cb..8bb5e4a 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -15,12 +15,15 @@ #include <linux/smp_lock.h> #include <linux/backing-dev.h> #include <linux/compat.h> +#include <linux/mount.h> #include <linux/mtd/mtd.h> #include <linux/mtd/compatmac.h> #include <asm/uaccess.h> +#define MTD_INODE_FS_MAGIC 0x11307854 +static struct vfsmount *mtd_inode_mnt __read_mostly; /* * Data structure to hold the pointer to the mtd device as well @@ -28,6 +31,7 @@ */ struct mtd_file_info { struct mtd_info *mtd; + struct inode *ino; enum mtd_file_modes mode; }; @@ -64,12 +68,10 @@ static int mtd_open(struct inode *inode, struct file *file) int ret = 0; struct mtd_info *mtd; struct mtd_file_info *mfi; + struct inode *mtd_ino; DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n"); - if (devnum >= MAX_MTD_DEVICES) - return -ENODEV; - /* You can't open the RO devices RW */ if ((file->f_mode & FMODE_WRITE) && (minor & 1)) return -EACCES; @@ -88,11 +90,23 @@ static int mtd_open(struct inode *inode, struct file *file) goto out; } - if (mtd->backing_dev_info) - file->f_mapping->backing_dev_info = mtd->backing_dev_info; + mtd_ino = iget_locked(mtd_inode_mnt->mnt_sb, devnum); + if (!mtd_ino) { + put_mtd_device(mtd); + ret = -ENOMEM; + goto out; + } + if (mtd_ino->i_state & I_NEW) { + mtd_ino->i_private = mtd; + mtd_ino->i_mode = S_IFCHR; + mtd_ino->i_data.backing_dev_info = mtd->backing_dev_info; + unlock_new_inode(mtd_ino); + } + file->f_mapping = mtd_ino->i_mapping; /* You can't open it RW if it's not a writeable device */ if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) { + iput(mtd_ino); put_mtd_device(mtd); ret = -EACCES; goto out; @@ -100,10 +114,12 @@ static int mtd_open(struct inode *inode, struct file *file) mfi = kzalloc(sizeof(*mfi), GFP_KERNEL); if (!mfi) { + iput(mtd_ino); put_mtd_device(mtd); ret = -ENOMEM; goto out; } + mfi->ino = mtd_ino; mfi->mtd = mtd; file->private_data = mfi; @@ -125,6 +141,8 @@ static int mtd_close(struct inode *inode, struct file *file) if ((file->f_mode & FMODE_WRITE) && mtd->sync) mtd->sync(mtd); + iput(mfi->ino); + put_mtd_device(mtd); file->private_data = NULL; kfree(mfi); @@ -373,7 +391,7 @@ static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd, if (!mtd->write_oob) ret = -EOPNOTSUPP; else - ret = access_ok(VERIFY_READ, ptr, length) ? 0 : EFAULT; + ret = access_ok(VERIFY_READ, ptr, length) ? 0 : -EFAULT; if (ret) return ret; @@ -482,7 +500,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, { uint32_t ur_idx; struct mtd_erase_region_info *kr; - struct region_info_user *ur = (struct region_info_user *) argp; + struct region_info_user __user *ur = argp; if (get_user(ur_idx, &(ur->regionindex))) return -EFAULT; @@ -954,22 +972,81 @@ static const struct file_operations mtd_fops = { #endif }; +static int mtd_inodefs_get_sb(struct file_system_type *fs_type, int flags, + const char *dev_name, void *data, + struct vfsmount *mnt) +{ + return get_sb_pseudo(fs_type, "mtd_inode:", NULL, MTD_INODE_FS_MAGIC, + mnt); +} + +static struct file_system_type mtd_inodefs_type = { + .name = "mtd_inodefs", + .get_sb = mtd_inodefs_get_sb, + .kill_sb = kill_anon_super, +}; + +static void mtdchar_notify_add(struct mtd_info *mtd) +{ +} + +static void mtdchar_notify_remove(struct mtd_info *mtd) +{ + struct inode *mtd_ino = ilookup(mtd_inode_mnt->mnt_sb, mtd->index); + + if (mtd_ino) { + /* Destroy the inode if it exists */ + mtd_ino->i_nlink = 0; + iput(mtd_ino); + } +} + +static struct mtd_notifier mtdchar_notifier = { + .add = mtdchar_notify_add, + .remove = mtdchar_notify_remove, +}; + static int __init init_mtdchar(void) { - int status; + int ret; - status = register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops); - if (status < 0) { - printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", - MTD_CHAR_MAJOR); + ret = __register_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, + "mtd", &mtd_fops); + if (ret < 0) { + pr_notice("Can't allocate major number %d for " + "Memory Technology Devices.\n", MTD_CHAR_MAJOR); + return ret; } - return status; + ret = register_filesystem(&mtd_inodefs_type); + if (ret) { + pr_notice("Can't register mtd_inodefs filesystem: %d\n", ret); + goto err_unregister_chdev; + } + + mtd_inode_mnt = kern_mount(&mtd_inodefs_type); + if (IS_ERR(mtd_inode_mnt)) { + ret = PTR_ERR(mtd_inode_mnt); + pr_notice("Error mounting mtd_inodefs filesystem: %d\n", ret); + goto err_unregister_filesystem; + } + register_mtd_user(&mtdchar_notifier); + + return ret; + +err_unregister_filesystem: + unregister_filesystem(&mtd_inodefs_type); +err_unregister_chdev: + __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd"); + return ret; } static void __exit cleanup_mtdchar(void) { - unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); + unregister_mtd_user(&mtdchar_notifier); + mntput(mtd_inode_mnt); + unregister_filesystem(&mtd_inodefs_type); + __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd"); } module_init(init_mtdchar); diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index db6de74..7e07562 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -183,10 +183,9 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs, } /* make a copy of vecs */ - vecs_copy = kmalloc(sizeof(struct kvec) * count, GFP_KERNEL); + vecs_copy = kmemdup(vecs, sizeof(struct kvec) * count, GFP_KERNEL); if (!vecs_copy) return -ENOMEM; - memcpy(vecs_copy, vecs, sizeof(struct kvec) * count); entry_low = 0; for (i = 0; i < concat->num_subdev; i++) { diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 5b38b17..a1b8b70 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -2,6 +2,9 @@ * Core registration and callback routines for MTD * drivers and users. * + * bdi bits are: + * Copyright © 2006 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) */ #include <linux/module.h> @@ -16,11 +19,41 @@ #include <linux/init.h> #include <linux/mtd/compatmac.h> #include <linux/proc_fs.h> +#include <linux/idr.h> +#include <linux/backing-dev.h> +#include <linux/gfp.h> #include <linux/mtd/mtd.h> -#include "internal.h" #include "mtdcore.h" +/* + * backing device capabilities for non-mappable devices (such as NAND flash) + * - permits private mappings, copies are taken of the data + */ +struct backing_dev_info mtd_bdi_unmappable = { + .capabilities = BDI_CAP_MAP_COPY, +}; + +/* + * backing device capabilities for R/O mappable devices (such as ROM) + * - permits private mappings, copies are taken of the data + * - permits non-writable shared mappings + */ +struct backing_dev_info mtd_bdi_ro_mappable = { + .capabilities = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT | + BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP), +}; + +/* + * backing device capabilities for writable mappable devices (such as RAM) + * - permits private mappings, copies are taken of the data + * - permits non-writable shared mappings + */ +struct backing_dev_info mtd_bdi_rw_mappable = { + .capabilities = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT | + BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP | + BDI_CAP_WRITE_MAP), +}; static int mtd_cls_suspend(struct device *dev, pm_message_t state); static int mtd_cls_resume(struct device *dev); @@ -32,13 +65,18 @@ static struct class mtd_class = { .resume = mtd_cls_resume, }; +static DEFINE_IDR(mtd_idr); + /* These are exported solely for the purpose of mtd_blkdevs.c. You should not use them for _anything_ else */ DEFINE_MUTEX(mtd_table_mutex); -struct mtd_info *mtd_table[MAX_MTD_DEVICES]; - EXPORT_SYMBOL_GPL(mtd_table_mutex); -EXPORT_SYMBOL_GPL(mtd_table); + +struct mtd_info *__mtd_next_device(int i) +{ + return idr_get_next(&mtd_idr, &i); +} +EXPORT_SYMBOL_GPL(__mtd_next_device); static LIST_HEAD(mtd_notifiers); @@ -234,13 +272,13 @@ static struct device_type mtd_devtype = { * Add a device to the list of MTD devices present in the system, and * notify each currently active MTD 'user' of its arrival. Returns * zero on success or 1 on failure, which currently will only happen - * if the number of present devices exceeds MAX_MTD_DEVICES (i.e. 16) - * or there's a sysfs error. + * if there is insufficient memory or a sysfs error. */ int add_mtd_device(struct mtd_info *mtd) { - int i; + struct mtd_notifier *not; + int i, error; if (!mtd->backing_dev_info) { switch (mtd->type) { @@ -259,70 +297,73 @@ int add_mtd_device(struct mtd_info *mtd) BUG_ON(mtd->writesize == 0); mutex_lock(&mtd_table_mutex); - for (i=0; i < MAX_MTD_DEVICES; i++) - if (!mtd_table[i]) { - struct mtd_notifier *not; - - mtd_table[i] = mtd; - mtd->index = i; - mtd->usecount = 0; - - if (is_power_of_2(mtd->erasesize)) - mtd->erasesize_shift = ffs(mtd->erasesize) - 1; - else - mtd->erasesize_shift = 0; - - if (is_power_of_2(mtd->writesize)) - mtd->writesize_shift = ffs(mtd->writesize) - 1; - else - mtd->writesize_shift = 0; - - mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1; - mtd->writesize_mask = (1 << mtd->writesize_shift) - 1; - - /* Some chips always power up locked. Unlock them now */ - if ((mtd->flags & MTD_WRITEABLE) - && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) { - if (mtd->unlock(mtd, 0, mtd->size)) - printk(KERN_WARNING - "%s: unlock failed, " - "writes may not work\n", - mtd->name); - } + do { + if (!idr_pre_get(&mtd_idr, GFP_KERNEL)) + goto fail_locked; + error = idr_get_new(&mtd_idr, mtd, &i); + } while (error == -EAGAIN); - /* Caller should have set dev.parent to match the - * physical device. - */ - mtd->dev.type = &mtd_devtype; - mtd->dev.class = &mtd_class; - mtd->dev.devt = MTD_DEVT(i); - dev_set_name(&mtd->dev, "mtd%d", i); - dev_set_drvdata(&mtd->dev, mtd); - if (device_register(&mtd->dev) != 0) { - mtd_table[i] = NULL; - break; - } + if (error) + goto fail_locked; - if (MTD_DEVT(i)) - device_create(&mtd_class, mtd->dev.parent, - MTD_DEVT(i) + 1, - NULL, "mtd%dro", i); - - DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name); - /* No need to get a refcount on the module containing - the notifier, since we hold the mtd_table_mutex */ - list_for_each_entry(not, &mtd_notifiers, list) - not->add(mtd); - - mutex_unlock(&mtd_table_mutex); - /* We _know_ we aren't being removed, because - our caller is still holding us here. So none - of this try_ nonsense, and no bitching about it - either. :) */ - __module_get(THIS_MODULE); - return 0; - } + mtd->index = i; + mtd->usecount = 0; + + if (is_power_of_2(mtd->erasesize)) + mtd->erasesize_shift = ffs(mtd->erasesize) - 1; + else + mtd->erasesize_shift = 0; + + if (is_power_of_2(mtd->writesize)) + mtd->writesize_shift = ffs(mtd->writesize) - 1; + else + mtd->writesize_shift = 0; + + mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1; + mtd->writesize_mask = (1 << mtd->writesize_shift) - 1; + + /* Some chips always power up locked. Unlock them now */ + if ((mtd->flags & MTD_WRITEABLE) + && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) { + if (mtd->unlock(mtd, 0, mtd->size)) + printk(KERN_WARNING + "%s: unlock failed, writes may not work\n", + mtd->name); + } + + /* Caller should have set dev.parent to match the + * physical device. + */ + mtd->dev.type = &mtd_devtype; + mtd->dev.class = &mtd_class; + mtd->dev.devt = MTD_DEVT(i); + dev_set_name(&mtd->dev, "mtd%d", i); + dev_set_drvdata(&mtd->dev, mtd); + if (device_register(&mtd->dev) != 0) + goto fail_added; + + if (MTD_DEVT(i)) + device_create(&mtd_class, mtd->dev.parent, + MTD_DEVT(i) + 1, + NULL, "mtd%dro", i); + + DEBUG(0, "mtd: Giving out device %d to %s\n", i, mtd->name); + /* No need to get a refcount on the module containing + the notifier, since we hold the mtd_table_mutex */ + list_for_each_entry(not, &mtd_notifiers, list) + not->add(mtd); + + mutex_unlock(&mtd_table_mutex); + /* We _know_ we aren't being removed, because + our caller is still holding us here. So none + of this try_ nonsense, and no bitching about it + either. :) */ + __module_get(THIS_MODULE); + return 0; +fail_added: + idr_remove(&mtd_idr, i); +fail_locked: mutex_unlock(&mtd_table_mutex); return 1; } @@ -340,31 +381,34 @@ int add_mtd_device(struct mtd_info *mtd) int del_mtd_device (struct mtd_info *mtd) { int ret; + struct mtd_notifier *not; mutex_lock(&mtd_table_mutex); - if (mtd_table[mtd->index] != mtd) { + if (idr_find(&mtd_idr, mtd->index) != mtd) { ret = -ENODEV; - } else if (mtd->usecount) { + goto out_error; + } + + /* No need to get a refcount on the module containing + the notifier, since we hold the mtd_table_mutex */ + list_for_each_entry(not, &mtd_notifiers, list) + not->remove(mtd); + + if (mtd->usecount) { printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n", mtd->index, mtd->name, mtd->usecount); ret = -EBUSY; } else { - struct mtd_notifier *not; - device_unregister(&mtd->dev); - /* No need to get a refcount on the module containing - the notifier, since we hold the mtd_table_mutex */ - list_for_each_entry(not, &mtd_notifiers, list) - not->remove(mtd); - - mtd_table[mtd->index] = NULL; + idr_remove(&mtd_idr, mtd->index); module_put(THIS_MODULE); ret = 0; } +out_error: mutex_unlock(&mtd_table_mutex); return ret; } @@ -380,7 +424,7 @@ int del_mtd_device (struct mtd_info *mtd) void register_mtd_user (struct mtd_notifier *new) { - int i; + struct mtd_info *mtd; mutex_lock(&mtd_table_mutex); @@ -388,9 +432,8 @@ void register_mtd_user (struct mtd_notifier *new) __module_get(THIS_MODULE); - for (i=0; i< MAX_MTD_DEVICES; i++) - if (mtd_table[i]) - new->add(mtd_table[i]); + mtd_for_each_device(mtd) + new->add(mtd); mutex_unlock(&mtd_table_mutex); } @@ -407,15 +450,14 @@ void register_mtd_user (struct mtd_notifier *new) int unregister_mtd_user (struct mtd_notifier *old) { - int i; + struct mtd_info *mtd; mutex_lock(&mtd_table_mutex); module_put(THIS_MODULE); - for (i=0; i< MAX_MTD_DEVICES; i++) - if (mtd_table[i]) - old->remove(mtd_table[i]); + mtd_for_each_device(mtd) + old->remove(mtd); list_del(&old->list); mutex_unlock(&mtd_table_mutex); @@ -437,42 +479,56 @@ int unregister_mtd_user (struct mtd_notifier *old) struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) { - struct mtd_info *ret = NULL; - int i, err = -ENODEV; + struct mtd_info *ret = NULL, *other; + int err = -ENODEV; mutex_lock(&mtd_table_mutex); if (num == -1) { - for (i=0; i< MAX_MTD_DEVICES; i++) - if (mtd_table[i] == mtd) - ret = mtd_table[i]; - } else if (num >= 0 && num < MAX_MTD_DEVICES) { - ret = mtd_table[num]; + mtd_for_each_device(other) { + if (other == mtd) { + ret = mtd; + break; + } + } + } else if (num >= 0) { + ret = idr_find(&mtd_idr, num); if (mtd && mtd != ret) ret = NULL; } - if (!ret) - goto out_unlock; - - if (!try_module_get(ret->owner)) - goto out_unlock; - - if (ret->get_device) { - err = ret->get_device(ret); - if (err) - goto out_put; + if (!ret) { + ret = ERR_PTR(err); + goto out; } - ret->usecount++; + err = __get_mtd_device(ret); + if (err) + ret = ERR_PTR(err); +out: mutex_unlock(&mtd_table_mutex); return ret; +} -out_put: - module_put(ret->owner); -out_unlock: - mutex_unlock(&mtd_table_mutex); - return ERR_PTR(err); + +int __get_mtd_device(struct mtd_info *mtd) +{ + int err; + + if (!try_module_get(mtd->owner)) + return -ENODEV; + + if (mtd->get_device) { + + err = mtd->get_device(mtd); + + if (err) { + module_put(mtd->owner); + return err; + } + } + mtd->usecount++; + return 0; } /** @@ -486,14 +542,14 @@ out_unlock: struct mtd_info *get_mtd_device_nm(const char *name) { - int i, err = -ENODEV; - struct mtd_info *mtd = NULL; + int err = -ENODEV; + struct mtd_info *mtd = NULL, *other; mutex_lock(&mtd_table_mutex); - for (i = 0; i < MAX_MTD_DEVICES; i++) { - if (mtd_table[i] && !strcmp(name, mtd_table[i]->name)) { - mtd = mtd_table[i]; + mtd_for_each_device(other) { + if (!strcmp(name, other->name)) { + mtd = other; break; } } @@ -523,14 +579,19 @@ out_unlock: void put_mtd_device(struct mtd_info *mtd) { - int c; - mutex_lock(&mtd_table_mutex); - c = --mtd->usecount; + __put_mtd_device(mtd); + mutex_unlock(&mtd_table_mutex); + +} + +void __put_mtd_device(struct mtd_info *mtd) +{ + --mtd->usecount; + BUG_ON(mtd->usecount < 0); + if (mtd->put_device) mtd->put_device(mtd); - mutex_unlock(&mtd_table_mutex); - BUG_ON(c < 0); module_put(mtd->owner); } @@ -568,7 +629,9 @@ EXPORT_SYMBOL_GPL(add_mtd_device); EXPORT_SYMBOL_GPL(del_mtd_device); EXPORT_SYMBOL_GPL(get_mtd_device); EXPORT_SYMBOL_GPL(get_mtd_device_nm); +EXPORT_SYMBOL_GPL(__get_mtd_device); EXPORT_SYMBOL_GPL(put_mtd_device); +EXPORT_SYMBOL_GPL(__put_mtd_device); EXPORT_SYMBOL_GPL(register_mtd_user); EXPORT_SYMBOL_GPL(unregister_mtd_user); EXPORT_SYMBOL_GPL(default_mtd_writev); @@ -580,14 +643,9 @@ EXPORT_SYMBOL_GPL(default_mtd_writev); static struct proc_dir_entry *proc_mtd; -static inline int mtd_proc_info (char *buf, int i) +static inline int mtd_proc_info(char *buf, struct mtd_info *this) { - struct mtd_info *this = mtd_table[i]; - - if (!this) - return 0; - - return sprintf(buf, "mtd%d: %8.8llx %8.8x \"%s\"\n", i, + return sprintf(buf, "mtd%d: %8.8llx %8.8x \"%s\"\n", this->index, (unsigned long long)this->size, this->erasesize, this->name); } @@ -595,15 +653,15 @@ static inline int mtd_proc_info (char *buf, int i) static int mtd_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data_unused) { - int len, l, i; + struct mtd_info *mtd; + int len, l; off_t begin = 0; mutex_lock(&mtd_table_mutex); len = sprintf(page, "dev: size erasesize name\n"); - for (i=0; i< MAX_MTD_DEVICES; i++) { - - l = mtd_proc_info(page + len, i); + mtd_for_each_device(mtd) { + l = mtd_proc_info(page + len, mtd); len += l; if (len+begin > off+count) goto done; @@ -628,20 +686,55 @@ done: /*====================================================================*/ /* Init code */ +static int __init mtd_bdi_init(struct backing_dev_info *bdi, const char *name) +{ + int ret; + + ret = bdi_init(bdi); + if (!ret) + ret = bdi_register(bdi, NULL, name); + + if (ret) + bdi_destroy(bdi); + + return ret; +} + static int __init init_mtd(void) { int ret; + ret = class_register(&mtd_class); + if (ret) + goto err_reg; + + ret = mtd_bdi_init(&mtd_bdi_unmappable, "mtd-unmap"); + if (ret) + goto err_bdi1; + + ret = mtd_bdi_init(&mtd_bdi_ro_mappable, "mtd-romap"); + if (ret) + goto err_bdi2; + + ret = mtd_bdi_init(&mtd_bdi_rw_mappable, "mtd-rwmap"); + if (ret) + goto err_bdi3; - if (ret) { - pr_err("Error registering mtd class: %d\n", ret); - return ret; - } #ifdef CONFIG_PROC_FS if ((proc_mtd = create_proc_entry( "mtd", 0, NULL ))) proc_mtd->read_proc = mtd_read_proc; #endif /* CONFIG_PROC_FS */ return 0; + +err_bdi3: + bdi_destroy(&mtd_bdi_ro_mappable); +err_bdi2: + bdi_destroy(&mtd_bdi_unmappable); +err_bdi1: + class_unregister(&mtd_class); +err_reg: + pr_err("Error registering mtd class or bdi: %d\n", ret); + return ret; } static void __exit cleanup_mtd(void) @@ -651,6 +744,9 @@ static void __exit cleanup_mtd(void) remove_proc_entry( "mtd", NULL); #endif /* CONFIG_PROC_FS */ class_unregister(&mtd_class); + bdi_destroy(&mtd_bdi_unmappable); + bdi_destroy(&mtd_bdi_ro_mappable); + bdi_destroy(&mtd_bdi_rw_mappable); } module_init(init_mtd); diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h index a33251f..6a64fde 100644 --- a/drivers/mtd/mtdcore.h +++ b/drivers/mtd/mtdcore.h @@ -8,4 +8,9 @@ should not use them for _anything_ else */ extern struct mutex mtd_table_mutex; -extern struct mtd_info *mtd_table[MAX_MTD_DEVICES]; +extern struct mtd_info *__mtd_next_device(int i); + +#define mtd_for_each_device(mtd) \ + for ((mtd) = __mtd_next_device(0); \ + (mtd) != NULL; \ + (mtd) = __mtd_next_device(mtd->index + 1)) diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 92e12df..328313c 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -429,11 +429,6 @@ static int __init mtdoops_init(void) mtd_index = simple_strtoul(mtddev, &endp, 0); if (*endp == '\0') cxt->mtd_index = mtd_index; - if (cxt->mtd_index > MAX_MTD_DEVICES) { - printk(KERN_ERR "mtdoops: invalid mtd device number (%u) given\n", - mtd_index); - return -EINVAL; - } cxt->oops_buf = vmalloc(record_size); if (!cxt->oops_buf) { diff --git a/drivers/mtd/mtdsuper.c b/drivers/mtd/mtdsuper.c index af8b42e..bd9a443 100644 --- a/drivers/mtd/mtdsuper.c +++ b/drivers/mtd/mtdsuper.c @@ -13,6 +13,7 @@ #include <linux/mtd/super.h> #include <linux/namei.h> #include <linux/ctype.h> +#include <linux/slab.h> /* * compare superblocks to see if they're equivalent @@ -44,6 +45,7 @@ static int get_sb_mtd_set(struct super_block *sb, void *_mtd) sb->s_mtd = mtd; sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, mtd->index); + sb->s_bdi = mtd->backing_dev_info; return 0; } @@ -150,18 +152,12 @@ int get_sb_mtd(struct file_system_type *fs_type, int flags, DEBUG(1, "MTDSB: mtd:%%s, name \"%s\"\n", dev_name + 4); - for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) { - mtd = get_mtd_device(NULL, mtdnr); - if (!IS_ERR(mtd)) { - if (!strcmp(mtd->name, dev_name + 4)) - return get_sb_mtd_aux( - fs_type, flags, - dev_name, data, mtd, - fill_super, mnt); - - put_mtd_device(mtd); - } - } + mtd = get_mtd_device_nm(dev_name + 4); + if (!IS_ERR(mtd)) + return get_sb_mtd_aux( + fs_type, flags, + dev_name, data, mtd, + fill_super, mnt); printk(KERN_NOTICE "MTD:" " MTD device with name \"%s\" not found.\n", diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 42e5ea49..98a04b3 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -2,11 +2,23 @@ menuconfig MTD_NAND tristate "NAND Device Support" depends on MTD select MTD_NAND_IDS + select MTD_NAND_ECC help This enables support for accessing all type of NAND flash devices. For further information see <http://www.linux-mtd.infradead.org/doc/nand.html>. +config MTD_NAND_ECC + tristate + +config MTD_NAND_ECC_SMC + bool "NAND ECC Smart Media byte order" + depends on MTD_NAND_ECC + default n + help + Software ECC according to the Smart Media Specification. + The original Linux implementation had byte 0 and 1 swapped. + if MTD_NAND config MTD_NAND_VERIFY_WRITE @@ -18,12 +30,9 @@ config MTD_NAND_VERIFY_WRITE device thinks the write was successful, a bit could have been flipped accidentally due to device wear or something else. -config MTD_NAND_ECC_SMC - bool "NAND ECC Smart Media byte order" +config MTD_SM_COMMON + tristate default n - help - Software ECC according to the Smart Media Specification. - The original Linux implementation had byte 0 and 1 swapped. config MTD_NAND_MUSEUM_IDS bool "Enable chip ids for obsolete ancient NAND devices" @@ -41,6 +50,23 @@ config MTD_NAND_AUTCPU12 This enables the driver for the autronix autcpu12 board to access the SmartMediaCard. +config MTD_NAND_DENALI + depends on PCI + tristate "Support Denali NAND controller on Intel Moorestown" + help + Enable the driver for NAND flash on Intel Moorestown, using the + Denali NAND controller core. + +config MTD_NAND_DENALI_SCRATCH_REG_ADDR + hex "Denali NAND size scratch register address" + default "0xFF108018" + help + Some platforms place the NAND chip size in a scratch register + because (some versions of) the driver aren't able to automatically + determine the size of certain chips. Set the address of the + scratch register here to enable this feature. On Intel Moorestown + boards, the scratch register is at 0xFF108018. + config MTD_NAND_EDB7312 tristate "Support for Cirrus Logic EBD7312 evaluation board" depends on ARCH_EDB7312 @@ -95,15 +121,21 @@ config MTD_NAND_OMAP_PREFETCH_DMA or in DMA interrupt mode. Say y for DMA mode or MPU mode will be used -config MTD_NAND_TS7250 - tristate "NAND Flash device on TS-7250 board" - depends on MACH_TS72XX - help - Support for NAND flash on Technologic Systems TS-7250 platform. - config MTD_NAND_IDS tristate +config MTD_NAND_RICOH + tristate "Ricoh xD card reader" + default n + depends on PCI + select MTD_SM_COMMON + help + Enable support for Ricoh R5C852 xD card reader + You also need to enable ether + NAND SSFDC (SmartMedia) read only translation layer' or new + expermental, readwrite + 'SmartMedia/xD new translation layer' + config MTD_NAND_AU1550 tristate "Au1550/1200 NAND support" depends on SOC_AU1200 || SOC_AU1550 @@ -358,8 +390,6 @@ config MTD_NAND_ATMEL_ECC_NONE If unsure, say N - endchoice - endchoice config MTD_NAND_PXA3xx @@ -442,6 +472,13 @@ config MTD_NAND_FSL_UPM Enables support for NAND Flash chips wired onto Freescale PowerPC processor localbus with User-Programmable Machine support. +config MTD_NAND_MPC5121_NFC + tristate "MPC5121 built-in NAND Flash Controller support" + depends on PPC_MPC512x + help + This enables the driver for the NAND flash controller on the + MPC5121 SoC. + config MTD_NAND_MXC tristate "MXC NAND support" depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 @@ -481,11 +518,11 @@ config MTD_NAND_SOCRATES help Enables support for NAND Flash chips wired onto Socrates board. -config MTD_NAND_W90P910 - tristate "Support for NAND on w90p910 evaluation board." +config MTD_NAND_NUC900 + tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards." depends on ARCH_W90X900 && MTD_PARTITIONS help This enables the driver for the NAND Flash on evaluation board based - on w90p910. + on w90p910 / NUC9xx. endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 1407bd1..e8ab884 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -2,13 +2,16 @@ # linux/drivers/nand/Makefile # -obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o +obj-$(CONFIG_MTD_NAND) += nand.o +obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o +obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o obj-$(CONFIG_MTD_NAND_SPIA) += spia.o obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o +obj-$(CONFIG_MTD_NAND_DENALI) += denali.o obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o @@ -19,7 +22,6 @@ obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o obj-$(CONFIG_MTD_NAND_H1900) += h1910.o obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o -obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o @@ -39,8 +41,10 @@ obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o -obj-$(CONFIG_MTD_NAND_W90P910) += w90p910_nand.o +obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o +obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o +obj-$(CONFIG_MTD_NAND_RICOH) += r852.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c index 2d67732..8691e04 100644 --- a/drivers/mtd/nand/alauda.c +++ b/drivers/mtd/nand/alauda.c @@ -49,7 +49,7 @@ #define TIMEOUT HZ -static struct usb_device_id alauda_table [] = { +static const struct usb_device_id alauda_table[] = { { USB_DEVICE(0x0584, 0x0008) }, /* Fujifilm DPC-R1 */ { USB_DEVICE(0x07b4, 0x010a) }, /* Olympus MAUSB-10 */ { } diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 524e6c9..04d30887c 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -474,7 +474,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) } /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, 1)) { + if (nand_scan_ident(mtd, 1, NULL)) { res = -ENXIO; goto err_scan_ident; } diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 43d46e4..3ffe05d 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -451,7 +451,7 @@ static int __init au1xxx_nand_init(void) u32 nand_phys; /* Allocate memory for MTD device structure and private data */ - au1550_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); + au1550_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); if (!au1550_mtd) { printk("Unable to allocate NAND MTD dev structure.\n"); return -ENOMEM; @@ -460,10 +460,6 @@ static int __init au1xxx_nand_init(void) /* Get pointer to private data */ this = (struct nand_chip *)(&au1550_mtd[1]); - /* Initialize structures */ - memset(au1550_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - /* Link the private data with the MTD structure */ au1550_mtd->priv = this; au1550_mtd->owner = THIS_MODULE; @@ -544,7 +540,7 @@ static int __init au1xxx_nand_init(void) } nand_phys = (mem_staddr << 4) & 0xFFFC0000; - p_nand = (void __iomem *)ioremap(nand_phys, 0x1000); + p_nand = ioremap(nand_phys, 0x1000); /* make controller and MTD agree */ if (NAND_CS == 0) @@ -589,7 +585,7 @@ static int __init au1xxx_nand_init(void) return 0; outio: - iounmap((void *)p_nand); + iounmap(p_nand); outmem: kfree(au1550_mtd); @@ -610,7 +606,7 @@ static void __exit au1550_cleanup(void) kfree(au1550_mtd); /* Unmap */ - iounmap((void *)p_nand); + iounmap(p_nand); } module_exit(au1550_cleanup); diff --git a/drivers/mtd/nand/bcm_umi_nand.c b/drivers/mtd/nand/bcm_umi_nand.c index c997f98..dfe262c 100644 --- a/drivers/mtd/nand/bcm_umi_nand.c +++ b/drivers/mtd/nand/bcm_umi_nand.c @@ -13,7 +13,6 @@ *****************************************************************************/ /* ---- Include Files ---------------------------------------------------- */ -#include <linux/version.h> #include <linux/module.h> #include <linux/types.h> #include <linux/init.h> @@ -447,7 +446,7 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev) * layout we'll be using. */ - err = nand_scan_ident(board_mtd, 1); + err = nand_scan_ident(board_mtd, 1, NULL); if (err) { printk(KERN_ERR "nand_scan failed: %d\n", err); iounmap(bcm_umi_io_base); diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 8506e7e..2974995 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -68,6 +68,27 @@ #define DRV_AUTHOR "Bryan Wu <bryan.wu@analog.com>" #define DRV_DESC "BF5xx on-chip NAND FLash Controller Driver" +/* NFC_STAT Masks */ +#define NBUSY 0x01 /* Not Busy */ +#define WB_FULL 0x02 /* Write Buffer Full */ +#define PG_WR_STAT 0x04 /* Page Write Pending */ +#define PG_RD_STAT 0x08 /* Page Read Pending */ +#define WB_EMPTY 0x10 /* Write Buffer Empty */ + +/* NFC_IRQSTAT Masks */ +#define NBUSYIRQ 0x01 /* Not Busy IRQ */ +#define WB_OVF 0x02 /* Write Buffer Overflow */ +#define WB_EDGE 0x04 /* Write Buffer Edge Detect */ +#define RD_RDY 0x08 /* Read Data Ready */ +#define WR_DONE 0x10 /* Page Write Done */ + +/* NFC_RST Masks */ +#define ECC_RST 0x01 /* ECC (and NFC counters) Reset */ + +/* NFC_PGCTL Masks */ +#define PG_RD_START 0x01 /* Page Read Start */ +#define PG_WR_START 0x02 /* Page Write Start */ + #ifdef CONFIG_MTD_NAND_BF5XX_HWECC static int hardware_ecc = 1; #else @@ -487,7 +508,7 @@ static void bf5xx_nand_dma_rw(struct mtd_info *mtd, * transferred to generate the correct ECC register * values. */ - bfin_write_NFC_RST(0x1); + bfin_write_NFC_RST(ECC_RST); SSYNC(); disable_dma(CH_NFC); @@ -497,7 +518,7 @@ static void bf5xx_nand_dma_rw(struct mtd_info *mtd, set_dma_config(CH_NFC, 0x0); set_dma_start_addr(CH_NFC, (unsigned long) buf); -/* The DMAs have different size on BF52x and BF54x */ + /* The DMAs have different size on BF52x and BF54x */ #ifdef CONFIG_BF52x set_dma_x_count(CH_NFC, (page_size >> 1)); set_dma_x_modify(CH_NFC, 2); @@ -517,9 +538,9 @@ static void bf5xx_nand_dma_rw(struct mtd_info *mtd, /* Start PAGE read/write operation */ if (is_read) - bfin_write_NFC_PGCTL(0x1); + bfin_write_NFC_PGCTL(PG_RD_START); else - bfin_write_NFC_PGCTL(0x2); + bfin_write_NFC_PGCTL(PG_WR_START); wait_for_completion(&info->dma_completion); } diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index e5a9f9c..db1dfc5 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -762,7 +762,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK)); /* Scan to find existence of the device */ - if (nand_scan_ident(mtd, 2)) { + if (nand_scan_ident(mtd, 2, NULL)) { err = -ENXIO; goto out_irq; } @@ -849,7 +849,7 @@ static void __devexit cafe_nand_remove(struct pci_dev *pdev) kfree(mtd); } -static struct pci_device_id cafe_nand_tbl[] = { +static const struct pci_device_id cafe_nand_tbl[] = { { PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_NAND, PCI_ANY_ID, PCI_ANY_ID }, { } diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 76e2dc8..9c9d893 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -567,8 +567,8 @@ static int __init nand_davinci_probe(struct platform_device *pdev) goto err_nomem; } - vaddr = ioremap(res1->start, res1->end - res1->start); - base = ioremap(res2->start, res2->end - res2->start); + vaddr = ioremap(res1->start, resource_size(res1)); + base = ioremap(res2->start, resource_size(res2)); if (!vaddr || !base) { dev_err(&pdev->dev, "ioremap failed\n"); ret = -EINVAL; @@ -691,7 +691,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) spin_unlock_irq(&davinci_nand_lock); /* Scan to find existence of the device(s) */ - ret = nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1); + ret = nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1, NULL); if (ret < 0) { dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); goto err_scan; diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c new file mode 100644 index 0000000..ca03428 --- /dev/null +++ b/drivers/mtd/nand/denali.c @@ -0,0 +1,2134 @@ +/* + * NAND Flash Controller Device Driver + * Copyright © 2009-2010, Intel Corporation and its suppliers. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/mutex.h> +#include <linux/pci.h> +#include <linux/mtd/mtd.h> +#include <linux/module.h> + +#include "denali.h" + +MODULE_LICENSE("GPL"); + +/* We define a module parameter that allows the user to override + * the hardware and decide what timing mode should be used. + */ +#define NAND_DEFAULT_TIMINGS -1 + +static int onfi_timing_mode = NAND_DEFAULT_TIMINGS; +module_param(onfi_timing_mode, int, S_IRUGO); +MODULE_PARM_DESC(onfi_timing_mode, "Overrides default ONFI setting. -1 indicates" + " use default timings"); + +#define DENALI_NAND_NAME "denali-nand" + +/* We define a macro here that combines all interrupts this driver uses into + * a single constant value, for convenience. */ +#define DENALI_IRQ_ALL (INTR_STATUS0__DMA_CMD_COMP | \ + INTR_STATUS0__ECC_TRANSACTION_DONE | \ + INTR_STATUS0__ECC_ERR | \ + INTR_STATUS0__PROGRAM_FAIL | \ + INTR_STATUS0__LOAD_COMP | \ + INTR_STATUS0__PROGRAM_COMP | \ + INTR_STATUS0__TIME_OUT | \ + INTR_STATUS0__ERASE_FAIL | \ + INTR_STATUS0__RST_COMP | \ + INTR_STATUS0__ERASE_COMP) + +/* indicates whether or not the internal value for the flash bank is + valid or not */ +#define CHIP_SELECT_INVALID -1 + +#define SUPPORT_8BITECC 1 + +/* This macro divides two integers and rounds fractional values up + * to the nearest integer value. */ +#define CEIL_DIV(X, Y) (((X)%(Y)) ? ((X)/(Y)+1) : ((X)/(Y))) + +/* this macro allows us to convert from an MTD structure to our own + * device context (denali) structure. + */ +#define mtd_to_denali(m) container_of(m, struct denali_nand_info, mtd) + +/* These constants are defined by the driver to enable common driver + configuration options. */ +#define SPARE_ACCESS 0x41 +#define MAIN_ACCESS 0x42 +#define MAIN_SPARE_ACCESS 0x43 + +#define DENALI_READ 0 +#define DENALI_WRITE 0x100 + +/* types of device accesses. We can issue commands and get status */ +#define COMMAND_CYCLE 0 +#define ADDR_CYCLE 1 +#define STATUS_CYCLE 2 + +/* this is a helper macro that allows us to + * format the bank into the proper bits for the controller */ +#define BANK(x) ((x) << 24) + +/* List of platforms this NAND controller has be integrated into */ +static const struct pci_device_id denali_pci_ids[] = { + { PCI_VDEVICE(INTEL, 0x0701), INTEL_CE4100 }, + { PCI_VDEVICE(INTEL, 0x0809), INTEL_MRST }, + { /* end: all zeroes */ } +}; + + +/* these are static lookup tables that give us easy access to + registers in the NAND controller. + */ +static const uint32_t intr_status_addresses[4] = {INTR_STATUS0, + INTR_STATUS1, + INTR_STATUS2, + INTR_STATUS3}; + +static const uint32_t device_reset_banks[4] = {DEVICE_RESET__BANK0, + DEVICE_RESET__BANK1, + DEVICE_RESET__BANK2, + DEVICE_RESET__BANK3}; + +static const uint32_t operation_timeout[4] = {INTR_STATUS0__TIME_OUT, + INTR_STATUS1__TIME_OUT, + INTR_STATUS2__TIME_OUT, + INTR_STATUS3__TIME_OUT}; + +static const uint32_t reset_complete[4] = {INTR_STATUS0__RST_COMP, + INTR_STATUS1__RST_COMP, + INTR_STATUS2__RST_COMP, + INTR_STATUS3__RST_COMP}; + +/* specifies the debug level of the driver */ +static int nand_debug_level = 0; + +/* forward declarations */ +static void clear_interrupts(struct denali_nand_info *denali); +static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask); +static void denali_irq_enable(struct denali_nand_info *denali, uint32_t int_mask); +static uint32_t read_interrupt_status(struct denali_nand_info *denali); + +#define DEBUG_DENALI 0 + +/* This is a wrapper for writing to the denali registers. + * this allows us to create debug information so we can + * observe how the driver is programming the device. + * it uses standard linux convention for (val, addr) */ +static void denali_write32(uint32_t value, void *addr) +{ + iowrite32(value, addr); + +#if DEBUG_DENALI + printk(KERN_ERR "wrote: 0x%x -> 0x%x\n", value, (uint32_t)((uint32_t)addr & 0x1fff)); +#endif +} + +/* Certain operations for the denali NAND controller use an indexed mode to read/write + data. The operation is performed by writing the address value of the command to + the device memory followed by the data. This function abstracts this common + operation. +*/ +static void index_addr(struct denali_nand_info *denali, uint32_t address, uint32_t data) +{ + denali_write32(address, denali->flash_mem); + denali_write32(data, denali->flash_mem + 0x10); +} + +/* Perform an indexed read of the device */ +static void index_addr_read_data(struct denali_nand_info *denali, + uint32_t address, uint32_t *pdata) +{ + denali_write32(address, denali->flash_mem); + *pdata = ioread32(denali->flash_mem + 0x10); +} + +/* We need to buffer some data for some of the NAND core routines. + * The operations manage buffering that data. */ +static void reset_buf(struct denali_nand_info *denali) +{ + denali->buf.head = denali->buf.tail = 0; +} + +static void write_byte_to_buf(struct denali_nand_info *denali, uint8_t byte) +{ + BUG_ON(denali->buf.tail >= sizeof(denali->buf.buf)); + denali->buf.buf[denali->buf.tail++] = byte; +} + +/* reads the status of the device */ +static void read_status(struct denali_nand_info *denali) +{ + uint32_t cmd = 0x0; + + /* initialize the data buffer to store status */ + reset_buf(denali); + + /* initiate a device status read */ + cmd = MODE_11 | BANK(denali->flash_bank); + index_addr(denali, cmd | COMMAND_CYCLE, 0x70); + denali_write32(cmd | STATUS_CYCLE, denali->flash_mem); + + /* update buffer with status value */ + write_byte_to_buf(denali, ioread32(denali->flash_mem + 0x10)); + +#if DEBUG_DENALI + printk("device reporting status value of 0x%2x\n", denali->buf.buf[0]); +#endif +} + +/* resets a specific device connected to the core */ +static void reset_bank(struct denali_nand_info *denali) +{ + uint32_t irq_status = 0; + uint32_t irq_mask = reset_complete[denali->flash_bank] | + operation_timeout[denali->flash_bank]; + int bank = 0; + + clear_interrupts(denali); + + bank = device_reset_banks[denali->flash_bank]; + denali_write32(bank, denali->flash_reg + DEVICE_RESET); + + irq_status = wait_for_irq(denali, irq_mask); + + if (irq_status & operation_timeout[denali->flash_bank]) + { + printk(KERN_ERR "reset bank failed.\n"); + } +} + +/* Reset the flash controller */ +static uint16_t NAND_Flash_Reset(struct denali_nand_info *denali) +{ + uint32_t i; + + nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + for (i = 0 ; i < LLD_MAX_FLASH_BANKS; i++) + denali_write32(reset_complete[i] | operation_timeout[i], + denali->flash_reg + intr_status_addresses[i]); + + for (i = 0 ; i < LLD_MAX_FLASH_BANKS; i++) { + denali_write32(device_reset_banks[i], denali->flash_reg + DEVICE_RESET); + while (!(ioread32(denali->flash_reg + intr_status_addresses[i]) & + (reset_complete[i] | operation_timeout[i]))) + ; + if (ioread32(denali->flash_reg + intr_status_addresses[i]) & + operation_timeout[i]) + nand_dbg_print(NAND_DBG_WARN, + "NAND Reset operation timed out on bank %d\n", i); + } + + for (i = 0; i < LLD_MAX_FLASH_BANKS; i++) + denali_write32(reset_complete[i] | operation_timeout[i], + denali->flash_reg + intr_status_addresses[i]); + + return PASS; +} + +/* this routine calculates the ONFI timing values for a given mode and programs + * the clocking register accordingly. The mode is determined by the get_onfi_nand_para + routine. + */ +static void NAND_ONFi_Timing_Mode(struct denali_nand_info *denali, uint16_t mode) +{ + uint16_t Trea[6] = {40, 30, 25, 20, 20, 16}; + uint16_t Trp[6] = {50, 25, 17, 15, 12, 10}; + uint16_t Treh[6] = {30, 15, 15, 10, 10, 7}; + uint16_t Trc[6] = {100, 50, 35, 30, 25, 20}; + uint16_t Trhoh[6] = {0, 15, 15, 15, 15, 15}; + uint16_t Trloh[6] = {0, 0, 0, 0, 5, 5}; + uint16_t Tcea[6] = {100, 45, 30, 25, 25, 25}; + uint16_t Tadl[6] = {200, 100, 100, 100, 70, 70}; + uint16_t Trhw[6] = {200, 100, 100, 100, 100, 100}; + uint16_t Trhz[6] = {200, 100, 100, 100, 100, 100}; + uint16_t Twhr[6] = {120, 80, 80, 60, 60, 60}; + uint16_t Tcs[6] = {70, 35, 25, 25, 20, 15}; + + uint16_t TclsRising = 1; + uint16_t data_invalid_rhoh, data_invalid_rloh, data_invalid; + uint16_t dv_window = 0; + uint16_t en_lo, en_hi; + uint16_t acc_clks; + uint16_t addr_2_data, re_2_we, re_2_re, we_2_re, cs_cnt; + + nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + en_lo = CEIL_DIV(Trp[mode], CLK_X); + en_hi = CEIL_DIV(Treh[mode], CLK_X); +#if ONFI_BLOOM_TIME + if ((en_hi * CLK_X) < (Treh[mode] + 2)) + en_hi++; +#endif + + if ((en_lo + en_hi) * CLK_X < Trc[mode]) + en_lo += CEIL_DIV((Trc[mode] - (en_lo + en_hi) * CLK_X), CLK_X); + + if ((en_lo + en_hi) < CLK_MULTI) + en_lo += CLK_MULTI - en_lo - en_hi; + + while (dv_window < 8) { + data_invalid_rhoh = en_lo * CLK_X + Trhoh[mode]; + + data_invalid_rloh = (en_lo + en_hi) * CLK_X + Trloh[mode]; + + data_invalid = + data_invalid_rhoh < + data_invalid_rloh ? data_invalid_rhoh : data_invalid_rloh; + + dv_window = data_invalid - Trea[mode]; + + if (dv_window < 8) + en_lo++; + } + + acc_clks = CEIL_DIV(Trea[mode], CLK_X); + + while (((acc_clks * CLK_X) - Trea[mode]) < 3) + acc_clks++; + + if ((data_invalid - acc_clks * CLK_X) < 2) + nand_dbg_print(NAND_DBG_WARN, "%s, Line %d: Warning!\n", + __FILE__, __LINE__); + + addr_2_data = CEIL_DIV(Tadl[mode], CLK_X); + re_2_we = CEIL_DIV(Trhw[mode], CLK_X); + re_2_re = CEIL_DIV(Trhz[mode], CLK_X); + we_2_re = CEIL_DIV(Twhr[mode], CLK_X); + cs_cnt = CEIL_DIV((Tcs[mode] - Trp[mode]), CLK_X); + if (!TclsRising) + cs_cnt = CEIL_DIV(Tcs[mode], CLK_X); + if (cs_cnt == 0) + cs_cnt = 1; + + if (Tcea[mode]) { + while (((cs_cnt * CLK_X) + Trea[mode]) < Tcea[mode]) + cs_cnt++; + } + +#if MODE5_WORKAROUND + if (mode == 5) + acc_clks = 5; +#endif + + /* Sighting 3462430: Temporary hack for MT29F128G08CJABAWP:B */ + if ((ioread32(denali->flash_reg + MANUFACTURER_ID) == 0) && + (ioread32(denali->flash_reg + DEVICE_ID) == 0x88)) + acc_clks = 6; + + denali_write32(acc_clks, denali->flash_reg + ACC_CLKS); + denali_write32(re_2_we, denali->flash_reg + RE_2_WE); + denali_write32(re_2_re, denali->flash_reg + RE_2_RE); + denali_write32(we_2_re, denali->flash_reg + WE_2_RE); + denali_write32(addr_2_data, denali->flash_reg + ADDR_2_DATA); + denali_write32(en_lo, denali->flash_reg + RDWR_EN_LO_CNT); + denali_write32(en_hi, denali->flash_reg + RDWR_EN_HI_CNT); + denali_write32(cs_cnt, denali->flash_reg + CS_SETUP_CNT); +} + +/* configures the initial ECC settings for the controller */ +static void set_ecc_config(struct denali_nand_info *denali) +{ +#if SUPPORT_8BITECC + if ((ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE) < 4096) || + (ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE) <= 128)) + denali_write32(8, denali->flash_reg + ECC_CORRECTION); +#endif + + if ((ioread32(denali->flash_reg + ECC_CORRECTION) & ECC_CORRECTION__VALUE) + == 1) { + denali->dev_info.wECCBytesPerSector = 4; + denali->dev_info.wECCBytesPerSector *= denali->dev_info.wDevicesConnected; + denali->dev_info.wNumPageSpareFlag = + denali->dev_info.wPageSpareSize - + denali->dev_info.wPageDataSize / + (ECC_SECTOR_SIZE * denali->dev_info.wDevicesConnected) * + denali->dev_info.wECCBytesPerSector + - denali->dev_info.wSpareSkipBytes; + } else { + denali->dev_info.wECCBytesPerSector = + (ioread32(denali->flash_reg + ECC_CORRECTION) & + ECC_CORRECTION__VALUE) * 13 / 8; + if ((denali->dev_info.wECCBytesPerSector) % 2 == 0) + denali->dev_info.wECCBytesPerSector += 2; + else + denali->dev_info.wECCBytesPerSector += 1; + + denali->dev_info.wECCBytesPerSector *= denali->dev_info.wDevicesConnected; + denali->dev_info.wNumPageSpareFlag = denali->dev_info.wPageSpareSize - + denali->dev_info.wPageDataSize / + (ECC_SECTOR_SIZE * denali->dev_info.wDevicesConnected) * + denali->dev_info.wECCBytesPerSector + - denali->dev_info.wSpareSkipBytes; + } +} + +/* queries the NAND device to see what ONFI modes it supports. */ +static uint16_t get_onfi_nand_para(struct denali_nand_info *denali) +{ + int i; + uint16_t blks_lun_l, blks_lun_h, n_of_luns; + uint32_t blockperlun, id; + + denali_write32(DEVICE_RESET__BANK0, denali->flash_reg + DEVICE_RESET); + + while (!((ioread32(denali->flash_reg + INTR_STATUS0) & + INTR_STATUS0__RST_COMP) | + (ioread32(denali->flash_reg + INTR_STATUS0) & + INTR_STATUS0__TIME_OUT))) + ; + + if (ioread32(denali->flash_reg + INTR_STATUS0) & INTR_STATUS0__RST_COMP) { + denali_write32(DEVICE_RESET__BANK1, denali->flash_reg + DEVICE_RESET); + while (!((ioread32(denali->flash_reg + INTR_STATUS1) & + INTR_STATUS1__RST_COMP) | + (ioread32(denali->flash_reg + INTR_STATUS1) & + INTR_STATUS1__TIME_OUT))) + ; + + if (ioread32(denali->flash_reg + INTR_STATUS1) & + INTR_STATUS1__RST_COMP) { + denali_write32(DEVICE_RESET__BANK2, + denali->flash_reg + DEVICE_RESET); + while (!((ioread32(denali->flash_reg + INTR_STATUS2) & + INTR_STATUS2__RST_COMP) | + (ioread32(denali->flash_reg + INTR_STATUS2) & + INTR_STATUS2__TIME_OUT))) + ; + + if (ioread32(denali->flash_reg + INTR_STATUS2) & + INTR_STATUS2__RST_COMP) { + denali_write32(DEVICE_RESET__BANK3, + denali->flash_reg + DEVICE_RESET); + while (!((ioread32(denali->flash_reg + INTR_STATUS3) & + INTR_STATUS3__RST_COMP) | + (ioread32(denali->flash_reg + INTR_STATUS3) & + INTR_STATUS3__TIME_OUT))) + ; + } else { + printk(KERN_ERR "Getting a time out for bank 2!\n"); + } + } else { + printk(KERN_ERR "Getting a time out for bank 1!\n"); + } + } + + denali_write32(INTR_STATUS0__TIME_OUT, denali->flash_reg + INTR_STATUS0); + denali_write32(INTR_STATUS1__TIME_OUT, denali->flash_reg + INTR_STATUS1); + denali_write32(INTR_STATUS2__TIME_OUT, denali->flash_reg + INTR_STATUS2); + denali_write32(INTR_STATUS3__TIME_OUT, denali->flash_reg + INTR_STATUS3); + + denali->dev_info.wONFIDevFeatures = + ioread32(denali->flash_reg + ONFI_DEVICE_FEATURES); + denali->dev_info.wONFIOptCommands = + ioread32(denali->flash_reg + ONFI_OPTIONAL_COMMANDS); + denali->dev_info.wONFITimingMode = + ioread32(denali->flash_reg + ONFI_TIMING_MODE); + denali->dev_info.wONFIPgmCacheTimingMode = + ioread32(denali->flash_reg + ONFI_PGM_CACHE_TIMING_MODE); + + n_of_luns = ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_LUNS) & + ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS; + blks_lun_l = ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L); + blks_lun_h = ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U); + + blockperlun = (blks_lun_h << 16) | blks_lun_l; + + denali->dev_info.wTotalBlocks = n_of_luns * blockperlun; + + if (!(ioread32(denali->flash_reg + ONFI_TIMING_MODE) & + ONFI_TIMING_MODE__VALUE)) + return FAIL; + + for (i = 5; i > 0; i--) { + if (ioread32(denali->flash_reg + ONFI_TIMING_MODE) & (0x01 << i)) + break; + } + + NAND_ONFi_Timing_Mode(denali, i); + + index_addr(denali, MODE_11 | 0, 0x90); + index_addr(denali, MODE_11 | 1, 0); + + for (i = 0; i < 3; i++) + index_addr_read_data(denali, MODE_11 | 2, &id); + + nand_dbg_print(NAND_DBG_DEBUG, "3rd ID: 0x%x\n", id); + + denali->dev_info.MLCDevice = id & 0x0C; + + /* By now, all the ONFI devices we know support the page cache */ + /* rw feature. So here we enable the pipeline_rw_ahead feature */ + /* iowrite32(1, denali->flash_reg + CACHE_WRITE_ENABLE); */ + /* iowrite32(1, denali->flash_reg + CACHE_READ_ENABLE); */ + + return PASS; +} + +static void get_samsung_nand_para(struct denali_nand_info *denali) +{ + uint8_t no_of_planes; + uint32_t blk_size; + uint64_t plane_size, capacity; + uint32_t id_bytes[5]; + int i; + + index_addr(denali, (uint32_t)(MODE_11 | 0), 0x90); + index_addr(denali, (uint32_t)(MODE_11 | 1), 0); + for (i = 0; i < 5; i++) + index_addr_read_data(denali, (uint32_t)(MODE_11 | 2), &id_bytes[i]); + + nand_dbg_print(NAND_DBG_DEBUG, + "ID bytes: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", + id_bytes[0], id_bytes[1], id_bytes[2], + id_bytes[3], id_bytes[4]); + + if ((id_bytes[1] & 0xff) == 0xd3) { /* Samsung K9WAG08U1A */ + /* Set timing register values according to datasheet */ + denali_write32(5, denali->flash_reg + ACC_CLKS); + denali_write32(20, denali->flash_reg + RE_2_WE); + denali_write32(12, denali->flash_reg + WE_2_RE); + denali_write32(14, denali->flash_reg + ADDR_2_DATA); + denali_write32(3, denali->flash_reg + RDWR_EN_LO_CNT); + denali_write32(2, denali->flash_reg + RDWR_EN_HI_CNT); + denali_write32(2, denali->flash_reg + CS_SETUP_CNT); + } + + no_of_planes = 1 << ((id_bytes[4] & 0x0c) >> 2); + plane_size = (uint64_t)64 << ((id_bytes[4] & 0x70) >> 4); + blk_size = 64 << ((ioread32(denali->flash_reg + DEVICE_PARAM_1) & 0x30) >> 4); + capacity = (uint64_t)128 * plane_size * no_of_planes; + + do_div(capacity, blk_size); + denali->dev_info.wTotalBlocks = capacity; +} + +static void get_toshiba_nand_para(struct denali_nand_info *denali) +{ + void __iomem *scratch_reg; + uint32_t tmp; + + /* Workaround to fix a controller bug which reports a wrong */ + /* spare area size for some kind of Toshiba NAND device */ + if ((ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE) == 4096) && + (ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE) == 64)) { + denali_write32(216, denali->flash_reg + DEVICE_SPARE_AREA_SIZE); + tmp = ioread32(denali->flash_reg + DEVICES_CONNECTED) * + ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE); + denali_write32(tmp, denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE); +#if SUPPORT_15BITECC + denali_write32(15, denali->flash_reg + ECC_CORRECTION); +#elif SUPPORT_8BITECC + denali_write32(8, denali->flash_reg + ECC_CORRECTION); +#endif + } + + /* As Toshiba NAND can not provide it's block number, */ + /* so here we need user to provide the correct block */ + /* number in a scratch register before the Linux NAND */ + /* driver is loaded. If no valid value found in the scratch */ + /* register, then we use default block number value */ + scratch_reg = ioremap_nocache(SCRATCH_REG_ADDR, SCRATCH_REG_SIZE); + if (!scratch_reg) { + printk(KERN_ERR "Spectra: ioremap failed in %s, Line %d", + __FILE__, __LINE__); + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + } else { + nand_dbg_print(NAND_DBG_WARN, + "Spectra: ioremap reg address: 0x%p\n", scratch_reg); + denali->dev_info.wTotalBlocks = 1 << ioread8(scratch_reg); + if (denali->dev_info.wTotalBlocks < 512) + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + iounmap(scratch_reg); + } +} + +static void get_hynix_nand_para(struct denali_nand_info *denali) +{ + void __iomem *scratch_reg; + uint32_t main_size, spare_size; + + switch (denali->dev_info.wDeviceID) { + case 0xD5: /* Hynix H27UAG8T2A, H27UBG8U5A or H27UCG8VFA */ + case 0xD7: /* Hynix H27UDG8VEM, H27UCG8UDM or H27UCG8V5A */ + denali_write32(128, denali->flash_reg + PAGES_PER_BLOCK); + denali_write32(4096, denali->flash_reg + DEVICE_MAIN_AREA_SIZE); + denali_write32(224, denali->flash_reg + DEVICE_SPARE_AREA_SIZE); + main_size = 4096 * ioread32(denali->flash_reg + DEVICES_CONNECTED); + spare_size = 224 * ioread32(denali->flash_reg + DEVICES_CONNECTED); + denali_write32(main_size, denali->flash_reg + LOGICAL_PAGE_DATA_SIZE); + denali_write32(spare_size, denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE); + denali_write32(0, denali->flash_reg + DEVICE_WIDTH); +#if SUPPORT_15BITECC + denali_write32(15, denali->flash_reg + ECC_CORRECTION); +#elif SUPPORT_8BITECC + denali_write32(8, denali->flash_reg + ECC_CORRECTION); +#endif + denali->dev_info.MLCDevice = 1; + break; + default: + nand_dbg_print(NAND_DBG_WARN, + "Spectra: Unknown Hynix NAND (Device ID: 0x%x)." + "Will use default parameter values instead.\n", + denali->dev_info.wDeviceID); + } + + scratch_reg = ioremap_nocache(SCRATCH_REG_ADDR, SCRATCH_REG_SIZE); + if (!scratch_reg) { + printk(KERN_ERR "Spectra: ioremap failed in %s, Line %d", + __FILE__, __LINE__); + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + } else { + nand_dbg_print(NAND_DBG_WARN, + "Spectra: ioremap reg address: 0x%p\n", scratch_reg); + denali->dev_info.wTotalBlocks = 1 << ioread8(scratch_reg); + if (denali->dev_info.wTotalBlocks < 512) + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + iounmap(scratch_reg); + } +} + +/* determines how many NAND chips are connected to the controller. Note for + Intel CE4100 devices we don't support more than one device. + */ +static void find_valid_banks(struct denali_nand_info *denali) +{ + uint32_t id[LLD_MAX_FLASH_BANKS]; + int i; + + denali->total_used_banks = 1; + for (i = 0; i < LLD_MAX_FLASH_BANKS; i++) { + index_addr(denali, (uint32_t)(MODE_11 | (i << 24) | 0), 0x90); + index_addr(denali, (uint32_t)(MODE_11 | (i << 24) | 1), 0); + index_addr_read_data(denali, (uint32_t)(MODE_11 | (i << 24) | 2), &id[i]); + + nand_dbg_print(NAND_DBG_DEBUG, + "Return 1st ID for bank[%d]: %x\n", i, id[i]); + + if (i == 0) { + if (!(id[i] & 0x0ff)) + break; /* WTF? */ + } else { + if ((id[i] & 0x0ff) == (id[0] & 0x0ff)) + denali->total_used_banks++; + else + break; + } + } + + if (denali->platform == INTEL_CE4100) + { + /* Platform limitations of the CE4100 device limit + * users to a single chip solution for NAND. + * Multichip support is not enabled. + */ + if (denali->total_used_banks != 1) + { + printk(KERN_ERR "Sorry, Intel CE4100 only supports " + "a single NAND device.\n"); + BUG(); + } + } + nand_dbg_print(NAND_DBG_DEBUG, + "denali->total_used_banks: %d\n", denali->total_used_banks); +} + +static void detect_partition_feature(struct denali_nand_info *denali) +{ + if (ioread32(denali->flash_reg + FEATURES) & FEATURES__PARTITION) { + if ((ioread32(denali->flash_reg + PERM_SRC_ID_1) & + PERM_SRC_ID_1__SRCID) == SPECTRA_PARTITION_ID) { + denali->dev_info.wSpectraStartBlock = + ((ioread32(denali->flash_reg + MIN_MAX_BANK_1) & + MIN_MAX_BANK_1__MIN_VALUE) * + denali->dev_info.wTotalBlocks) + + + (ioread32(denali->flash_reg + MIN_BLK_ADDR_1) & + MIN_BLK_ADDR_1__VALUE); + + denali->dev_info.wSpectraEndBlock = + (((ioread32(denali->flash_reg + MIN_MAX_BANK_1) & + MIN_MAX_BANK_1__MAX_VALUE) >> 2) * + denali->dev_info.wTotalBlocks) + + + (ioread32(denali->flash_reg + MAX_BLK_ADDR_1) & + MAX_BLK_ADDR_1__VALUE); + + denali->dev_info.wTotalBlocks *= denali->total_used_banks; + + if (denali->dev_info.wSpectraEndBlock >= + denali->dev_info.wTotalBlocks) { + denali->dev_info.wSpectraEndBlock = + denali->dev_info.wTotalBlocks - 1; + } + + denali->dev_info.wDataBlockNum = + denali->dev_info.wSpectraEndBlock - + denali->dev_info.wSpectraStartBlock + 1; + } else { + denali->dev_info.wTotalBlocks *= denali->total_used_banks; + denali->dev_info.wSpectraStartBlock = SPECTRA_START_BLOCK; + denali->dev_info.wSpectraEndBlock = + denali->dev_info.wTotalBlocks - 1; + denali->dev_info.wDataBlockNum = + denali->dev_info.wSpectraEndBlock - + denali->dev_info.wSpectraStartBlock + 1; + } + } else { + denali->dev_info.wTotalBlocks *= denali->total_used_banks; + denali->dev_info.wSpectraStartBlock = SPECTRA_START_BLOCK; + denali->dev_info.wSpectraEndBlock = denali->dev_info.wTotalBlocks - 1; + denali->dev_info.wDataBlockNum = + denali->dev_info.wSpectraEndBlock - + denali->dev_info.wSpectraStartBlock + 1; + } +} + +static void dump_device_info(struct denali_nand_info *denali) +{ + nand_dbg_print(NAND_DBG_DEBUG, "denali->dev_info:\n"); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceMaker: 0x%x\n", + denali->dev_info.wDeviceMaker); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceID: 0x%x\n", + denali->dev_info.wDeviceID); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceType: 0x%x\n", + denali->dev_info.wDeviceType); + nand_dbg_print(NAND_DBG_DEBUG, "SpectraStartBlock: %d\n", + denali->dev_info.wSpectraStartBlock); + nand_dbg_print(NAND_DBG_DEBUG, "SpectraEndBlock: %d\n", + denali->dev_info.wSpectraEndBlock); + nand_dbg_print(NAND_DBG_DEBUG, "TotalBlocks: %d\n", + denali->dev_info.wTotalBlocks); + nand_dbg_print(NAND_DBG_DEBUG, "PagesPerBlock: %d\n", + denali->dev_info.wPagesPerBlock); + nand_dbg_print(NAND_DBG_DEBUG, "PageSize: %d\n", + denali->dev_info.wPageSize); + nand_dbg_print(NAND_DBG_DEBUG, "PageDataSize: %d\n", + denali->dev_info.wPageDataSize); + nand_dbg_print(NAND_DBG_DEBUG, "PageSpareSize: %d\n", + denali->dev_info.wPageSpareSize); + nand_dbg_print(NAND_DBG_DEBUG, "NumPageSpareFlag: %d\n", + denali->dev_info.wNumPageSpareFlag); + nand_dbg_print(NAND_DBG_DEBUG, "ECCBytesPerSector: %d\n", + denali->dev_info.wECCBytesPerSector); + nand_dbg_print(NAND_DBG_DEBUG, "BlockSize: %d\n", + denali->dev_info.wBlockSize); + nand_dbg_print(NAND_DBG_DEBUG, "BlockDataSize: %d\n", + denali->dev_info.wBlockDataSize); + nand_dbg_print(NAND_DBG_DEBUG, "DataBlockNum: %d\n", + denali->dev_info.wDataBlockNum); + nand_dbg_print(NAND_DBG_DEBUG, "PlaneNum: %d\n", + denali->dev_info.bPlaneNum); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceMainAreaSize: %d\n", + denali->dev_info.wDeviceMainAreaSize); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceSpareAreaSize: %d\n", + denali->dev_info.wDeviceSpareAreaSize); + nand_dbg_print(NAND_DBG_DEBUG, "DevicesConnected: %d\n", + denali->dev_info.wDevicesConnected); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceWidth: %d\n", + denali->dev_info.wDeviceWidth); + nand_dbg_print(NAND_DBG_DEBUG, "HWRevision: 0x%x\n", + denali->dev_info.wHWRevision); + nand_dbg_print(NAND_DBG_DEBUG, "HWFeatures: 0x%x\n", + denali->dev_info.wHWFeatures); + nand_dbg_print(NAND_DBG_DEBUG, "ONFIDevFeatures: 0x%x\n", + denali->dev_info.wONFIDevFeatures); + nand_dbg_print(NAND_DBG_DEBUG, "ONFIOptCommands: 0x%x\n", + denali->dev_info.wONFIOptCommands); + nand_dbg_print(NAND_DBG_DEBUG, "ONFITimingMode: 0x%x\n", + denali->dev_info.wONFITimingMode); + nand_dbg_print(NAND_DBG_DEBUG, "ONFIPgmCacheTimingMode: 0x%x\n", + denali->dev_info.wONFIPgmCacheTimingMode); + nand_dbg_print(NAND_DBG_DEBUG, "MLCDevice: %s\n", + denali->dev_info.MLCDevice ? "Yes" : "No"); + nand_dbg_print(NAND_DBG_DEBUG, "SpareSkipBytes: %d\n", + denali->dev_info.wSpareSkipBytes); + nand_dbg_print(NAND_DBG_DEBUG, "BitsInPageNumber: %d\n", + denali->dev_info.nBitsInPageNumber); + nand_dbg_print(NAND_DBG_DEBUG, "BitsInPageDataSize: %d\n", + denali->dev_info.nBitsInPageDataSize); + nand_dbg_print(NAND_DBG_DEBUG, "BitsInBlockDataSize: %d\n", + denali->dev_info.nBitsInBlockDataSize); +} + +static uint16_t NAND_Read_Device_ID(struct denali_nand_info *denali) +{ + uint16_t status = PASS; + uint8_t no_of_planes; + + nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + denali->dev_info.wDeviceMaker = ioread32(denali->flash_reg + MANUFACTURER_ID); + denali->dev_info.wDeviceID = ioread32(denali->flash_reg + DEVICE_ID); + denali->dev_info.bDeviceParam0 = ioread32(denali->flash_reg + DEVICE_PARAM_0); + denali->dev_info.bDeviceParam1 = ioread32(denali->flash_reg + DEVICE_PARAM_1); + denali->dev_info.bDeviceParam2 = ioread32(denali->flash_reg + DEVICE_PARAM_2); + + denali->dev_info.MLCDevice = ioread32(denali->flash_reg + DEVICE_PARAM_0) & 0x0c; + + if (ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_LUNS) & + ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE) { /* ONFI 1.0 NAND */ + if (FAIL == get_onfi_nand_para(denali)) + return FAIL; + } else if (denali->dev_info.wDeviceMaker == 0xEC) { /* Samsung NAND */ + get_samsung_nand_para(denali); + } else if (denali->dev_info.wDeviceMaker == 0x98) { /* Toshiba NAND */ + get_toshiba_nand_para(denali); + } else if (denali->dev_info.wDeviceMaker == 0xAD) { /* Hynix NAND */ + get_hynix_nand_para(denali); + } else { + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + } + + nand_dbg_print(NAND_DBG_DEBUG, "Dump timing register values:" + "acc_clks: %d, re_2_we: %d, we_2_re: %d," + "addr_2_data: %d, rdwr_en_lo_cnt: %d, " + "rdwr_en_hi_cnt: %d, cs_setup_cnt: %d\n", + ioread32(denali->flash_reg + ACC_CLKS), + ioread32(denali->flash_reg + RE_2_WE), + ioread32(denali->flash_reg + WE_2_RE), + ioread32(denali->flash_reg + ADDR_2_DATA), + ioread32(denali->flash_reg + RDWR_EN_LO_CNT), + ioread32(denali->flash_reg + RDWR_EN_HI_CNT), + ioread32(denali->flash_reg + CS_SETUP_CNT)); + + denali->dev_info.wHWRevision = ioread32(denali->flash_reg + REVISION); + denali->dev_info.wHWFeatures = ioread32(denali->flash_reg + FEATURES); + + denali->dev_info.wDeviceMainAreaSize = + ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE); + denali->dev_info.wDeviceSpareAreaSize = + ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE); + + denali->dev_info.wPageDataSize = + ioread32(denali->flash_reg + LOGICAL_PAGE_DATA_SIZE); + + /* Note: When using the Micon 4K NAND device, the controller will report + * Page Spare Size as 216 bytes. But Micron's Spec say it's 218 bytes. + * And if force set it to 218 bytes, the controller can not work + * correctly. So just let it be. But keep in mind that this bug may + * cause + * other problems in future. - Yunpeng 2008-10-10 + */ + denali->dev_info.wPageSpareSize = + ioread32(denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE); + + denali->dev_info.wPagesPerBlock = ioread32(denali->flash_reg + PAGES_PER_BLOCK); + + denali->dev_info.wPageSize = + denali->dev_info.wPageDataSize + denali->dev_info.wPageSpareSize; + denali->dev_info.wBlockSize = + denali->dev_info.wPageSize * denali->dev_info.wPagesPerBlock; + denali->dev_info.wBlockDataSize = + denali->dev_info.wPagesPerBlock * denali->dev_info.wPageDataSize; + + denali->dev_info.wDeviceWidth = ioread32(denali->flash_reg + DEVICE_WIDTH); + denali->dev_info.wDeviceType = + ((ioread32(denali->flash_reg + DEVICE_WIDTH) > 0) ? 16 : 8); + + denali->dev_info.wDevicesConnected = ioread32(denali->flash_reg + DEVICES_CONNECTED); + + denali->dev_info.wSpareSkipBytes = + ioread32(denali->flash_reg + SPARE_AREA_SKIP_BYTES) * + denali->dev_info.wDevicesConnected; + + denali->dev_info.nBitsInPageNumber = + ilog2(denali->dev_info.wPagesPerBlock); + denali->dev_info.nBitsInPageDataSize = + ilog2(denali->dev_info.wPageDataSize); + denali->dev_info.nBitsInBlockDataSize = + ilog2(denali->dev_info.wBlockDataSize); + + set_ecc_config(denali); + + no_of_planes = ioread32(denali->flash_reg + NUMBER_OF_PLANES) & + NUMBER_OF_PLANES__VALUE; + + switch (no_of_planes) { + case 0: + case 1: + case 3: + case 7: + denali->dev_info.bPlaneNum = no_of_planes + 1; + break; + default: + status = FAIL; + break; + } + + find_valid_banks(denali); + + detect_partition_feature(denali); + + dump_device_info(denali); + + /* If the user specified to override the default timings + * with a specific ONFI mode, we apply those changes here. + */ + if (onfi_timing_mode != NAND_DEFAULT_TIMINGS) + { + NAND_ONFi_Timing_Mode(denali, onfi_timing_mode); + } + + return status; +} + +static void NAND_LLD_Enable_Disable_Interrupts(struct denali_nand_info *denali, + uint16_t INT_ENABLE) +{ + nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + if (INT_ENABLE) + denali_write32(1, denali->flash_reg + GLOBAL_INT_ENABLE); + else + denali_write32(0, denali->flash_reg + GLOBAL_INT_ENABLE); +} + +/* validation function to verify that the controlling software is making + a valid request + */ +static inline bool is_flash_bank_valid(int flash_bank) +{ + return (flash_bank >= 0 && flash_bank < 4); +} + +static void denali_irq_init(struct denali_nand_info *denali) +{ + uint32_t int_mask = 0; + + /* Disable global interrupts */ + NAND_LLD_Enable_Disable_Interrupts(denali, false); + + int_mask = DENALI_IRQ_ALL; + + /* Clear all status bits */ + denali_write32(0xFFFF, denali->flash_reg + INTR_STATUS0); + denali_write32(0xFFFF, denali->flash_reg + INTR_STATUS1); + denali_write32(0xFFFF, denali->flash_reg + INTR_STATUS2); + denali_write32(0xFFFF, denali->flash_reg + INTR_STATUS3); + + denali_irq_enable(denali, int_mask); +} + +static void denali_irq_cleanup(int irqnum, struct denali_nand_info *denali) +{ + NAND_LLD_Enable_Disable_Interrupts(denali, false); + free_irq(irqnum, denali); +} + +static void denali_irq_enable(struct denali_nand_info *denali, uint32_t int_mask) +{ + denali_write32(int_mask, denali->flash_reg + INTR_EN0); + denali_write32(int_mask, denali->flash_reg + INTR_EN1); + denali_write32(int_mask, denali->flash_reg + INTR_EN2); + denali_write32(int_mask, denali->flash_reg + INTR_EN3); +} + +/* This function only returns when an interrupt that this driver cares about + * occurs. This is to reduce the overhead of servicing interrupts + */ +static inline uint32_t denali_irq_detected(struct denali_nand_info *denali) +{ + return (read_interrupt_status(denali) & DENALI_IRQ_ALL); +} + +/* Interrupts are cleared by writing a 1 to the appropriate status bit */ +static inline void clear_interrupt(struct denali_nand_info *denali, uint32_t irq_mask) +{ + uint32_t intr_status_reg = 0; + + intr_status_reg = intr_status_addresses[denali->flash_bank]; + + denali_write32(irq_mask, denali->flash_reg + intr_status_reg); +} + +static void clear_interrupts(struct denali_nand_info *denali) +{ + uint32_t status = 0x0; + spin_lock_irq(&denali->irq_lock); + + status = read_interrupt_status(denali); + +#if DEBUG_DENALI + denali->irq_debug_array[denali->idx++] = 0x30000000 | status; + denali->idx %= 32; +#endif + + denali->irq_status = 0x0; + spin_unlock_irq(&denali->irq_lock); +} + +static uint32_t read_interrupt_status(struct denali_nand_info *denali) +{ + uint32_t intr_status_reg = 0; + + intr_status_reg = intr_status_addresses[denali->flash_bank]; + + return ioread32(denali->flash_reg + intr_status_reg); +} + +#if DEBUG_DENALI +static void print_irq_log(struct denali_nand_info *denali) +{ + int i = 0; + + printk("ISR debug log index = %X\n", denali->idx); + for (i = 0; i < 32; i++) + { + printk("%08X: %08X\n", i, denali->irq_debug_array[i]); + } +} +#endif + +/* This is the interrupt service routine. It handles all interrupts + * sent to this device. Note that on CE4100, this is a shared + * interrupt. + */ +static irqreturn_t denali_isr(int irq, void *dev_id) +{ + struct denali_nand_info *denali = dev_id; + uint32_t irq_status = 0x0; + irqreturn_t result = IRQ_NONE; + + spin_lock(&denali->irq_lock); + + /* check to see if a valid NAND chip has + * been selected. + */ + if (is_flash_bank_valid(denali->flash_bank)) + { + /* check to see if controller generated + * the interrupt, since this is a shared interrupt */ + if ((irq_status = denali_irq_detected(denali)) != 0) + { +#if DEBUG_DENALI + denali->irq_debug_array[denali->idx++] = 0x10000000 | irq_status; + denali->idx %= 32; + + printk("IRQ status = 0x%04x\n", irq_status); +#endif + /* handle interrupt */ + /* first acknowledge it */ + clear_interrupt(denali, irq_status); + /* store the status in the device context for someone + to read */ + denali->irq_status |= irq_status; + /* notify anyone who cares that it happened */ + complete(&denali->complete); + /* tell the OS that we've handled this */ + result = IRQ_HANDLED; + } + } + spin_unlock(&denali->irq_lock); + return result; +} +#define BANK(x) ((x) << 24) + +static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask) +{ + unsigned long comp_res = 0; + uint32_t intr_status = 0; + bool retry = false; + unsigned long timeout = msecs_to_jiffies(1000); + + do + { +#if DEBUG_DENALI + printk("waiting for 0x%x\n", irq_mask); +#endif + comp_res = wait_for_completion_timeout(&denali->complete, timeout); + spin_lock_irq(&denali->irq_lock); + intr_status = denali->irq_status; + +#if DEBUG_DENALI + denali->irq_debug_array[denali->idx++] = 0x20000000 | (irq_mask << 16) | intr_status; + denali->idx %= 32; +#endif + + if (intr_status & irq_mask) + { + denali->irq_status &= ~irq_mask; + spin_unlock_irq(&denali->irq_lock); +#if DEBUG_DENALI + if (retry) printk("status on retry = 0x%x\n", intr_status); +#endif + /* our interrupt was detected */ + break; + } + else + { + /* these are not the interrupts you are looking for - + need to wait again */ + spin_unlock_irq(&denali->irq_lock); +#if DEBUG_DENALI + print_irq_log(denali); + printk("received irq nobody cared: irq_status = 0x%x," + " irq_mask = 0x%x, timeout = %ld\n", intr_status, irq_mask, comp_res); +#endif + retry = true; + } + } while (comp_res != 0); + + if (comp_res == 0) + { + /* timeout */ + printk(KERN_ERR "timeout occurred, status = 0x%x, mask = 0x%x\n", + intr_status, irq_mask); + + intr_status = 0; + } + return intr_status; +} + +/* This helper function setups the registers for ECC and whether or not + the spare area will be transfered. */ +static void setup_ecc_for_xfer(struct denali_nand_info *denali, bool ecc_en, + bool transfer_spare) +{ + int ecc_en_flag = 0, transfer_spare_flag = 0; + + /* set ECC, transfer spare bits if needed */ + ecc_en_flag = ecc_en ? ECC_ENABLE__FLAG : 0; + transfer_spare_flag = transfer_spare ? TRANSFER_SPARE_REG__FLAG : 0; + + /* Enable spare area/ECC per user's request. */ + denali_write32(ecc_en_flag, denali->flash_reg + ECC_ENABLE); + denali_write32(transfer_spare_flag, denali->flash_reg + TRANSFER_SPARE_REG); +} + +/* sends a pipeline command operation to the controller. See the Denali NAND + controller's user guide for more information (section 4.2.3.6). + */ +static int denali_send_pipeline_cmd(struct denali_nand_info *denali, bool ecc_en, + bool transfer_spare, int access_type, + int op) +{ + int status = PASS; + uint32_t addr = 0x0, cmd = 0x0, page_count = 1, irq_status = 0, + irq_mask = 0; + + if (op == DENALI_READ) irq_mask = INTR_STATUS0__LOAD_COMP; + else if (op == DENALI_WRITE) irq_mask = 0; + else BUG(); + + setup_ecc_for_xfer(denali, ecc_en, transfer_spare); + +#if DEBUG_DENALI + spin_lock_irq(&denali->irq_lock); + denali->irq_debug_array[denali->idx++] = 0x40000000 | ioread32(denali->flash_reg + ECC_ENABLE) | (access_type << 4); + denali->idx %= 32; + spin_unlock_irq(&denali->irq_lock); +#endif + + + /* clear interrupts */ + clear_interrupts(denali); + + addr = BANK(denali->flash_bank) | denali->page; + + if (op == DENALI_WRITE && access_type != SPARE_ACCESS) + { + cmd = MODE_01 | addr; + denali_write32(cmd, denali->flash_mem); + } + else if (op == DENALI_WRITE && access_type == SPARE_ACCESS) + { + /* read spare area */ + cmd = MODE_10 | addr; + index_addr(denali, (uint32_t)cmd, access_type); + + cmd = MODE_01 | addr; + denali_write32(cmd, denali->flash_mem); + } + else if (op == DENALI_READ) + { + /* setup page read request for access type */ + cmd = MODE_10 | addr; + index_addr(denali, (uint32_t)cmd, access_type); + + /* page 33 of the NAND controller spec indicates we should not + use the pipeline commands in Spare area only mode. So we + don't. + */ + if (access_type == SPARE_ACCESS) + { + cmd = MODE_01 | addr; + denali_write32(cmd, denali->flash_mem); + } + else + { + index_addr(denali, (uint32_t)cmd, 0x2000 | op | page_count); + + /* wait for command to be accepted + * can always use status0 bit as the mask is identical for each + * bank. */ + irq_status = wait_for_irq(denali, irq_mask); + + if (irq_status == 0) + { + printk(KERN_ERR "cmd, page, addr on timeout " + "(0x%x, 0x%x, 0x%x)\n", cmd, denali->page, addr); + status = FAIL; + } + else + { + cmd = MODE_01 | addr; + denali_write32(cmd, denali->flash_mem); + } + } + } + return status; +} + +/* helper function that simply writes a buffer to the flash */ +static int write_data_to_flash_mem(struct denali_nand_info *denali, const uint8_t *buf, + int len) +{ + uint32_t i = 0, *buf32; + + /* verify that the len is a multiple of 4. see comment in + * read_data_from_flash_mem() */ + BUG_ON((len % 4) != 0); + + /* write the data to the flash memory */ + buf32 = (uint32_t *)buf; + for (i = 0; i < len / 4; i++) + { + denali_write32(*buf32++, denali->flash_mem + 0x10); + } + return i*4; /* intent is to return the number of bytes read */ +} + +/* helper function that simply reads a buffer from the flash */ +static int read_data_from_flash_mem(struct denali_nand_info *denali, uint8_t *buf, + int len) +{ + uint32_t i = 0, *buf32; + + /* we assume that len will be a multiple of 4, if not + * it would be nice to know about it ASAP rather than + * have random failures... + * + * This assumption is based on the fact that this + * function is designed to be used to read flash pages, + * which are typically multiples of 4... + */ + + BUG_ON((len % 4) != 0); + + /* transfer the data from the flash */ + buf32 = (uint32_t *)buf; + for (i = 0; i < len / 4; i++) + { + *buf32++ = ioread32(denali->flash_mem + 0x10); + } + return i*4; /* intent is to return the number of bytes read */ +} + +/* writes OOB data to the device */ +static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + uint32_t irq_status = 0; + uint32_t irq_mask = INTR_STATUS0__PROGRAM_COMP | + INTR_STATUS0__PROGRAM_FAIL; + int status = 0; + + denali->page = page; + + if (denali_send_pipeline_cmd(denali, false, false, SPARE_ACCESS, + DENALI_WRITE) == PASS) + { + write_data_to_flash_mem(denali, buf, mtd->oobsize); + +#if DEBUG_DENALI + spin_lock_irq(&denali->irq_lock); + denali->irq_debug_array[denali->idx++] = 0x80000000 | mtd->oobsize; + denali->idx %= 32; + spin_unlock_irq(&denali->irq_lock); +#endif + + + /* wait for operation to complete */ + irq_status = wait_for_irq(denali, irq_mask); + + if (irq_status == 0) + { + printk(KERN_ERR "OOB write failed\n"); + status = -EIO; + } + } + else + { + printk(KERN_ERR "unable to send pipeline command\n"); + status = -EIO; + } + return status; +} + +/* reads OOB data from the device */ +static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + uint32_t irq_mask = INTR_STATUS0__LOAD_COMP, irq_status = 0, addr = 0x0, cmd = 0x0; + + denali->page = page; + +#if DEBUG_DENALI + printk("read_oob %d\n", page); +#endif + if (denali_send_pipeline_cmd(denali, false, true, SPARE_ACCESS, + DENALI_READ) == PASS) + { + read_data_from_flash_mem(denali, buf, mtd->oobsize); + + /* wait for command to be accepted + * can always use status0 bit as the mask is identical for each + * bank. */ + irq_status = wait_for_irq(denali, irq_mask); + + if (irq_status == 0) + { + printk(KERN_ERR "page on OOB timeout %d\n", denali->page); + } + + /* We set the device back to MAIN_ACCESS here as I observed + * instability with the controller if you do a block erase + * and the last transaction was a SPARE_ACCESS. Block erase + * is reliable (according to the MTD test infrastructure) + * if you are in MAIN_ACCESS. + */ + addr = BANK(denali->flash_bank) | denali->page; + cmd = MODE_10 | addr; + index_addr(denali, (uint32_t)cmd, MAIN_ACCESS); + +#if DEBUG_DENALI + spin_lock_irq(&denali->irq_lock); + denali->irq_debug_array[denali->idx++] = 0x60000000 | mtd->oobsize; + denali->idx %= 32; + spin_unlock_irq(&denali->irq_lock); +#endif + } +} + +/* this function examines buffers to see if they contain data that + * indicate that the buffer is part of an erased region of flash. + */ +bool is_erased(uint8_t *buf, int len) +{ + int i = 0; + for (i = 0; i < len; i++) + { + if (buf[i] != 0xFF) + { + return false; + } + } + return true; +} +#define ECC_SECTOR_SIZE 512 + +#define ECC_SECTOR(x) (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12) +#define ECC_BYTE(x) (((x) & ECC_ERROR_ADDRESS__OFFSET)) +#define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK) +#define ECC_ERROR_CORRECTABLE(x) (!((x) & ERR_CORRECTION_INFO)) +#define ECC_ERR_DEVICE(x) ((x) & ERR_CORRECTION_INFO__DEVICE_NR >> 8) +#define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO) + +static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, + uint8_t *oobbuf, uint32_t irq_status) +{ + bool check_erased_page = false; + + if (irq_status & INTR_STATUS0__ECC_ERR) + { + /* read the ECC errors. we'll ignore them for now */ + uint32_t err_address = 0, err_correction_info = 0; + uint32_t err_byte = 0, err_sector = 0, err_device = 0; + uint32_t err_correction_value = 0; + + do + { + err_address = ioread32(denali->flash_reg + + ECC_ERROR_ADDRESS); + err_sector = ECC_SECTOR(err_address); + err_byte = ECC_BYTE(err_address); + + + err_correction_info = ioread32(denali->flash_reg + + ERR_CORRECTION_INFO); + err_correction_value = + ECC_CORRECTION_VALUE(err_correction_info); + err_device = ECC_ERR_DEVICE(err_correction_info); + + if (ECC_ERROR_CORRECTABLE(err_correction_info)) + { + /* offset in our buffer is computed as: + sector number * sector size + offset in + sector + */ + int offset = err_sector * ECC_SECTOR_SIZE + + err_byte; + if (offset < denali->mtd.writesize) + { + /* correct the ECC error */ + buf[offset] ^= err_correction_value; + denali->mtd.ecc_stats.corrected++; + } + else + { + /* bummer, couldn't correct the error */ + printk(KERN_ERR "ECC offset invalid\n"); + denali->mtd.ecc_stats.failed++; + } + } + else + { + /* if the error is not correctable, need to + * look at the page to see if it is an erased page. + * if so, then it's not a real ECC error */ + check_erased_page = true; + } + +#if DEBUG_DENALI + printk("Detected ECC error in page %d: err_addr = 0x%08x," + " info to fix is 0x%08x\n", denali->page, err_address, + err_correction_info); +#endif + } while (!ECC_LAST_ERR(err_correction_info)); + } + return check_erased_page; +} + +/* programs the controller to either enable/disable DMA transfers */ +static void denali_enable_dma(struct denali_nand_info *denali, bool en) +{ + uint32_t reg_val = 0x0; + + if (en) reg_val = DMA_ENABLE__FLAG; + + denali_write32(reg_val, denali->flash_reg + DMA_ENABLE); + ioread32(denali->flash_reg + DMA_ENABLE); +} + +/* setups the HW to perform the data DMA */ +static void denali_setup_dma(struct denali_nand_info *denali, int op) +{ + uint32_t mode = 0x0; + const int page_count = 1; + dma_addr_t addr = denali->buf.dma_buf; + + mode = MODE_10 | BANK(denali->flash_bank); + + /* DMA is a four step process */ + + /* 1. setup transfer type and # of pages */ + index_addr(denali, mode | denali->page, 0x2000 | op | page_count); + + /* 2. set memory high address bits 23:8 */ + index_addr(denali, mode | ((uint16_t)(addr >> 16) << 8), 0x2200); + + /* 3. set memory low address bits 23:8 */ + index_addr(denali, mode | ((uint16_t)addr << 8), 0x2300); + + /* 4. interrupt when complete, burst len = 64 bytes*/ + index_addr(denali, mode | 0x14000, 0x2400); +} + +/* writes a page. user specifies type, and this function handles the + configuration details. */ +static void write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, bool raw_xfer) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + struct pci_dev *pci_dev = denali->dev; + + dma_addr_t addr = denali->buf.dma_buf; + size_t size = denali->mtd.writesize + denali->mtd.oobsize; + + uint32_t irq_status = 0; + uint32_t irq_mask = INTR_STATUS0__DMA_CMD_COMP | + INTR_STATUS0__PROGRAM_FAIL; + + /* if it is a raw xfer, we want to disable ecc, and send + * the spare area. + * !raw_xfer - enable ecc + * raw_xfer - transfer spare + */ + setup_ecc_for_xfer(denali, !raw_xfer, raw_xfer); + + /* copy buffer into DMA buffer */ + memcpy(denali->buf.buf, buf, mtd->writesize); + + if (raw_xfer) + { + /* transfer the data to the spare area */ + memcpy(denali->buf.buf + mtd->writesize, + chip->oob_poi, + mtd->oobsize); + } + + pci_dma_sync_single_for_device(pci_dev, addr, size, PCI_DMA_TODEVICE); + + clear_interrupts(denali); + denali_enable_dma(denali, true); + + denali_setup_dma(denali, DENALI_WRITE); + + /* wait for operation to complete */ + irq_status = wait_for_irq(denali, irq_mask); + + if (irq_status == 0) + { + printk(KERN_ERR "timeout on write_page (type = %d)\n", raw_xfer); + denali->status = + (irq_status & INTR_STATUS0__PROGRAM_FAIL) ? NAND_STATUS_FAIL : + PASS; + } + + denali_enable_dma(denali, false); + pci_dma_sync_single_for_cpu(pci_dev, addr, size, PCI_DMA_TODEVICE); +} + +/* NAND core entry points */ + +/* this is the callback that the NAND core calls to write a page. Since + writing a page with ECC or without is similar, all the work is done + by write_page above. */ +static void denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + /* for regular page writes, we let HW handle all the ECC + * data written to the device. */ + write_page(mtd, chip, buf, false); +} + +/* This is the callback that the NAND core calls to write a page without ECC. + raw access is similiar to ECC page writes, so all the work is done in the + write_page() function above. + */ +static void denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + /* for raw page writes, we want to disable ECC and simply write + whatever data is in the buffer. */ + write_page(mtd, chip, buf, true); +} + +static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return write_oob_data(mtd, chip->oob_poi, page); +} + +static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + read_oob_data(mtd, chip->oob_poi, page); + + return 0; /* notify NAND core to send command to + * NAND device. */ +} + +static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + struct pci_dev *pci_dev = denali->dev; + + dma_addr_t addr = denali->buf.dma_buf; + size_t size = denali->mtd.writesize + denali->mtd.oobsize; + + uint32_t irq_status = 0; + uint32_t irq_mask = INTR_STATUS0__ECC_TRANSACTION_DONE | + INTR_STATUS0__ECC_ERR; + bool check_erased_page = false; + + setup_ecc_for_xfer(denali, true, false); + + denali_enable_dma(denali, true); + pci_dma_sync_single_for_device(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + + clear_interrupts(denali); + denali_setup_dma(denali, DENALI_READ); + + /* wait for operation to complete */ + irq_status = wait_for_irq(denali, irq_mask); + + pci_dma_sync_single_for_cpu(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + + memcpy(buf, denali->buf.buf, mtd->writesize); + + check_erased_page = handle_ecc(denali, buf, chip->oob_poi, irq_status); + denali_enable_dma(denali, false); + + if (check_erased_page) + { + read_oob_data(&denali->mtd, chip->oob_poi, denali->page); + + /* check ECC failures that may have occurred on erased pages */ + if (check_erased_page) + { + if (!is_erased(buf, denali->mtd.writesize)) + { + denali->mtd.ecc_stats.failed++; + } + if (!is_erased(buf, denali->mtd.oobsize)) + { + denali->mtd.ecc_stats.failed++; + } + } + } + return 0; +} + +static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + struct pci_dev *pci_dev = denali->dev; + + dma_addr_t addr = denali->buf.dma_buf; + size_t size = denali->mtd.writesize + denali->mtd.oobsize; + + uint32_t irq_status = 0; + uint32_t irq_mask = INTR_STATUS0__DMA_CMD_COMP; + + setup_ecc_for_xfer(denali, false, true); + denali_enable_dma(denali, true); + + pci_dma_sync_single_for_device(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + + clear_interrupts(denali); + denali_setup_dma(denali, DENALI_READ); + + /* wait for operation to complete */ + irq_status = wait_for_irq(denali, irq_mask); + + pci_dma_sync_single_for_cpu(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + + denali_enable_dma(denali, false); + + memcpy(buf, denali->buf.buf, mtd->writesize); + memcpy(chip->oob_poi, denali->buf.buf + mtd->writesize, mtd->oobsize); + + return 0; +} + +static uint8_t denali_read_byte(struct mtd_info *mtd) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + uint8_t result = 0xff; + + if (denali->buf.head < denali->buf.tail) + { + result = denali->buf.buf[denali->buf.head++]; + } + +#if DEBUG_DENALI + printk("read byte -> 0x%02x\n", result); +#endif + return result; +} + +static void denali_select_chip(struct mtd_info *mtd, int chip) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); +#if DEBUG_DENALI + printk("denali select chip %d\n", chip); +#endif + spin_lock_irq(&denali->irq_lock); + denali->flash_bank = chip; + spin_unlock_irq(&denali->irq_lock); +} + +static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + int status = denali->status; + denali->status = 0; + +#if DEBUG_DENALI + printk("waitfunc %d\n", status); +#endif + return status; +} + +static void denali_erase(struct mtd_info *mtd, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + + uint32_t cmd = 0x0, irq_status = 0; + +#if DEBUG_DENALI + printk("erase page: %d\n", page); +#endif + /* clear interrupts */ + clear_interrupts(denali); + + /* setup page read request for access type */ + cmd = MODE_10 | BANK(denali->flash_bank) | page; + index_addr(denali, (uint32_t)cmd, 0x1); + + /* wait for erase to complete or failure to occur */ + irq_status = wait_for_irq(denali, INTR_STATUS0__ERASE_COMP | + INTR_STATUS0__ERASE_FAIL); + + denali->status = (irq_status & INTR_STATUS0__ERASE_FAIL) ? NAND_STATUS_FAIL : + PASS; +} + +static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, + int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + +#if DEBUG_DENALI + printk("cmdfunc: 0x%x %d %d\n", cmd, col, page); +#endif + switch (cmd) + { + case NAND_CMD_PAGEPROG: + break; + case NAND_CMD_STATUS: + read_status(denali); + break; + case NAND_CMD_READID: + reset_buf(denali); + if (denali->flash_bank < denali->total_used_banks) + { + /* write manufacturer information into nand + buffer for NAND subsystem to fetch. + */ + write_byte_to_buf(denali, denali->dev_info.wDeviceMaker); + write_byte_to_buf(denali, denali->dev_info.wDeviceID); + write_byte_to_buf(denali, denali->dev_info.bDeviceParam0); + write_byte_to_buf(denali, denali->dev_info.bDeviceParam1); + write_byte_to_buf(denali, denali->dev_info.bDeviceParam2); + } + else + { + int i; + for (i = 0; i < 5; i++) + write_byte_to_buf(denali, 0xff); + } + break; + case NAND_CMD_READ0: + case NAND_CMD_SEQIN: + denali->page = page; + break; + case NAND_CMD_RESET: + reset_bank(denali); + break; + case NAND_CMD_READOOB: + /* TODO: Read OOB data */ + break; + default: + printk(KERN_ERR ": unsupported command received 0x%x\n", cmd); + break; + } +} + +/* stubs for ECC functions not used by the NAND core */ +static int denali_ecc_calculate(struct mtd_info *mtd, const uint8_t *data, + uint8_t *ecc_code) +{ + printk(KERN_ERR "denali_ecc_calculate called unexpectedly\n"); + BUG(); + return -EIO; +} + +static int denali_ecc_correct(struct mtd_info *mtd, uint8_t *data, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + printk(KERN_ERR "denali_ecc_correct called unexpectedly\n"); + BUG(); + return -EIO; +} + +static void denali_ecc_hwctl(struct mtd_info *mtd, int mode) +{ + printk(KERN_ERR "denali_ecc_hwctl called unexpectedly\n"); + BUG(); +} +/* end NAND core entry points */ + +/* Initialization code to bring the device up to a known good state */ +static void denali_hw_init(struct denali_nand_info *denali) +{ + denali_irq_init(denali); + NAND_Flash_Reset(denali); + denali_write32(0x0F, denali->flash_reg + RB_PIN_ENABLED); + denali_write32(CHIP_EN_DONT_CARE__FLAG, denali->flash_reg + CHIP_ENABLE_DONT_CARE); + + denali_write32(0x0, denali->flash_reg + SPARE_AREA_SKIP_BYTES); + denali_write32(0xffff, denali->flash_reg + SPARE_AREA_MARKER); + + /* Should set value for these registers when init */ + denali_write32(0, denali->flash_reg + TWO_ROW_ADDR_CYCLES); + denali_write32(1, denali->flash_reg + ECC_ENABLE); +} + +/* ECC layout for SLC devices. Denali spec indicates SLC fixed at 4 bytes */ +#define ECC_BYTES_SLC 4 * (2048 / ECC_SECTOR_SIZE) +static struct nand_ecclayout nand_oob_slc = { + .eccbytes = 4, + .eccpos = { 0, 1, 2, 3 }, /* not used */ + .oobfree = {{ + .offset = ECC_BYTES_SLC, + .length = 64 - ECC_BYTES_SLC + }} +}; + +#define ECC_BYTES_MLC 14 * (2048 / ECC_SECTOR_SIZE) +static struct nand_ecclayout nand_oob_mlc_14bit = { + .eccbytes = 14, + .eccpos = { 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13 }, /* not used */ + .oobfree = {{ + .offset = ECC_BYTES_MLC, + .length = 64 - ECC_BYTES_MLC + }} +}; + +static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + +/* initalize driver data structures */ +void denali_drv_init(struct denali_nand_info *denali) +{ + denali->idx = 0; + + /* setup interrupt handler */ + /* the completion object will be used to notify + * the callee that the interrupt is done */ + init_completion(&denali->complete); + + /* the spinlock will be used to synchronize the ISR + * with any element that might be access shared + * data (interrupt status) */ + spin_lock_init(&denali->irq_lock); + + /* indicate that MTD has not selected a valid bank yet */ + denali->flash_bank = CHIP_SELECT_INVALID; + + /* initialize our irq_status variable to indicate no interrupts */ + denali->irq_status = 0; +} + +/* driver entry point */ +static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int ret = -ENODEV; + resource_size_t csr_base, mem_base; + unsigned long csr_len, mem_len; + struct denali_nand_info *denali; + + nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + denali = kzalloc(sizeof(*denali), GFP_KERNEL); + if (!denali) + return -ENOMEM; + + ret = pci_enable_device(dev); + if (ret) { + printk(KERN_ERR "Spectra: pci_enable_device failed.\n"); + goto failed_enable; + } + + if (id->driver_data == INTEL_CE4100) { + /* Due to a silicon limitation, we can only support + * ONFI timing mode 1 and below. + */ + if (onfi_timing_mode < -1 || onfi_timing_mode > 1) + { + printk("Intel CE4100 only supports ONFI timing mode 1 " + "or below\n"); + ret = -EINVAL; + goto failed_enable; + } + denali->platform = INTEL_CE4100; + mem_base = pci_resource_start(dev, 0); + mem_len = pci_resource_len(dev, 1); + csr_base = pci_resource_start(dev, 1); + csr_len = pci_resource_len(dev, 1); + } else { + denali->platform = INTEL_MRST; + csr_base = pci_resource_start(dev, 0); + csr_len = pci_resource_start(dev, 0); + mem_base = pci_resource_start(dev, 1); + mem_len = pci_resource_len(dev, 1); + if (!mem_len) { + mem_base = csr_base + csr_len; + mem_len = csr_len; + nand_dbg_print(NAND_DBG_WARN, + "Spectra: No second BAR for PCI device; assuming %08Lx\n", + (uint64_t)csr_base); + } + } + + /* Is 32-bit DMA supported? */ + ret = pci_set_dma_mask(dev, DMA_BIT_MASK(32)); + + if (ret) + { + printk(KERN_ERR "Spectra: no usable DMA configuration\n"); + goto failed_enable; + } + denali->buf.dma_buf = pci_map_single(dev, denali->buf.buf, DENALI_BUF_SIZE, + PCI_DMA_BIDIRECTIONAL); + + if (pci_dma_mapping_error(dev, denali->buf.dma_buf)) + { + printk(KERN_ERR "Spectra: failed to map DMA buffer\n"); + goto failed_enable; + } + + pci_set_master(dev); + denali->dev = dev; + + ret = pci_request_regions(dev, DENALI_NAND_NAME); + if (ret) { + printk(KERN_ERR "Spectra: Unable to request memory regions\n"); + goto failed_req_csr; + } + + denali->flash_reg = ioremap_nocache(csr_base, csr_len); + if (!denali->flash_reg) { + printk(KERN_ERR "Spectra: Unable to remap memory region\n"); + ret = -ENOMEM; + goto failed_remap_csr; + } + nand_dbg_print(NAND_DBG_DEBUG, "Spectra: CSR 0x%08Lx -> 0x%p (0x%lx)\n", + (uint64_t)csr_base, denali->flash_reg, csr_len); + + denali->flash_mem = ioremap_nocache(mem_base, mem_len); + if (!denali->flash_mem) { + printk(KERN_ERR "Spectra: ioremap_nocache failed!"); + iounmap(denali->flash_reg); + ret = -ENOMEM; + goto failed_remap_csr; + } + + nand_dbg_print(NAND_DBG_WARN, + "Spectra: Remapped flash base address: " + "0x%p, len: %ld\n", + denali->flash_mem, csr_len); + + denali_hw_init(denali); + denali_drv_init(denali); + + nand_dbg_print(NAND_DBG_DEBUG, "Spectra: IRQ %d\n", dev->irq); + if (request_irq(dev->irq, denali_isr, IRQF_SHARED, + DENALI_NAND_NAME, denali)) { + printk(KERN_ERR "Spectra: Unable to allocate IRQ\n"); + ret = -ENODEV; + goto failed_request_irq; + } + + /* now that our ISR is registered, we can enable interrupts */ + NAND_LLD_Enable_Disable_Interrupts(denali, true); + + pci_set_drvdata(dev, denali); + + NAND_Read_Device_ID(denali); + + /* MTD supported page sizes vary by kernel. We validate our + kernel supports the device here. + */ + if (denali->dev_info.wPageSize > NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE) + { + ret = -ENODEV; + printk(KERN_ERR "Spectra: device size not supported by this " + "version of MTD."); + goto failed_nand; + } + + nand_dbg_print(NAND_DBG_DEBUG, "Dump timing register values:" + "acc_clks: %d, re_2_we: %d, we_2_re: %d," + "addr_2_data: %d, rdwr_en_lo_cnt: %d, " + "rdwr_en_hi_cnt: %d, cs_setup_cnt: %d\n", + ioread32(denali->flash_reg + ACC_CLKS), + ioread32(denali->flash_reg + RE_2_WE), + ioread32(denali->flash_reg + WE_2_RE), + ioread32(denali->flash_reg + ADDR_2_DATA), + ioread32(denali->flash_reg + RDWR_EN_LO_CNT), + ioread32(denali->flash_reg + RDWR_EN_HI_CNT), + ioread32(denali->flash_reg + CS_SETUP_CNT)); + + denali->mtd.name = "Denali NAND"; + denali->mtd.owner = THIS_MODULE; + denali->mtd.priv = &denali->nand; + + /* register the driver with the NAND core subsystem */ + denali->nand.select_chip = denali_select_chip; + denali->nand.cmdfunc = denali_cmdfunc; + denali->nand.read_byte = denali_read_byte; + denali->nand.waitfunc = denali_waitfunc; + + /* scan for NAND devices attached to the controller + * this is the first stage in a two step process to register + * with the nand subsystem */ + if (nand_scan_ident(&denali->mtd, LLD_MAX_FLASH_BANKS, NULL)) + { + ret = -ENXIO; + goto failed_nand; + } + + /* second stage of the NAND scan + * this stage requires information regarding ECC and + * bad block management. */ + + /* Bad block management */ + denali->nand.bbt_td = &bbt_main_descr; + denali->nand.bbt_md = &bbt_mirror_descr; + + /* skip the scan for now until we have OOB read and write support */ + denali->nand.options |= NAND_USE_FLASH_BBT | NAND_SKIP_BBTSCAN; + denali->nand.ecc.mode = NAND_ECC_HW_SYNDROME; + + if (denali->dev_info.MLCDevice) + { + denali->nand.ecc.layout = &nand_oob_mlc_14bit; + denali->nand.ecc.bytes = ECC_BYTES_MLC; + } + else /* SLC */ + { + denali->nand.ecc.layout = &nand_oob_slc; + denali->nand.ecc.bytes = ECC_BYTES_SLC; + } + + /* These functions are required by the NAND core framework, otherwise, + the NAND core will assert. However, we don't need them, so we'll stub + them out. */ + denali->nand.ecc.calculate = denali_ecc_calculate; + denali->nand.ecc.correct = denali_ecc_correct; + denali->nand.ecc.hwctl = denali_ecc_hwctl; + + /* override the default read operations */ + denali->nand.ecc.size = denali->mtd.writesize; + denali->nand.ecc.read_page = denali_read_page; + denali->nand.ecc.read_page_raw = denali_read_page_raw; + denali->nand.ecc.write_page = denali_write_page; + denali->nand.ecc.write_page_raw = denali_write_page_raw; + denali->nand.ecc.read_oob = denali_read_oob; + denali->nand.ecc.write_oob = denali_write_oob; + denali->nand.erase_cmd = denali_erase; + + if (nand_scan_tail(&denali->mtd)) + { + ret = -ENXIO; + goto failed_nand; + } + + ret = add_mtd_device(&denali->mtd); + if (ret) { + printk(KERN_ERR "Spectra: Failed to register MTD device: %d\n", ret); + goto failed_nand; + } + return 0; + + failed_nand: + denali_irq_cleanup(dev->irq, denali); + failed_request_irq: + iounmap(denali->flash_reg); + iounmap(denali->flash_mem); + failed_remap_csr: + pci_release_regions(dev); + failed_req_csr: + pci_unmap_single(dev, denali->buf.dma_buf, DENALI_BUF_SIZE, + PCI_DMA_BIDIRECTIONAL); + failed_enable: + kfree(denali); + return ret; +} + +/* driver exit point */ +static void denali_pci_remove(struct pci_dev *dev) +{ + struct denali_nand_info *denali = pci_get_drvdata(dev); + + nand_dbg_print(NAND_DBG_WARN, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + nand_release(&denali->mtd); + del_mtd_device(&denali->mtd); + + denali_irq_cleanup(dev->irq, denali); + + iounmap(denali->flash_reg); + iounmap(denali->flash_mem); + pci_release_regions(dev); + pci_disable_device(dev); + pci_unmap_single(dev, denali->buf.dma_buf, DENALI_BUF_SIZE, + PCI_DMA_BIDIRECTIONAL); + pci_set_drvdata(dev, NULL); + kfree(denali); +} + +MODULE_DEVICE_TABLE(pci, denali_pci_ids); + +static struct pci_driver denali_pci_driver = { + .name = DENALI_NAND_NAME, + .id_table = denali_pci_ids, + .probe = denali_pci_probe, + .remove = denali_pci_remove, +}; + +static int __devinit denali_init(void) +{ + printk(KERN_INFO "Spectra MTD driver built on %s @ %s\n", __DATE__, __TIME__); + return pci_register_driver(&denali_pci_driver); +} + +/* Free memory */ +static void __devexit denali_exit(void) +{ + pci_unregister_driver(&denali_pci_driver); +} + +module_init(denali_init); +module_exit(denali_exit); diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h new file mode 100644 index 0000000..422a29a --- /dev/null +++ b/drivers/mtd/nand/denali.h @@ -0,0 +1,816 @@ +/* + * NAND Flash Controller Device Driver + * Copyright (c) 2009 - 2010, Intel Corporation and its suppliers. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/mtd/nand.h> + +#define DEVICE_RESET 0x0 +#define DEVICE_RESET__BANK0 0x0001 +#define DEVICE_RESET__BANK1 0x0002 +#define DEVICE_RESET__BANK2 0x0004 +#define DEVICE_RESET__BANK3 0x0008 + +#define TRANSFER_SPARE_REG 0x10 +#define TRANSFER_SPARE_REG__FLAG 0x0001 + +#define LOAD_WAIT_CNT 0x20 +#define LOAD_WAIT_CNT__VALUE 0xffff + +#define PROGRAM_WAIT_CNT 0x30 +#define PROGRAM_WAIT_CNT__VALUE 0xffff + +#define ERASE_WAIT_CNT 0x40 +#define ERASE_WAIT_CNT__VALUE 0xffff + +#define INT_MON_CYCCNT 0x50 +#define INT_MON_CYCCNT__VALUE 0xffff + +#define RB_PIN_ENABLED 0x60 +#define RB_PIN_ENABLED__BANK0 0x0001 +#define RB_PIN_ENABLED__BANK1 0x0002 +#define RB_PIN_ENABLED__BANK2 0x0004 +#define RB_PIN_ENABLED__BANK3 0x0008 + +#define MULTIPLANE_OPERATION 0x70 +#define MULTIPLANE_OPERATION__FLAG 0x0001 + +#define MULTIPLANE_READ_ENABLE 0x80 +#define MULTIPLANE_READ_ENABLE__FLAG 0x0001 + +#define COPYBACK_DISABLE 0x90 +#define COPYBACK_DISABLE__FLAG 0x0001 + +#define CACHE_WRITE_ENABLE 0xa0 +#define CACHE_WRITE_ENABLE__FLAG 0x0001 + +#define CACHE_READ_ENABLE 0xb0 +#define CACHE_READ_ENABLE__FLAG 0x0001 + +#define PREFETCH_MODE 0xc0 +#define PREFETCH_MODE__PREFETCH_EN 0x0001 +#define PREFETCH_MODE__PREFETCH_BURST_LENGTH 0xfff0 + +#define CHIP_ENABLE_DONT_CARE 0xd0 +#define CHIP_EN_DONT_CARE__FLAG 0x01 + +#define ECC_ENABLE 0xe0 +#define ECC_ENABLE__FLAG 0x0001 + +#define GLOBAL_INT_ENABLE 0xf0 +#define GLOBAL_INT_EN_FLAG 0x01 + +#define WE_2_RE 0x100 +#define WE_2_RE__VALUE 0x003f + +#define ADDR_2_DATA 0x110 +#define ADDR_2_DATA__VALUE 0x003f + +#define RE_2_WE 0x120 +#define RE_2_WE__VALUE 0x003f + +#define ACC_CLKS 0x130 +#define ACC_CLKS__VALUE 0x000f + +#define NUMBER_OF_PLANES 0x140 +#define NUMBER_OF_PLANES__VALUE 0x0007 + +#define PAGES_PER_BLOCK 0x150 +#define PAGES_PER_BLOCK__VALUE 0xffff + +#define DEVICE_WIDTH 0x160 +#define DEVICE_WIDTH__VALUE 0x0003 + +#define DEVICE_MAIN_AREA_SIZE 0x170 +#define DEVICE_MAIN_AREA_SIZE__VALUE 0xffff + +#define DEVICE_SPARE_AREA_SIZE 0x180 +#define DEVICE_SPARE_AREA_SIZE__VALUE 0xffff + +#define TWO_ROW_ADDR_CYCLES 0x190 +#define TWO_ROW_ADDR_CYCLES__FLAG 0x0001 + +#define MULTIPLANE_ADDR_RESTRICT 0x1a0 +#define MULTIPLANE_ADDR_RESTRICT__FLAG 0x0001 + +#define ECC_CORRECTION 0x1b0 +#define ECC_CORRECTION__VALUE 0x001f + +#define READ_MODE 0x1c0 +#define READ_MODE__VALUE 0x000f + +#define WRITE_MODE 0x1d0 +#define WRITE_MODE__VALUE 0x000f + +#define COPYBACK_MODE 0x1e0 +#define COPYBACK_MODE__VALUE 0x000f + +#define RDWR_EN_LO_CNT 0x1f0 +#define RDWR_EN_LO_CNT__VALUE 0x001f + +#define RDWR_EN_HI_CNT 0x200 +#define RDWR_EN_HI_CNT__VALUE 0x001f + +#define MAX_RD_DELAY 0x210 +#define MAX_RD_DELAY__VALUE 0x000f + +#define CS_SETUP_CNT 0x220 +#define CS_SETUP_CNT__VALUE 0x001f + +#define SPARE_AREA_SKIP_BYTES 0x230 +#define SPARE_AREA_SKIP_BYTES__VALUE 0x003f + +#define SPARE_AREA_MARKER 0x240 +#define SPARE_AREA_MARKER__VALUE 0xffff + +#define DEVICES_CONNECTED 0x250 +#define DEVICES_CONNECTED__VALUE 0x0007 + +#define DIE_MASK 0x260 +#define DIE_MASK__VALUE 0x00ff + +#define FIRST_BLOCK_OF_NEXT_PLANE 0x270 +#define FIRST_BLOCK_OF_NEXT_PLANE__VALUE 0xffff + +#define WRITE_PROTECT 0x280 +#define WRITE_PROTECT__FLAG 0x0001 + +#define RE_2_RE 0x290 +#define RE_2_RE__VALUE 0x003f + +#define MANUFACTURER_ID 0x300 +#define MANUFACTURER_ID__VALUE 0x00ff + +#define DEVICE_ID 0x310 +#define DEVICE_ID__VALUE 0x00ff + +#define DEVICE_PARAM_0 0x320 +#define DEVICE_PARAM_0__VALUE 0x00ff + +#define DEVICE_PARAM_1 0x330 +#define DEVICE_PARAM_1__VALUE 0x00ff + +#define DEVICE_PARAM_2 0x340 +#define DEVICE_PARAM_2__VALUE 0x00ff + +#define LOGICAL_PAGE_DATA_SIZE 0x350 +#define LOGICAL_PAGE_DATA_SIZE__VALUE 0xffff + +#define LOGICAL_PAGE_SPARE_SIZE 0x360 +#define LOGICAL_PAGE_SPARE_SIZE__VALUE 0xffff + +#define REVISION 0x370 +#define REVISION__VALUE 0xffff + +#define ONFI_DEVICE_FEATURES 0x380 +#define ONFI_DEVICE_FEATURES__VALUE 0x003f + +#define ONFI_OPTIONAL_COMMANDS 0x390 +#define ONFI_OPTIONAL_COMMANDS__VALUE 0x003f + +#define ONFI_TIMING_MODE 0x3a0 +#define ONFI_TIMING_MODE__VALUE 0x003f + +#define ONFI_PGM_CACHE_TIMING_MODE 0x3b0 +#define ONFI_PGM_CACHE_TIMING_MODE__VALUE 0x003f + +#define ONFI_DEVICE_NO_OF_LUNS 0x3c0 +#define ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS 0x00ff +#define ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE 0x0100 + +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L 0x3d0 +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE 0xffff + +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U 0x3e0 +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE 0xffff + +#define FEATURES 0x3f0 +#define FEATURES__N_BANKS 0x0003 +#define FEATURES__ECC_MAX_ERR 0x003c +#define FEATURES__DMA 0x0040 +#define FEATURES__CMD_DMA 0x0080 +#define FEATURES__PARTITION 0x0100 +#define FEATURES__XDMA_SIDEBAND 0x0200 +#define FEATURES__GPREG 0x0400 +#define FEATURES__INDEX_ADDR 0x0800 + +#define TRANSFER_MODE 0x400 +#define TRANSFER_MODE__VALUE 0x0003 + +#define INTR_STATUS0 0x410 +#define INTR_STATUS0__ECC_TRANSACTION_DONE 0x0001 +#define INTR_STATUS0__ECC_ERR 0x0002 +#define INTR_STATUS0__DMA_CMD_COMP 0x0004 +#define INTR_STATUS0__TIME_OUT 0x0008 +#define INTR_STATUS0__PROGRAM_FAIL 0x0010 +#define INTR_STATUS0__ERASE_FAIL 0x0020 +#define INTR_STATUS0__LOAD_COMP 0x0040 +#define INTR_STATUS0__PROGRAM_COMP 0x0080 +#define INTR_STATUS0__ERASE_COMP 0x0100 +#define INTR_STATUS0__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_STATUS0__LOCKED_BLK 0x0400 +#define INTR_STATUS0__UNSUP_CMD 0x0800 +#define INTR_STATUS0__INT_ACT 0x1000 +#define INTR_STATUS0__RST_COMP 0x2000 +#define INTR_STATUS0__PIPE_CMD_ERR 0x4000 +#define INTR_STATUS0__PAGE_XFER_INC 0x8000 + +#define INTR_EN0 0x420 +#define INTR_EN0__ECC_TRANSACTION_DONE 0x0001 +#define INTR_EN0__ECC_ERR 0x0002 +#define INTR_EN0__DMA_CMD_COMP 0x0004 +#define INTR_EN0__TIME_OUT 0x0008 +#define INTR_EN0__PROGRAM_FAIL 0x0010 +#define INTR_EN0__ERASE_FAIL 0x0020 +#define INTR_EN0__LOAD_COMP 0x0040 +#define INTR_EN0__PROGRAM_COMP 0x0080 +#define INTR_EN0__ERASE_COMP 0x0100 +#define INTR_EN0__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_EN0__LOCKED_BLK 0x0400 +#define INTR_EN0__UNSUP_CMD 0x0800 +#define INTR_EN0__INT_ACT 0x1000 +#define INTR_EN0__RST_COMP 0x2000 +#define INTR_EN0__PIPE_CMD_ERR 0x4000 +#define INTR_EN0__PAGE_XFER_INC 0x8000 + +#define PAGE_CNT0 0x430 +#define PAGE_CNT0__VALUE 0x00ff + +#define ERR_PAGE_ADDR0 0x440 +#define ERR_PAGE_ADDR0__VALUE 0xffff + +#define ERR_BLOCK_ADDR0 0x450 +#define ERR_BLOCK_ADDR0__VALUE 0xffff + +#define INTR_STATUS1 0x460 +#define INTR_STATUS1__ECC_TRANSACTION_DONE 0x0001 +#define INTR_STATUS1__ECC_ERR 0x0002 +#define INTR_STATUS1__DMA_CMD_COMP 0x0004 +#define INTR_STATUS1__TIME_OUT 0x0008 +#define INTR_STATUS1__PROGRAM_FAIL 0x0010 +#define INTR_STATUS1__ERASE_FAIL 0x0020 +#define INTR_STATUS1__LOAD_COMP 0x0040 +#define INTR_STATUS1__PROGRAM_COMP 0x0080 +#define INTR_STATUS1__ERASE_COMP 0x0100 +#define INTR_STATUS1__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_STATUS1__LOCKED_BLK 0x0400 +#define INTR_STATUS1__UNSUP_CMD 0x0800 +#define INTR_STATUS1__INT_ACT 0x1000 +#define INTR_STATUS1__RST_COMP 0x2000 +#define INTR_STATUS1__PIPE_CMD_ERR 0x4000 +#define INTR_STATUS1__PAGE_XFER_INC 0x8000 + +#define INTR_EN1 0x470 +#define INTR_EN1__ECC_TRANSACTION_DONE 0x0001 +#define INTR_EN1__ECC_ERR 0x0002 +#define INTR_EN1__DMA_CMD_COMP 0x0004 +#define INTR_EN1__TIME_OUT 0x0008 +#define INTR_EN1__PROGRAM_FAIL 0x0010 +#define INTR_EN1__ERASE_FAIL 0x0020 +#define INTR_EN1__LOAD_COMP 0x0040 +#define INTR_EN1__PROGRAM_COMP 0x0080 +#define INTR_EN1__ERASE_COMP 0x0100 +#define INTR_EN1__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_EN1__LOCKED_BLK 0x0400 +#define INTR_EN1__UNSUP_CMD 0x0800 +#define INTR_EN1__INT_ACT 0x1000 +#define INTR_EN1__RST_COMP 0x2000 +#define INTR_EN1__PIPE_CMD_ERR 0x4000 +#define INTR_EN1__PAGE_XFER_INC 0x8000 + +#define PAGE_CNT1 0x480 +#define PAGE_CNT1__VALUE 0x00ff + +#define ERR_PAGE_ADDR1 0x490 +#define ERR_PAGE_ADDR1__VALUE 0xffff + +#define ERR_BLOCK_ADDR1 0x4a0 +#define ERR_BLOCK_ADDR1__VALUE 0xffff + +#define INTR_STATUS2 0x4b0 +#define INTR_STATUS2__ECC_TRANSACTION_DONE 0x0001 +#define INTR_STATUS2__ECC_ERR 0x0002 +#define INTR_STATUS2__DMA_CMD_COMP 0x0004 +#define INTR_STATUS2__TIME_OUT 0x0008 +#define INTR_STATUS2__PROGRAM_FAIL 0x0010 +#define INTR_STATUS2__ERASE_FAIL 0x0020 +#define INTR_STATUS2__LOAD_COMP 0x0040 +#define INTR_STATUS2__PROGRAM_COMP 0x0080 +#define INTR_STATUS2__ERASE_COMP 0x0100 +#define INTR_STATUS2__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_STATUS2__LOCKED_BLK 0x0400 +#define INTR_STATUS2__UNSUP_CMD 0x0800 +#define INTR_STATUS2__INT_ACT 0x1000 +#define INTR_STATUS2__RST_COMP 0x2000 +#define INTR_STATUS2__PIPE_CMD_ERR 0x4000 +#define INTR_STATUS2__PAGE_XFER_INC 0x8000 + +#define INTR_EN2 0x4c0 +#define INTR_EN2__ECC_TRANSACTION_DONE 0x0001 +#define INTR_EN2__ECC_ERR 0x0002 +#define INTR_EN2__DMA_CMD_COMP 0x0004 +#define INTR_EN2__TIME_OUT 0x0008 +#define INTR_EN2__PROGRAM_FAIL 0x0010 +#define INTR_EN2__ERASE_FAIL 0x0020 +#define INTR_EN2__LOAD_COMP 0x0040 +#define INTR_EN2__PROGRAM_COMP 0x0080 +#define INTR_EN2__ERASE_COMP 0x0100 +#define INTR_EN2__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_EN2__LOCKED_BLK 0x0400 +#define INTR_EN2__UNSUP_CMD 0x0800 +#define INTR_EN2__INT_ACT 0x1000 +#define INTR_EN2__RST_COMP 0x2000 +#define INTR_EN2__PIPE_CMD_ERR 0x4000 +#define INTR_EN2__PAGE_XFER_INC 0x8000 + +#define PAGE_CNT2 0x4d0 +#define PAGE_CNT2__VALUE 0x00ff + +#define ERR_PAGE_ADDR2 0x4e0 +#define ERR_PAGE_ADDR2__VALUE 0xffff + +#define ERR_BLOCK_ADDR2 0x4f0 +#define ERR_BLOCK_ADDR2__VALUE 0xffff + +#define INTR_STATUS3 0x500 +#define INTR_STATUS3__ECC_TRANSACTION_DONE 0x0001 +#define INTR_STATUS3__ECC_ERR 0x0002 +#define INTR_STATUS3__DMA_CMD_COMP 0x0004 +#define INTR_STATUS3__TIME_OUT 0x0008 +#define INTR_STATUS3__PROGRAM_FAIL 0x0010 +#define INTR_STATUS3__ERASE_FAIL 0x0020 +#define INTR_STATUS3__LOAD_COMP 0x0040 +#define INTR_STATUS3__PROGRAM_COMP 0x0080 +#define INTR_STATUS3__ERASE_COMP 0x0100 +#define INTR_STATUS3__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_STATUS3__LOCKED_BLK 0x0400 +#define INTR_STATUS3__UNSUP_CMD 0x0800 +#define INTR_STATUS3__INT_ACT 0x1000 +#define INTR_STATUS3__RST_COMP 0x2000 +#define INTR_STATUS3__PIPE_CMD_ERR 0x4000 +#define INTR_STATUS3__PAGE_XFER_INC 0x8000 + +#define INTR_EN3 0x510 +#define INTR_EN3__ECC_TRANSACTION_DONE 0x0001 +#define INTR_EN3__ECC_ERR 0x0002 +#define INTR_EN3__DMA_CMD_COMP 0x0004 +#define INTR_EN3__TIME_OUT 0x0008 +#define INTR_EN3__PROGRAM_FAIL 0x0010 +#define INTR_EN3__ERASE_FAIL 0x0020 +#define INTR_EN3__LOAD_COMP 0x0040 +#define INTR_EN3__PROGRAM_COMP 0x0080 +#define INTR_EN3__ERASE_COMP 0x0100 +#define INTR_EN3__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_EN3__LOCKED_BLK 0x0400 +#define INTR_EN3__UNSUP_CMD 0x0800 +#define INTR_EN3__INT_ACT 0x1000 +#define INTR_EN3__RST_COMP 0x2000 +#define INTR_EN3__PIPE_CMD_ERR 0x4000 +#define INTR_EN3__PAGE_XFER_INC 0x8000 + +#define PAGE_CNT3 0x520 +#define PAGE_CNT3__VALUE 0x00ff + +#define ERR_PAGE_ADDR3 0x530 +#define ERR_PAGE_ADDR3__VALUE 0xffff + +#define ERR_BLOCK_ADDR3 0x540 +#define ERR_BLOCK_ADDR3__VALUE 0xffff + +#define DATA_INTR 0x550 +#define DATA_INTR__WRITE_SPACE_AV 0x0001 +#define DATA_INTR__READ_DATA_AV 0x0002 + +#define DATA_INTR_EN 0x560 +#define DATA_INTR_EN__WRITE_SPACE_AV 0x0001 +#define DATA_INTR_EN__READ_DATA_AV 0x0002 + +#define GPREG_0 0x570 +#define GPREG_0__VALUE 0xffff + +#define GPREG_1 0x580 +#define GPREG_1__VALUE 0xffff + +#define GPREG_2 0x590 +#define GPREG_2__VALUE 0xffff + +#define GPREG_3 0x5a0 +#define GPREG_3__VALUE 0xffff + +#define ECC_THRESHOLD 0x600 +#define ECC_THRESHOLD__VALUE 0x03ff + +#define ECC_ERROR_BLOCK_ADDRESS 0x610 +#define ECC_ERROR_BLOCK_ADDRESS__VALUE 0xffff + +#define ECC_ERROR_PAGE_ADDRESS 0x620 +#define ECC_ERROR_PAGE_ADDRESS__VALUE 0x0fff +#define ECC_ERROR_PAGE_ADDRESS__BANK 0xf000 + +#define ECC_ERROR_ADDRESS 0x630 +#define ECC_ERROR_ADDRESS__OFFSET 0x0fff +#define ECC_ERROR_ADDRESS__SECTOR_NR 0xf000 + +#define ERR_CORRECTION_INFO 0x640 +#define ERR_CORRECTION_INFO__BYTEMASK 0x00ff +#define ERR_CORRECTION_INFO__DEVICE_NR 0x0f00 +#define ERR_CORRECTION_INFO__ERROR_TYPE 0x4000 +#define ERR_CORRECTION_INFO__LAST_ERR_INFO 0x8000 + +#define DMA_ENABLE 0x700 +#define DMA_ENABLE__FLAG 0x0001 + +#define IGNORE_ECC_DONE 0x710 +#define IGNORE_ECC_DONE__FLAG 0x0001 + +#define DMA_INTR 0x720 +#define DMA_INTR__TARGET_ERROR 0x0001 +#define DMA_INTR__DESC_COMP_CHANNEL0 0x0002 +#define DMA_INTR__DESC_COMP_CHANNEL1 0x0004 +#define DMA_INTR__DESC_COMP_CHANNEL2 0x0008 +#define DMA_INTR__DESC_COMP_CHANNEL3 0x0010 +#define DMA_INTR__MEMCOPY_DESC_COMP 0x0020 + +#define DMA_INTR_EN 0x730 +#define DMA_INTR_EN__TARGET_ERROR 0x0001 +#define DMA_INTR_EN__DESC_COMP_CHANNEL0 0x0002 +#define DMA_INTR_EN__DESC_COMP_CHANNEL1 0x0004 +#define DMA_INTR_EN__DESC_COMP_CHANNEL2 0x0008 +#define DMA_INTR_EN__DESC_COMP_CHANNEL3 0x0010 +#define DMA_INTR_EN__MEMCOPY_DESC_COMP 0x0020 + +#define TARGET_ERR_ADDR_LO 0x740 +#define TARGET_ERR_ADDR_LO__VALUE 0xffff + +#define TARGET_ERR_ADDR_HI 0x750 +#define TARGET_ERR_ADDR_HI__VALUE 0xffff + +#define CHNL_ACTIVE 0x760 +#define CHNL_ACTIVE__CHANNEL0 0x0001 +#define CHNL_ACTIVE__CHANNEL1 0x0002 +#define CHNL_ACTIVE__CHANNEL2 0x0004 +#define CHNL_ACTIVE__CHANNEL3 0x0008 + +#define ACTIVE_SRC_ID 0x800 +#define ACTIVE_SRC_ID__VALUE 0x00ff + +#define PTN_INTR 0x810 +#define PTN_INTR__CONFIG_ERROR 0x0001 +#define PTN_INTR__ACCESS_ERROR_BANK0 0x0002 +#define PTN_INTR__ACCESS_ERROR_BANK1 0x0004 +#define PTN_INTR__ACCESS_ERROR_BANK2 0x0008 +#define PTN_INTR__ACCESS_ERROR_BANK3 0x0010 +#define PTN_INTR__REG_ACCESS_ERROR 0x0020 + +#define PTN_INTR_EN 0x820 +#define PTN_INTR_EN__CONFIG_ERROR 0x0001 +#define PTN_INTR_EN__ACCESS_ERROR_BANK0 0x0002 +#define PTN_INTR_EN__ACCESS_ERROR_BANK1 0x0004 +#define PTN_INTR_EN__ACCESS_ERROR_BANK2 0x0008 +#define PTN_INTR_EN__ACCESS_ERROR_BANK3 0x0010 +#define PTN_INTR_EN__REG_ACCESS_ERROR 0x0020 + +#define PERM_SRC_ID_0 0x830 +#define PERM_SRC_ID_0__SRCID 0x00ff +#define PERM_SRC_ID_0__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_0__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_0__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_0__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_0 0x840 +#define MIN_BLK_ADDR_0__VALUE 0xffff + +#define MAX_BLK_ADDR_0 0x850 +#define MAX_BLK_ADDR_0__VALUE 0xffff + +#define MIN_MAX_BANK_0 0x860 +#define MIN_MAX_BANK_0__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_0__MAX_VALUE 0x000c + +#define PERM_SRC_ID_1 0x870 +#define PERM_SRC_ID_1__SRCID 0x00ff +#define PERM_SRC_ID_1__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_1__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_1__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_1__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_1 0x880 +#define MIN_BLK_ADDR_1__VALUE 0xffff + +#define MAX_BLK_ADDR_1 0x890 +#define MAX_BLK_ADDR_1__VALUE 0xffff + +#define MIN_MAX_BANK_1 0x8a0 +#define MIN_MAX_BANK_1__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_1__MAX_VALUE 0x000c + +#define PERM_SRC_ID_2 0x8b0 +#define PERM_SRC_ID_2__SRCID 0x00ff +#define PERM_SRC_ID_2__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_2__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_2__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_2__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_2 0x8c0 +#define MIN_BLK_ADDR_2__VALUE 0xffff + +#define MAX_BLK_ADDR_2 0x8d0 +#define MAX_BLK_ADDR_2__VALUE 0xffff + +#define MIN_MAX_BANK_2 0x8e0 +#define MIN_MAX_BANK_2__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_2__MAX_VALUE 0x000c + +#define PERM_SRC_ID_3 0x8f0 +#define PERM_SRC_ID_3__SRCID 0x00ff +#define PERM_SRC_ID_3__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_3__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_3__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_3__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_3 0x900 +#define MIN_BLK_ADDR_3__VALUE 0xffff + +#define MAX_BLK_ADDR_3 0x910 +#define MAX_BLK_ADDR_3__VALUE 0xffff + +#define MIN_MAX_BANK_3 0x920 +#define MIN_MAX_BANK_3__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_3__MAX_VALUE 0x000c + +#define PERM_SRC_ID_4 0x930 +#define PERM_SRC_ID_4__SRCID 0x00ff +#define PERM_SRC_ID_4__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_4__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_4__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_4__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_4 0x940 +#define MIN_BLK_ADDR_4__VALUE 0xffff + +#define MAX_BLK_ADDR_4 0x950 +#define MAX_BLK_ADDR_4__VALUE 0xffff + +#define MIN_MAX_BANK_4 0x960 +#define MIN_MAX_BANK_4__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_4__MAX_VALUE 0x000c + +#define PERM_SRC_ID_5 0x970 +#define PERM_SRC_ID_5__SRCID 0x00ff +#define PERM_SRC_ID_5__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_5__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_5__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_5__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_5 0x980 +#define MIN_BLK_ADDR_5__VALUE 0xffff + +#define MAX_BLK_ADDR_5 0x990 +#define MAX_BLK_ADDR_5__VALUE 0xffff + +#define MIN_MAX_BANK_5 0x9a0 +#define MIN_MAX_BANK_5__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_5__MAX_VALUE 0x000c + +#define PERM_SRC_ID_6 0x9b0 +#define PERM_SRC_ID_6__SRCID 0x00ff +#define PERM_SRC_ID_6__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_6__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_6__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_6__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_6 0x9c0 +#define MIN_BLK_ADDR_6__VALUE 0xffff + +#define MAX_BLK_ADDR_6 0x9d0 +#define MAX_BLK_ADDR_6__VALUE 0xffff + +#define MIN_MAX_BANK_6 0x9e0 +#define MIN_MAX_BANK_6__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_6__MAX_VALUE 0x000c + +#define PERM_SRC_ID_7 0x9f0 +#define PERM_SRC_ID_7__SRCID 0x00ff +#define PERM_SRC_ID_7__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_7__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_7__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_7__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_7 0xa00 +#define MIN_BLK_ADDR_7__VALUE 0xffff + +#define MAX_BLK_ADDR_7 0xa10 +#define MAX_BLK_ADDR_7__VALUE 0xffff + +#define MIN_MAX_BANK_7 0xa20 +#define MIN_MAX_BANK_7__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_7__MAX_VALUE 0x000c + +/* flash.h */ +struct device_info_tag { + uint16_t wDeviceMaker; + uint16_t wDeviceID; + uint8_t bDeviceParam0; + uint8_t bDeviceParam1; + uint8_t bDeviceParam2; + uint32_t wDeviceType; + uint32_t wSpectraStartBlock; + uint32_t wSpectraEndBlock; + uint32_t wTotalBlocks; + uint16_t wPagesPerBlock; + uint16_t wPageSize; + uint16_t wPageDataSize; + uint16_t wPageSpareSize; + uint16_t wNumPageSpareFlag; + uint16_t wECCBytesPerSector; + uint32_t wBlockSize; + uint32_t wBlockDataSize; + uint32_t wDataBlockNum; + uint8_t bPlaneNum; + uint16_t wDeviceMainAreaSize; + uint16_t wDeviceSpareAreaSize; + uint16_t wDevicesConnected; + uint16_t wDeviceWidth; + uint16_t wHWRevision; + uint16_t wHWFeatures; + + uint16_t wONFIDevFeatures; + uint16_t wONFIOptCommands; + uint16_t wONFITimingMode; + uint16_t wONFIPgmCacheTimingMode; + + uint16_t MLCDevice; + uint16_t wSpareSkipBytes; + + uint8_t nBitsInPageNumber; + uint8_t nBitsInPageDataSize; + uint8_t nBitsInBlockDataSize; +}; + +/* ffsdefs.h */ +#define CLEAR 0 /*use this to clear a field instead of "fail"*/ +#define SET 1 /*use this to set a field instead of "pass"*/ +#define FAIL 1 /*failed flag*/ +#define PASS 0 /*success flag*/ +#define ERR -1 /*error flag*/ + +/* lld.h */ +#define GOOD_BLOCK 0 +#define DEFECTIVE_BLOCK 1 +#define READ_ERROR 2 + +#define CLK_X 5 +#define CLK_MULTI 4 + +/* ffsport.h */ +#define VERBOSE 1 + +#define NAND_DBG_WARN 1 +#define NAND_DBG_DEBUG 2 +#define NAND_DBG_TRACE 3 + +#ifdef VERBOSE +#define nand_dbg_print(level, args...) \ + do { \ + if (level <= nand_debug_level) \ + printk(KERN_ALERT args); \ + } while (0) +#else +#define nand_dbg_print(level, args...) +#endif + + +/* spectraswconfig.h */ +#define CMD_DMA 0 + +#define SPECTRA_PARTITION_ID 0 +/**** Block Table and Reserved Block Parameters *****/ +#define SPECTRA_START_BLOCK 3 +#define NUM_FREE_BLOCKS_GATE 30 + +/* KBV - Updated to LNW scratch register address */ +#define SCRATCH_REG_ADDR CONFIG_MTD_NAND_DENALI_SCRATCH_REG_ADDR +#define SCRATCH_REG_SIZE 64 + +#define GLOB_HWCTL_DEFAULT_BLKS 2048 + +#define SUPPORT_15BITECC 1 +#define SUPPORT_8BITECC 1 + +#define CUSTOM_CONF_PARAMS 0 + +#define ONFI_BLOOM_TIME 1 +#define MODE5_WORKAROUND 0 + +/* lld_nand.h */ +/* + * NAND Flash Controller Device Driver + * Copyright (c) 2009, Intel Corporation and its suppliers. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef _LLD_NAND_ +#define _LLD_NAND_ + +#define MODE_00 0x00000000 +#define MODE_01 0x04000000 +#define MODE_10 0x08000000 +#define MODE_11 0x0C000000 + + +#define DATA_TRANSFER_MODE 0 +#define PROTECTION_PER_BLOCK 1 +#define LOAD_WAIT_COUNT 2 +#define PROGRAM_WAIT_COUNT 3 +#define ERASE_WAIT_COUNT 4 +#define INT_MONITOR_CYCLE_COUNT 5 +#define READ_BUSY_PIN_ENABLED 6 +#define MULTIPLANE_OPERATION_SUPPORT 7 +#define PRE_FETCH_MODE 8 +#define CE_DONT_CARE_SUPPORT 9 +#define COPYBACK_SUPPORT 10 +#define CACHE_WRITE_SUPPORT 11 +#define CACHE_READ_SUPPORT 12 +#define NUM_PAGES_IN_BLOCK 13 +#define ECC_ENABLE_SELECT 14 +#define WRITE_ENABLE_2_READ_ENABLE 15 +#define ADDRESS_2_DATA 16 +#define READ_ENABLE_2_WRITE_ENABLE 17 +#define TWO_ROW_ADDRESS_CYCLES 18 +#define MULTIPLANE_ADDRESS_RESTRICT 19 +#define ACC_CLOCKS 20 +#define READ_WRITE_ENABLE_LOW_COUNT 21 +#define READ_WRITE_ENABLE_HIGH_COUNT 22 + +#define ECC_SECTOR_SIZE 512 +#define LLD_MAX_FLASH_BANKS 4 + +#define DENALI_BUF_SIZE NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE + +struct nand_buf +{ + int head; + int tail; + uint8_t buf[DENALI_BUF_SIZE]; + dma_addr_t dma_buf; +}; + +#define INTEL_CE4100 1 +#define INTEL_MRST 2 + +struct denali_nand_info { + struct mtd_info mtd; + struct nand_chip nand; + struct device_info_tag dev_info; + int flash_bank; /* currently selected chip */ + int status; + int platform; + struct nand_buf buf; + struct pci_dev *dev; + int total_used_banks; + uint32_t block; /* stored for future use */ + uint16_t page; + void __iomem *flash_reg; /* Mapped io reg base address */ + void __iomem *flash_mem; /* Mapped io reg base address */ + + /* elements used by ISR */ + struct completion complete; + spinlock_t irq_lock; + uint32_t irq_status; + int irq_debug_array[32]; + int idx; +}; + +static uint16_t NAND_Flash_Reset(struct denali_nand_info *denali); +static uint16_t NAND_Read_Device_ID(struct denali_nand_info *denali); +static void NAND_LLD_Enable_Disable_Interrupts(struct denali_nand_info *denali, uint16_t INT_ENABLE); + +#endif /*_LLD_NAND_*/ + diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index f45a8d0..5084cc5 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -874,7 +874,7 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, priv->ctrl = ctrl; priv->dev = ctrl->dev; - priv->vbase = ioremap(res.start, res.end - res.start + 1); + priv->vbase = ioremap(res.start, resource_size(&res)); if (!priv->vbase) { dev_err(ctrl->dev, "failed to map chip region\n"); ret = -ENOMEM; @@ -891,7 +891,7 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, if (ret) goto err; - ret = nand_scan_ident(&priv->mtd, 1); + ret = nand_scan_ident(&priv->mtd, 1, NULL); if (ret) goto err; diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index b7ab5a0..00aea6f 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -49,7 +49,10 @@ struct fsl_upm_nand { uint32_t wait_flags; }; -#define to_fsl_upm_nand(mtd) container_of(mtd, struct fsl_upm_nand, mtd) +static inline struct fsl_upm_nand *to_fsl_upm_nand(struct mtd_info *mtdinfo) +{ + return container_of(mtdinfo, struct fsl_upm_nand, mtd); +} static int fun_chip_ready(struct mtd_info *mtd) { @@ -303,7 +306,7 @@ static int __devinit fun_probe(struct of_device *ofdev, FSL_UPM_WAIT_WRITE_BYTE; fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start, - io_res.end - io_res.start + 1); + resource_size(&io_res)); if (!fun->io_base) { ret = -ENOMEM; goto err2; @@ -350,7 +353,7 @@ static int __devexit fun_remove(struct of_device *ofdev) return 0; } -static struct of_device_id of_fun_match[] = { +static const struct of_device_id of_fun_match[] = { { .compatible = "fsl,upm-nand" }, {}, }; diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index 8f902e7..0cde618 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -181,11 +181,11 @@ static int __devexit gpio_nand_remove(struct platform_device *dev) res = platform_get_resource(dev, IORESOURCE_MEM, 1); iounmap(gpiomtd->io_sync); if (res) - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); res = platform_get_resource(dev, IORESOURCE_MEM, 0); iounmap(gpiomtd->nand_chip.IO_ADDR_R); - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) gpio_set_value(gpiomtd->plat.gpio_nwp, 0); @@ -208,14 +208,14 @@ static void __iomem *request_and_remap(struct resource *res, size_t size, { void __iomem *ptr; - if (!request_mem_region(res->start, res->end - res->start + 1, name)) { + if (!request_mem_region(res->start, resource_size(res), name)) { *err = -EBUSY; return NULL; } ptr = ioremap(res->start, size); if (!ptr) { - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); *err = -ENOMEM; } return ptr; @@ -338,10 +338,10 @@ err_nwp: err_nce: iounmap(gpiomtd->io_sync); if (res1) - release_mem_region(res1->start, res1->end - res1->start + 1); + release_mem_region(res1->start, resource_size(res1)); err_sync: iounmap(gpiomtd->nand_chip.IO_ADDR_R); - release_mem_region(res0->start, res0->end - res0->start + 1); + release_mem_region(res0->start, resource_size(res0)); err_map: kfree(gpiomtd); return ret; diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c new file mode 100644 index 0000000..3d0867d --- /dev/null +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -0,0 +1,917 @@ +/* + * Copyright 2004-2008 Freescale Semiconductor, Inc. + * Copyright 2009 Semihalf. + * + * Approved as OSADL project by a majority of OSADL members and funded + * by OSADL membership fees in 2009; for details see www.osadl.org. + * + * Based on original driver from Freescale Semiconductor + * written by John Rigby <jrigby@freescale.com> on basis + * of drivers/mtd/nand/mxc_nand.c. Reworked and extended + * Piotr Ziecik <kosmo@semihalf.com>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include <linux/module.h> +#include <linux/clk.h> +#include <linux/gfp.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> + +#include <asm/mpc5121.h> + +/* Addresses for NFC MAIN RAM BUFFER areas */ +#define NFC_MAIN_AREA(n) ((n) * 0x200) + +/* Addresses for NFC SPARE BUFFER areas */ +#define NFC_SPARE_BUFFERS 8 +#define NFC_SPARE_LEN 0x40 +#define NFC_SPARE_AREA(n) (0x1000 + ((n) * NFC_SPARE_LEN)) + +/* MPC5121 NFC registers */ +#define NFC_BUF_ADDR 0x1E04 +#define NFC_FLASH_ADDR 0x1E06 +#define NFC_FLASH_CMD 0x1E08 +#define NFC_CONFIG 0x1E0A +#define NFC_ECC_STATUS1 0x1E0C +#define NFC_ECC_STATUS2 0x1E0E +#define NFC_SPAS 0x1E10 +#define NFC_WRPROT 0x1E12 +#define NFC_NF_WRPRST 0x1E18 +#define NFC_CONFIG1 0x1E1A +#define NFC_CONFIG2 0x1E1C +#define NFC_UNLOCKSTART_BLK0 0x1E20 +#define NFC_UNLOCKEND_BLK0 0x1E22 +#define NFC_UNLOCKSTART_BLK1 0x1E24 +#define NFC_UNLOCKEND_BLK1 0x1E26 +#define NFC_UNLOCKSTART_BLK2 0x1E28 +#define NFC_UNLOCKEND_BLK2 0x1E2A +#define NFC_UNLOCKSTART_BLK3 0x1E2C +#define NFC_UNLOCKEND_BLK3 0x1E2E + +/* Bit Definitions: NFC_BUF_ADDR */ +#define NFC_RBA_MASK (7 << 0) +#define NFC_ACTIVE_CS_SHIFT 5 +#define NFC_ACTIVE_CS_MASK (3 << NFC_ACTIVE_CS_SHIFT) + +/* Bit Definitions: NFC_CONFIG */ +#define NFC_BLS_UNLOCKED (1 << 1) + +/* Bit Definitions: NFC_CONFIG1 */ +#define NFC_ECC_4BIT (1 << 0) +#define NFC_FULL_PAGE_DMA (1 << 1) +#define NFC_SPARE_ONLY (1 << 2) +#define NFC_ECC_ENABLE (1 << 3) +#define NFC_INT_MASK (1 << 4) +#define NFC_BIG_ENDIAN (1 << 5) +#define NFC_RESET (1 << 6) +#define NFC_CE (1 << 7) +#define NFC_ONE_CYCLE (1 << 8) +#define NFC_PPB_32 (0 << 9) +#define NFC_PPB_64 (1 << 9) +#define NFC_PPB_128 (2 << 9) +#define NFC_PPB_256 (3 << 9) +#define NFC_PPB_MASK (3 << 9) +#define NFC_FULL_PAGE_INT (1 << 11) + +/* Bit Definitions: NFC_CONFIG2 */ +#define NFC_COMMAND (1 << 0) +#define NFC_ADDRESS (1 << 1) +#define NFC_INPUT (1 << 2) +#define NFC_OUTPUT (1 << 3) +#define NFC_ID (1 << 4) +#define NFC_STATUS (1 << 5) +#define NFC_CMD_FAIL (1 << 15) +#define NFC_INT (1 << 15) + +/* Bit Definitions: NFC_WRPROT */ +#define NFC_WPC_LOCK_TIGHT (1 << 0) +#define NFC_WPC_LOCK (1 << 1) +#define NFC_WPC_UNLOCK (1 << 2) + +#define DRV_NAME "mpc5121_nfc" + +/* Timeouts */ +#define NFC_RESET_TIMEOUT 1000 /* 1 ms */ +#define NFC_TIMEOUT (HZ / 10) /* 1/10 s */ + +struct mpc5121_nfc_prv { + struct mtd_info mtd; + struct nand_chip chip; + int irq; + void __iomem *regs; + struct clk *clk; + wait_queue_head_t irq_waitq; + uint column; + int spareonly; + void __iomem *csreg; + struct device *dev; +}; + +static void mpc5121_nfc_done(struct mtd_info *mtd); + +#ifdef CONFIG_MTD_PARTITIONS +static const char *mpc5121_nfc_pprobes[] = { "cmdlinepart", NULL }; +#endif + +/* Read NFC register */ +static inline u16 nfc_read(struct mtd_info *mtd, uint reg) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + return in_be16(prv->regs + reg); +} + +/* Write NFC register */ +static inline void nfc_write(struct mtd_info *mtd, uint reg, u16 val) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + out_be16(prv->regs + reg, val); +} + +/* Set bits in NFC register */ +static inline void nfc_set(struct mtd_info *mtd, uint reg, u16 bits) +{ + nfc_write(mtd, reg, nfc_read(mtd, reg) | bits); +} + +/* Clear bits in NFC register */ +static inline void nfc_clear(struct mtd_info *mtd, uint reg, u16 bits) +{ + nfc_write(mtd, reg, nfc_read(mtd, reg) & ~bits); +} + +/* Invoke address cycle */ +static inline void mpc5121_nfc_send_addr(struct mtd_info *mtd, u16 addr) +{ + nfc_write(mtd, NFC_FLASH_ADDR, addr); + nfc_write(mtd, NFC_CONFIG2, NFC_ADDRESS); + mpc5121_nfc_done(mtd); +} + +/* Invoke command cycle */ +static inline void mpc5121_nfc_send_cmd(struct mtd_info *mtd, u16 cmd) +{ + nfc_write(mtd, NFC_FLASH_CMD, cmd); + nfc_write(mtd, NFC_CONFIG2, NFC_COMMAND); + mpc5121_nfc_done(mtd); +} + +/* Send data from NFC buffers to NAND flash */ +static inline void mpc5121_nfc_send_prog_page(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_INPUT); + mpc5121_nfc_done(mtd); +} + +/* Receive data from NAND flash */ +static inline void mpc5121_nfc_send_read_page(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_OUTPUT); + mpc5121_nfc_done(mtd); +} + +/* Receive ID from NAND flash */ +static inline void mpc5121_nfc_send_read_id(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_ID); + mpc5121_nfc_done(mtd); +} + +/* Receive status from NAND flash */ +static inline void mpc5121_nfc_send_read_status(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_STATUS); + mpc5121_nfc_done(mtd); +} + +/* NFC interrupt handler */ +static irqreturn_t mpc5121_nfc_irq(int irq, void *data) +{ + struct mtd_info *mtd = data; + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + nfc_set(mtd, NFC_CONFIG1, NFC_INT_MASK); + wake_up(&prv->irq_waitq); + + return IRQ_HANDLED; +} + +/* Wait for operation complete */ +static void mpc5121_nfc_done(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + int rv; + + if ((nfc_read(mtd, NFC_CONFIG2) & NFC_INT) == 0) { + nfc_clear(mtd, NFC_CONFIG1, NFC_INT_MASK); + rv = wait_event_timeout(prv->irq_waitq, + (nfc_read(mtd, NFC_CONFIG2) & NFC_INT), NFC_TIMEOUT); + + if (!rv) + dev_warn(prv->dev, + "Timeout while waiting for interrupt.\n"); + } + + nfc_clear(mtd, NFC_CONFIG2, NFC_INT); +} + +/* Do address cycle(s) */ +static void mpc5121_nfc_addr_cycle(struct mtd_info *mtd, int column, int page) +{ + struct nand_chip *chip = mtd->priv; + u32 pagemask = chip->pagemask; + + if (column != -1) { + mpc5121_nfc_send_addr(mtd, column); + if (mtd->writesize > 512) + mpc5121_nfc_send_addr(mtd, column >> 8); + } + + if (page != -1) { + do { + mpc5121_nfc_send_addr(mtd, page & 0xFF); + page >>= 8; + pagemask >>= 8; + } while (pagemask); + } +} + +/* Control chip select signals */ +static void mpc5121_nfc_select_chip(struct mtd_info *mtd, int chip) +{ + if (chip < 0) { + nfc_clear(mtd, NFC_CONFIG1, NFC_CE); + return; + } + + nfc_clear(mtd, NFC_BUF_ADDR, NFC_ACTIVE_CS_MASK); + nfc_set(mtd, NFC_BUF_ADDR, (chip << NFC_ACTIVE_CS_SHIFT) & + NFC_ACTIVE_CS_MASK); + nfc_set(mtd, NFC_CONFIG1, NFC_CE); +} + +/* Init external chip select logic on ADS5121 board */ +static int ads5121_chipselect_init(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + struct device_node *dn; + + dn = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld"); + if (dn) { + prv->csreg = of_iomap(dn, 0); + of_node_put(dn); + if (!prv->csreg) + return -ENOMEM; + + /* CPLD Register 9 controls NAND /CE Lines */ + prv->csreg += 9; + return 0; + } + + return -EINVAL; +} + +/* Control chips select signal on ADS5121 board */ +static void ads5121_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand = mtd->priv; + struct mpc5121_nfc_prv *prv = nand->priv; + u8 v; + + v = in_8(prv->csreg); + v |= 0x0F; + + if (chip >= 0) { + mpc5121_nfc_select_chip(mtd, 0); + v &= ~(1 << chip); + } else + mpc5121_nfc_select_chip(mtd, -1); + + out_8(prv->csreg, v); +} + +/* Read NAND Ready/Busy signal */ +static int mpc5121_nfc_dev_ready(struct mtd_info *mtd) +{ + /* + * NFC handles ready/busy signal internally. Therefore, this function + * always returns status as ready. + */ + return 1; +} + +/* Write command to NAND flash */ +static void mpc5121_nfc_command(struct mtd_info *mtd, unsigned command, + int column, int page) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + prv->column = (column >= 0) ? column : 0; + prv->spareonly = 0; + + switch (command) { + case NAND_CMD_PAGEPROG: + mpc5121_nfc_send_prog_page(mtd); + break; + /* + * NFC does not support sub-page reads and writes, + * so emulate them using full page transfers. + */ + case NAND_CMD_READ0: + column = 0; + break; + + case NAND_CMD_READ1: + prv->column += 256; + command = NAND_CMD_READ0; + column = 0; + break; + + case NAND_CMD_READOOB: + prv->spareonly = 1; + command = NAND_CMD_READ0; + column = 0; + break; + + case NAND_CMD_SEQIN: + mpc5121_nfc_command(mtd, NAND_CMD_READ0, column, page); + column = 0; + break; + + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_READID: + case NAND_CMD_STATUS: + break; + + default: + return; + } + + mpc5121_nfc_send_cmd(mtd, command); + mpc5121_nfc_addr_cycle(mtd, column, page); + + switch (command) { + case NAND_CMD_READ0: + if (mtd->writesize > 512) + mpc5121_nfc_send_cmd(mtd, NAND_CMD_READSTART); + mpc5121_nfc_send_read_page(mtd); + break; + + case NAND_CMD_READID: + mpc5121_nfc_send_read_id(mtd); + break; + + case NAND_CMD_STATUS: + mpc5121_nfc_send_read_status(mtd); + if (chip->options & NAND_BUSWIDTH_16) + prv->column = 1; + else + prv->column = 0; + break; + } +} + +/* Copy data from/to NFC spare buffers. */ +static void mpc5121_nfc_copy_spare(struct mtd_info *mtd, uint offset, + u8 *buffer, uint size, int wr) +{ + struct nand_chip *nand = mtd->priv; + struct mpc5121_nfc_prv *prv = nand->priv; + uint o, s, sbsize, blksize; + + /* + * NAND spare area is available through NFC spare buffers. + * The NFC divides spare area into (page_size / 512) chunks. + * Each chunk is placed into separate spare memory area, using + * first (spare_size / num_of_chunks) bytes of the buffer. + * + * For NAND device in which the spare area is not divided fully + * by the number of chunks, number of used bytes in each spare + * buffer is rounded down to the nearest even number of bytes, + * and all remaining bytes are added to the last used spare area. + * + * For more information read section 26.6.10 of MPC5121e + * Microcontroller Reference Manual, Rev. 3. + */ + + /* Calculate number of valid bytes in each spare buffer */ + sbsize = (mtd->oobsize / (mtd->writesize / 512)) & ~1; + + while (size) { + /* Calculate spare buffer number */ + s = offset / sbsize; + if (s > NFC_SPARE_BUFFERS - 1) + s = NFC_SPARE_BUFFERS - 1; + + /* + * Calculate offset to requested data block in selected spare + * buffer and its size. + */ + o = offset - (s * sbsize); + blksize = min(sbsize - o, size); + + if (wr) + memcpy_toio(prv->regs + NFC_SPARE_AREA(s) + o, + buffer, blksize); + else + memcpy_fromio(buffer, + prv->regs + NFC_SPARE_AREA(s) + o, blksize); + + buffer += blksize; + offset += blksize; + size -= blksize; + }; +} + +/* Copy data from/to NFC main and spare buffers */ +static void mpc5121_nfc_buf_copy(struct mtd_info *mtd, u_char *buf, int len, + int wr) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + uint c = prv->column; + uint l; + + /* Handle spare area access */ + if (prv->spareonly || c >= mtd->writesize) { + /* Calculate offset from beginning of spare area */ + if (c >= mtd->writesize) + c -= mtd->writesize; + + prv->column += len; + mpc5121_nfc_copy_spare(mtd, c, buf, len, wr); + return; + } + + /* + * Handle main area access - limit copy length to prevent + * crossing main/spare boundary. + */ + l = min((uint)len, mtd->writesize - c); + prv->column += l; + + if (wr) + memcpy_toio(prv->regs + NFC_MAIN_AREA(0) + c, buf, l); + else + memcpy_fromio(buf, prv->regs + NFC_MAIN_AREA(0) + c, l); + + /* Handle crossing main/spare boundary */ + if (l != len) { + buf += l; + len -= l; + mpc5121_nfc_buf_copy(mtd, buf, len, wr); + } +} + +/* Read data from NFC buffers */ +static void mpc5121_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + mpc5121_nfc_buf_copy(mtd, buf, len, 0); +} + +/* Write data to NFC buffers */ +static void mpc5121_nfc_write_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + mpc5121_nfc_buf_copy(mtd, (u_char *)buf, len, 1); +} + +/* Compare buffer with NAND flash */ +static int mpc5121_nfc_verify_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + u_char tmp[256]; + uint bsize; + + while (len) { + bsize = min(len, 256); + mpc5121_nfc_read_buf(mtd, tmp, bsize); + + if (memcmp(buf, tmp, bsize)) + return 1; + + buf += bsize; + len -= bsize; + } + + return 0; +} + +/* Read byte from NFC buffers */ +static u8 mpc5121_nfc_read_byte(struct mtd_info *mtd) +{ + u8 tmp; + + mpc5121_nfc_read_buf(mtd, &tmp, sizeof(tmp)); + + return tmp; +} + +/* Read word from NFC buffers */ +static u16 mpc5121_nfc_read_word(struct mtd_info *mtd) +{ + u16 tmp; + + mpc5121_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp)); + + return tmp; +} + +/* + * Read NFC configuration from Reset Config Word + * + * NFC is configured during reset in basis of information stored + * in Reset Config Word. There is no other way to set NAND block + * size, spare size and bus width. + */ +static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + struct mpc512x_reset_module *rm; + struct device_node *rmnode; + uint rcw_pagesize = 0; + uint rcw_sparesize = 0; + uint rcw_width; + uint rcwh; + uint romloc, ps; + + rmnode = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-reset"); + if (!rmnode) { + dev_err(prv->dev, "Missing 'fsl,mpc5121-reset' " + "node in device tree!\n"); + return -ENODEV; + } + + rm = of_iomap(rmnode, 0); + if (!rm) { + dev_err(prv->dev, "Error mapping reset module node!\n"); + return -EBUSY; + } + + rcwh = in_be32(&rm->rcwhr); + + /* Bit 6: NFC bus width */ + rcw_width = ((rcwh >> 6) & 0x1) ? 2 : 1; + + /* Bit 7: NFC Page/Spare size */ + ps = (rcwh >> 7) & 0x1; + + /* Bits [22:21]: ROM Location */ + romloc = (rcwh >> 21) & 0x3; + + /* Decode RCW bits */ + switch ((ps << 2) | romloc) { + case 0x00: + case 0x01: + rcw_pagesize = 512; + rcw_sparesize = 16; + break; + case 0x02: + case 0x03: + rcw_pagesize = 4096; + rcw_sparesize = 128; + break; + case 0x04: + case 0x05: + rcw_pagesize = 2048; + rcw_sparesize = 64; + break; + case 0x06: + case 0x07: + rcw_pagesize = 4096; + rcw_sparesize = 218; + break; + } + + mtd->writesize = rcw_pagesize; + mtd->oobsize = rcw_sparesize; + if (rcw_width == 2) + chip->options |= NAND_BUSWIDTH_16; + + dev_notice(prv->dev, "Configured for " + "%u-bit NAND, page size %u " + "with %u spare.\n", + rcw_width * 8, rcw_pagesize, + rcw_sparesize); + iounmap(rm); + of_node_put(rmnode); + return 0; +} + +/* Free driver resources */ +static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + if (prv->clk) { + clk_disable(prv->clk); + clk_put(prv->clk); + } + + if (prv->csreg) + iounmap(prv->csreg); +} + +static int __devinit mpc5121_nfc_probe(struct of_device *op, + const struct of_device_id *match) +{ + struct device_node *rootnode, *dn = op->node; + struct device *dev = &op->dev; + struct mpc5121_nfc_prv *prv; + struct resource res; + struct mtd_info *mtd; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts; +#endif + struct nand_chip *chip; + unsigned long regs_paddr, regs_size; + const uint *chips_no; + int resettime = 0; + int retval = 0; + int rev, len; + + /* + * Check SoC revision. This driver supports only NFC + * in MPC5121 revision 2 and MPC5123 revision 3. + */ + rev = (mfspr(SPRN_SVR) >> 4) & 0xF; + if ((rev != 2) && (rev != 3)) { + dev_err(dev, "SoC revision %u is not supported!\n", rev); + return -ENXIO; + } + + prv = devm_kzalloc(dev, sizeof(*prv), GFP_KERNEL); + if (!prv) { + dev_err(dev, "Memory exhausted!\n"); + return -ENOMEM; + } + + mtd = &prv->mtd; + chip = &prv->chip; + + mtd->priv = chip; + chip->priv = prv; + prv->dev = dev; + + /* Read NFC configuration from Reset Config Word */ + retval = mpc5121_nfc_read_hw_config(mtd); + if (retval) { + dev_err(dev, "Unable to read NFC config!\n"); + return retval; + } + + prv->irq = irq_of_parse_and_map(dn, 0); + if (prv->irq == NO_IRQ) { + dev_err(dev, "Error mapping IRQ!\n"); + return -EINVAL; + } + + retval = of_address_to_resource(dn, 0, &res); + if (retval) { + dev_err(dev, "Error parsing memory region!\n"); + return retval; + } + + chips_no = of_get_property(dn, "chips", &len); + if (!chips_no || len != sizeof(*chips_no)) { + dev_err(dev, "Invalid/missing 'chips' property!\n"); + return -EINVAL; + } + + regs_paddr = res.start; + regs_size = res.end - res.start + 1; + + if (!devm_request_mem_region(dev, regs_paddr, regs_size, DRV_NAME)) { + dev_err(dev, "Error requesting memory region!\n"); + return -EBUSY; + } + + prv->regs = devm_ioremap(dev, regs_paddr, regs_size); + if (!prv->regs) { + dev_err(dev, "Error mapping memory region!\n"); + return -ENOMEM; + } + + mtd->name = "MPC5121 NAND"; + chip->dev_ready = mpc5121_nfc_dev_ready; + chip->cmdfunc = mpc5121_nfc_command; + chip->read_byte = mpc5121_nfc_read_byte; + chip->read_word = mpc5121_nfc_read_word; + chip->read_buf = mpc5121_nfc_read_buf; + chip->write_buf = mpc5121_nfc_write_buf; + chip->verify_buf = mpc5121_nfc_verify_buf; + chip->select_chip = mpc5121_nfc_select_chip; + chip->options = NAND_NO_AUTOINCR | NAND_USE_FLASH_BBT; + chip->ecc.mode = NAND_ECC_SOFT; + + /* Support external chip-select logic on ADS5121 board */ + rootnode = of_find_node_by_path("/"); + if (of_device_is_compatible(rootnode, "fsl,mpc5121ads")) { + retval = ads5121_chipselect_init(mtd); + if (retval) { + dev_err(dev, "Chipselect init error!\n"); + of_node_put(rootnode); + return retval; + } + + chip->select_chip = ads5121_select_chip; + } + of_node_put(rootnode); + + /* Enable NFC clock */ + prv->clk = clk_get(dev, "nfc_clk"); + if (!prv->clk) { + dev_err(dev, "Unable to acquire NFC clock!\n"); + retval = -ENODEV; + goto error; + } + + clk_enable(prv->clk); + + /* Reset NAND Flash controller */ + nfc_set(mtd, NFC_CONFIG1, NFC_RESET); + while (nfc_read(mtd, NFC_CONFIG1) & NFC_RESET) { + if (resettime++ >= NFC_RESET_TIMEOUT) { + dev_err(dev, "Timeout while resetting NFC!\n"); + retval = -EINVAL; + goto error; + } + + udelay(1); + } + + /* Enable write to NFC memory */ + nfc_write(mtd, NFC_CONFIG, NFC_BLS_UNLOCKED); + + /* Enable write to all NAND pages */ + nfc_write(mtd, NFC_UNLOCKSTART_BLK0, 0x0000); + nfc_write(mtd, NFC_UNLOCKEND_BLK0, 0xFFFF); + nfc_write(mtd, NFC_WRPROT, NFC_WPC_UNLOCK); + + /* + * Setup NFC: + * - Big Endian transfers, + * - Interrupt after full page read/write. + */ + nfc_write(mtd, NFC_CONFIG1, NFC_BIG_ENDIAN | NFC_INT_MASK | + NFC_FULL_PAGE_INT); + + /* Set spare area size */ + nfc_write(mtd, NFC_SPAS, mtd->oobsize >> 1); + + init_waitqueue_head(&prv->irq_waitq); + retval = devm_request_irq(dev, prv->irq, &mpc5121_nfc_irq, 0, DRV_NAME, + mtd); + if (retval) { + dev_err(dev, "Error requesting IRQ!\n"); + goto error; + } + + /* Detect NAND chips */ + if (nand_scan(mtd, *chips_no)) { + dev_err(dev, "NAND Flash not found !\n"); + devm_free_irq(dev, prv->irq, mtd); + retval = -ENXIO; + goto error; + } + + /* Set erase block size */ + switch (mtd->erasesize / mtd->writesize) { + case 32: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_32); + break; + + case 64: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_64); + break; + + case 128: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_128); + break; + + case 256: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_256); + break; + + default: + dev_err(dev, "Unsupported NAND flash!\n"); + devm_free_irq(dev, prv->irq, mtd); + retval = -ENXIO; + goto error; + } + + dev_set_drvdata(dev, mtd); + + /* Register device in MTD */ +#ifdef CONFIG_MTD_PARTITIONS + retval = parse_mtd_partitions(mtd, mpc5121_nfc_pprobes, &parts, 0); +#ifdef CONFIG_MTD_OF_PARTS + if (retval == 0) + retval = of_mtd_parse_partitions(dev, dn, &parts); +#endif + if (retval < 0) { + dev_err(dev, "Error parsing MTD partitions!\n"); + devm_free_irq(dev, prv->irq, mtd); + retval = -EINVAL; + goto error; + } + + if (retval > 0) + retval = add_mtd_partitions(mtd, parts, retval); + else +#endif + retval = add_mtd_device(mtd); + + if (retval) { + dev_err(dev, "Error adding MTD device!\n"); + devm_free_irq(dev, prv->irq, mtd); + goto error; + } + + return 0; +error: + mpc5121_nfc_free(dev, mtd); + return retval; +} + +static int __devexit mpc5121_nfc_remove(struct of_device *op) +{ + struct device *dev = &op->dev; + struct mtd_info *mtd = dev_get_drvdata(dev); + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + nand_release(mtd); + devm_free_irq(dev, prv->irq, mtd); + mpc5121_nfc_free(dev, mtd); + + return 0; +} + +static struct of_device_id mpc5121_nfc_match[] __devinitdata = { + { .compatible = "fsl,mpc5121-nfc", }, + {}, +}; + +static struct of_platform_driver mpc5121_nfc_driver = { + .match_table = mpc5121_nfc_match, + .probe = mpc5121_nfc_probe, + .remove = __devexit_p(mpc5121_nfc_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init mpc5121_nfc_init(void) +{ + return of_register_platform_driver(&mpc5121_nfc_driver); +} + +module_init(mpc5121_nfc_init); + +static void __exit mpc5121_nfc_cleanup(void) +{ + of_unregister_platform_driver(&mpc5121_nfc_driver); +} + +module_exit(mpc5121_nfc_cleanup); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MPC5121 NAND MTD driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index b2900d8..82e9438 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -38,7 +38,7 @@ #define DRIVER_NAME "mxc_nand" #define nfc_is_v21() (cpu_is_mx25() || cpu_is_mx35()) -#define nfc_is_v1() (cpu_is_mx31() || cpu_is_mx27()) +#define nfc_is_v1() (cpu_is_mx31() || cpu_is_mx27() || cpu_is_mx21()) /* Addresses for NFC registers */ #define NFC_BUF_SIZE 0xE00 @@ -168,11 +168,7 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) { struct mxc_nand_host *host = dev_id; - uint16_t tmp; - - tmp = readw(host->regs + NFC_CONFIG1); - tmp |= NFC_INT_MSK; /* Disable interrupt */ - writew(tmp, host->regs + NFC_CONFIG1); + disable_irq_nosync(irq); wake_up(&host->irq_waitq); @@ -184,15 +180,13 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) */ static void wait_op_done(struct mxc_nand_host *host, int useirq) { - uint32_t tmp; - int max_retries = 2000; + uint16_t tmp; + int max_retries = 8000; if (useirq) { if ((readw(host->regs + NFC_CONFIG2) & NFC_INT) == 0) { - tmp = readw(host->regs + NFC_CONFIG1); - tmp &= ~NFC_INT_MSK; /* Enable interrupt */ - writew(tmp, host->regs + NFC_CONFIG1); + enable_irq(host->irq); wait_event(host->irq_waitq, readw(host->regs + NFC_CONFIG2) & NFC_INT); @@ -226,8 +220,23 @@ static void send_cmd(struct mxc_nand_host *host, uint16_t cmd, int useirq) writew(cmd, host->regs + NFC_FLASH_CMD); writew(NFC_CMD, host->regs + NFC_CONFIG2); - /* Wait for operation to complete */ - wait_op_done(host, useirq); + if (cpu_is_mx21() && (cmd == NAND_CMD_RESET)) { + int max_retries = 100; + /* Reset completion is indicated by NFC_CONFIG2 */ + /* being set to 0 */ + while (max_retries-- > 0) { + if (readw(host->regs + NFC_CONFIG2) == 0) { + break; + } + udelay(1); + } + if (max_retries < 0) + DEBUG(MTD_DEBUG_LEVEL0, "%s: RESET failed\n", + __func__); + } else { + /* Wait for operation to complete */ + wait_op_done(host, useirq); + } } /* This function sends an address (or partial address) to the @@ -542,6 +551,41 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) } } +static void preset(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + uint16_t tmp; + + /* enable interrupt, disable spare enable */ + tmp = readw(host->regs + NFC_CONFIG1); + tmp &= ~NFC_INT_MSK; + tmp &= ~NFC_SP_EN; + if (nand_chip->ecc.mode == NAND_ECC_HW) { + tmp |= NFC_ECC_EN; + } else { + tmp &= ~NFC_ECC_EN; + } + writew(tmp, host->regs + NFC_CONFIG1); + /* preset operation */ + + /* Unlock the internal RAM Buffer */ + writew(0x2, host->regs + NFC_CONFIG); + + /* Blocks to be unlocked */ + if (nfc_is_v21()) { + writew(0x0, host->regs + NFC_V21_UNLOCKSTART_BLKADDR); + writew(0xffff, host->regs + NFC_V21_UNLOCKEND_BLKADDR); + } else if (nfc_is_v1()) { + writew(0x0, host->regs + NFC_V1_UNLOCKSTART_BLKADDR); + writew(0x4000, host->regs + NFC_V1_UNLOCKEND_BLKADDR); + } else + BUG(); + + /* Unlock Block Command for given address range */ + writew(0x4, host->regs + NFC_WRPROT); +} + /* Used by the upper layer to write command to NAND Flash for * different operations to be carried out on NAND Flash */ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, @@ -559,6 +603,10 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, /* Command pre-processing step */ switch (command) { + case NAND_CMD_RESET: + send_cmd(host, command, false); + preset(mtd); + break; case NAND_CMD_STATUS: host->buf_start = 0; @@ -679,7 +727,6 @@ static int __init mxcnd_probe(struct platform_device *pdev) struct mxc_nand_platform_data *pdata = pdev->dev.platform_data; struct mxc_nand_host *host; struct resource *res; - uint16_t tmp; int err = 0, nr_parts = 0; struct nand_ecclayout *oob_smallpage, *oob_largepage; @@ -743,51 +790,17 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->spare_len = 64; oob_smallpage = &nandv2_hw_eccoob_smallpage; oob_largepage = &nandv2_hw_eccoob_largepage; + this->ecc.bytes = 9; } else if (nfc_is_v1()) { host->regs = host->base; host->spare0 = host->base + 0x800; host->spare_len = 16; oob_smallpage = &nandv1_hw_eccoob_smallpage; oob_largepage = &nandv1_hw_eccoob_largepage; - } else - BUG(); - - /* disable interrupt and spare enable */ - tmp = readw(host->regs + NFC_CONFIG1); - tmp |= NFC_INT_MSK; - tmp &= ~NFC_SP_EN; - writew(tmp, host->regs + NFC_CONFIG1); - - init_waitqueue_head(&host->irq_waitq); - - host->irq = platform_get_irq(pdev, 0); - - err = request_irq(host->irq, mxc_nfc_irq, 0, DRIVER_NAME, host); - if (err) - goto eirq; - - /* Reset NAND */ - this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - - /* preset operation */ - /* Unlock the internal RAM Buffer */ - writew(0x2, host->regs + NFC_CONFIG); - - /* Blocks to be unlocked */ - if (nfc_is_v21()) { - writew(0x0, host->regs + NFC_V21_UNLOCKSTART_BLKADDR); - writew(0xffff, host->regs + NFC_V21_UNLOCKEND_BLKADDR); - this->ecc.bytes = 9; - } else if (nfc_is_v1()) { - writew(0x0, host->regs + NFC_V1_UNLOCKSTART_BLKADDR); - writew(0x4000, host->regs + NFC_V1_UNLOCKEND_BLKADDR); this->ecc.bytes = 3; } else BUG(); - /* Unlock Block Command for given address range */ - writew(0x4, host->regs + NFC_WRPROT); - this->ecc.size = 512; this->ecc.layout = oob_smallpage; @@ -796,14 +809,8 @@ static int __init mxcnd_probe(struct platform_device *pdev) this->ecc.hwctl = mxc_nand_enable_hwecc; this->ecc.correct = mxc_nand_correct_data; this->ecc.mode = NAND_ECC_HW; - tmp = readw(host->regs + NFC_CONFIG1); - tmp |= NFC_ECC_EN; - writew(tmp, host->regs + NFC_CONFIG1); } else { this->ecc.mode = NAND_ECC_SOFT; - tmp = readw(host->regs + NFC_CONFIG1); - tmp &= ~NFC_ECC_EN; - writew(tmp, host->regs + NFC_CONFIG1); } /* NAND bus width determines access funtions used by upper layer */ @@ -817,8 +824,16 @@ static int __init mxcnd_probe(struct platform_device *pdev) this->options |= NAND_USE_FLASH_BBT; } + init_waitqueue_head(&host->irq_waitq); + + host->irq = platform_get_irq(pdev, 0); + + err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host); + if (err) + goto eirq; + /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, 1)) { + if (nand_scan_ident(mtd, 1, NULL)) { err = -ENXIO; goto escan; } @@ -886,11 +901,14 @@ static int mxcnd_suspend(struct platform_device *pdev, pm_message_t state) int ret = 0; DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND suspend\n"); - if (mtd) { - ret = mtd->suspend(mtd); - /* Disable the NFC clock */ - clk_disable(host->clk); - } + + ret = mtd->suspend(mtd); + + /* + * nand_suspend locks the device for exclusive access, so + * the clock must already be off. + */ + BUG_ON(!ret && host->clk_act); return ret; } @@ -904,11 +922,7 @@ static int mxcnd_resume(struct platform_device *pdev) DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND resume\n"); - if (mtd) { - /* Enable the NFC clock */ - clk_enable(host->clk); - mtd->resume(mtd); - } + mtd->resume(mtd); return ret; } diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 8f2958f..4a7b864 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -108,6 +108,35 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, */ DEFINE_LED_TRIGGER(nand_led_trigger); +static int check_offs_len(struct mtd_info *mtd, + loff_t ofs, uint64_t len) +{ + struct nand_chip *chip = mtd->priv; + int ret = 0; + + /* Start address must align on block boundary */ + if (ofs & ((1 << chip->phys_erase_shift) - 1)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__); + ret = -EINVAL; + } + + /* Length must align on block boundary */ + if (len & ((1 << chip->phys_erase_shift) - 1)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n", + __func__); + ret = -EINVAL; + } + + /* Do not allow past end of device */ + if (ofs + len > mtd->size) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Past end of device\n", + __func__); + ret = -EINVAL; + } + + return ret; +} + /** * nand_release_device - [GENERIC] release chip * @mtd: MTD device structure @@ -318,6 +347,9 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) struct nand_chip *chip = mtd->priv; u16 bad; + if (chip->options & NAND_BB_LAST_PAGE) + ofs += mtd->erasesize - mtd->writesize; + page = (int)(ofs >> chip->page_shift) & chip->pagemask; if (getchip) { @@ -335,14 +367,18 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) bad = cpu_to_le16(chip->read_word(mtd)); if (chip->badblockpos & 0x1) bad >>= 8; - if ((bad & 0xFF) != 0xff) - res = 1; + else + bad &= 0xFF; } else { chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page); - if (chip->read_byte(mtd) != 0xff) - res = 1; + bad = chip->read_byte(mtd); } + if (likely(chip->badblockbits == 8)) + res = bad != 0xFF; + else + res = hweight8(bad) < chip->badblockbits; + if (getchip) nand_release_device(mtd); @@ -363,6 +399,9 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) uint8_t buf[2] = { 0, 0 }; int block, ret; + if (chip->options & NAND_BB_LAST_PAGE) + ofs += mtd->erasesize - mtd->writesize; + /* Get block number */ block = (int)(ofs >> chip->bbt_erase_shift); if (chip->bbt) @@ -401,6 +440,11 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) static int nand_check_wp(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; + + /* broken xD cards report WP despite being writable */ + if (chip->options & NAND_BROKEN_XD) + return 0; + /* Check the WP bit */ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; @@ -744,9 +788,6 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) chip->state = FL_PM_SUSPENDED; spin_unlock(lock); return 0; - } else { - spin_unlock(lock); - return -EAGAIN; } } set_current_state(TASK_UNINTERRUPTIBLE); @@ -835,6 +876,168 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) } /** + * __nand_unlock - [REPLACABLE] unlocks specified locked blockes + * + * @param mtd - mtd info + * @param ofs - offset to start unlock from + * @param len - length to unlock + * @invert - when = 0, unlock the range of blocks within the lower and + * upper boundary address + * whne = 1, unlock the range of blocks outside the boundaries + * of the lower and upper boundary address + * + * @return - unlock status + */ +static int __nand_unlock(struct mtd_info *mtd, loff_t ofs, + uint64_t len, int invert) +{ + int ret = 0; + int status, page; + struct nand_chip *chip = mtd->priv; + + /* Submit address of first page to unlock */ + page = ofs >> chip->page_shift; + chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask); + + /* Submit address of last page to unlock */ + page = (ofs + len) >> chip->page_shift; + chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, + (page | invert) & chip->pagemask); + + /* Call wait ready function */ + status = chip->waitfunc(mtd, chip); + udelay(1000); + /* See if device thinks it succeeded */ + if (status & 0x01) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Error status = 0x%08x\n", + __func__, status); + ret = -EIO; + } + + return ret; +} + +/** + * nand_unlock - [REPLACABLE] unlocks specified locked blockes + * + * @param mtd - mtd info + * @param ofs - offset to start unlock from + * @param len - length to unlock + * + * @return - unlock status + */ +int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + int ret = 0; + int chipnr; + struct nand_chip *chip = mtd->priv; + + DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n", + __func__, (unsigned long long)ofs, len); + + if (check_offs_len(mtd, ofs, len)) + ret = -EINVAL; + + /* Align to last block address if size addresses end of the device */ + if (ofs + len == mtd->size) + len -= mtd->erasesize; + + nand_get_device(chip, mtd, FL_UNLOCKING); + + /* Shift to get chip number */ + chipnr = ofs >> chip->chip_shift; + + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n", + __func__); + ret = -EIO; + goto out; + } + + ret = __nand_unlock(mtd, ofs, len, 0); + +out: + /* de-select the NAND device */ + chip->select_chip(mtd, -1); + + nand_release_device(mtd); + + return ret; +} + +/** + * nand_lock - [REPLACABLE] locks all blockes present in the device + * + * @param mtd - mtd info + * @param ofs - offset to start unlock from + * @param len - length to unlock + * + * @return - lock status + * + * This feature is not support in many NAND parts. 'Micron' NAND parts + * do have this feature, but it allows only to lock all blocks not for + * specified range for block. + * + * Implementing 'lock' feature by making use of 'unlock', for now. + */ +int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + int ret = 0; + int chipnr, status, page; + struct nand_chip *chip = mtd->priv; + + DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n", + __func__, (unsigned long long)ofs, len); + + if (check_offs_len(mtd, ofs, len)) + ret = -EINVAL; + + nand_get_device(chip, mtd, FL_LOCKING); + + /* Shift to get chip number */ + chipnr = ofs >> chip->chip_shift; + + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n", + __func__); + status = MTD_ERASE_FAILED; + ret = -EIO; + goto out; + } + + /* Submit address of first page to lock */ + page = ofs >> chip->page_shift; + chip->cmdfunc(mtd, NAND_CMD_LOCK, -1, page & chip->pagemask); + + /* Call wait ready function */ + status = chip->waitfunc(mtd, chip); + udelay(1000); + /* See if device thinks it succeeded */ + if (status & 0x01) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Error status = 0x%08x\n", + __func__, status); + ret = -EIO; + goto out; + } + + ret = __nand_unlock(mtd, ofs, len, 0x1); + +out: + /* de-select the NAND device */ + chip->select_chip(mtd, -1); + + nand_release_device(mtd); + + return ret; +} + +/** * nand_read_page_raw - [Intern] read raw page data without ecc * @mtd: mtd info structure * @chip: nand chip info structure @@ -1232,6 +1435,9 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, int ret = 0; uint32_t readlen = ops->len; uint32_t oobreadlen = ops->ooblen; + uint32_t max_oobsize = ops->mode == MTD_OOB_AUTO ? + mtd->oobavail : mtd->oobsize; + uint8_t *bufpoi, *oob, *buf; stats = mtd->ecc_stats; @@ -1282,18 +1488,14 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, buf += bytes; if (unlikely(oob)) { - /* Raw mode does data:oob:data:oob */ - if (ops->mode != MTD_OOB_RAW) { - int toread = min(oobreadlen, - chip->ecc.layout->oobavail); - if (toread) { - oob = nand_transfer_oob(chip, - oob, ops, toread); - oobreadlen -= toread; - } - } else - buf = nand_transfer_oob(chip, - buf, ops, mtd->oobsize); + + int toread = min(oobreadlen, max_oobsize); + + if (toread) { + oob = nand_transfer_oob(chip, + oob, ops, toread); + oobreadlen -= toread; + } } if (!(chip->options & NAND_NO_READRDY)) { @@ -1880,11 +2082,9 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, * @oob: oob data buffer * @ops: oob ops structure */ -static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, - struct mtd_oob_ops *ops) +static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len, + struct mtd_oob_ops *ops) { - size_t len = ops->ooblen; - switch(ops->mode) { case MTD_OOB_PLACE: @@ -1939,6 +2139,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, int chipnr, realpage, page, blockmask, column; struct nand_chip *chip = mtd->priv; uint32_t writelen = ops->len; + + uint32_t oobwritelen = ops->ooblen; + uint32_t oobmaxlen = ops->mode == MTD_OOB_AUTO ? + mtd->oobavail : mtd->oobsize; + uint8_t *oob = ops->oobbuf; uint8_t *buf = ops->datbuf; int ret, subpage; @@ -1980,6 +2185,10 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, if (likely(!oob)) memset(chip->oob_poi, 0xff, mtd->oobsize); + /* Don't allow multipage oob writes with offset */ + if (ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) + return -EINVAL; + while(1) { int bytes = mtd->writesize; int cached = writelen > bytes && page != blockmask; @@ -1995,8 +2204,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, wbuf = chip->buffers->databuf; } - if (unlikely(oob)) - oob = nand_fill_oob(chip, oob, ops); + if (unlikely(oob)) { + size_t len = min(oobwritelen, oobmaxlen); + oob = nand_fill_oob(chip, oob, len, ops); + oobwritelen -= len; + } ret = chip->write_page(mtd, chip, wbuf, page, cached, (ops->mode == MTD_OOB_RAW)); @@ -2170,7 +2382,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, chip->pagebuf = -1; memset(chip->oob_poi, 0xff, mtd->oobsize); - nand_fill_oob(chip, ops->oobbuf, ops); + nand_fill_oob(chip, ops->oobbuf, ops->ooblen, ops); status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); memset(chip->oob_poi, 0xff, mtd->oobsize); @@ -2293,25 +2505,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, __func__, (unsigned long long)instr->addr, (unsigned long long)instr->len); - /* Start address must align on block boundary */ - if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__); + if (check_offs_len(mtd, instr->addr, instr->len)) return -EINVAL; - } - - /* Length must align on block boundary */ - if (instr->len & ((1 << chip->phys_erase_shift) - 1)) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n", - __func__); - return -EINVAL; - } - - /* Do not allow erase past end of device */ - if ((instr->len + instr->addr) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Erase past end of device\n", - __func__); - return -EINVAL; - } instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; @@ -2582,11 +2777,11 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) */ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip, - int busw, int *maf_id) + int busw, int *maf_id, + struct nand_flash_dev *type) { - struct nand_flash_dev *type = NULL; int i, dev_id, maf_idx; - int tmp_id, tmp_manf; + u8 id_data[8]; /* Select the device */ chip->select_chip(mtd, 0); @@ -2612,27 +2807,26 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - /* Read manufacturer and device IDs */ + /* Read entire ID string */ - tmp_manf = chip->read_byte(mtd); - tmp_id = chip->read_byte(mtd); + for (i = 0; i < 8; i++) + id_data[i] = chip->read_byte(mtd); - if (tmp_manf != *maf_id || tmp_id != dev_id) { + if (id_data[0] != *maf_id || id_data[1] != dev_id) { printk(KERN_INFO "%s: second ID read did not match " "%02x,%02x against %02x,%02x\n", __func__, - *maf_id, dev_id, tmp_manf, tmp_id); + *maf_id, dev_id, id_data[0], id_data[1]); return ERR_PTR(-ENODEV); } - /* Lookup the flash id */ - for (i = 0; nand_flash_ids[i].name != NULL; i++) { - if (dev_id == nand_flash_ids[i].id) { - type = &nand_flash_ids[i]; - break; - } - } - if (!type) + type = nand_flash_ids; + + for (; type->name != NULL; type++) + if (dev_id == type->id) + break; + + if (!type->name) return ERR_PTR(-ENODEV); if (!mtd->name) @@ -2644,21 +2838,45 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, if (!type->pagesize) { int extid; /* The 3rd id byte holds MLC / multichip data */ - chip->cellinfo = chip->read_byte(mtd); + chip->cellinfo = id_data[2]; /* The 4th id byte is the important one */ - extid = chip->read_byte(mtd); - /* Calc pagesize */ - mtd->writesize = 1024 << (extid & 0x3); - extid >>= 2; - /* Calc oobsize */ - mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); - extid >>= 2; - /* Calc blocksize. Blocksize is multiples of 64KiB */ - mtd->erasesize = (64 * 1024) << (extid & 0x03); - extid >>= 2; - /* Get buswidth information */ - busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + extid = id_data[3]; + /* + * Field definitions are in the following datasheets: + * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32) + * New style (6 byte ID): Samsung K9GAG08U0D (p.40) + * + * Check for wraparound + Samsung ID + nonzero 6th byte + * to decide what to do. + */ + if (id_data[0] == id_data[6] && id_data[1] == id_data[7] && + id_data[0] == NAND_MFR_SAMSUNG && + id_data[5] != 0x00) { + /* Calc pagesize */ + mtd->writesize = 2048 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (extid & 0x03) == 0x01 ? 128 : 218; + extid >>= 2; + /* Calc blocksize */ + mtd->erasesize = (128 * 1024) << + (((extid >> 1) & 0x04) | (extid & 0x03)); + busw = 0; + } else { + /* Calc pagesize */ + mtd->writesize = 1024 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (8 << (extid & 0x01)) * + (mtd->writesize >> 9); + extid >>= 2; + /* Calc blocksize. Blocksize is multiples of 64KiB */ + mtd->erasesize = (64 * 1024) << (extid & 0x03); + extid >>= 2; + /* Get buswidth information */ + busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + } } else { /* * Old devices have chip data hardcoded in the device id table @@ -2704,6 +2922,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, /* Set the bad block position */ chip->badblockpos = mtd->writesize > 512 ? NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; + chip->badblockbits = 8; /* Get chip options, preserve non chip based options */ chip->options &= ~NAND_CHIPOPTIONS_MSK; @@ -2720,6 +2939,15 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; + /* + * Bad block marker is stored in the last page of each block + * on Samsung and Hynix MLC devices + */ + if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) && + (*maf_id == NAND_MFR_SAMSUNG || + *maf_id == NAND_MFR_HYNIX)) + chip->options |= NAND_BB_LAST_PAGE; + /* Check for AND chips with 4 page planes */ if (chip->options & NAND_4PAGE_ARRAY) chip->erase_cmd = multi_erase_cmd; @@ -2741,13 +2969,15 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, * nand_scan_ident - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure * @maxchips: Number of chips to scan for + * @table: Alternative NAND ID table * * This is the first phase of the normal nand_scan() function. It * reads the flash ID and sets up MTD fields accordingly. * * The mtd->owner field must be set to the module of the caller. */ -int nand_scan_ident(struct mtd_info *mtd, int maxchips) +int nand_scan_ident(struct mtd_info *mtd, int maxchips, + struct nand_flash_dev *table) { int i, busw, nand_maf_id; struct nand_chip *chip = mtd->priv; @@ -2759,7 +2989,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips) nand_set_defaults(chip, busw); /* Read the flash type */ - type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id); + type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, table); if (IS_ERR(type)) { if (!(chip->options & NAND_SCAN_SILENT_NODEV)) @@ -2989,7 +3219,8 @@ int nand_scan_tail(struct mtd_info *mtd) /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; - mtd->flags = MTD_CAP_NANDFLASH; + mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : + MTD_CAP_NANDFLASH; mtd->erase = nand_erase; mtd->point = NULL; mtd->unpoint = NULL; @@ -3050,7 +3281,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) BUG(); } - ret = nand_scan_ident(mtd, maxchips); + ret = nand_scan_ident(mtd, maxchips, NULL); if (!ret) ret = nand_scan_tail(mtd); return ret; @@ -3077,6 +3308,8 @@ void nand_release(struct mtd_info *mtd) kfree(chip->buffers); } +EXPORT_SYMBOL_GPL(nand_lock); +EXPORT_SYMBOL_GPL(nand_unlock); EXPORT_SYMBOL_GPL(nand_scan); EXPORT_SYMBOL_GPL(nand_scan_ident); EXPORT_SYMBOL_GPL(nand_scan_tail); diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 55c23e5..ad97c0c 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -237,15 +237,33 @@ static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, size_t len) { struct mtd_oob_ops ops; + int res; ops.mode = MTD_OOB_RAW; ops.ooboffs = 0; ops.ooblen = mtd->oobsize; - ops.oobbuf = buf; - ops.datbuf = buf; - ops.len = len; - return mtd->read_oob(mtd, offs, &ops); + + while (len > 0) { + if (len <= mtd->writesize) { + ops.oobbuf = buf + len; + ops.datbuf = buf; + ops.len = len; + return mtd->read_oob(mtd, offs, &ops); + } else { + ops.oobbuf = buf + mtd->writesize; + ops.datbuf = buf; + ops.len = mtd->writesize; + res = mtd->read_oob(mtd, offs, &ops); + + if (res) + return res; + } + + buf += mtd->oobsize + mtd->writesize; + len -= mtd->writesize; + } + return 0; } /* @@ -414,6 +432,9 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, from = (loff_t)startblock << (this->bbt_erase_shift - 1); } + if (this->options & NAND_BB_LAST_PAGE) + from += mtd->erasesize - (mtd->writesize * len); + for (i = startblock; i < numblocks;) { int ret; diff --git a/drivers/mtd/nand/nand_bcm_umi.h b/drivers/mtd/nand/nand_bcm_umi.h index 7cec2cd..198b304 100644 --- a/drivers/mtd/nand/nand_bcm_umi.h +++ b/drivers/mtd/nand/nand_bcm_umi.h @@ -167,18 +167,27 @@ static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize, int numToRead = 16; /* There are 16 bytes per sector in the OOB */ /* ECC is already paused when this function is called */ + if (pageSize != NAND_DATA_ACCESS_SIZE) { + /* skip BI */ +#if defined(__KERNEL__) && !defined(STANDALONE) + *oobp++ = REG_NAND_DATA8; +#else + REG_NAND_DATA8; +#endif + numToRead--; + } - if (pageSize == NAND_DATA_ACCESS_SIZE) { - while (numToRead > numEccBytes) { - /* skip free oob region */ + while (numToRead > numEccBytes) { + /* skip free oob region */ #if defined(__KERNEL__) && !defined(STANDALONE) - *oobp++ = REG_NAND_DATA8; + *oobp++ = REG_NAND_DATA8; #else - REG_NAND_DATA8; + REG_NAND_DATA8; #endif - numToRead--; - } + numToRead--; + } + if (pageSize == NAND_DATA_ACCESS_SIZE) { /* read ECC bytes before BI */ nand_bcm_umi_bch_resume_read_ecc_calc(); @@ -190,6 +199,7 @@ static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize, #else eccCalc[eccPos++] = REG_NAND_DATA8; #endif + numToRead--; } nand_bcm_umi_bch_pause_read_ecc_calc(); @@ -204,49 +214,18 @@ static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize, numToRead--; } - /* read ECC bytes */ - nand_bcm_umi_bch_resume_read_ecc_calc(); - while (numToRead) { -#if defined(__KERNEL__) && !defined(STANDALONE) - *oobp = REG_NAND_DATA8; - eccCalc[eccPos++] = *oobp; - oobp++; -#else - eccCalc[eccPos++] = REG_NAND_DATA8; -#endif - numToRead--; - } - } else { - /* skip BI */ + } + /* read ECC bytes */ + nand_bcm_umi_bch_resume_read_ecc_calc(); + while (numToRead) { #if defined(__KERNEL__) && !defined(STANDALONE) - *oobp++ = REG_NAND_DATA8; + *oobp = REG_NAND_DATA8; + eccCalc[eccPos++] = *oobp; + oobp++; #else - REG_NAND_DATA8; + eccCalc[eccPos++] = REG_NAND_DATA8; #endif numToRead--; - - while (numToRead > numEccBytes) { - /* skip free oob region */ -#if defined(__KERNEL__) && !defined(STANDALONE) - *oobp++ = REG_NAND_DATA8; -#else - REG_NAND_DATA8; -#endif - numToRead--; - } - - /* read ECC bytes */ - nand_bcm_umi_bch_resume_read_ecc_calc(); - while (numToRead) { -#if defined(__KERNEL__) && !defined(STANDALONE) - *oobp = REG_NAND_DATA8; - eccCalc[eccPos++] = *oobp; - oobp++; -#else - eccCalc[eccPos++] = REG_NAND_DATA8; -#endif - numToRead--; - } } } diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 69ee2c9..89907ed9 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -82,6 +82,7 @@ struct nand_flash_dev nand_flash_ids[] = { /* 1 Gigabit */ {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS}, {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS}, + {"NAND 128MiB 3,3V 8-bit", 0xD1, 0, 128, 0, LP_OPTIONS}, {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16}, {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16}, diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 7281000..261337e 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -80,6 +80,9 @@ #ifndef CONFIG_NANDSIM_DBG #define CONFIG_NANDSIM_DBG 0 #endif +#ifndef CONFIG_NANDSIM_MAX_PARTS +#define CONFIG_NANDSIM_MAX_PARTS 32 +#endif static uint first_id_byte = CONFIG_NANDSIM_FIRST_ID_BYTE; static uint second_id_byte = CONFIG_NANDSIM_SECOND_ID_BYTE; @@ -94,7 +97,7 @@ static uint bus_width = CONFIG_NANDSIM_BUS_WIDTH; static uint do_delays = CONFIG_NANDSIM_DO_DELAYS; static uint log = CONFIG_NANDSIM_LOG; static uint dbg = CONFIG_NANDSIM_DBG; -static unsigned long parts[MAX_MTD_DEVICES]; +static unsigned long parts[CONFIG_NANDSIM_MAX_PARTS]; static unsigned int parts_num; static char *badblocks = NULL; static char *weakblocks = NULL; @@ -135,8 +138,8 @@ MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read I MODULE_PARM_DESC(access_delay, "Initial page access delay (microseconds)"); MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds"); MODULE_PARM_DESC(erase_delay, "Sector erase delay (milliseconds)"); -MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanodeconds)"); -MODULE_PARM_DESC(input_cycle, "Word input (to flash) time (nanodeconds)"); +MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanoseconds)"); +MODULE_PARM_DESC(input_cycle, "Word input (to flash) time (nanoseconds)"); MODULE_PARM_DESC(bus_width, "Chip's bus width (8- or 16-bit)"); MODULE_PARM_DESC(do_delays, "Simulate NAND delays using busy-waits if not zero"); MODULE_PARM_DESC(log, "Perform logging if not zero"); @@ -288,7 +291,7 @@ union ns_mem { * The structure which describes all the internal simulator data. */ struct nandsim { - struct mtd_partition partitions[MAX_MTD_DEVICES]; + struct mtd_partition partitions[CONFIG_NANDSIM_MAX_PARTS]; unsigned int nbparts; uint busw; /* flash chip bus width (8 or 16) */ @@ -312,7 +315,7 @@ struct nandsim { union ns_mem buf; /* NAND flash "geometry" */ - struct nandsin_geometry { + struct { uint64_t totsz; /* total flash size, bytes */ uint32_t secsz; /* flash sector (erase block) size, bytes */ uint pgsz; /* NAND flash page size, bytes */ @@ -331,7 +334,7 @@ struct nandsim { } geom; /* NAND flash internal registers */ - struct nandsim_regs { + struct { unsigned command; /* the command register */ u_char status; /* the status register */ uint row; /* the page number */ @@ -342,7 +345,7 @@ struct nandsim { } regs; /* NAND flash lines state */ - struct ns_lines_status { + struct { int ce; /* chip Enable */ int cle; /* command Latch Enable */ int ale; /* address Latch Enable */ diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c index 1f6f741..8c0b693 100644 --- a/drivers/mtd/nand/nomadik_nand.c +++ b/drivers/mtd/nand/nomadik_nand.c @@ -105,21 +105,21 @@ static int nomadik_nand_probe(struct platform_device *pdev) ret = -EIO; goto err_unmap; } - host->addr_va = ioremap(res->start, res->end - res->start + 1); + host->addr_va = ioremap(res->start, resource_size(res)); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); if (!res) { ret = -EIO; goto err_unmap; } - host->data_va = ioremap(res->start, res->end - res->start + 1); + host->data_va = ioremap(res->start, resource_size(res)); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd"); if (!res) { ret = -EIO; goto err_unmap; } - host->cmd_va = ioremap(res->start, res->end - res->start + 1); + host->cmd_va = ioremap(res->start, resource_size(res)); if (!host->addr_va || !host->data_va || !host->cmd_va) { ret = -ENOMEM; diff --git a/drivers/mtd/nand/w90p910_nand.c b/drivers/mtd/nand/nuc900_nand.c index 7680e73..6eddf73 100644 --- a/drivers/mtd/nand/w90p910_nand.c +++ b/drivers/mtd/nand/nuc900_nand.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009 Nuvoton technology corporation. + * Copyright © 2009 Nuvoton technology corporation. * * Wan ZongShun <mcuos.com@gmail.com> * @@ -55,7 +55,7 @@ #define write_addr_reg(dev, val) \ __raw_writel((val), (dev)->reg + REG_SMADDR) -struct w90p910_nand { +struct nuc900_nand { struct mtd_info mtd; struct nand_chip chip; void __iomem *reg; @@ -76,49 +76,49 @@ static const struct mtd_partition partitions[] = { } }; -static unsigned char w90p910_nand_read_byte(struct mtd_info *mtd) +static unsigned char nuc900_nand_read_byte(struct mtd_info *mtd) { unsigned char ret; - struct w90p910_nand *nand; + struct nuc900_nand *nand; - nand = container_of(mtd, struct w90p910_nand, mtd); + nand = container_of(mtd, struct nuc900_nand, mtd); ret = (unsigned char)read_data_reg(nand); return ret; } -static void w90p910_nand_read_buf(struct mtd_info *mtd, - unsigned char *buf, int len) +static void nuc900_nand_read_buf(struct mtd_info *mtd, + unsigned char *buf, int len) { int i; - struct w90p910_nand *nand; + struct nuc900_nand *nand; - nand = container_of(mtd, struct w90p910_nand, mtd); + nand = container_of(mtd, struct nuc900_nand, mtd); for (i = 0; i < len; i++) buf[i] = (unsigned char)read_data_reg(nand); } -static void w90p910_nand_write_buf(struct mtd_info *mtd, - const unsigned char *buf, int len) +static void nuc900_nand_write_buf(struct mtd_info *mtd, + const unsigned char *buf, int len) { int i; - struct w90p910_nand *nand; + struct nuc900_nand *nand; - nand = container_of(mtd, struct w90p910_nand, mtd); + nand = container_of(mtd, struct nuc900_nand, mtd); for (i = 0; i < len; i++) write_data_reg(nand, buf[i]); } -static int w90p910_verify_buf(struct mtd_info *mtd, - const unsigned char *buf, int len) +static int nuc900_verify_buf(struct mtd_info *mtd, + const unsigned char *buf, int len) { int i; - struct w90p910_nand *nand; + struct nuc900_nand *nand; - nand = container_of(mtd, struct w90p910_nand, mtd); + nand = container_of(mtd, struct nuc900_nand, mtd); for (i = 0; i < len; i++) { if (buf[i] != (unsigned char)read_data_reg(nand)) @@ -128,7 +128,7 @@ static int w90p910_verify_buf(struct mtd_info *mtd, return 0; } -static int w90p910_check_rb(struct w90p910_nand *nand) +static int nuc900_check_rb(struct nuc900_nand *nand) { unsigned int val; spin_lock(&nand->lock); @@ -139,24 +139,24 @@ static int w90p910_check_rb(struct w90p910_nand *nand) return val; } -static int w90p910_nand_devready(struct mtd_info *mtd) +static int nuc900_nand_devready(struct mtd_info *mtd) { - struct w90p910_nand *nand; + struct nuc900_nand *nand; int ready; - nand = container_of(mtd, struct w90p910_nand, mtd); + nand = container_of(mtd, struct nuc900_nand, mtd); - ready = (w90p910_check_rb(nand)) ? 1 : 0; + ready = (nuc900_check_rb(nand)) ? 1 : 0; return ready; } -static void w90p910_nand_command_lp(struct mtd_info *mtd, - unsigned int command, int column, int page_addr) +static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) { register struct nand_chip *chip = mtd->priv; - struct w90p910_nand *nand; + struct nuc900_nand *nand; - nand = container_of(mtd, struct w90p910_nand, mtd); + nand = container_of(mtd, struct nuc900_nand, mtd); if (command == NAND_CMD_READOOB) { column += mtd->writesize; @@ -212,7 +212,7 @@ static void w90p910_nand_command_lp(struct mtd_info *mtd, write_cmd_reg(nand, NAND_CMD_STATUS); write_cmd_reg(nand, command); - while (!w90p910_check_rb(nand)) + while (!nuc900_check_rb(nand)) ; return; @@ -241,7 +241,7 @@ static void w90p910_nand_command_lp(struct mtd_info *mtd, } -static void w90p910_nand_enable(struct w90p910_nand *nand) +static void nuc900_nand_enable(struct nuc900_nand *nand) { unsigned int val; spin_lock(&nand->lock); @@ -262,37 +262,37 @@ static void w90p910_nand_enable(struct w90p910_nand *nand) spin_unlock(&nand->lock); } -static int __devinit w90p910_nand_probe(struct platform_device *pdev) +static int __devinit nuc900_nand_probe(struct platform_device *pdev) { - struct w90p910_nand *w90p910_nand; + struct nuc900_nand *nuc900_nand; struct nand_chip *chip; int retval; struct resource *res; retval = 0; - w90p910_nand = kzalloc(sizeof(struct w90p910_nand), GFP_KERNEL); - if (!w90p910_nand) + nuc900_nand = kzalloc(sizeof(struct nuc900_nand), GFP_KERNEL); + if (!nuc900_nand) return -ENOMEM; - chip = &(w90p910_nand->chip); + chip = &(nuc900_nand->chip); - w90p910_nand->mtd.priv = chip; - w90p910_nand->mtd.owner = THIS_MODULE; - spin_lock_init(&w90p910_nand->lock); + nuc900_nand->mtd.priv = chip; + nuc900_nand->mtd.owner = THIS_MODULE; + spin_lock_init(&nuc900_nand->lock); - w90p910_nand->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(w90p910_nand->clk)) { + nuc900_nand->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(nuc900_nand->clk)) { retval = -ENOENT; goto fail1; } - clk_enable(w90p910_nand->clk); - - chip->cmdfunc = w90p910_nand_command_lp; - chip->dev_ready = w90p910_nand_devready; - chip->read_byte = w90p910_nand_read_byte; - chip->write_buf = w90p910_nand_write_buf; - chip->read_buf = w90p910_nand_read_buf; - chip->verify_buf = w90p910_verify_buf; + clk_enable(nuc900_nand->clk); + + chip->cmdfunc = nuc900_nand_command_lp; + chip->dev_ready = nuc900_nand_devready; + chip->read_byte = nuc900_nand_read_byte; + chip->write_buf = nuc900_nand_write_buf; + chip->read_buf = nuc900_nand_read_buf; + chip->verify_buf = nuc900_verify_buf; chip->chip_delay = 50; chip->options = 0; chip->ecc.mode = NAND_ECC_SOFT; @@ -308,75 +308,75 @@ static int __devinit w90p910_nand_probe(struct platform_device *pdev) goto fail1; } - w90p910_nand->reg = ioremap(res->start, resource_size(res)); - if (!w90p910_nand->reg) { + nuc900_nand->reg = ioremap(res->start, resource_size(res)); + if (!nuc900_nand->reg) { retval = -ENOMEM; goto fail2; } - w90p910_nand_enable(w90p910_nand); + nuc900_nand_enable(nuc900_nand); - if (nand_scan(&(w90p910_nand->mtd), 1)) { + if (nand_scan(&(nuc900_nand->mtd), 1)) { retval = -ENXIO; goto fail3; } - add_mtd_partitions(&(w90p910_nand->mtd), partitions, + add_mtd_partitions(&(nuc900_nand->mtd), partitions, ARRAY_SIZE(partitions)); - platform_set_drvdata(pdev, w90p910_nand); + platform_set_drvdata(pdev, nuc900_nand); return retval; -fail3: iounmap(w90p910_nand->reg); +fail3: iounmap(nuc900_nand->reg); fail2: release_mem_region(res->start, resource_size(res)); -fail1: kfree(w90p910_nand); +fail1: kfree(nuc900_nand); return retval; } -static int __devexit w90p910_nand_remove(struct platform_device *pdev) +static int __devexit nuc900_nand_remove(struct platform_device *pdev) { - struct w90p910_nand *w90p910_nand = platform_get_drvdata(pdev); + struct nuc900_nand *nuc900_nand = platform_get_drvdata(pdev); struct resource *res; - iounmap(w90p910_nand->reg); + iounmap(nuc900_nand->reg); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, resource_size(res)); - clk_disable(w90p910_nand->clk); - clk_put(w90p910_nand->clk); + clk_disable(nuc900_nand->clk); + clk_put(nuc900_nand->clk); - kfree(w90p910_nand); + kfree(nuc900_nand); platform_set_drvdata(pdev, NULL); return 0; } -static struct platform_driver w90p910_nand_driver = { - .probe = w90p910_nand_probe, - .remove = __devexit_p(w90p910_nand_remove), +static struct platform_driver nuc900_nand_driver = { + .probe = nuc900_nand_probe, + .remove = __devexit_p(nuc900_nand_remove), .driver = { - .name = "w90p910-fmi", + .name = "nuc900-fmi", .owner = THIS_MODULE, }, }; -static int __init w90p910_nand_init(void) +static int __init nuc900_nand_init(void) { - return platform_driver_register(&w90p910_nand_driver); + return platform_driver_register(&nuc900_nand_driver); } -static void __exit w90p910_nand_exit(void) +static void __exit nuc900_nand_exit(void) { - platform_driver_unregister(&w90p910_nand_driver); + platform_driver_unregister(&nuc900_nand_driver); } -module_init(w90p910_nand_init); -module_exit(w90p910_nand_exit); +module_init(nuc900_nand_init); +module_exit(nuc900_nand_exit); MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>"); -MODULE_DESCRIPTION("w90p910 nand driver!"); +MODULE_DESCRIPTION("w90p910/NUC9xx nand driver!"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:w90p910-fmi"); +MODULE_ALIAS("platform:nuc900-fmi"); diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 7545568..ee87325 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -292,11 +292,14 @@ static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len) u32 *p = (u32 *)buf; /* take care of subpage reads */ - for (; len % 4 != 0; ) { - *buf++ = __raw_readb(info->nand.IO_ADDR_R); - len--; + if (len % 4) { + if (info->nand.options & NAND_BUSWIDTH_16) + omap_read_buf16(mtd, buf, len % 4); + else + omap_read_buf8(mtd, buf, len % 4); + p = (u32 *) (buf + len % 4); + len -= len % 4; } - p = (u32 *) buf; /* configure and start prefetch transfer */ ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x0); @@ -502,7 +505,7 @@ static void omap_write_buf_dma_pref(struct mtd_info *mtd, omap_write_buf_pref(mtd, buf, len); else /* start transfer in DMA mode */ - omap_nand_dma_transfer(mtd, buf, len, 0x1); + omap_nand_dma_transfer(mtd, (u_char *) buf, len, 0x1); } /** @@ -1028,7 +1031,8 @@ out_free_info: static int omap_nand_remove(struct platform_device *pdev) { struct mtd_info *mtd = platform_get_drvdata(pdev); - struct omap_nand_info *info = mtd->priv; + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); platform_set_drvdata(pdev, NULL); if (use_dma) diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index f59c074..da6e753 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -60,7 +60,13 @@ static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) } buf64 = (uint64_t *)buf; while (i < len/8) { - uint64_t x; + /* + * Since GCC has no proper constraint (PR 43518) + * force x variable to r2/r3 registers as ldrd instruction + * requires first register to be even. + */ + register uint64_t x asm ("r2"); + asm volatile ("ldrd\t%0, [%1]" : "=&r" (x) : "r" (io_base)); buf64[i++] = x; } @@ -74,6 +80,7 @@ static int __init orion_nand_probe(struct platform_device *pdev) struct mtd_info *mtd; struct nand_chip *nc; struct orion_nand_data *board; + struct resource *res; void __iomem *io_base; int ret = 0; #ifdef CONFIG_MTD_PARTITIONS @@ -89,8 +96,13 @@ static int __init orion_nand_probe(struct platform_device *pdev) } mtd = (struct mtd_info *)(nc + 1); - io_base = ioremap(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start + 1); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + goto no_res; + } + + io_base = ioremap(res->start, resource_size(res)); if (!io_base) { printk(KERN_ERR "orion_nand: ioremap failed\n"); ret = -EIO; @@ -114,6 +126,9 @@ static int __init orion_nand_probe(struct platform_device *pdev) if (board->width == 16) nc->options |= NAND_BUSWIDTH_16; + if (board->dev_ready) + nc->dev_ready = board->dev_ready; + platform_set_drvdata(pdev, mtd); if (nand_scan(mtd, 1)) { diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c index a97e9c9..f02af24 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/pasemi_nand.c @@ -209,7 +209,7 @@ static int __devexit pasemi_nand_remove(struct of_device *ofdev) return 0; } -static struct of_device_id pasemi_nand_match[] = +static const struct of_device_id pasemi_nand_match[] = { { .compatible = "pasemi,localbus-nand", diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 5d55152..e02fa4f 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1320,6 +1320,17 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) goto fail_free_irq; } + if (mtd_has_cmdlinepart()) { + static const char *probes[] = { "cmdlinepart", NULL }; + struct mtd_partition *parts; + int nr_parts; + + nr_parts = parse_mtd_partitions(mtd, probes, &parts, 0); + + if (nr_parts) + return add_mtd_partitions(mtd, parts, nr_parts); + } + return add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts); fail_free_irq: diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c new file mode 100644 index 0000000..78a4232 --- /dev/null +++ b/drivers/mtd/nand/r852.c @@ -0,0 +1,1140 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * driver for Ricoh xD readers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/jiffies.h> +#include <linux/workqueue.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <asm/byteorder.h> +#include <linux/sched.h> +#include "sm_common.h" +#include "r852.h" + + +static int r852_enable_dma = 1; +module_param(r852_enable_dma, bool, S_IRUGO); +MODULE_PARM_DESC(r852_enable_dma, "Enable usage of the DMA (default)"); + +static int debug; +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + +/* read register */ +static inline uint8_t r852_read_reg(struct r852_device *dev, int address) +{ + uint8_t reg = readb(dev->mmio + address); + return reg; +} + +/* write register */ +static inline void r852_write_reg(struct r852_device *dev, + int address, uint8_t value) +{ + writeb(value, dev->mmio + address); + mmiowb(); +} + + +/* read dword sized register */ +static inline uint32_t r852_read_reg_dword(struct r852_device *dev, int address) +{ + uint32_t reg = le32_to_cpu(readl(dev->mmio + address)); + return reg; +} + +/* write dword sized register */ +static inline void r852_write_reg_dword(struct r852_device *dev, + int address, uint32_t value) +{ + writel(cpu_to_le32(value), dev->mmio + address); + mmiowb(); +} + +/* returns pointer to our private structure */ +static inline struct r852_device *r852_get_dev(struct mtd_info *mtd) +{ + struct nand_chip *chip = (struct nand_chip *)mtd->priv; + return (struct r852_device *)chip->priv; +} + + +/* check if controller supports dma */ +static void r852_dma_test(struct r852_device *dev) +{ + dev->dma_usable = (r852_read_reg(dev, R852_DMA_CAP) & + (R852_DMA1 | R852_DMA2)) == (R852_DMA1 | R852_DMA2); + + if (!dev->dma_usable) + message("Non dma capable device detected, dma disabled"); + + if (!r852_enable_dma) { + message("disabling dma on user request"); + dev->dma_usable = 0; + } +} + +/* + * Enable dma. Enables ether first or second stage of the DMA, + * Expects dev->dma_dir and dev->dma_state be set + */ +static void r852_dma_enable(struct r852_device *dev) +{ + uint8_t dma_reg, dma_irq_reg; + + /* Set up dma settings */ + dma_reg = r852_read_reg_dword(dev, R852_DMA_SETTINGS); + dma_reg &= ~(R852_DMA_READ | R852_DMA_INTERNAL | R852_DMA_MEMORY); + + if (dev->dma_dir) + dma_reg |= R852_DMA_READ; + + if (dev->dma_state == DMA_INTERNAL) { + dma_reg |= R852_DMA_INTERNAL; + /* Precaution to make sure HW doesn't write */ + /* to random kernel memory */ + r852_write_reg_dword(dev, R852_DMA_ADDR, + cpu_to_le32(dev->phys_bounce_buffer)); + } else { + dma_reg |= R852_DMA_MEMORY; + r852_write_reg_dword(dev, R852_DMA_ADDR, + cpu_to_le32(dev->phys_dma_addr)); + } + + /* Precaution: make sure write reached the device */ + r852_read_reg_dword(dev, R852_DMA_ADDR); + + r852_write_reg_dword(dev, R852_DMA_SETTINGS, dma_reg); + + /* Set dma irq */ + dma_irq_reg = r852_read_reg_dword(dev, R852_DMA_IRQ_ENABLE); + r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, + dma_irq_reg | + R852_DMA_IRQ_INTERNAL | + R852_DMA_IRQ_ERROR | + R852_DMA_IRQ_MEMORY); +} + +/* + * Disable dma, called from the interrupt handler, which specifies + * success of the operation via 'error' argument + */ +static void r852_dma_done(struct r852_device *dev, int error) +{ + WARN_ON(dev->dma_stage == 0); + + r852_write_reg_dword(dev, R852_DMA_IRQ_STA, + r852_read_reg_dword(dev, R852_DMA_IRQ_STA)); + + r852_write_reg_dword(dev, R852_DMA_SETTINGS, 0); + r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, 0); + + /* Precaution to make sure HW doesn't write to random kernel memory */ + r852_write_reg_dword(dev, R852_DMA_ADDR, + cpu_to_le32(dev->phys_bounce_buffer)); + r852_read_reg_dword(dev, R852_DMA_ADDR); + + dev->dma_error = error; + dev->dma_stage = 0; + + if (dev->phys_dma_addr && dev->phys_dma_addr != dev->phys_bounce_buffer) + pci_unmap_single(dev->pci_dev, dev->phys_dma_addr, R852_DMA_LEN, + dev->dma_dir ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); + complete(&dev->dma_done); +} + +/* + * Wait, till dma is done, which includes both phases of it + */ +static int r852_dma_wait(struct r852_device *dev) +{ + long timeout = wait_for_completion_timeout(&dev->dma_done, + msecs_to_jiffies(1000)); + if (!timeout) { + dbg("timeout waiting for DMA interrupt"); + return -ETIMEDOUT; + } + + return 0; +} + +/* + * Read/Write one page using dma. Only pages can be read (512 bytes) +*/ +static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read) +{ + int bounce = 0; + unsigned long flags; + int error; + + dev->dma_error = 0; + + /* Set dma direction */ + dev->dma_dir = do_read; + dev->dma_stage = 1; + + dbg_verbose("doing dma %s ", do_read ? "read" : "write"); + + /* Set intial dma state: for reading first fill on board buffer, + from device, for writes first fill the buffer from memory*/ + dev->dma_state = do_read ? DMA_INTERNAL : DMA_MEMORY; + + /* if incoming buffer is not page aligned, we should do bounce */ + if ((unsigned long)buf & (R852_DMA_LEN-1)) + bounce = 1; + + if (!bounce) { + dev->phys_dma_addr = pci_map_single(dev->pci_dev, (void *)buf, + R852_DMA_LEN, + (do_read ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE)); + + if (pci_dma_mapping_error(dev->pci_dev, dev->phys_dma_addr)) + bounce = 1; + } + + if (bounce) { + dbg_verbose("dma: using bounce buffer"); + dev->phys_dma_addr = dev->phys_bounce_buffer; + if (!do_read) + memcpy(dev->bounce_buffer, buf, R852_DMA_LEN); + } + + /* Enable DMA */ + spin_lock_irqsave(&dev->irqlock, flags); + r852_dma_enable(dev); + spin_unlock_irqrestore(&dev->irqlock, flags); + + /* Wait till complete */ + error = r852_dma_wait(dev); + + if (error) { + r852_dma_done(dev, error); + return; + } + + if (do_read && bounce) + memcpy((void *)buf, dev->bounce_buffer, R852_DMA_LEN); +} + +/* + * Program data lines of the nand chip to send data to it + */ +void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct r852_device *dev = r852_get_dev(mtd); + uint32_t reg; + + /* Don't allow any access to hardware if we suspect card removal */ + if (dev->card_unstable) + return; + + /* Special case for whole sector read */ + if (len == R852_DMA_LEN && dev->dma_usable) { + r852_do_dma(dev, (uint8_t *)buf, 0); + return; + } + + /* write DWORD chinks - faster */ + while (len) { + reg = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24; + r852_write_reg_dword(dev, R852_DATALINE, reg); + buf += 4; + len -= 4; + + } + + /* write rest */ + while (len) + r852_write_reg(dev, R852_DATALINE, *buf++); +} + +/* + * Read data lines of the nand chip to retrieve data + */ +void r852_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct r852_device *dev = r852_get_dev(mtd); + uint32_t reg; + + if (dev->card_unstable) { + /* since we can't signal error here, at least, return + predictable buffer */ + memset(buf, 0, len); + return; + } + + /* special case for whole sector read */ + if (len == R852_DMA_LEN && dev->dma_usable) { + r852_do_dma(dev, buf, 1); + return; + } + + /* read in dword sized chunks */ + while (len >= 4) { + + reg = r852_read_reg_dword(dev, R852_DATALINE); + *buf++ = reg & 0xFF; + *buf++ = (reg >> 8) & 0xFF; + *buf++ = (reg >> 16) & 0xFF; + *buf++ = (reg >> 24) & 0xFF; + len -= 4; + } + + /* read the reset by bytes */ + while (len--) + *buf++ = r852_read_reg(dev, R852_DATALINE); +} + +/* + * Read one byte from nand chip + */ +static uint8_t r852_read_byte(struct mtd_info *mtd) +{ + struct r852_device *dev = r852_get_dev(mtd); + + /* Same problem as in r852_read_buf.... */ + if (dev->card_unstable) + return 0; + + return r852_read_reg(dev, R852_DATALINE); +} + + +/* + * Readback the buffer to verify it + */ +int r852_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct r852_device *dev = r852_get_dev(mtd); + + /* We can't be sure about anything here... */ + if (dev->card_unstable) + return -1; + + /* This will never happen, unless you wired up a nand chip + with > 512 bytes page size to the reader */ + if (len > SM_SECTOR_SIZE) + return 0; + + r852_read_buf(mtd, dev->tmp_buffer, len); + return memcmp(buf, dev->tmp_buffer, len); +} + +/* + * Control several chip lines & send commands + */ +void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl) +{ + struct r852_device *dev = r852_get_dev(mtd); + + if (dev->card_unstable) + return; + + if (ctrl & NAND_CTRL_CHANGE) { + + dev->ctlreg &= ~(R852_CTL_DATA | R852_CTL_COMMAND | + R852_CTL_ON | R852_CTL_CARDENABLE); + + if (ctrl & NAND_ALE) + dev->ctlreg |= R852_CTL_DATA; + + if (ctrl & NAND_CLE) + dev->ctlreg |= R852_CTL_COMMAND; + + if (ctrl & NAND_NCE) + dev->ctlreg |= (R852_CTL_CARDENABLE | R852_CTL_ON); + else + dev->ctlreg &= ~R852_CTL_WRITE; + + /* when write is stareted, enable write access */ + if (dat == NAND_CMD_ERASE1) + dev->ctlreg |= R852_CTL_WRITE; + + r852_write_reg(dev, R852_CTL, dev->ctlreg); + } + + /* HACK: NAND_CMD_SEQIN is called without NAND_CTRL_CHANGE, but we need + to set write mode */ + if (dat == NAND_CMD_SEQIN && (dev->ctlreg & R852_CTL_COMMAND)) { + dev->ctlreg |= R852_CTL_WRITE; + r852_write_reg(dev, R852_CTL, dev->ctlreg); + } + + if (dat != NAND_CMD_NONE) + r852_write_reg(dev, R852_DATALINE, dat); +} + +/* + * Wait till card is ready. + * based on nand_wait, but returns errors on DMA error + */ +int r852_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct r852_device *dev = (struct r852_device *)chip->priv; + + unsigned long timeout; + int status; + + timeout = jiffies + (chip->state == FL_ERASING ? + msecs_to_jiffies(400) : msecs_to_jiffies(20)); + + while (time_before(jiffies, timeout)) + if (chip->dev_ready(mtd)) + break; + + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + status = (int)chip->read_byte(mtd); + + /* Unfortunelly, no way to send detailed error status... */ + if (dev->dma_error) { + status |= NAND_STATUS_FAIL; + dev->dma_error = 0; + } + return status; +} + +/* + * Check if card is ready + */ + +int r852_ready(struct mtd_info *mtd) +{ + struct r852_device *dev = r852_get_dev(mtd); + return !(r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_BUSY); +} + + +/* + * Set ECC engine mode +*/ + +void r852_ecc_hwctl(struct mtd_info *mtd, int mode) +{ + struct r852_device *dev = r852_get_dev(mtd); + + if (dev->card_unstable) + return; + + switch (mode) { + case NAND_ECC_READ: + case NAND_ECC_WRITE: + /* enable ecc generation/check*/ + dev->ctlreg |= R852_CTL_ECC_ENABLE; + + /* flush ecc buffer */ + r852_write_reg(dev, R852_CTL, + dev->ctlreg | R852_CTL_ECC_ACCESS); + + r852_read_reg_dword(dev, R852_DATALINE); + r852_write_reg(dev, R852_CTL, dev->ctlreg); + return; + + case NAND_ECC_READSYN: + /* disable ecc generation */ + dev->ctlreg &= ~R852_CTL_ECC_ENABLE; + r852_write_reg(dev, R852_CTL, dev->ctlreg); + } +} + +/* + * Calculate ECC, only used for writes + */ + +int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + struct r852_device *dev = r852_get_dev(mtd); + struct sm_oob *oob = (struct sm_oob *)ecc_code; + uint32_t ecc1, ecc2; + + if (dev->card_unstable) + return 0; + + dev->ctlreg &= ~R852_CTL_ECC_ENABLE; + r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS); + + ecc1 = r852_read_reg_dword(dev, R852_DATALINE); + ecc2 = r852_read_reg_dword(dev, R852_DATALINE); + + oob->ecc1[0] = (ecc1) & 0xFF; + oob->ecc1[1] = (ecc1 >> 8) & 0xFF; + oob->ecc1[2] = (ecc1 >> 16) & 0xFF; + + oob->ecc2[0] = (ecc2) & 0xFF; + oob->ecc2[1] = (ecc2 >> 8) & 0xFF; + oob->ecc2[2] = (ecc2 >> 16) & 0xFF; + + r852_write_reg(dev, R852_CTL, dev->ctlreg); + return 0; +} + +/* + * Correct the data using ECC, hw did almost everything for us + */ + +int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + uint16_t ecc_reg; + uint8_t ecc_status, err_byte; + int i, error = 0; + + struct r852_device *dev = r852_get_dev(mtd); + + if (dev->card_unstable) + return 0; + + r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS); + ecc_reg = r852_read_reg_dword(dev, R852_DATALINE); + r852_write_reg(dev, R852_CTL, dev->ctlreg); + + for (i = 0 ; i <= 1 ; i++) { + + ecc_status = (ecc_reg >> 8) & 0xFF; + + /* ecc uncorrectable error */ + if (ecc_status & R852_ECC_FAIL) { + dbg("ecc: unrecoverable error, in half %d", i); + error = -1; + goto exit; + } + + /* correctable error */ + if (ecc_status & R852_ECC_CORRECTABLE) { + + err_byte = ecc_reg & 0xFF; + dbg("ecc: recoverable error, " + "in half %d, byte %d, bit %d", i, + err_byte, ecc_status & R852_ECC_ERR_BIT_MSK); + + dat[err_byte] ^= + 1 << (ecc_status & R852_ECC_ERR_BIT_MSK); + error++; + } + + dat += 256; + ecc_reg >>= 16; + } +exit: + return error; +} + +/* + * This is copy of nand_read_oob_std + * nand_read_oob_syndrome assumes we can send column address - we can't + */ +static int r852_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + sndcmd = 0; + } + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return sndcmd; +} + +/* + * Start the nand engine + */ + +void r852_engine_enable(struct r852_device *dev) +{ + if (r852_read_reg_dword(dev, R852_HW) & R852_HW_UNKNOWN) { + r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON); + r852_write_reg_dword(dev, R852_HW, R852_HW_ENABLED); + } else { + r852_write_reg_dword(dev, R852_HW, R852_HW_ENABLED); + r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON); + } + msleep(300); + r852_write_reg(dev, R852_CTL, 0); +} + + +/* + * Stop the nand engine + */ + +void r852_engine_disable(struct r852_device *dev) +{ + r852_write_reg_dword(dev, R852_HW, 0); + r852_write_reg(dev, R852_CTL, R852_CTL_RESET); +} + +/* + * Test if card is present + */ + +void r852_card_update_present(struct r852_device *dev) +{ + unsigned long flags; + uint8_t reg; + + spin_lock_irqsave(&dev->irqlock, flags); + reg = r852_read_reg(dev, R852_CARD_STA); + dev->card_detected = !!(reg & R852_CARD_STA_PRESENT); + spin_unlock_irqrestore(&dev->irqlock, flags); +} + +/* + * Update card detection IRQ state according to current card state + * which is read in r852_card_update_present + */ +void r852_update_card_detect(struct r852_device *dev) +{ + int card_detect_reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE); + dev->card_unstable = 0; + + card_detect_reg &= ~(R852_CARD_IRQ_REMOVE | R852_CARD_IRQ_INSERT); + card_detect_reg |= R852_CARD_IRQ_GENABLE; + + card_detect_reg |= dev->card_detected ? + R852_CARD_IRQ_REMOVE : R852_CARD_IRQ_INSERT; + + r852_write_reg(dev, R852_CARD_IRQ_ENABLE, card_detect_reg); +} + +ssize_t r852_media_type_show(struct device *sys_dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = container_of(sys_dev, struct mtd_info, dev); + struct r852_device *dev = r852_get_dev(mtd); + char *data = dev->sm ? "smartmedia" : "xd"; + + strcpy(buf, data); + return strlen(data); +} + +DEVICE_ATTR(media_type, S_IRUGO, r852_media_type_show, NULL); + + +/* Detect properties of card in slot */ +void r852_update_media_status(struct r852_device *dev) +{ + uint8_t reg; + unsigned long flags; + int readonly; + + spin_lock_irqsave(&dev->irqlock, flags); + if (!dev->card_detected) { + message("card removed"); + spin_unlock_irqrestore(&dev->irqlock, flags); + return ; + } + + readonly = r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_RO; + reg = r852_read_reg(dev, R852_DMA_CAP); + dev->sm = (reg & (R852_DMA1 | R852_DMA2)) && (reg & R852_SMBIT); + + message("detected %s %s card in slot", + dev->sm ? "SmartMedia" : "xD", + readonly ? "readonly" : "writeable"); + + dev->readonly = readonly; + spin_unlock_irqrestore(&dev->irqlock, flags); +} + +/* + * Register the nand device + * Called when the card is detected + */ +int r852_register_nand_device(struct r852_device *dev) +{ + dev->mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); + + if (!dev->mtd) + goto error1; + + WARN_ON(dev->card_registred); + + dev->mtd->owner = THIS_MODULE; + dev->mtd->priv = dev->chip; + dev->mtd->dev.parent = &dev->pci_dev->dev; + + if (dev->readonly) + dev->chip->options |= NAND_ROM; + + r852_engine_enable(dev); + + if (sm_register_device(dev->mtd, dev->sm)) + goto error2; + + if (device_create_file(&dev->mtd->dev, &dev_attr_media_type)) + message("can't create media type sysfs attribute"); + + dev->card_registred = 1; + return 0; +error2: + kfree(dev->mtd); +error1: + /* Force card redetect */ + dev->card_detected = 0; + return -1; +} + +/* + * Unregister the card + */ + +void r852_unregister_nand_device(struct r852_device *dev) +{ + if (!dev->card_registred) + return; + + device_remove_file(&dev->mtd->dev, &dev_attr_media_type); + nand_release(dev->mtd); + r852_engine_disable(dev); + dev->card_registred = 0; + kfree(dev->mtd); + dev->mtd = NULL; +} + +/* Card state updater */ +void r852_card_detect_work(struct work_struct *work) +{ + struct r852_device *dev = + container_of(work, struct r852_device, card_detect_work.work); + + r852_card_update_present(dev); + dev->card_unstable = 0; + + /* False alarm */ + if (dev->card_detected == dev->card_registred) + goto exit; + + /* Read media properties */ + r852_update_media_status(dev); + + /* Register the card */ + if (dev->card_detected) + r852_register_nand_device(dev); + else + r852_unregister_nand_device(dev); +exit: + /* Update detection logic */ + r852_update_card_detect(dev); +} + +/* Ack + disable IRQ generation */ +static void r852_disable_irqs(struct r852_device *dev) +{ + uint8_t reg; + reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE); + r852_write_reg(dev, R852_CARD_IRQ_ENABLE, reg & ~R852_CARD_IRQ_MASK); + + reg = r852_read_reg_dword(dev, R852_DMA_IRQ_ENABLE); + r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, + reg & ~R852_DMA_IRQ_MASK); + + r852_write_reg(dev, R852_CARD_IRQ_STA, R852_CARD_IRQ_MASK); + r852_write_reg_dword(dev, R852_DMA_IRQ_STA, R852_DMA_IRQ_MASK); +} + +/* Interrupt handler */ +static irqreturn_t r852_irq(int irq, void *data) +{ + struct r852_device *dev = (struct r852_device *)data; + + uint8_t card_status, dma_status; + unsigned long flags; + irqreturn_t ret = IRQ_NONE; + + spin_lock_irqsave(&dev->irqlock, flags); + + /* We can recieve shared interrupt while pci is suspended + in that case reads will return 0xFFFFFFFF.... */ + if (dev->insuspend) + goto out; + + /* handle card detection interrupts first */ + card_status = r852_read_reg(dev, R852_CARD_IRQ_STA); + r852_write_reg(dev, R852_CARD_IRQ_STA, card_status); + + if (card_status & (R852_CARD_IRQ_INSERT|R852_CARD_IRQ_REMOVE)) { + + ret = IRQ_HANDLED; + dev->card_detected = !!(card_status & R852_CARD_IRQ_INSERT); + + /* we shouldn't recieve any interrupts if we wait for card + to settle */ + WARN_ON(dev->card_unstable); + + /* disable irqs while card is unstable */ + /* this will timeout DMA if active, but better that garbage */ + r852_disable_irqs(dev); + + if (dev->card_unstable) + goto out; + + /* let, card state to settle a bit, and then do the work */ + dev->card_unstable = 1; + queue_delayed_work(dev->card_workqueue, + &dev->card_detect_work, msecs_to_jiffies(100)); + goto out; + } + + + /* Handle dma interrupts */ + dma_status = r852_read_reg_dword(dev, R852_DMA_IRQ_STA); + r852_write_reg_dword(dev, R852_DMA_IRQ_STA, dma_status); + + if (dma_status & R852_DMA_IRQ_MASK) { + + ret = IRQ_HANDLED; + + if (dma_status & R852_DMA_IRQ_ERROR) { + dbg("recieved dma error IRQ"); + r852_dma_done(dev, -EIO); + goto out; + } + + /* recieved DMA interrupt out of nowhere? */ + WARN_ON_ONCE(dev->dma_stage == 0); + + if (dev->dma_stage == 0) + goto out; + + /* done device access */ + if (dev->dma_state == DMA_INTERNAL && + (dma_status & R852_DMA_IRQ_INTERNAL)) { + + dev->dma_state = DMA_MEMORY; + dev->dma_stage++; + } + + /* done memory DMA */ + if (dev->dma_state == DMA_MEMORY && + (dma_status & R852_DMA_IRQ_MEMORY)) { + dev->dma_state = DMA_INTERNAL; + dev->dma_stage++; + } + + /* Enable 2nd half of dma dance */ + if (dev->dma_stage == 2) + r852_dma_enable(dev); + + /* Operation done */ + if (dev->dma_stage == 3) + r852_dma_done(dev, 0); + goto out; + } + + /* Handle unknown interrupts */ + if (dma_status) + dbg("bad dma IRQ status = %x", dma_status); + + if (card_status & ~R852_CARD_STA_CD) + dbg("strange card status = %x", card_status); + +out: + spin_unlock_irqrestore(&dev->irqlock, flags); + return ret; +} + +int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) +{ + int error; + struct nand_chip *chip; + struct r852_device *dev; + + /* pci initialization */ + error = pci_enable_device(pci_dev); + + if (error) + goto error1; + + pci_set_master(pci_dev); + + error = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)); + if (error) + goto error2; + + error = pci_request_regions(pci_dev, DRV_NAME); + + if (error) + goto error3; + + error = -ENOMEM; + + /* init nand chip, but register it only on card insert */ + chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); + + if (!chip) + goto error4; + + /* commands */ + chip->cmd_ctrl = r852_cmdctl; + chip->waitfunc = r852_wait; + chip->dev_ready = r852_ready; + + /* I/O */ + chip->read_byte = r852_read_byte; + chip->read_buf = r852_read_buf; + chip->write_buf = r852_write_buf; + chip->verify_buf = r852_verify_buf; + + /* ecc */ + chip->ecc.mode = NAND_ECC_HW_SYNDROME; + chip->ecc.size = R852_DMA_LEN; + chip->ecc.bytes = SM_OOB_SIZE; + chip->ecc.hwctl = r852_ecc_hwctl; + chip->ecc.calculate = r852_ecc_calculate; + chip->ecc.correct = r852_ecc_correct; + + /* TODO: hack */ + chip->ecc.read_oob = r852_read_oob; + + /* init our device structure */ + dev = kzalloc(sizeof(struct r852_device), GFP_KERNEL); + + if (!dev) + goto error5; + + chip->priv = dev; + dev->chip = chip; + dev->pci_dev = pci_dev; + pci_set_drvdata(pci_dev, dev); + + dev->bounce_buffer = pci_alloc_consistent(pci_dev, R852_DMA_LEN, + &dev->phys_bounce_buffer); + + if (!dev->bounce_buffer) + goto error6; + + + error = -ENODEV; + dev->mmio = pci_ioremap_bar(pci_dev, 0); + + if (!dev->mmio) + goto error7; + + error = -ENOMEM; + dev->tmp_buffer = kzalloc(SM_SECTOR_SIZE, GFP_KERNEL); + + if (!dev->tmp_buffer) + goto error8; + + init_completion(&dev->dma_done); + + dev->card_workqueue = create_freezeable_workqueue(DRV_NAME); + + if (!dev->card_workqueue) + goto error9; + + INIT_DELAYED_WORK(&dev->card_detect_work, r852_card_detect_work); + + /* shutdown everything - precation */ + r852_engine_disable(dev); + r852_disable_irqs(dev); + + r852_dma_test(dev); + + /*register irq handler*/ + error = -ENODEV; + if (request_irq(pci_dev->irq, &r852_irq, IRQF_SHARED, + DRV_NAME, dev)) + goto error10; + + dev->irq = pci_dev->irq; + spin_lock_init(&dev->irqlock); + + /* kick initial present test */ + dev->card_detected = 0; + r852_card_update_present(dev); + queue_delayed_work(dev->card_workqueue, + &dev->card_detect_work, 0); + + + printk(KERN_NOTICE DRV_NAME ": driver loaded succesfully\n"); + return 0; + +error10: + destroy_workqueue(dev->card_workqueue); +error9: + kfree(dev->tmp_buffer); +error8: + pci_iounmap(pci_dev, dev->mmio); +error7: + pci_free_consistent(pci_dev, R852_DMA_LEN, + dev->bounce_buffer, dev->phys_bounce_buffer); +error6: + kfree(dev); +error5: + kfree(chip); +error4: + pci_release_regions(pci_dev); +error3: +error2: + pci_disable_device(pci_dev); +error1: + return error; +} + +void r852_remove(struct pci_dev *pci_dev) +{ + struct r852_device *dev = pci_get_drvdata(pci_dev); + + /* Stop detect workqueue - + we are going to unregister the device anyway*/ + cancel_delayed_work_sync(&dev->card_detect_work); + destroy_workqueue(dev->card_workqueue); + + /* Unregister the device, this might make more IO */ + r852_unregister_nand_device(dev); + + /* Stop interrupts */ + r852_disable_irqs(dev); + synchronize_irq(dev->irq); + free_irq(dev->irq, dev); + + /* Cleanup */ + kfree(dev->tmp_buffer); + pci_iounmap(pci_dev, dev->mmio); + pci_free_consistent(pci_dev, R852_DMA_LEN, + dev->bounce_buffer, dev->phys_bounce_buffer); + + kfree(dev->chip); + kfree(dev); + + /* Shutdown the PCI device */ + pci_release_regions(pci_dev); + pci_disable_device(pci_dev); +} + +void r852_shutdown(struct pci_dev *pci_dev) +{ + struct r852_device *dev = pci_get_drvdata(pci_dev); + + cancel_delayed_work_sync(&dev->card_detect_work); + r852_disable_irqs(dev); + synchronize_irq(dev->irq); + pci_disable_device(pci_dev); +} + +#ifdef CONFIG_PM +int r852_suspend(struct device *device) +{ + struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); + unsigned long flags; + + if (dev->ctlreg & R852_CTL_CARDENABLE) + return -EBUSY; + + /* First make sure the detect work is gone */ + cancel_delayed_work_sync(&dev->card_detect_work); + + /* Turn off the interrupts and stop the device */ + r852_disable_irqs(dev); + r852_engine_disable(dev); + + spin_lock_irqsave(&dev->irqlock, flags); + dev->insuspend = 1; + spin_unlock_irqrestore(&dev->irqlock, flags); + + /* At that point, even if interrupt handler is running, it will quit */ + /* So wait for this to happen explictly */ + synchronize_irq(dev->irq); + + /* If card was pulled off just during the suspend, which is very + unlikely, we will remove it on resume, it too late now + anyway... */ + dev->card_unstable = 0; + + pci_save_state(to_pci_dev(device)); + return pci_prepare_to_sleep(to_pci_dev(device)); +} + +int r852_resume(struct device *device) +{ + struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); + unsigned long flags; + + /* Turn on the hardware */ + pci_back_from_sleep(to_pci_dev(device)); + pci_restore_state(to_pci_dev(device)); + + r852_disable_irqs(dev); + r852_card_update_present(dev); + r852_engine_disable(dev); + + + /* Now its safe for IRQ to run */ + spin_lock_irqsave(&dev->irqlock, flags); + dev->insuspend = 0; + spin_unlock_irqrestore(&dev->irqlock, flags); + + + /* If card status changed, just do the work */ + if (dev->card_detected != dev->card_registred) { + dbg("card was %s during low power state", + dev->card_detected ? "added" : "removed"); + + queue_delayed_work(dev->card_workqueue, + &dev->card_detect_work, 1000); + return 0; + } + + /* Otherwise, initialize the card */ + if (dev->card_registred) { + r852_engine_enable(dev); + dev->chip->select_chip(dev->mtd, 0); + dev->chip->cmdfunc(dev->mtd, NAND_CMD_RESET, -1, -1); + dev->chip->select_chip(dev->mtd, -1); + } + + /* Program card detection IRQ */ + r852_update_card_detect(dev); + return 0; +} +#else +#define r852_suspend NULL +#define r852_resume NULL +#endif + +static const struct pci_device_id r852_pci_id_tbl[] = { + + { PCI_VDEVICE(RICOH, 0x0852), }, + { }, +}; + +MODULE_DEVICE_TABLE(pci, r852_pci_id_tbl); + +SIMPLE_DEV_PM_OPS(r852_pm_ops, r852_suspend, r852_resume); + + +static struct pci_driver r852_pci_driver = { + .name = DRV_NAME, + .id_table = r852_pci_id_tbl, + .probe = r852_probe, + .remove = r852_remove, + .shutdown = r852_shutdown, + .driver.pm = &r852_pm_ops, +}; + +static __init int r852_module_init(void) +{ + return pci_register_driver(&r852_pci_driver); +} + +static void __exit r852_module_exit(void) +{ + pci_unregister_driver(&r852_pci_driver); +} + +module_init(r852_module_init); +module_exit(r852_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>"); +MODULE_DESCRIPTION("Ricoh 85xx xD/smartmedia card reader driver"); diff --git a/drivers/mtd/nand/r852.h b/drivers/mtd/nand/r852.h new file mode 100644 index 0000000..8096cc2 --- /dev/null +++ b/drivers/mtd/nand/r852.h @@ -0,0 +1,163 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * driver for Ricoh xD readers + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/pci.h> +#include <linux/completion.h> +#include <linux/workqueue.h> +#include <linux/mtd/nand.h> +#include <linux/spinlock.h> + + +/* nand interface + ecc + byte write/read does one cycle on nand data lines. + dword write/read does 4 cycles + if R852_CTL_ECC_ACCESS is set in R852_CTL, then dword read reads + results of ecc correction, if DMA read was done before. + If write was done two dword reads read generated ecc checksums +*/ +#define R852_DATALINE 0x00 + +/* control register */ +#define R852_CTL 0x04 +#define R852_CTL_COMMAND 0x01 /* send command (#CLE)*/ +#define R852_CTL_DATA 0x02 /* read/write data (#ALE)*/ +#define R852_CTL_ON 0x04 /* only seem to controls the hd led, */ + /* but has to be set on start...*/ +#define R852_CTL_RESET 0x08 /* unknown, set only on start once*/ +#define R852_CTL_CARDENABLE 0x10 /* probably (#CE) - always set*/ +#define R852_CTL_ECC_ENABLE 0x20 /* enable ecc engine */ +#define R852_CTL_ECC_ACCESS 0x40 /* read/write ecc via reg #0*/ +#define R852_CTL_WRITE 0x80 /* set when performing writes (#WP) */ + +/* card detection status */ +#define R852_CARD_STA 0x05 + +#define R852_CARD_STA_CD 0x01 /* state of #CD line, same as 0x04 */ +#define R852_CARD_STA_RO 0x02 /* card is readonly */ +#define R852_CARD_STA_PRESENT 0x04 /* card is present (#CD) */ +#define R852_CARD_STA_ABSENT 0x08 /* card is absent */ +#define R852_CARD_STA_BUSY 0x80 /* card is busy - (#R/B) */ + +/* card detection irq status & enable*/ +#define R852_CARD_IRQ_STA 0x06 /* IRQ status */ +#define R852_CARD_IRQ_ENABLE 0x07 /* IRQ enable */ + +#define R852_CARD_IRQ_CD 0x01 /* fire when #CD lights, same as 0x04*/ +#define R852_CARD_IRQ_REMOVE 0x04 /* detect card removal */ +#define R852_CARD_IRQ_INSERT 0x08 /* detect card insert */ +#define R852_CARD_IRQ_UNK1 0x10 /* unknown */ +#define R852_CARD_IRQ_GENABLE 0x80 /* general enable */ +#define R852_CARD_IRQ_MASK 0x1D + + + +/* hardware enable */ +#define R852_HW 0x08 +#define R852_HW_ENABLED 0x01 /* hw enabled */ +#define R852_HW_UNKNOWN 0x80 + + +/* dma capabilities */ +#define R852_DMA_CAP 0x09 +#define R852_SMBIT 0x20 /* if set with bit #6 or bit #7, then */ + /* hw is smartmedia */ +#define R852_DMA1 0x40 /* if set w/bit #7, dma is supported */ +#define R852_DMA2 0x80 /* if set w/bit #6, dma is supported */ + + +/* physical DMA address - 32 bit value*/ +#define R852_DMA_ADDR 0x0C + + +/* dma settings */ +#define R852_DMA_SETTINGS 0x10 +#define R852_DMA_MEMORY 0x01 /* (memory <-> internal hw buffer) */ +#define R852_DMA_READ 0x02 /* 0 = write, 1 = read */ +#define R852_DMA_INTERNAL 0x04 /* (internal hw buffer <-> card) */ + +/* dma IRQ status */ +#define R852_DMA_IRQ_STA 0x14 + +/* dma IRQ enable */ +#define R852_DMA_IRQ_ENABLE 0x18 + +#define R852_DMA_IRQ_MEMORY 0x01 /* (memory <-> internal hw buffer) */ +#define R852_DMA_IRQ_ERROR 0x02 /* error did happen */ +#define R852_DMA_IRQ_INTERNAL 0x04 /* (internal hw buffer <-> card) */ +#define R852_DMA_IRQ_MASK 0x07 /* mask of all IRQ bits */ + + +/* ECC syndrome format - read from reg #0 will return two copies of these for + each half of the page. + first byte is error byte location, and second, bit location + flags */ +#define R852_ECC_ERR_BIT_MSK 0x07 /* error bit location */ +#define R852_ECC_CORRECT 0x10 /* no errors - (guessed) */ +#define R852_ECC_CORRECTABLE 0x20 /* correctable error exist */ +#define R852_ECC_FAIL 0x40 /* non correctable error detected */ + +#define R852_DMA_LEN 512 + +#define DMA_INTERNAL 0 +#define DMA_MEMORY 1 + +struct r852_device { + void __iomem *mmio; /* mmio */ + struct mtd_info *mtd; /* mtd backpointer */ + struct nand_chip *chip; /* nand chip backpointer */ + struct pci_dev *pci_dev; /* pci backpointer */ + + /* dma area */ + dma_addr_t phys_dma_addr; /* bus address of buffer*/ + struct completion dma_done; /* data transfer done */ + + dma_addr_t phys_bounce_buffer; /* bus address of bounce buffer */ + uint8_t *bounce_buffer; /* virtual address of bounce buffer */ + + int dma_dir; /* 1 = read, 0 = write */ + int dma_stage; /* 0 - idle, 1 - first step, + 2 - second step */ + + int dma_state; /* 0 = internal, 1 = memory */ + int dma_error; /* dma errors */ + int dma_usable; /* is it possible to use dma */ + + /* card status area */ + struct delayed_work card_detect_work; + struct workqueue_struct *card_workqueue; + int card_registred; /* card registered with mtd */ + int card_detected; /* card detected in slot */ + int card_unstable; /* whenever the card is inserted, + is not known yet */ + int readonly; /* card is readonly */ + int sm; /* Is card smartmedia */ + + /* interrupt handling */ + spinlock_t irqlock; /* IRQ protecting lock */ + int irq; /* irq num */ + int insuspend; /* device is suspended */ + + /* misc */ + void *tmp_buffer; /* temporary buffer */ + uint8_t ctlreg; /* cached contents of control reg */ +}; + +#define DRV_NAME "r852" + + +#define dbg(format, ...) \ + if (debug) \ + printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__) + +#define dbg_verbose(format, ...) \ + if (debug > 1) \ + printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__) + + +#define message(format, ...) \ + printk(KERN_INFO DRV_NAME ": " format "\n", ## __VA_ARGS__) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index fa6e9c7..239aadf 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -929,14 +929,13 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) pr_debug("s3c2410_nand_probe(%p)\n", pdev); - info = kmalloc(sizeof(*info), GFP_KERNEL); + info = kzalloc(sizeof(*info), GFP_KERNEL); if (info == NULL) { dev_err(&pdev->dev, "no memory for flash info\n"); err = -ENOMEM; goto exit_error; } - memset(info, 0, sizeof(*info)); platform_set_drvdata(pdev, info); spin_lock_init(&info->controller.lock); @@ -957,7 +956,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) /* currently we assume we have the one resource */ res = pdev->resource; - size = res->end - res->start + 1; + size = resource_size(res); info->area = request_mem_region(res->start, size, pdev->name); @@ -994,15 +993,13 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) /* allocate our information */ size = nr_sets * sizeof(*info->mtds); - info->mtds = kmalloc(size, GFP_KERNEL); + info->mtds = kzalloc(size, GFP_KERNEL); if (info->mtds == NULL) { dev_err(&pdev->dev, "failed to allocate mtd storage\n"); err = -ENOMEM; goto exit_error; } - memset(info->mtds, 0, size); - /* initialise all possible chips */ nmtd = info->mtds; @@ -1013,7 +1010,8 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) s3c2410_nand_init_chip(info, nmtd, sets); nmtd->scan_res = nand_scan_ident(&nmtd->mtd, - (sets) ? sets->nr_chips : 1); + (sets) ? sets->nr_chips : 1, + NULL); if (nmtd->scan_res == 0) { s3c2410_nand_update_chip(info, nmtd); diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 34752fc..546c2f0 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -855,7 +855,7 @@ static int __devinit flctl_probe(struct platform_device *pdev) nand->read_word = flctl_read_word; } - ret = nand_scan_ident(flctl_mtd, 1); + ret = nand_scan_ident(flctl_mtd, 1, NULL); if (ret) goto err; diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c new file mode 100644 index 0000000..ac80fb362 --- /dev/null +++ b/drivers/mtd/nand/sm_common.c @@ -0,0 +1,148 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * Common routines & support for xD format + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/kernel.h> +#include <linux/mtd/nand.h> +#include "sm_common.h" + +static struct nand_ecclayout nand_oob_sm = { + .eccbytes = 6, + .eccpos = {8, 9, 10, 13, 14, 15}, + .oobfree = { + {.offset = 0 , .length = 4}, /* reserved */ + {.offset = 6 , .length = 2}, /* LBA1 */ + {.offset = 11, .length = 2} /* LBA2 */ + } +}; + +/* NOTE: This layout is is not compatabable with SmartMedia, */ +/* because the 256 byte devices have page depenent oob layout */ +/* However it does preserve the bad block markers */ +/* If you use smftl, it will bypass this and work correctly */ +/* If you not, then you break SmartMedia compliance anyway */ + +static struct nand_ecclayout nand_oob_sm_small = { + .eccbytes = 3, + .eccpos = {0, 1, 2}, + .oobfree = { + {.offset = 3 , .length = 2}, /* reserved */ + {.offset = 6 , .length = 2}, /* LBA1 */ + } +}; + + +static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_oob_ops ops; + struct sm_oob oob; + int ret, error = 0; + + memset(&oob, -1, SM_OOB_SIZE); + oob.block_status = 0x0F; + + /* As long as this function is called on erase block boundaries + it will work correctly for 256 byte nand */ + ops.mode = MTD_OOB_PLACE; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.oobbuf = (void *)&oob; + ops.datbuf = NULL; + + + ret = mtd->write_oob(mtd, ofs, &ops); + if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) { + printk(KERN_NOTICE + "sm_common: can't mark sector at %i as bad\n", + (int)ofs); + error = -EIO; + } else + mtd->ecc_stats.badblocks++; + + return error; +} + + +static struct nand_flash_dev nand_smartmedia_flash_ids[] = { + {"SmartMedia 1MiB 5V", 0x6e, 256, 1, 0x1000, 0}, + {"SmartMedia 1MiB 3,3V", 0xe8, 256, 1, 0x1000, 0}, + {"SmartMedia 1MiB 3,3V", 0xec, 256, 1, 0x1000, 0}, + {"SmartMedia 2MiB 3,3V", 0xea, 256, 2, 0x1000, 0}, + {"SmartMedia 2MiB 5V", 0x64, 256, 2, 0x1000, 0}, + {"SmartMedia 2MiB 3,3V ROM", 0x5d, 512, 2, 0x2000, NAND_ROM}, + {"SmartMedia 4MiB 3,3V", 0xe3, 512, 4, 0x2000, 0}, + {"SmartMedia 4MiB 3,3/5V", 0xe5, 512, 4, 0x2000, 0}, + {"SmartMedia 4MiB 5V", 0x6b, 512, 4, 0x2000, 0}, + {"SmartMedia 4MiB 3,3V ROM", 0xd5, 512, 4, 0x2000, NAND_ROM}, + {"SmartMedia 8MiB 3,3V", 0xe6, 512, 8, 0x2000, 0}, + {"SmartMedia 8MiB 3,3V ROM", 0xd6, 512, 8, 0x2000, NAND_ROM}, + {"SmartMedia 16MiB 3,3V", 0x73, 512, 16, 0x4000, 0}, + {"SmartMedia 16MiB 3,3V ROM", 0x57, 512, 16, 0x4000, NAND_ROM}, + {"SmartMedia 32MiB 3,3V", 0x75, 512, 32, 0x4000, 0}, + {"SmartMedia 32MiB 3,3V ROM", 0x58, 512, 32, 0x4000, NAND_ROM}, + {"SmartMedia 64MiB 3,3V", 0x76, 512, 64, 0x4000, 0}, + {"SmartMedia 64MiB 3,3V ROM", 0xd9, 512, 64, 0x4000, NAND_ROM}, + {"SmartMedia 128MiB 3,3V", 0x79, 512, 128, 0x4000, 0}, + {"SmartMedia 128MiB 3,3V ROM", 0xda, 512, 128, 0x4000, NAND_ROM}, + {"SmartMedia 256MiB 3,3V", 0x71, 512, 256, 0x4000 }, + {"SmartMedia 256MiB 3,3V ROM", 0x5b, 512, 256, 0x4000, NAND_ROM}, + {NULL,} +}; + +#define XD_TYPEM (NAND_NO_AUTOINCR | NAND_BROKEN_XD) +static struct nand_flash_dev nand_xd_flash_ids[] = { + + {"xD 16MiB 3,3V", 0x73, 512, 16, 0x4000, 0}, + {"xD 32MiB 3,3V", 0x75, 512, 32, 0x4000, 0}, + {"xD 64MiB 3,3V", 0x76, 512, 64, 0x4000, 0}, + {"xD 128MiB 3,3V", 0x79, 512, 128, 0x4000, 0}, + {"xD 256MiB 3,3V", 0x71, 512, 256, 0x4000, XD_TYPEM}, + {"xD 512MiB 3,3V", 0xdc, 512, 512, 0x4000, XD_TYPEM}, + {"xD 1GiB 3,3V", 0xd3, 512, 1024, 0x4000, XD_TYPEM}, + {"xD 2GiB 3,3V", 0xd5, 512, 2048, 0x4000, XD_TYPEM}, + {NULL,} +}; + +int sm_register_device(struct mtd_info *mtd, int smartmedia) +{ + struct nand_chip *chip = (struct nand_chip *)mtd->priv; + int ret; + + chip->options |= NAND_SKIP_BBTSCAN; + + /* Scan for card properties */ + ret = nand_scan_ident(mtd, 1, smartmedia ? + nand_smartmedia_flash_ids : nand_xd_flash_ids); + + if (ret) + return ret; + + /* Bad block marker postion */ + chip->badblockpos = 0x05; + chip->badblockbits = 7; + chip->block_markbad = sm_block_markbad; + + /* ECC layout */ + if (mtd->writesize == SM_SECTOR_SIZE) + chip->ecc.layout = &nand_oob_sm; + else if (mtd->writesize == SM_SMALL_PAGE) + chip->ecc.layout = &nand_oob_sm_small; + else + return -ENODEV; + + ret = nand_scan_tail(mtd); + + if (ret) + return ret; + + return add_mtd_device(mtd); +} +EXPORT_SYMBOL_GPL(sm_register_device); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>"); +MODULE_DESCRIPTION("Common SmartMedia/xD functions"); diff --git a/drivers/mtd/nand/sm_common.h b/drivers/mtd/nand/sm_common.h new file mode 100644 index 0000000..00f4a83 --- /dev/null +++ b/drivers/mtd/nand/sm_common.h @@ -0,0 +1,61 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * Common routines & support for SmartMedia/xD format + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/bitops.h> +#include <linux/mtd/mtd.h> + +/* Full oob structure as written on the flash */ +struct sm_oob { + uint32_t reserved; + uint8_t data_status; + uint8_t block_status; + uint8_t lba_copy1[2]; + uint8_t ecc2[3]; + uint8_t lba_copy2[2]; + uint8_t ecc1[3]; +} __attribute__((packed)); + + +/* one sector is always 512 bytes, but it can consist of two nand pages */ +#define SM_SECTOR_SIZE 512 + +/* oob area is also 16 bytes, but might be from two pages */ +#define SM_OOB_SIZE 16 + +/* This is maximum zone size, and all devices that have more that one zone + have this size */ +#define SM_MAX_ZONE_SIZE 1024 + +/* support for small page nand */ +#define SM_SMALL_PAGE 256 +#define SM_SMALL_OOB_SIZE 8 + + +extern int sm_register_device(struct mtd_info *mtd, int smartmedia); + + +static inline int sm_sector_valid(struct sm_oob *oob) +{ + return hweight16(oob->data_status) >= 5; +} + +static inline int sm_block_valid(struct sm_oob *oob) +{ + return hweight16(oob->block_status) >= 7; +} + +static inline int sm_block_erased(struct sm_oob *oob) +{ + static const uint32_t erased_pattern[4] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + + /* First test for erased block */ + if (!memcmp(oob, erased_pattern, sizeof(*oob))) + return 1; + return 0; +} diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c index edb9b13..884852d 100644 --- a/drivers/mtd/nand/socrates_nand.c +++ b/drivers/mtd/nand/socrates_nand.c @@ -220,7 +220,7 @@ static int __devinit socrates_nand_probe(struct of_device *ofdev, dev_set_drvdata(&ofdev->dev, host); /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, 1)) { + if (nand_scan_ident(mtd, 1, NULL)) { res = -ENXIO; goto out; } @@ -290,7 +290,7 @@ static int __devexit socrates_nand_remove(struct of_device *ofdev) return 0; } -static struct of_device_id socrates_nand_match[] = +static const struct of_device_id socrates_nand_match[] = { { .compatible = "abb,socrates-nand", diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index fa28f01..3041d1f 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -319,7 +319,7 @@ static int tmio_nand_correct_data(struct mtd_info *mtd, unsigned char *buf, static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) { - struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + struct mfd_cell *cell = dev_get_platdata(&dev->dev); int ret; if (cell->enable) { @@ -363,7 +363,7 @@ static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) { - struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + struct mfd_cell *cell = dev_get_platdata(&dev->dev); tmio_iowrite8(FCR_MODE_POWER_OFF, tmio->fcr + FCR_MODE); if (cell->disable) @@ -372,7 +372,7 @@ static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) static int tmio_probe(struct platform_device *dev) { - struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + struct mfd_cell *cell = dev_get_platdata(&dev->dev); struct tmio_nand_data *data = cell->driver_data; struct resource *fcr = platform_get_resource(dev, IORESOURCE_MEM, 0); @@ -405,14 +405,14 @@ static int tmio_probe(struct platform_device *dev) mtd->priv = nand_chip; mtd->name = "tmio-nand"; - tmio->ccr = ioremap(ccr->start, ccr->end - ccr->start + 1); + tmio->ccr = ioremap(ccr->start, resource_size(ccr)); if (!tmio->ccr) { retval = -EIO; goto err_iomap_ccr; } tmio->fcr_base = fcr->start & 0xfffff; - tmio->fcr = ioremap(fcr->start, fcr->end - fcr->start + 1); + tmio->fcr = ioremap(fcr->start, resource_size(fcr)); if (!tmio->fcr) { retval = -EIO; goto err_iomap_fcr; @@ -516,7 +516,7 @@ static int tmio_remove(struct platform_device *dev) #ifdef CONFIG_PM static int tmio_suspend(struct platform_device *dev, pm_message_t state) { - struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + struct mfd_cell *cell = dev_get_platdata(&dev->dev); if (cell->suspend) cell->suspend(dev); @@ -527,7 +527,7 @@ static int tmio_suspend(struct platform_device *dev, pm_message_t state) static int tmio_resume(struct platform_device *dev) { - struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + struct mfd_cell *cell = dev_get_platdata(&dev->dev); /* FIXME - is this required or merely another attack of the broken * SHARP platform? Looks suspicious. diff --git a/drivers/mtd/nand/ts7250.c b/drivers/mtd/nand/ts7250.c deleted file mode 100644 index 0f5562a..0000000 --- a/drivers/mtd/nand/ts7250.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * drivers/mtd/nand/ts7250.c - * - * Copyright (C) 2004 Technologic Systems (support@embeddedARM.com) - * - * Derived from drivers/mtd/nand/edb7312.c - * Copyright (C) 2004 Marius Gröger (mag@sysgo.de) - * - * Derived from drivers/mtd/nand/autcpu12.c - * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Overview: - * This is a device driver for the NAND flash device found on the - * TS-7250 board which utilizes a Samsung 32 Mbyte part. - */ - -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/partitions.h> -#include <linux/io.h> - -#include <mach/hardware.h> -#include <mach/ts72xx.h> - -#include <asm/sizes.h> -#include <asm/mach-types.h> - -/* - * MTD structure for TS7250 board - */ -static struct mtd_info *ts7250_mtd = NULL; - -#ifdef CONFIG_MTD_PARTITIONS -static const char *part_probes[] = { "cmdlinepart", NULL }; - -#define NUM_PARTITIONS 3 - -/* - * Define static partitions for flash device - */ -static struct mtd_partition partition_info32[] = { - { - .name = "TS-BOOTROM", - .offset = 0x00000000, - .size = 0x00004000, - }, { - .name = "Linux", - .offset = 0x00004000, - .size = 0x01d00000, - }, { - .name = "RedBoot", - .offset = 0x01d04000, - .size = 0x002fc000, - }, -}; - -/* - * Define static partitions for flash device - */ -static struct mtd_partition partition_info128[] = { - { - .name = "TS-BOOTROM", - .offset = 0x00000000, - .size = 0x00004000, - }, { - .name = "Linux", - .offset = 0x00004000, - .size = 0x07d00000, - }, { - .name = "RedBoot", - .offset = 0x07d04000, - .size = 0x002fc000, - }, -}; -#endif - - -/* - * hardware specific access to control-lines - * - * ctrl: - * NAND_NCE: bit 0 -> bit 2 - * NAND_CLE: bit 1 -> bit 1 - * NAND_ALE: bit 2 -> bit 0 - */ -static void ts7250_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct nand_chip *chip = mtd->priv; - - if (ctrl & NAND_CTRL_CHANGE) { - unsigned long addr = TS72XX_NAND_CONTROL_VIRT_BASE; - unsigned char bits; - - bits = (ctrl & NAND_NCE) << 2; - bits |= ctrl & NAND_CLE; - bits |= (ctrl & NAND_ALE) >> 2; - - __raw_writeb((__raw_readb(addr) & ~0x7) | bits, addr); - } - - if (cmd != NAND_CMD_NONE) - writeb(cmd, chip->IO_ADDR_W); -} - -/* - * read device ready pin - */ -static int ts7250_device_ready(struct mtd_info *mtd) -{ - return __raw_readb(TS72XX_NAND_BUSY_VIRT_BASE) & 0x20; -} - -/* - * Main initialization routine - */ -static int __init ts7250_init(void) -{ - struct nand_chip *this; - const char *part_type = 0; - int mtd_parts_nb = 0; - struct mtd_partition *mtd_parts = 0; - - if (!machine_is_ts72xx() || board_is_ts7200()) - return -ENXIO; - - /* Allocate memory for MTD device structure and private data */ - ts7250_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); - if (!ts7250_mtd) { - printk("Unable to allocate TS7250 NAND MTD device structure.\n"); - return -ENOMEM; - } - - /* Get pointer to private data */ - this = (struct nand_chip *)(&ts7250_mtd[1]); - - /* Initialize structures */ - memset(ts7250_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - - /* Link the private data with the MTD structure */ - ts7250_mtd->priv = this; - ts7250_mtd->owner = THIS_MODULE; - - /* insert callbacks */ - this->IO_ADDR_R = (void *)TS72XX_NAND_DATA_VIRT_BASE; - this->IO_ADDR_W = (void *)TS72XX_NAND_DATA_VIRT_BASE; - this->cmd_ctrl = ts7250_hwcontrol; - this->dev_ready = ts7250_device_ready; - this->chip_delay = 15; - this->ecc.mode = NAND_ECC_SOFT; - - printk("Searching for NAND flash...\n"); - /* Scan to find existence of the device */ - if (nand_scan(ts7250_mtd, 1)) { - kfree(ts7250_mtd); - return -ENXIO; - } -#ifdef CONFIG_MTD_PARTITIONS - ts7250_mtd->name = "ts7250-nand"; - mtd_parts_nb = parse_mtd_partitions(ts7250_mtd, part_probes, &mtd_parts, 0); - if (mtd_parts_nb > 0) - part_type = "command line"; - else - mtd_parts_nb = 0; -#endif - if (mtd_parts_nb == 0) { - mtd_parts = partition_info32; - if (ts7250_mtd->size >= (128 * 0x100000)) - mtd_parts = partition_info128; - mtd_parts_nb = NUM_PARTITIONS; - part_type = "static"; - } - - /* Register the partitions */ - printk(KERN_NOTICE "Using %s partition definition\n", part_type); - add_mtd_partitions(ts7250_mtd, mtd_parts, mtd_parts_nb); - - /* Return happy */ - return 0; -} - -module_init(ts7250_init); - -/* - * Clean up routine - */ -static void __exit ts7250_cleanup(void) -{ - /* Unregister the device */ - del_mtd_device(ts7250_mtd); - - /* Free the MTD device structure */ - kfree(ts7250_mtd); -} - -module_exit(ts7250_cleanup); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jesse Off <joff@embeddedARM.com>"); -MODULE_DESCRIPTION("MTD map driver for Technologic Systems TS-7250 board"); diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 863513c..054a41c 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -274,7 +274,7 @@ static int txx9ndfmc_nand_scan(struct mtd_info *mtd) struct nand_chip *chip = mtd->priv; int ret; - ret = nand_scan_ident(mtd, 1); + ret = nand_scan_ident(mtd, 1, NULL); if (!ret) { if (mtd->writesize >= 512) { chip->ecc.size = mtd->writesize; diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index 1002e18..a4578bf 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -126,7 +126,6 @@ static void nftl_remove_dev(struct mtd_blktrans_dev *dev) del_mtd_blktrans_dev(dev); kfree(nftl->ReplUnitTable); kfree(nftl->EUNtable); - kfree(nftl); } /* diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index 3a9f157..9a49d68 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -30,6 +30,13 @@ config MTD_ONENAND_OMAP2 Support for a OneNAND flash device connected to an OMAP2/OMAP3 CPU via the GPMC memory controller. +config MTD_ONENAND_SAMSUNG + tristate "OneNAND on Samsung SOC controller support" + depends on MTD_ONENAND && (ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210) + help + Support for a OneNAND flash device connected to an Samsung SOC + S3C64XX/S5PC1XX controller. + config MTD_ONENAND_OTP bool "OneNAND OTP Support" select HAVE_MTD_OTP diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile index 64b6cc6..2b7884c 100644 --- a/drivers/mtd/onenand/Makefile +++ b/drivers/mtd/onenand/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_MTD_ONENAND) += onenand.o # Board specific. obj-$(CONFIG_MTD_ONENAND_GENERIC) += generic.o obj-$(CONFIG_MTD_ONENAND_OMAP2) += omap2.o +obj-$(CONFIG_MTD_ONENAND_SAMSUNG) += samsung.o # Simulator obj-$(CONFIG_MTD_ONENAND_SIM) += onenand_sim.o diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index fd40634..9f322f1 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -309,7 +309,7 @@ static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area, goto out_copy; /* panic_write() may be in an interrupt context */ - if (in_interrupt()) + if (in_interrupt() || oops_in_progress) goto out_copy; if (buf >= high_memory) { @@ -386,7 +386,7 @@ static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, goto out_copy; /* panic_write() may be in an interrupt context */ - if (in_interrupt()) + if (in_interrupt() || oops_in_progress) goto out_copy; if (buf >= high_memory) { @@ -403,7 +403,7 @@ static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, dma_src = dma_map_single(&c->pdev->dev, buf, count, DMA_TO_DEVICE); dma_dst = c->phys_base + bram_offset; - if (dma_mapping_error(&c->pdev->dev, dma_dst)) { + if (dma_mapping_error(&c->pdev->dev, dma_src)) { dev_err(&c->pdev->dev, "Couldn't DMA map a %d byte buffer\n", count); @@ -426,7 +426,7 @@ static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, if (*done) break; - dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_TO_DEVICE); + dma_unmap_single(&c->pdev->dev, dma_src, count, DMA_TO_DEVICE); if (!*done) { dev_err(&c->pdev->dev, "timeout waiting for DMA\n"); @@ -521,7 +521,7 @@ static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, dma_src = dma_map_single(&c->pdev->dev, (void *) buffer, count, DMA_TO_DEVICE); dma_dst = c->phys_base + bram_offset; - if (dma_mapping_error(&c->pdev->dev, dma_dst)) { + if (dma_mapping_error(&c->pdev->dev, dma_src)) { dev_err(&c->pdev->dev, "Couldn't DMA map a %d byte buffer\n", count); @@ -539,7 +539,7 @@ static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, omap_start_dma(c->dma_channel); wait_for_completion(&c->dma_done); - dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_TO_DEVICE); + dma_unmap_single(&c->pdev->dev, dma_src, count, DMA_TO_DEVICE); return 0; } diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 32f0ed3..26caf25 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -397,7 +397,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le value = onenand_bufferram_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); - if (ONENAND_IS_MLC(this) || ONENAND_IS_2PLANE(this)) + if (ONENAND_IS_MLC(this) || ONENAND_IS_2PLANE(this) || + ONENAND_IS_4KB_PAGE(this)) /* It is always BufferRAM0 */ ONENAND_SET_BUFFERRAM0(this); else @@ -426,7 +427,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le case FLEXONENAND_CMD_RECOVER_LSB: case ONENAND_CMD_READ: case ONENAND_CMD_READOOB: - if (ONENAND_IS_MLC(this)) + if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this)) /* It is always BufferRAM0 */ dataram = ONENAND_SET_BUFFERRAM0(this); else @@ -466,11 +467,11 @@ static inline int onenand_read_ecc(struct onenand_chip *this) { int ecc, i, result = 0; - if (!FLEXONENAND(this)) + if (!FLEXONENAND(this) && !ONENAND_IS_4KB_PAGE(this)) return this->read_word(this->base + ONENAND_REG_ECC_STATUS); for (i = 0; i < 4; i++) { - ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS + i); + ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS + i*2); if (likely(!ecc)) continue; if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR) @@ -1425,7 +1426,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, int ret; onenand_get_device(mtd, FL_READING); - ret = ONENAND_IS_MLC(this) ? + ret = ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this) ? onenand_mlc_read_ops_nolock(mtd, from, &ops) : onenand_read_ops_nolock(mtd, from, &ops); onenand_release_device(mtd); @@ -1460,7 +1461,7 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, onenand_get_device(mtd, FL_READING); if (ops->datbuf) - ret = ONENAND_IS_MLC(this) ? + ret = ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this) ? onenand_mlc_read_ops_nolock(mtd, from, ops) : onenand_read_ops_nolock(mtd, from, ops); else @@ -1634,7 +1635,6 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len) { struct onenand_chip *this = mtd->priv; - void __iomem *dataram; int ret = 0; int thislen, column; @@ -1654,10 +1654,9 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, onenand_update_bufferram(mtd, addr, 1); - dataram = this->base + ONENAND_DATARAM; - dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM); + this->read_bufferram(mtd, ONENAND_DATARAM, this->verify_buf, 0, mtd->writesize); - if (memcmp(buf, dataram + column, thislen)) + if (memcmp(buf, this->verify_buf, thislen)) return -EBADMSG; len -= thislen; @@ -1926,7 +1925,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, * 2 PLANE, MLC, and Flex-OneNAND do not support * write-while-program feature. */ - if (!ONENAND_IS_2PLANE(this) && !first) { + if (!ONENAND_IS_2PLANE(this) && !ONENAND_IS_4KB_PAGE(this) && !first) { ONENAND_SET_PREV_BUFFERRAM(this); ret = this->wait(mtd, FL_WRITING); @@ -1957,7 +1956,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, /* * 2 PLANE, MLC, and Flex-OneNAND wait here */ - if (ONENAND_IS_2PLANE(this)) { + if (ONENAND_IS_2PLANE(this) || ONENAND_IS_4KB_PAGE(this)) { ret = this->wait(mtd, FL_WRITING); /* In partial page write we don't update bufferram */ @@ -2084,7 +2083,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, memcpy(oobbuf + column, buf, thislen); this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); - if (ONENAND_IS_MLC(this)) { + if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this)) { /* Set main area of DataRAM to 0xff*/ memset(this->page_buf, 0xff, mtd->writesize); this->write_bufferram(mtd, ONENAND_DATARAM, @@ -3027,7 +3026,7 @@ static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len, this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); - ret = ONENAND_IS_MLC(this) ? + ret = ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this) ? onenand_mlc_read_ops_nolock(mtd, from, &ops) : onenand_read_ops_nolock(mtd, from, &ops); @@ -3372,7 +3371,10 @@ static void onenand_check_features(struct mtd_info *mtd) /* Lock scheme */ switch (density) { case ONENAND_DEVICE_DENSITY_4Gb: - this->options |= ONENAND_HAS_2PLANE; + if (ONENAND_IS_DDP(this)) + this->options |= ONENAND_HAS_2PLANE; + else + this->options |= ONENAND_HAS_4KB_PAGE; case ONENAND_DEVICE_DENSITY_2Gb: /* 2Gb DDP does not have 2 plane */ @@ -3393,7 +3395,7 @@ static void onenand_check_features(struct mtd_info *mtd) break; } - if (ONENAND_IS_MLC(this)) + if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this)) this->options &= ~ONENAND_HAS_2PLANE; if (FLEXONENAND(this)) { @@ -3407,6 +3409,8 @@ static void onenand_check_features(struct mtd_info *mtd) printk(KERN_DEBUG "Chip support all block unlock\n"); if (this->options & ONENAND_HAS_2PLANE) printk(KERN_DEBUG "Chip has 2 plane\n"); + if (this->options & ONENAND_HAS_4KB_PAGE) + printk(KERN_DEBUG "Chip has 4KiB pagesize\n"); } /** @@ -3759,6 +3763,12 @@ static int onenand_probe(struct mtd_info *mtd) /* Restore system configuration 1 */ this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); + /* Workaround */ + if (syscfg & ONENAND_SYS_CFG1_SYNC_WRITE) { + bram_maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); + bram_dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); + } + /* Check manufacturer ID */ if (onenand_check_maf(bram_maf_id)) return -ENXIO; @@ -3778,6 +3788,9 @@ static int onenand_probe(struct mtd_info *mtd) this->device_id = dev_id; this->version_id = ver_id; + /* Check OneNAND features */ + onenand_check_features(mtd); + density = onenand_get_density(dev_id); if (FLEXONENAND(this)) { this->dies = ONENAND_IS_DDP(this) ? 2 : 1; @@ -3799,7 +3812,7 @@ static int onenand_probe(struct mtd_info *mtd) /* The data buffer size is equal to page size */ mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); /* We use the full BufferRAM */ - if (ONENAND_IS_MLC(this)) + if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this)) mtd->writesize <<= 1; mtd->oobsize = mtd->writesize >> 5; @@ -3829,9 +3842,6 @@ static int onenand_probe(struct mtd_info *mtd) else mtd->size = this->chipsize; - /* Check OneNAND features */ - onenand_check_features(mtd); - /* * We emulate the 4KiB page and 256KiB erase block size * But oobsize is still 64 bytes. @@ -3926,6 +3936,13 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) __func__); return -ENOMEM; } +#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE + this->verify_buf = kzalloc(mtd->writesize, GFP_KERNEL); + if (!this->verify_buf) { + kfree(this->page_buf); + return -ENOMEM; + } +#endif this->options |= ONENAND_PAGEBUF_ALLOC; } if (!this->oob_buf) { @@ -4053,8 +4070,12 @@ void onenand_release(struct mtd_info *mtd) kfree(this->bbm); } /* Buffers allocated by onenand_scan */ - if (this->options & ONENAND_PAGEBUF_ALLOC) + if (this->options & ONENAND_PAGEBUF_ALLOC) { kfree(this->page_buf); +#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE + kfree(this->verify_buf); +#endif + } if (this->options & ONENAND_OOBBUF_ALLOC) kfree(this->oob_buf); kfree(mtd->eraseregions); diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c new file mode 100644 index 0000000..2750317 --- /dev/null +++ b/drivers/mtd/onenand/samsung.c @@ -0,0 +1,1071 @@ +/* + * Samsung S3C64XX/S5PC1XX OneNAND driver + * + * Copyright © 2008-2010 Samsung Electronics + * Kyungmin Park <kyungmin.park@samsung.com> + * Marek Szyprowski <m.szyprowski@samsung.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. + * + * Implementation: + * S3C64XX and S5PC100: emulate the pseudo BufferRAM + * S5PC110: use DMA + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/onenand.h> +#include <linux/mtd/partitions.h> +#include <linux/dma-mapping.h> + +#include <asm/mach/flash.h> +#include <plat/regs-onenand.h> + +#include <linux/io.h> + +enum soc_type { + TYPE_S3C6400, + TYPE_S3C6410, + TYPE_S5PC100, + TYPE_S5PC110, +}; + +#define ONENAND_ERASE_STATUS 0x00 +#define ONENAND_MULTI_ERASE_SET 0x01 +#define ONENAND_ERASE_START 0x03 +#define ONENAND_UNLOCK_START 0x08 +#define ONENAND_UNLOCK_END 0x09 +#define ONENAND_LOCK_START 0x0A +#define ONENAND_LOCK_END 0x0B +#define ONENAND_LOCK_TIGHT_START 0x0C +#define ONENAND_LOCK_TIGHT_END 0x0D +#define ONENAND_UNLOCK_ALL 0x0E +#define ONENAND_OTP_ACCESS 0x12 +#define ONENAND_SPARE_ACCESS_ONLY 0x13 +#define ONENAND_MAIN_ACCESS_ONLY 0x14 +#define ONENAND_ERASE_VERIFY 0x15 +#define ONENAND_MAIN_SPARE_ACCESS 0x16 +#define ONENAND_PIPELINE_READ 0x4000 + +#define MAP_00 (0x0) +#define MAP_01 (0x1) +#define MAP_10 (0x2) +#define MAP_11 (0x3) + +#define S3C64XX_CMD_MAP_SHIFT 24 +#define S5PC1XX_CMD_MAP_SHIFT 26 + +#define S3C6400_FBA_SHIFT 10 +#define S3C6400_FPA_SHIFT 4 +#define S3C6400_FSA_SHIFT 2 + +#define S3C6410_FBA_SHIFT 12 +#define S3C6410_FPA_SHIFT 6 +#define S3C6410_FSA_SHIFT 4 + +#define S5PC100_FBA_SHIFT 13 +#define S5PC100_FPA_SHIFT 7 +#define S5PC100_FSA_SHIFT 5 + +/* S5PC110 specific definitions */ +#define S5PC110_DMA_SRC_ADDR 0x400 +#define S5PC110_DMA_SRC_CFG 0x404 +#define S5PC110_DMA_DST_ADDR 0x408 +#define S5PC110_DMA_DST_CFG 0x40C +#define S5PC110_DMA_TRANS_SIZE 0x414 +#define S5PC110_DMA_TRANS_CMD 0x418 +#define S5PC110_DMA_TRANS_STATUS 0x41C +#define S5PC110_DMA_TRANS_DIR 0x420 + +#define S5PC110_DMA_CFG_SINGLE (0x0 << 16) +#define S5PC110_DMA_CFG_4BURST (0x2 << 16) +#define S5PC110_DMA_CFG_8BURST (0x3 << 16) +#define S5PC110_DMA_CFG_16BURST (0x4 << 16) + +#define S5PC110_DMA_CFG_INC (0x0 << 8) +#define S5PC110_DMA_CFG_CNT (0x1 << 8) + +#define S5PC110_DMA_CFG_8BIT (0x0 << 0) +#define S5PC110_DMA_CFG_16BIT (0x1 << 0) +#define S5PC110_DMA_CFG_32BIT (0x2 << 0) + +#define S5PC110_DMA_SRC_CFG_READ (S5PC110_DMA_CFG_16BURST | \ + S5PC110_DMA_CFG_INC | \ + S5PC110_DMA_CFG_16BIT) +#define S5PC110_DMA_DST_CFG_READ (S5PC110_DMA_CFG_16BURST | \ + S5PC110_DMA_CFG_INC | \ + S5PC110_DMA_CFG_32BIT) +#define S5PC110_DMA_SRC_CFG_WRITE (S5PC110_DMA_CFG_16BURST | \ + S5PC110_DMA_CFG_INC | \ + S5PC110_DMA_CFG_32BIT) +#define S5PC110_DMA_DST_CFG_WRITE (S5PC110_DMA_CFG_16BURST | \ + S5PC110_DMA_CFG_INC | \ + S5PC110_DMA_CFG_16BIT) + +#define S5PC110_DMA_TRANS_CMD_TDC (0x1 << 18) +#define S5PC110_DMA_TRANS_CMD_TEC (0x1 << 16) +#define S5PC110_DMA_TRANS_CMD_TR (0x1 << 0) + +#define S5PC110_DMA_TRANS_STATUS_TD (0x1 << 18) +#define S5PC110_DMA_TRANS_STATUS_TB (0x1 << 17) +#define S5PC110_DMA_TRANS_STATUS_TE (0x1 << 16) + +#define S5PC110_DMA_DIR_READ 0x0 +#define S5PC110_DMA_DIR_WRITE 0x1 + +struct s3c_onenand { + struct mtd_info *mtd; + struct platform_device *pdev; + enum soc_type type; + void __iomem *base; + struct resource *base_res; + void __iomem *ahb_addr; + struct resource *ahb_res; + int bootram_command; + void __iomem *page_buf; + void __iomem *oob_buf; + unsigned int (*mem_addr)(int fba, int fpa, int fsa); + unsigned int (*cmd_map)(unsigned int type, unsigned int val); + void __iomem *dma_addr; + struct resource *dma_res; + unsigned long phys_base; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts; +#endif +}; + +#define CMD_MAP_00(dev, addr) (dev->cmd_map(MAP_00, ((addr) << 1))) +#define CMD_MAP_01(dev, mem_addr) (dev->cmd_map(MAP_01, (mem_addr))) +#define CMD_MAP_10(dev, mem_addr) (dev->cmd_map(MAP_10, (mem_addr))) +#define CMD_MAP_11(dev, addr) (dev->cmd_map(MAP_11, ((addr) << 2))) + +static struct s3c_onenand *onenand; + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probes[] = { "cmdlinepart", NULL, }; +#endif + +static inline int s3c_read_reg(int offset) +{ + return readl(onenand->base + offset); +} + +static inline void s3c_write_reg(int value, int offset) +{ + writel(value, onenand->base + offset); +} + +static inline int s3c_read_cmd(unsigned int cmd) +{ + return readl(onenand->ahb_addr + cmd); +} + +static inline void s3c_write_cmd(int value, unsigned int cmd) +{ + writel(value, onenand->ahb_addr + cmd); +} + +#ifdef SAMSUNG_DEBUG +static void s3c_dump_reg(void) +{ + int i; + + for (i = 0; i < 0x400; i += 0x40) { + printk(KERN_INFO "0x%08X: 0x%08x 0x%08x 0x%08x 0x%08x\n", + (unsigned int) onenand->base + i, + s3c_read_reg(i), s3c_read_reg(i + 0x10), + s3c_read_reg(i + 0x20), s3c_read_reg(i + 0x30)); + } +} +#endif + +static unsigned int s3c64xx_cmd_map(unsigned type, unsigned val) +{ + return (type << S3C64XX_CMD_MAP_SHIFT) | val; +} + +static unsigned int s5pc1xx_cmd_map(unsigned type, unsigned val) +{ + return (type << S5PC1XX_CMD_MAP_SHIFT) | val; +} + +static unsigned int s3c6400_mem_addr(int fba, int fpa, int fsa) +{ + return (fba << S3C6400_FBA_SHIFT) | (fpa << S3C6400_FPA_SHIFT) | + (fsa << S3C6400_FSA_SHIFT); +} + +static unsigned int s3c6410_mem_addr(int fba, int fpa, int fsa) +{ + return (fba << S3C6410_FBA_SHIFT) | (fpa << S3C6410_FPA_SHIFT) | + (fsa << S3C6410_FSA_SHIFT); +} + +static unsigned int s5pc100_mem_addr(int fba, int fpa, int fsa) +{ + return (fba << S5PC100_FBA_SHIFT) | (fpa << S5PC100_FPA_SHIFT) | + (fsa << S5PC100_FSA_SHIFT); +} + +static void s3c_onenand_reset(void) +{ + unsigned long timeout = 0x10000; + int stat; + + s3c_write_reg(ONENAND_MEM_RESET_COLD, MEM_RESET_OFFSET); + while (1 && timeout--) { + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + if (stat & RST_CMP) + break; + } + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + s3c_write_reg(stat, INT_ERR_ACK_OFFSET); + + /* Clear interrupt */ + s3c_write_reg(0x0, INT_ERR_ACK_OFFSET); + /* Clear the ECC status */ + s3c_write_reg(0x0, ECC_ERR_STAT_OFFSET); +} + +static unsigned short s3c_onenand_readw(void __iomem *addr) +{ + struct onenand_chip *this = onenand->mtd->priv; + struct device *dev = &onenand->pdev->dev; + int reg = addr - this->base; + int word_addr = reg >> 1; + int value; + + /* It's used for probing time */ + switch (reg) { + case ONENAND_REG_MANUFACTURER_ID: + return s3c_read_reg(MANUFACT_ID_OFFSET); + case ONENAND_REG_DEVICE_ID: + return s3c_read_reg(DEVICE_ID_OFFSET); + case ONENAND_REG_VERSION_ID: + return s3c_read_reg(FLASH_VER_ID_OFFSET); + case ONENAND_REG_DATA_BUFFER_SIZE: + return s3c_read_reg(DATA_BUF_SIZE_OFFSET); + case ONENAND_REG_TECHNOLOGY: + return s3c_read_reg(TECH_OFFSET); + case ONENAND_REG_SYS_CFG1: + return s3c_read_reg(MEM_CFG_OFFSET); + + /* Used at unlock all status */ + case ONENAND_REG_CTRL_STATUS: + return 0; + + case ONENAND_REG_WP_STATUS: + return ONENAND_WP_US; + + default: + break; + } + + /* BootRAM access control */ + if ((unsigned int) addr < ONENAND_DATARAM && onenand->bootram_command) { + if (word_addr == 0) + return s3c_read_reg(MANUFACT_ID_OFFSET); + if (word_addr == 1) + return s3c_read_reg(DEVICE_ID_OFFSET); + if (word_addr == 2) + return s3c_read_reg(FLASH_VER_ID_OFFSET); + } + + value = s3c_read_cmd(CMD_MAP_11(onenand, word_addr)) & 0xffff; + dev_info(dev, "%s: Illegal access at reg 0x%x, value 0x%x\n", __func__, + word_addr, value); + return value; +} + +static void s3c_onenand_writew(unsigned short value, void __iomem *addr) +{ + struct onenand_chip *this = onenand->mtd->priv; + struct device *dev = &onenand->pdev->dev; + unsigned int reg = addr - this->base; + unsigned int word_addr = reg >> 1; + + /* It's used for probing time */ + switch (reg) { + case ONENAND_REG_SYS_CFG1: + s3c_write_reg(value, MEM_CFG_OFFSET); + return; + + case ONENAND_REG_START_ADDRESS1: + case ONENAND_REG_START_ADDRESS2: + return; + + /* Lock/lock-tight/unlock/unlock_all */ + case ONENAND_REG_START_BLOCK_ADDRESS: + return; + + default: + break; + } + + /* BootRAM access control */ + if ((unsigned int)addr < ONENAND_DATARAM) { + if (value == ONENAND_CMD_READID) { + onenand->bootram_command = 1; + return; + } + if (value == ONENAND_CMD_RESET) { + s3c_write_reg(ONENAND_MEM_RESET_COLD, MEM_RESET_OFFSET); + onenand->bootram_command = 0; + return; + } + } + + dev_info(dev, "%s: Illegal access at reg 0x%x, value 0x%x\n", __func__, + word_addr, value); + + s3c_write_cmd(value, CMD_MAP_11(onenand, word_addr)); +} + +static int s3c_onenand_wait(struct mtd_info *mtd, int state) +{ + struct device *dev = &onenand->pdev->dev; + unsigned int flags = INT_ACT; + unsigned int stat, ecc; + unsigned long timeout; + + switch (state) { + case FL_READING: + flags |= BLK_RW_CMP | LOAD_CMP; + break; + case FL_WRITING: + flags |= BLK_RW_CMP | PGM_CMP; + break; + case FL_ERASING: + flags |= BLK_RW_CMP | ERS_CMP; + break; + case FL_LOCKING: + flags |= BLK_RW_CMP; + break; + default: + break; + } + + /* The 20 msec is enough */ + timeout = jiffies + msecs_to_jiffies(20); + while (time_before(jiffies, timeout)) { + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + if (stat & flags) + break; + + if (state != FL_READING) + cond_resched(); + } + /* To get correct interrupt status in timeout case */ + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + s3c_write_reg(stat, INT_ERR_ACK_OFFSET); + + /* + * In the Spec. it checks the controller status first + * However if you get the correct information in case of + * power off recovery (POR) test, it should read ECC status first + */ + if (stat & LOAD_CMP) { + ecc = s3c_read_reg(ECC_ERR_STAT_OFFSET); + if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) { + dev_info(dev, "%s: ECC error = 0x%04x\n", __func__, + ecc); + mtd->ecc_stats.failed++; + return -EBADMSG; + } + } + + if (stat & (LOCKED_BLK | ERS_FAIL | PGM_FAIL | LD_FAIL_ECC_ERR)) { + dev_info(dev, "%s: controller error = 0x%04x\n", __func__, + stat); + if (stat & LOCKED_BLK) + dev_info(dev, "%s: it's locked error = 0x%04x\n", + __func__, stat); + + return -EIO; + } + + return 0; +} + +static int s3c_onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, + size_t len) +{ + struct onenand_chip *this = mtd->priv; + unsigned int *m, *s; + int fba, fpa, fsa = 0; + unsigned int mem_addr, cmd_map_01, cmd_map_10; + int i, mcount, scount; + int index; + + fba = (int) (addr >> this->erase_shift); + fpa = (int) (addr >> this->page_shift); + fpa &= this->page_mask; + + mem_addr = onenand->mem_addr(fba, fpa, fsa); + cmd_map_01 = CMD_MAP_01(onenand, mem_addr); + cmd_map_10 = CMD_MAP_10(onenand, mem_addr); + + switch (cmd) { + case ONENAND_CMD_READ: + case ONENAND_CMD_READOOB: + case ONENAND_CMD_BUFFERRAM: + ONENAND_SET_NEXT_BUFFERRAM(this); + default: + break; + } + + index = ONENAND_CURRENT_BUFFERRAM(this); + + /* + * Emulate Two BufferRAMs and access with 4 bytes pointer + */ + m = (unsigned int *) onenand->page_buf; + s = (unsigned int *) onenand->oob_buf; + + if (index) { + m += (this->writesize >> 2); + s += (mtd->oobsize >> 2); + } + + mcount = mtd->writesize >> 2; + scount = mtd->oobsize >> 2; + + switch (cmd) { + case ONENAND_CMD_READ: + /* Main */ + for (i = 0; i < mcount; i++) + *m++ = s3c_read_cmd(cmd_map_01); + return 0; + + case ONENAND_CMD_READOOB: + s3c_write_reg(TSRF, TRANS_SPARE_OFFSET); + /* Main */ + for (i = 0; i < mcount; i++) + *m++ = s3c_read_cmd(cmd_map_01); + + /* Spare */ + for (i = 0; i < scount; i++) + *s++ = s3c_read_cmd(cmd_map_01); + + s3c_write_reg(0, TRANS_SPARE_OFFSET); + return 0; + + case ONENAND_CMD_PROG: + /* Main */ + for (i = 0; i < mcount; i++) + s3c_write_cmd(*m++, cmd_map_01); + return 0; + + case ONENAND_CMD_PROGOOB: + s3c_write_reg(TSRF, TRANS_SPARE_OFFSET); + + /* Main - dummy write */ + for (i = 0; i < mcount; i++) + s3c_write_cmd(0xffffffff, cmd_map_01); + + /* Spare */ + for (i = 0; i < scount; i++) + s3c_write_cmd(*s++, cmd_map_01); + + s3c_write_reg(0, TRANS_SPARE_OFFSET); + return 0; + + case ONENAND_CMD_UNLOCK_ALL: + s3c_write_cmd(ONENAND_UNLOCK_ALL, cmd_map_10); + return 0; + + case ONENAND_CMD_ERASE: + s3c_write_cmd(ONENAND_ERASE_START, cmd_map_10); + return 0; + + default: + break; + } + + return 0; +} + +static unsigned char *s3c_get_bufferram(struct mtd_info *mtd, int area) +{ + struct onenand_chip *this = mtd->priv; + int index = ONENAND_CURRENT_BUFFERRAM(this); + unsigned char *p; + + if (area == ONENAND_DATARAM) { + p = (unsigned char *) onenand->page_buf; + if (index == 1) + p += this->writesize; + } else { + p = (unsigned char *) onenand->oob_buf; + if (index == 1) + p += mtd->oobsize; + } + + return p; +} + +static int onenand_read_bufferram(struct mtd_info *mtd, int area, + unsigned char *buffer, int offset, + size_t count) +{ + unsigned char *p; + + p = s3c_get_bufferram(mtd, area); + memcpy(buffer, p + offset, count); + return 0; +} + +static int onenand_write_bufferram(struct mtd_info *mtd, int area, + const unsigned char *buffer, int offset, + size_t count) +{ + unsigned char *p; + + p = s3c_get_bufferram(mtd, area); + memcpy(p + offset, buffer, count); + return 0; +} + +static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction) +{ + void __iomem *base = onenand->dma_addr; + int status; + + writel(src, base + S5PC110_DMA_SRC_ADDR); + writel(dst, base + S5PC110_DMA_DST_ADDR); + + if (direction == S5PC110_DMA_DIR_READ) { + writel(S5PC110_DMA_SRC_CFG_READ, base + S5PC110_DMA_SRC_CFG); + writel(S5PC110_DMA_DST_CFG_READ, base + S5PC110_DMA_DST_CFG); + } else { + writel(S5PC110_DMA_SRC_CFG_WRITE, base + S5PC110_DMA_SRC_CFG); + writel(S5PC110_DMA_DST_CFG_WRITE, base + S5PC110_DMA_DST_CFG); + } + + writel(count, base + S5PC110_DMA_TRANS_SIZE); + writel(direction, base + S5PC110_DMA_TRANS_DIR); + + writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD); + + do { + status = readl(base + S5PC110_DMA_TRANS_STATUS); + } while (!(status & S5PC110_DMA_TRANS_STATUS_TD)); + + if (status & S5PC110_DMA_TRANS_STATUS_TE) { + writel(S5PC110_DMA_TRANS_CMD_TEC, base + S5PC110_DMA_TRANS_CMD); + writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD); + return -EIO; + } + + writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD); + + return 0; +} + +static int s5pc110_read_bufferram(struct mtd_info *mtd, int area, + unsigned char *buffer, int offset, size_t count) +{ + struct onenand_chip *this = mtd->priv; + void __iomem *bufferram; + void __iomem *p; + void *buf = (void *) buffer; + dma_addr_t dma_src, dma_dst; + int err; + + p = bufferram = this->base + area; + if (ONENAND_CURRENT_BUFFERRAM(this)) { + if (area == ONENAND_DATARAM) + p += this->writesize; + else + p += mtd->oobsize; + } + + if (offset & 3 || (size_t) buf & 3 || + !onenand->dma_addr || count != mtd->writesize) + goto normal; + + /* Handle vmalloc address */ + if (buf >= high_memory) { + struct page *page; + + if (((size_t) buf & PAGE_MASK) != + ((size_t) (buf + count - 1) & PAGE_MASK)) + goto normal; + page = vmalloc_to_page(buf); + if (!page) + goto normal; + buf = page_address(page) + ((size_t) buf & ~PAGE_MASK); + } + + /* DMA routine */ + dma_src = onenand->phys_base + (p - this->base); + dma_dst = dma_map_single(&onenand->pdev->dev, + buf, count, DMA_FROM_DEVICE); + if (dma_mapping_error(&onenand->pdev->dev, dma_dst)) { + dev_err(&onenand->pdev->dev, + "Couldn't map a %d byte buffer for DMA\n", count); + goto normal; + } + err = s5pc110_dma_ops((void *) dma_dst, (void *) dma_src, + count, S5PC110_DMA_DIR_READ); + dma_unmap_single(&onenand->pdev->dev, dma_dst, count, DMA_FROM_DEVICE); + + if (!err) + return 0; + +normal: + if (count != mtd->writesize) { + /* Copy the bufferram to memory to prevent unaligned access */ + memcpy(this->page_buf, bufferram, mtd->writesize); + p = this->page_buf + offset; + } + + memcpy(buffer, p, count); + + return 0; +} + +static int s3c_onenand_bbt_wait(struct mtd_info *mtd, int state) +{ + unsigned int flags = INT_ACT | LOAD_CMP; + unsigned int stat; + unsigned long timeout; + + /* The 20 msec is enough */ + timeout = jiffies + msecs_to_jiffies(20); + while (time_before(jiffies, timeout)) { + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + if (stat & flags) + break; + } + /* To get correct interrupt status in timeout case */ + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + s3c_write_reg(stat, INT_ERR_ACK_OFFSET); + + if (stat & LD_FAIL_ECC_ERR) { + s3c_onenand_reset(); + return ONENAND_BBT_READ_ERROR; + } + + if (stat & LOAD_CMP) { + int ecc = s3c_read_reg(ECC_ERR_STAT_OFFSET); + if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) { + s3c_onenand_reset(); + return ONENAND_BBT_READ_ERROR; + } + } + + return 0; +} + +static void s3c_onenand_check_lock_status(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + struct device *dev = &onenand->pdev->dev; + unsigned int block, end; + int tmp; + + end = this->chipsize >> this->erase_shift; + + for (block = 0; block < end; block++) { + unsigned int mem_addr = onenand->mem_addr(block, 0, 0); + tmp = s3c_read_cmd(CMD_MAP_01(onenand, mem_addr)); + + if (s3c_read_reg(INT_ERR_STAT_OFFSET) & LOCKED_BLK) { + dev_err(dev, "block %d is write-protected!\n", block); + s3c_write_reg(LOCKED_BLK, INT_ERR_ACK_OFFSET); + } + } +} + +static void s3c_onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, + size_t len, int cmd) +{ + struct onenand_chip *this = mtd->priv; + int start, end, start_mem_addr, end_mem_addr; + + start = ofs >> this->erase_shift; + start_mem_addr = onenand->mem_addr(start, 0, 0); + end = start + (len >> this->erase_shift) - 1; + end_mem_addr = onenand->mem_addr(end, 0, 0); + + if (cmd == ONENAND_CMD_LOCK) { + s3c_write_cmd(ONENAND_LOCK_START, CMD_MAP_10(onenand, + start_mem_addr)); + s3c_write_cmd(ONENAND_LOCK_END, CMD_MAP_10(onenand, + end_mem_addr)); + } else { + s3c_write_cmd(ONENAND_UNLOCK_START, CMD_MAP_10(onenand, + start_mem_addr)); + s3c_write_cmd(ONENAND_UNLOCK_END, CMD_MAP_10(onenand, + end_mem_addr)); + } + + this->wait(mtd, FL_LOCKING); +} + +static void s3c_unlock_all(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + loff_t ofs = 0; + size_t len = this->chipsize; + + if (this->options & ONENAND_HAS_UNLOCK_ALL) { + /* Write unlock command */ + this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); + + /* No need to check return value */ + this->wait(mtd, FL_LOCKING); + + /* Workaround for all block unlock in DDP */ + if (!ONENAND_IS_DDP(this)) { + s3c_onenand_check_lock_status(mtd); + return; + } + + /* All blocks on another chip */ + ofs = this->chipsize >> 1; + len = this->chipsize >> 1; + } + + s3c_onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); + + s3c_onenand_check_lock_status(mtd); +} + +static void s3c_onenand_setup(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + + onenand->mtd = mtd; + + if (onenand->type == TYPE_S3C6400) { + onenand->mem_addr = s3c6400_mem_addr; + onenand->cmd_map = s3c64xx_cmd_map; + } else if (onenand->type == TYPE_S3C6410) { + onenand->mem_addr = s3c6410_mem_addr; + onenand->cmd_map = s3c64xx_cmd_map; + } else if (onenand->type == TYPE_S5PC100) { + onenand->mem_addr = s5pc100_mem_addr; + onenand->cmd_map = s5pc1xx_cmd_map; + } else if (onenand->type == TYPE_S5PC110) { + /* Use generic onenand functions */ + onenand->cmd_map = s5pc1xx_cmd_map; + this->read_bufferram = s5pc110_read_bufferram; + return; + } else { + BUG(); + } + + this->read_word = s3c_onenand_readw; + this->write_word = s3c_onenand_writew; + + this->wait = s3c_onenand_wait; + this->bbt_wait = s3c_onenand_bbt_wait; + this->unlock_all = s3c_unlock_all; + this->command = s3c_onenand_command; + + this->read_bufferram = onenand_read_bufferram; + this->write_bufferram = onenand_write_bufferram; +} + +static int s3c_onenand_probe(struct platform_device *pdev) +{ + struct onenand_platform_data *pdata; + struct onenand_chip *this; + struct mtd_info *mtd; + struct resource *r; + int size, err; + unsigned long onenand_ctrl_cfg = 0; + + pdata = pdev->dev.platform_data; + /* No need to check pdata. the platform data is optional */ + + size = sizeof(struct mtd_info) + sizeof(struct onenand_chip); + mtd = kzalloc(size, GFP_KERNEL); + if (!mtd) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + onenand = kzalloc(sizeof(struct s3c_onenand), GFP_KERNEL); + if (!onenand) { + err = -ENOMEM; + goto onenand_fail; + } + + this = (struct onenand_chip *) &mtd[1]; + mtd->priv = this; + mtd->dev.parent = &pdev->dev; + mtd->owner = THIS_MODULE; + onenand->pdev = pdev; + onenand->type = platform_get_device_id(pdev)->driver_data; + + s3c_onenand_setup(mtd); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no memory resource defined\n"); + return -ENOENT; + goto ahb_resource_failed; + } + + onenand->base_res = request_mem_region(r->start, resource_size(r), + pdev->name); + if (!onenand->base_res) { + dev_err(&pdev->dev, "failed to request memory resource\n"); + err = -EBUSY; + goto resource_failed; + } + + onenand->base = ioremap(r->start, resource_size(r)); + if (!onenand->base) { + dev_err(&pdev->dev, "failed to map memory resource\n"); + err = -EFAULT; + goto ioremap_failed; + } + /* Set onenand_chip also */ + this->base = onenand->base; + + /* Use runtime badblock check */ + this->options |= ONENAND_SKIP_UNLOCK_CHECK; + + if (onenand->type != TYPE_S5PC110) { + r = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!r) { + dev_err(&pdev->dev, "no buffer memory resource defined\n"); + return -ENOENT; + goto ahb_resource_failed; + } + + onenand->ahb_res = request_mem_region(r->start, resource_size(r), + pdev->name); + if (!onenand->ahb_res) { + dev_err(&pdev->dev, "failed to request buffer memory resource\n"); + err = -EBUSY; + goto ahb_resource_failed; + } + + onenand->ahb_addr = ioremap(r->start, resource_size(r)); + if (!onenand->ahb_addr) { + dev_err(&pdev->dev, "failed to map buffer memory resource\n"); + err = -EINVAL; + goto ahb_ioremap_failed; + } + + /* Allocate 4KiB BufferRAM */ + onenand->page_buf = kzalloc(SZ_4K, GFP_KERNEL); + if (!onenand->page_buf) { + err = -ENOMEM; + goto page_buf_fail; + } + + /* Allocate 128 SpareRAM */ + onenand->oob_buf = kzalloc(128, GFP_KERNEL); + if (!onenand->oob_buf) { + err = -ENOMEM; + goto oob_buf_fail; + } + + /* S3C doesn't handle subpage write */ + mtd->subpage_sft = 0; + this->subpagesize = mtd->writesize; + + } else { /* S5PC110 */ + r = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!r) { + dev_err(&pdev->dev, "no dma memory resource defined\n"); + return -ENOENT; + goto dma_resource_failed; + } + + onenand->dma_res = request_mem_region(r->start, resource_size(r), + pdev->name); + if (!onenand->dma_res) { + dev_err(&pdev->dev, "failed to request dma memory resource\n"); + err = -EBUSY; + goto dma_resource_failed; + } + + onenand->dma_addr = ioremap(r->start, resource_size(r)); + if (!onenand->dma_addr) { + dev_err(&pdev->dev, "failed to map dma memory resource\n"); + err = -EINVAL; + goto dma_ioremap_failed; + } + + onenand->phys_base = onenand->base_res->start; + + onenand_ctrl_cfg = readl(onenand->dma_addr + 0x100); + if ((onenand_ctrl_cfg & ONENAND_SYS_CFG1_SYNC_WRITE) && + onenand->dma_addr) + writel(onenand_ctrl_cfg & ~ONENAND_SYS_CFG1_SYNC_WRITE, + onenand->dma_addr + 0x100); + else + onenand_ctrl_cfg = 0; + } + + if (onenand_scan(mtd, 1)) { + err = -EFAULT; + goto scan_failed; + } + + if (onenand->type == TYPE_S5PC110) { + if (onenand_ctrl_cfg && onenand->dma_addr) + writel(onenand_ctrl_cfg, onenand->dma_addr + 0x100); + } else { + /* S3C doesn't handle subpage write */ + mtd->subpage_sft = 0; + this->subpagesize = mtd->writesize; + } + + if (s3c_read_reg(MEM_CFG_OFFSET) & ONENAND_SYS_CFG1_SYNC_READ) + dev_info(&onenand->pdev->dev, "OneNAND Sync. Burst Read enabled\n"); + +#ifdef CONFIG_MTD_PARTITIONS + err = parse_mtd_partitions(mtd, part_probes, &onenand->parts, 0); + if (err > 0) + add_mtd_partitions(mtd, onenand->parts, err); + else if (err <= 0 && pdata && pdata->parts) + add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts); + else +#endif + err = add_mtd_device(mtd); + + platform_set_drvdata(pdev, mtd); + + return 0; + +scan_failed: + if (onenand->dma_addr) + iounmap(onenand->dma_addr); +dma_ioremap_failed: + if (onenand->dma_res) + release_mem_region(onenand->dma_res->start, + resource_size(onenand->dma_res)); + kfree(onenand->oob_buf); +oob_buf_fail: + kfree(onenand->page_buf); +page_buf_fail: + if (onenand->ahb_addr) + iounmap(onenand->ahb_addr); +ahb_ioremap_failed: + if (onenand->ahb_res) + release_mem_region(onenand->ahb_res->start, + resource_size(onenand->ahb_res)); +dma_resource_failed: +ahb_resource_failed: + iounmap(onenand->base); +ioremap_failed: + if (onenand->base_res) + release_mem_region(onenand->base_res->start, + resource_size(onenand->base_res)); +resource_failed: + kfree(onenand); +onenand_fail: + kfree(mtd); + return err; +} + +static int __devexit s3c_onenand_remove(struct platform_device *pdev) +{ + struct mtd_info *mtd = platform_get_drvdata(pdev); + + onenand_release(mtd); + if (onenand->ahb_addr) + iounmap(onenand->ahb_addr); + if (onenand->ahb_res) + release_mem_region(onenand->ahb_res->start, + resource_size(onenand->ahb_res)); + if (onenand->dma_addr) + iounmap(onenand->dma_addr); + if (onenand->dma_res) + release_mem_region(onenand->dma_res->start, + resource_size(onenand->dma_res)); + + iounmap(onenand->base); + 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); + kfree(mtd); + return 0; +} + +static int s3c_pm_ops_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct onenand_chip *this = mtd->priv; + + this->wait(mtd, FL_PM_SUSPENDED); + return mtd->suspend(mtd); +} + +static int s3c_pm_ops_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct onenand_chip *this = mtd->priv; + + mtd->resume(mtd); + this->unlock_all(mtd); + return 0; +} + +static const struct dev_pm_ops s3c_pm_ops = { + .suspend = s3c_pm_ops_suspend, + .resume = s3c_pm_ops_resume, +}; + +static struct platform_device_id s3c_onenand_driver_ids[] = { + { + .name = "s3c6400-onenand", + .driver_data = TYPE_S3C6400, + }, { + .name = "s3c6410-onenand", + .driver_data = TYPE_S3C6410, + }, { + .name = "s5pc100-onenand", + .driver_data = TYPE_S5PC100, + }, { + .name = "s5pc110-onenand", + .driver_data = TYPE_S5PC110, + }, { }, +}; +MODULE_DEVICE_TABLE(platform, s3c_onenand_driver_ids); + +static struct platform_driver s3c_onenand_driver = { + .driver = { + .name = "samsung-onenand", + .pm = &s3c_pm_ops, + }, + .id_table = s3c_onenand_driver_ids, + .probe = s3c_onenand_probe, + .remove = __devexit_p(s3c_onenand_remove), +}; + +static int __init s3c_onenand_init(void) +{ + return platform_driver_register(&s3c_onenand_driver); +} + +static void __exit s3c_onenand_exit(void) +{ + platform_driver_unregister(&s3c_onenand_driver); +} + +module_init(s3c_onenand_init); +module_exit(s3c_onenand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>"); +MODULE_DESCRIPTION("Samsung OneNAND controller support"); diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index d2aa9c46..63b83c0 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -817,7 +817,6 @@ static void rfd_ftl_remove_dev(struct mtd_blktrans_dev *dev) vfree(part->sector_map); kfree(part->header_cache); kfree(part->blocks); - kfree(part); } static struct mtd_blktrans_ops rfd_ftl_tr = { diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c new file mode 100644 index 0000000..67822cf --- /dev/null +++ b/drivers/mtd/sm_ftl.c @@ -0,0 +1,1284 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * SmartMedia/xD translation layer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/random.h> +#include <linux/hdreg.h> +#include <linux/kthread.h> +#include <linux/freezer.h> +#include <linux/sysfs.h> +#include <linux/bitops.h> +#include <linux/slab.h> +#include <linux/mtd/nand_ecc.h> +#include "nand/sm_common.h" +#include "sm_ftl.h" + + + +struct workqueue_struct *cache_flush_workqueue; + +static int cache_timeout = 1000; +module_param(cache_timeout, bool, S_IRUGO); +MODULE_PARM_DESC(cache_timeout, + "Timeout (in ms) for cache flush (1000 ms default"); + +static int debug; +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + + +/* ------------------- sysfs attributtes ---------------------------------- */ +struct sm_sysfs_attribute { + struct device_attribute dev_attr; + char *data; + int len; +}; + +ssize_t sm_attr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sm_sysfs_attribute *sm_attr = + container_of(attr, struct sm_sysfs_attribute, dev_attr); + + strncpy(buf, sm_attr->data, sm_attr->len); + return sm_attr->len; +} + + +#define NUM_ATTRIBUTES 1 +#define SM_CIS_VENDOR_OFFSET 0x59 +struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl) +{ + struct attribute_group *attr_group; + struct attribute **attributes; + struct sm_sysfs_attribute *vendor_attribute; + + int vendor_len = strnlen(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, + SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET); + + char *vendor = kmalloc(vendor_len, GFP_KERNEL); + memcpy(vendor, ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, vendor_len); + vendor[vendor_len] = 0; + + /* Initialize sysfs attributes */ + vendor_attribute = + kzalloc(sizeof(struct sm_sysfs_attribute), GFP_KERNEL); + + sysfs_attr_init(&vendor_attribute->dev_attr.attr); + + vendor_attribute->data = vendor; + vendor_attribute->len = vendor_len; + vendor_attribute->dev_attr.attr.name = "vendor"; + vendor_attribute->dev_attr.attr.mode = S_IRUGO; + vendor_attribute->dev_attr.show = sm_attr_show; + + + /* Create array of pointers to the attributes */ + attributes = kzalloc(sizeof(struct attribute *) * (NUM_ATTRIBUTES + 1), + GFP_KERNEL); + attributes[0] = &vendor_attribute->dev_attr.attr; + + /* Finally create the attribute group */ + attr_group = kzalloc(sizeof(struct attribute_group), GFP_KERNEL); + attr_group->attrs = attributes; + return attr_group; +} + +void sm_delete_sysfs_attributes(struct sm_ftl *ftl) +{ + struct attribute **attributes = ftl->disk_attributes->attrs; + int i; + + for (i = 0; attributes[i] ; i++) { + + struct device_attribute *dev_attr = container_of(attributes[i], + struct device_attribute, attr); + + struct sm_sysfs_attribute *sm_attr = + container_of(dev_attr, + struct sm_sysfs_attribute, dev_attr); + + kfree(sm_attr->data); + kfree(sm_attr); + } + + kfree(ftl->disk_attributes->attrs); + kfree(ftl->disk_attributes); +} + + +/* ----------------------- oob helpers -------------------------------------- */ + +static int sm_get_lba(uint8_t *lba) +{ + /* check fixed bits */ + if ((lba[0] & 0xF8) != 0x10) + return -2; + + /* check parity - endianess doesn't matter */ + if (hweight16(*(uint16_t *)lba) & 1) + return -2; + + return (lba[1] >> 1) | ((lba[0] & 0x07) << 7); +} + + +/* + * Read LBA asscociated with block + * returns -1, if block is erased + * returns -2 if error happens + */ +static int sm_read_lba(struct sm_oob *oob) +{ + static const uint32_t erased_pattern[4] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + + uint16_t lba_test; + int lba; + + /* First test for erased block */ + if (!memcmp(oob, erased_pattern, SM_OOB_SIZE)) + return -1; + + /* Now check is both copies of the LBA differ too much */ + lba_test = *(uint16_t *)oob->lba_copy1 ^ *(uint16_t*)oob->lba_copy2; + if (lba_test && !is_power_of_2(lba_test)) + return -2; + + /* And read it */ + lba = sm_get_lba(oob->lba_copy1); + + if (lba == -2) + lba = sm_get_lba(oob->lba_copy2); + + return lba; +} + +static void sm_write_lba(struct sm_oob *oob, uint16_t lba) +{ + uint8_t tmp[2]; + + WARN_ON(lba >= 1000); + + tmp[0] = 0x10 | ((lba >> 7) & 0x07); + tmp[1] = (lba << 1) & 0xFF; + + if (hweight16(*(uint16_t *)tmp) & 0x01) + tmp[1] |= 1; + + oob->lba_copy1[0] = oob->lba_copy2[0] = tmp[0]; + oob->lba_copy1[1] = oob->lba_copy2[1] = tmp[1]; +} + + +/* Make offset from parts */ +static loff_t sm_mkoffset(struct sm_ftl *ftl, int zone, int block, int boffset) +{ + WARN_ON(boffset & (SM_SECTOR_SIZE - 1)); + WARN_ON(zone < 0 || zone >= ftl->zone_count); + WARN_ON(block >= ftl->zone_size); + WARN_ON(boffset >= ftl->block_size); + + if (block == -1) + return -1; + + return (zone * SM_MAX_ZONE_SIZE + block) * ftl->block_size + boffset; +} + +/* Breaks offset into parts */ +static void sm_break_offset(struct sm_ftl *ftl, loff_t offset, + int *zone, int *block, int *boffset) +{ + *boffset = do_div(offset, ftl->block_size); + *block = do_div(offset, ftl->max_lba); + *zone = offset >= ftl->zone_count ? -1 : offset; +} + +/* ---------------------- low level IO ------------------------------------- */ + +static int sm_correct_sector(uint8_t *buffer, struct sm_oob *oob) +{ + uint8_t ecc[3]; + + __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc); + if (__nand_correct_data(buffer, ecc, oob->ecc1, SM_SMALL_PAGE) < 0) + return -EIO; + + buffer += SM_SMALL_PAGE; + + __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc); + if (__nand_correct_data(buffer, ecc, oob->ecc2, SM_SMALL_PAGE) < 0) + return -EIO; + return 0; +} + +/* Reads a sector + oob*/ +static int sm_read_sector(struct sm_ftl *ftl, + int zone, int block, int boffset, + uint8_t *buffer, struct sm_oob *oob) +{ + struct mtd_info *mtd = ftl->trans->mtd; + struct mtd_oob_ops ops; + struct sm_oob tmp_oob; + int ret = -EIO; + int try = 0; + + /* FTL can contain -1 entries that are by default filled with bits */ + if (block == -1) { + memset(buffer, 0xFF, SM_SECTOR_SIZE); + return 0; + } + + /* User might not need the oob, but we do for data vertification */ + if (!oob) + oob = &tmp_oob; + + ops.mode = ftl->smallpagenand ? MTD_OOB_RAW : MTD_OOB_PLACE; + ops.ooboffs = 0; + ops.ooblen = SM_OOB_SIZE; + ops.oobbuf = (void *)oob; + ops.len = SM_SECTOR_SIZE; + ops.datbuf = buffer; + +again: + if (try++) { + /* Avoid infinite recursion on CIS reads, sm_recheck_media + won't help anyway */ + if (zone == 0 && block == ftl->cis_block && boffset == + ftl->cis_boffset) + return ret; + + /* Test if media is stable */ + if (try == 3 || sm_recheck_media(ftl)) + return ret; + } + + /* Unfortunelly, oob read will _always_ succeed, + despite card removal..... */ + ret = mtd->read_oob(mtd, sm_mkoffset(ftl, zone, block, boffset), &ops); + + /* Test for unknown errors */ + if (ret != 0 && ret != -EUCLEAN && ret != -EBADMSG) { + dbg("read of block %d at zone %d, failed due to error (%d)", + block, zone, ret); + goto again; + } + + /* Do a basic test on the oob, to guard against returned garbage */ + if (oob->reserved != 0xFFFFFFFF && !is_power_of_2(~oob->reserved)) + goto again; + + /* This should never happen, unless there is a bug in the mtd driver */ + WARN_ON(ops.oobretlen != SM_OOB_SIZE); + WARN_ON(buffer && ops.retlen != SM_SECTOR_SIZE); + + if (!buffer) + return 0; + + /* Test if sector marked as bad */ + if (!sm_sector_valid(oob)) { + dbg("read of block %d at zone %d, failed because it is marked" + " as bad" , block, zone); + goto again; + } + + /* Test ECC*/ + if (ret == -EBADMSG || + (ftl->smallpagenand && sm_correct_sector(buffer, oob))) { + + dbg("read of block %d at zone %d, failed due to ECC error", + block, zone); + goto again; + } + + return 0; +} + +/* Writes a sector to media */ +static int sm_write_sector(struct sm_ftl *ftl, + int zone, int block, int boffset, + uint8_t *buffer, struct sm_oob *oob) +{ + struct mtd_oob_ops ops; + struct mtd_info *mtd = ftl->trans->mtd; + int ret; + + BUG_ON(ftl->readonly); + + if (zone == 0 && (block == ftl->cis_block || block == 0)) { + dbg("attempted to write the CIS!"); + return -EIO; + } + + if (ftl->unstable) + return -EIO; + + ops.mode = ftl->smallpagenand ? MTD_OOB_RAW : MTD_OOB_PLACE; + ops.len = SM_SECTOR_SIZE; + ops.datbuf = buffer; + ops.ooboffs = 0; + ops.ooblen = SM_OOB_SIZE; + ops.oobbuf = (void *)oob; + + ret = mtd->write_oob(mtd, sm_mkoffset(ftl, zone, block, boffset), &ops); + + /* Now we assume that hardware will catch write bitflip errors */ + /* If you are paranoid, use CONFIG_MTD_NAND_VERIFY_WRITE */ + + if (ret) { + dbg("write to block %d at zone %d, failed with error %d", + block, zone, ret); + + sm_recheck_media(ftl); + return ret; + } + + /* This should never happen, unless there is a bug in the driver */ + WARN_ON(ops.oobretlen != SM_OOB_SIZE); + WARN_ON(buffer && ops.retlen != SM_SECTOR_SIZE); + + return 0; +} + +/* ------------------------ block IO ------------------------------------- */ + +/* Write a block using data and lba, and invalid sector bitmap */ +static int sm_write_block(struct sm_ftl *ftl, uint8_t *buf, + int zone, int block, int lba, + unsigned long invalid_bitmap) +{ + struct sm_oob oob; + int boffset; + int retry = 0; + + /* Initialize the oob with requested values */ + memset(&oob, 0xFF, SM_OOB_SIZE); + sm_write_lba(&oob, lba); +restart: + if (ftl->unstable) + return -EIO; + + for (boffset = 0; boffset < ftl->block_size; + boffset += SM_SECTOR_SIZE) { + + oob.data_status = 0xFF; + + if (test_bit(boffset / SM_SECTOR_SIZE, &invalid_bitmap)) { + + sm_printk("sector %d of block at LBA %d of zone %d" + " coudn't be read, marking it as invalid", + boffset / SM_SECTOR_SIZE, lba, zone); + + oob.data_status = 0; + } + + if (ftl->smallpagenand) { + __nand_calculate_ecc(buf + boffset, + SM_SMALL_PAGE, oob.ecc1); + + __nand_calculate_ecc(buf + boffset + SM_SMALL_PAGE, + SM_SMALL_PAGE, oob.ecc2); + } + if (!sm_write_sector(ftl, zone, block, boffset, + buf + boffset, &oob)) + continue; + + if (!retry) { + + /* If write fails. try to erase the block */ + /* This is safe, because we never write in blocks + that contain valuable data. + This is intended to repair block that are marked + as erased, but that isn't fully erased*/ + + if (sm_erase_block(ftl, zone, block, 0)) + return -EIO; + + retry = 1; + goto restart; + } else { + sm_mark_block_bad(ftl, zone, block); + return -EIO; + } + } + return 0; +} + + +/* Mark whole block at offset 'offs' as bad. */ +static void sm_mark_block_bad(struct sm_ftl *ftl, int zone, int block) +{ + struct sm_oob oob; + int boffset; + + memset(&oob, 0xFF, SM_OOB_SIZE); + oob.block_status = 0xF0; + + if (ftl->unstable) + return; + + if (sm_recheck_media(ftl)) + return; + + sm_printk("marking block %d of zone %d as bad", block, zone); + + /* We aren't checking the return value, because we don't care */ + /* This also fails on fake xD cards, but I guess these won't expose + any bad blocks till fail completly */ + for (boffset = 0; boffset < ftl->block_size; boffset += SM_SECTOR_SIZE) + sm_write_sector(ftl, zone, block, boffset, NULL, &oob); +} + +/* + * Erase a block within a zone + * If erase succedes, it updates free block fifo, otherwise marks block as bad + */ +static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block, + int put_free) +{ + struct ftl_zone *zone = &ftl->zones[zone_num]; + struct mtd_info *mtd = ftl->trans->mtd; + struct erase_info erase; + + erase.mtd = mtd; + erase.callback = sm_erase_callback; + erase.addr = sm_mkoffset(ftl, zone_num, block, 0); + erase.len = ftl->block_size; + erase.priv = (u_long)ftl; + + if (ftl->unstable) + return -EIO; + + BUG_ON(ftl->readonly); + + if (zone_num == 0 && (block == ftl->cis_block || block == 0)) { + sm_printk("attempted to erase the CIS!"); + return -EIO; + } + + if (mtd->erase(mtd, &erase)) { + sm_printk("erase of block %d in zone %d failed", + block, zone_num); + goto error; + } + + if (erase.state == MTD_ERASE_PENDING) + wait_for_completion(&ftl->erase_completion); + + if (erase.state != MTD_ERASE_DONE) { + sm_printk("erase of block %d in zone %d failed after wait", + block, zone_num); + goto error; + } + + if (put_free) + kfifo_in(&zone->free_sectors, + (const unsigned char *)&block, sizeof(block)); + + return 0; +error: + sm_mark_block_bad(ftl, zone_num, block); + return -EIO; +} + +static void sm_erase_callback(struct erase_info *self) +{ + struct sm_ftl *ftl = (struct sm_ftl *)self->priv; + complete(&ftl->erase_completion); +} + +/* Throughtly test that block is valid. */ +static int sm_check_block(struct sm_ftl *ftl, int zone, int block) +{ + int boffset; + struct sm_oob oob; + int lbas[] = { -3, 0, 0, 0 }; + int i = 0; + int test_lba; + + + /* First just check that block doesn't look fishy */ + /* Only blocks that are valid or are sliced in two parts, are + accepted */ + for (boffset = 0; boffset < ftl->block_size; + boffset += SM_SECTOR_SIZE) { + + /* This shoudn't happen anyway */ + if (sm_read_sector(ftl, zone, block, boffset, NULL, &oob)) + return -2; + + test_lba = sm_read_lba(&oob); + + if (lbas[i] != test_lba) + lbas[++i] = test_lba; + + /* If we found three different LBAs, something is fishy */ + if (i == 3) + return -EIO; + } + + /* If the block is sliced (partialy erased usually) erase it */ + if (i == 2) { + sm_erase_block(ftl, zone, block, 1); + return 1; + } + + return 0; +} + +/* ----------------- media scanning --------------------------------- */ +static const struct chs_entry chs_table[] = { + { 1, 125, 4, 4 }, + { 2, 125, 4, 8 }, + { 4, 250, 4, 8 }, + { 8, 250, 4, 16 }, + { 16, 500, 4, 16 }, + { 32, 500, 8, 16 }, + { 64, 500, 8, 32 }, + { 128, 500, 16, 32 }, + { 256, 1000, 16, 32 }, + { 512, 1015, 32, 63 }, + { 1024, 985, 33, 63 }, + { 2048, 985, 33, 63 }, + { 0 }, +}; + + +static const uint8_t cis_signature[] = { + 0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20 +}; +/* Find out media parameters. + * This ideally has to be based on nand id, but for now device size is enough */ +int sm_get_media_info(struct sm_ftl *ftl, struct mtd_info *mtd) +{ + int i; + int size_in_megs = mtd->size / (1024 * 1024); + + ftl->readonly = mtd->type == MTD_ROM; + + /* Manual settings for very old devices */ + ftl->zone_count = 1; + ftl->smallpagenand = 0; + + switch (size_in_megs) { + case 1: + /* 1 MiB flash/rom SmartMedia card (256 byte pages)*/ + ftl->zone_size = 256; + ftl->max_lba = 250; + ftl->block_size = 8 * SM_SECTOR_SIZE; + ftl->smallpagenand = 1; + + break; + case 2: + /* 2 MiB flash SmartMedia (256 byte pages)*/ + if (mtd->writesize == SM_SMALL_PAGE) { + ftl->zone_size = 512; + ftl->max_lba = 500; + ftl->block_size = 8 * SM_SECTOR_SIZE; + ftl->smallpagenand = 1; + /* 2 MiB rom SmartMedia */ + } else { + + if (!ftl->readonly) + return -ENODEV; + + ftl->zone_size = 256; + ftl->max_lba = 250; + ftl->block_size = 16 * SM_SECTOR_SIZE; + } + break; + case 4: + /* 4 MiB flash/rom SmartMedia device */ + ftl->zone_size = 512; + ftl->max_lba = 500; + ftl->block_size = 16 * SM_SECTOR_SIZE; + break; + case 8: + /* 8 MiB flash/rom SmartMedia device */ + ftl->zone_size = 1024; + ftl->max_lba = 1000; + ftl->block_size = 16 * SM_SECTOR_SIZE; + } + + /* Minimum xD size is 16MiB. Also, all xD cards have standard zone + sizes. SmartMedia cards exist up to 128 MiB and have same layout*/ + if (size_in_megs >= 16) { + ftl->zone_count = size_in_megs / 16; + ftl->zone_size = 1024; + ftl->max_lba = 1000; + ftl->block_size = 32 * SM_SECTOR_SIZE; + } + + /* Test for proper write,erase and oob sizes */ + if (mtd->erasesize > ftl->block_size) + return -ENODEV; + + if (mtd->writesize > SM_SECTOR_SIZE) + return -ENODEV; + + if (ftl->smallpagenand && mtd->oobsize < SM_SMALL_OOB_SIZE) + return -ENODEV; + + if (!ftl->smallpagenand && mtd->oobsize < SM_OOB_SIZE) + return -ENODEV; + + /* We use these functions for IO */ + if (!mtd->read_oob || !mtd->write_oob) + return -ENODEV; + + /* Find geometry information */ + for (i = 0 ; i < ARRAY_SIZE(chs_table) ; i++) { + if (chs_table[i].size == size_in_megs) { + ftl->cylinders = chs_table[i].cyl; + ftl->heads = chs_table[i].head; + ftl->sectors = chs_table[i].sec; + return 0; + } + } + + sm_printk("media has unknown size : %dMiB", size_in_megs); + ftl->cylinders = 985; + ftl->heads = 33; + ftl->sectors = 63; + return 0; +} + +/* Validate the CIS */ +static int sm_read_cis(struct sm_ftl *ftl) +{ + struct sm_oob oob; + + if (sm_read_sector(ftl, + 0, ftl->cis_block, ftl->cis_boffset, ftl->cis_buffer, &oob)) + return -EIO; + + if (!sm_sector_valid(&oob) || !sm_block_valid(&oob)) + return -EIO; + + if (!memcmp(ftl->cis_buffer + ftl->cis_page_offset, + cis_signature, sizeof(cis_signature))) { + return 0; + } + + return -EIO; +} + +/* Scan the media for the CIS */ +static int sm_find_cis(struct sm_ftl *ftl) +{ + struct sm_oob oob; + int block, boffset; + int block_found = 0; + int cis_found = 0; + + /* Search for first valid block */ + for (block = 0 ; block < ftl->zone_size - ftl->max_lba ; block++) { + + if (sm_read_sector(ftl, 0, block, 0, NULL, &oob)) + continue; + + if (!sm_block_valid(&oob)) + continue; + block_found = 1; + break; + } + + if (!block_found) + return -EIO; + + /* Search for first valid sector in this block */ + for (boffset = 0 ; boffset < ftl->block_size; + boffset += SM_SECTOR_SIZE) { + + if (sm_read_sector(ftl, 0, block, boffset, NULL, &oob)) + continue; + + if (!sm_sector_valid(&oob)) + continue; + break; + } + + if (boffset == ftl->block_size) + return -EIO; + + ftl->cis_block = block; + ftl->cis_boffset = boffset; + ftl->cis_page_offset = 0; + + cis_found = !sm_read_cis(ftl); + + if (!cis_found) { + ftl->cis_page_offset = SM_SMALL_PAGE; + cis_found = !sm_read_cis(ftl); + } + + if (cis_found) { + dbg("CIS block found at offset %x", + block * ftl->block_size + + boffset + ftl->cis_page_offset); + return 0; + } + return -EIO; +} + +/* Basic test to determine if underlying mtd device if functional */ +static int sm_recheck_media(struct sm_ftl *ftl) +{ + if (sm_read_cis(ftl)) { + + if (!ftl->unstable) { + sm_printk("media unstable, not allowing writes"); + ftl->unstable = 1; + } + return -EIO; + } + return 0; +} + +/* Initialize a FTL zone */ +static int sm_init_zone(struct sm_ftl *ftl, int zone_num) +{ + struct ftl_zone *zone = &ftl->zones[zone_num]; + struct sm_oob oob; + uint16_t block; + int lba; + int i = 0; + int len; + + dbg("initializing zone %d", zone_num); + + /* Allocate memory for FTL table */ + zone->lba_to_phys_table = kmalloc(ftl->max_lba * 2, GFP_KERNEL); + + if (!zone->lba_to_phys_table) + return -ENOMEM; + memset(zone->lba_to_phys_table, -1, ftl->max_lba * 2); + + + /* Allocate memory for free sectors FIFO */ + if (kfifo_alloc(&zone->free_sectors, ftl->zone_size * 2, GFP_KERNEL)) { + kfree(zone->lba_to_phys_table); + return -ENOMEM; + } + + /* Now scan the zone */ + for (block = 0 ; block < ftl->zone_size ; block++) { + + /* Skip blocks till the CIS (including) */ + if (zone_num == 0 && block <= ftl->cis_block) + continue; + + /* Read the oob of first sector */ + if (sm_read_sector(ftl, zone_num, block, 0, NULL, &oob)) + return -EIO; + + /* Test to see if block is erased. It is enough to test + first sector, because erase happens in one shot */ + if (sm_block_erased(&oob)) { + kfifo_in(&zone->free_sectors, + (unsigned char *)&block, 2); + continue; + } + + /* If block is marked as bad, skip it */ + /* This assumes we can trust first sector*/ + /* However the way the block valid status is defined, ensures + very low probability of failure here */ + if (!sm_block_valid(&oob)) { + dbg("PH %04d <-> <marked bad>", block); + continue; + } + + + lba = sm_read_lba(&oob); + + /* Invalid LBA means that block is damaged. */ + /* We can try to erase it, or mark it as bad, but + lets leave that to recovery application */ + if (lba == -2 || lba >= ftl->max_lba) { + dbg("PH %04d <-> LBA %04d(bad)", block, lba); + continue; + } + + + /* If there is no collision, + just put the sector in the FTL table */ + if (zone->lba_to_phys_table[lba] < 0) { + dbg_verbose("PH %04d <-> LBA %04d", block, lba); + zone->lba_to_phys_table[lba] = block; + continue; + } + + sm_printk("collision" + " of LBA %d between blocks %d and %d in zone %d", + lba, zone->lba_to_phys_table[lba], block, zone_num); + + /* Test that this block is valid*/ + if (sm_check_block(ftl, zone_num, block)) + continue; + + /* Test now the old block */ + if (sm_check_block(ftl, zone_num, + zone->lba_to_phys_table[lba])) { + zone->lba_to_phys_table[lba] = block; + continue; + } + + /* If both blocks are valid and share same LBA, it means that + they hold different versions of same data. It not + known which is more recent, thus just erase one of them + */ + sm_printk("both blocks are valid, erasing the later"); + sm_erase_block(ftl, zone_num, block, 1); + } + + dbg("zone initialized"); + zone->initialized = 1; + + /* No free sectors, means that the zone is heavily damaged, write won't + work, but it can still can be (partially) read */ + if (!kfifo_len(&zone->free_sectors)) { + sm_printk("no free blocks in zone %d", zone_num); + return 0; + } + + /* Randomize first block we write to */ + get_random_bytes(&i, 2); + i %= (kfifo_len(&zone->free_sectors) / 2); + + while (i--) { + len = kfifo_out(&zone->free_sectors, + (unsigned char *)&block, 2); + WARN_ON(len != 2); + kfifo_in(&zone->free_sectors, (const unsigned char *)&block, 2); + } + return 0; +} + +/* Get and automaticly initialize an FTL mapping for one zone */ +struct ftl_zone *sm_get_zone(struct sm_ftl *ftl, int zone_num) +{ + struct ftl_zone *zone; + int error; + + BUG_ON(zone_num >= ftl->zone_count); + zone = &ftl->zones[zone_num]; + + if (!zone->initialized) { + error = sm_init_zone(ftl, zone_num); + + if (error) + return ERR_PTR(error); + } + return zone; +} + + +/* ----------------- cache handling ------------------------------------------*/ + +/* Initialize the one block cache */ +void sm_cache_init(struct sm_ftl *ftl) +{ + ftl->cache_data_invalid_bitmap = 0xFFFFFFFF; + ftl->cache_clean = 1; + ftl->cache_zone = -1; + ftl->cache_block = -1; + /*memset(ftl->cache_data, 0xAA, ftl->block_size);*/ +} + +/* Put sector in one block cache */ +void sm_cache_put(struct sm_ftl *ftl, char *buffer, int boffset) +{ + memcpy(ftl->cache_data + boffset, buffer, SM_SECTOR_SIZE); + clear_bit(boffset / SM_SECTOR_SIZE, &ftl->cache_data_invalid_bitmap); + ftl->cache_clean = 0; +} + +/* Read a sector from the cache */ +int sm_cache_get(struct sm_ftl *ftl, char *buffer, int boffset) +{ + if (test_bit(boffset / SM_SECTOR_SIZE, + &ftl->cache_data_invalid_bitmap)) + return -1; + + memcpy(buffer, ftl->cache_data + boffset, SM_SECTOR_SIZE); + return 0; +} + +/* Write the cache to hardware */ +int sm_cache_flush(struct sm_ftl *ftl) +{ + struct ftl_zone *zone; + + int sector_num; + uint16_t write_sector; + int zone_num = ftl->cache_zone; + int block_num; + + if (ftl->cache_clean) + return 0; + + if (ftl->unstable) + return -EIO; + + BUG_ON(zone_num < 0); + zone = &ftl->zones[zone_num]; + block_num = zone->lba_to_phys_table[ftl->cache_block]; + + + /* Try to read all unread areas of the cache block*/ + for_each_set_bit(sector_num, &ftl->cache_data_invalid_bitmap, + ftl->block_size / SM_SECTOR_SIZE) { + + if (!sm_read_sector(ftl, + zone_num, block_num, sector_num * SM_SECTOR_SIZE, + ftl->cache_data + sector_num * SM_SECTOR_SIZE, NULL)) + clear_bit(sector_num, + &ftl->cache_data_invalid_bitmap); + } +restart: + + if (ftl->unstable) + return -EIO; + + /* If there are no spare blocks, */ + /* we could still continue by erasing/writing the current block, + but for such worn out media it doesn't worth the trouble, + and the dangers */ + if (kfifo_out(&zone->free_sectors, + (unsigned char *)&write_sector, 2) != 2) { + dbg("no free sectors for write!"); + return -EIO; + } + + + if (sm_write_block(ftl, ftl->cache_data, zone_num, write_sector, + ftl->cache_block, ftl->cache_data_invalid_bitmap)) + goto restart; + + /* Update the FTL table */ + zone->lba_to_phys_table[ftl->cache_block] = write_sector; + + /* Write succesfull, so erase and free the old block */ + if (block_num > 0) + sm_erase_block(ftl, zone_num, block_num, 1); + + sm_cache_init(ftl); + return 0; +} + + +/* flush timer, runs a second after last write */ +static void sm_cache_flush_timer(unsigned long data) +{ + struct sm_ftl *ftl = (struct sm_ftl *)data; + queue_work(cache_flush_workqueue, &ftl->flush_work); +} + +/* cache flush work, kicked by timer */ +static void sm_cache_flush_work(struct work_struct *work) +{ + struct sm_ftl *ftl = container_of(work, struct sm_ftl, flush_work); + mutex_lock(&ftl->mutex); + sm_cache_flush(ftl); + mutex_unlock(&ftl->mutex); + return; +} + +/* ---------------- outside interface -------------------------------------- */ + +/* outside interface: read a sector */ +static int sm_read(struct mtd_blktrans_dev *dev, + unsigned long sect_no, char *buf) +{ + struct sm_ftl *ftl = dev->priv; + struct ftl_zone *zone; + int error = 0, in_cache = 0; + int zone_num, block, boffset; + + sm_break_offset(ftl, sect_no << 9, &zone_num, &block, &boffset); + mutex_lock(&ftl->mutex); + + + zone = sm_get_zone(ftl, zone_num); + if (IS_ERR(zone)) { + error = PTR_ERR(zone); + goto unlock; + } + + /* Have to look at cache first */ + if (ftl->cache_zone == zone_num && ftl->cache_block == block) { + in_cache = 1; + if (!sm_cache_get(ftl, buf, boffset)) + goto unlock; + } + + /* Translate the block and return if doesn't exist in the table */ + block = zone->lba_to_phys_table[block]; + + if (block == -1) { + memset(buf, 0xFF, SM_SECTOR_SIZE); + goto unlock; + } + + if (sm_read_sector(ftl, zone_num, block, boffset, buf, NULL)) { + error = -EIO; + goto unlock; + } + + if (in_cache) + sm_cache_put(ftl, buf, boffset); +unlock: + mutex_unlock(&ftl->mutex); + return error; +} + +/* outside interface: write a sector */ +static int sm_write(struct mtd_blktrans_dev *dev, + unsigned long sec_no, char *buf) +{ + struct sm_ftl *ftl = dev->priv; + struct ftl_zone *zone; + int error, zone_num, block, boffset; + + BUG_ON(ftl->readonly); + sm_break_offset(ftl, sec_no << 9, &zone_num, &block, &boffset); + + /* No need in flush thread running now */ + del_timer(&ftl->timer); + mutex_lock(&ftl->mutex); + + zone = sm_get_zone(ftl, zone_num); + if (IS_ERR(zone)) { + error = PTR_ERR(zone); + goto unlock; + } + + /* If entry is not in cache, flush it */ + if (ftl->cache_block != block || ftl->cache_zone != zone_num) { + + error = sm_cache_flush(ftl); + if (error) + goto unlock; + + ftl->cache_block = block; + ftl->cache_zone = zone_num; + } + + sm_cache_put(ftl, buf, boffset); +unlock: + mod_timer(&ftl->timer, jiffies + msecs_to_jiffies(cache_timeout)); + mutex_unlock(&ftl->mutex); + return error; +} + +/* outside interface: flush everything */ +static int sm_flush(struct mtd_blktrans_dev *dev) +{ + struct sm_ftl *ftl = dev->priv; + int retval; + + mutex_lock(&ftl->mutex); + retval = sm_cache_flush(ftl); + mutex_unlock(&ftl->mutex); + return retval; +} + +/* outside interface: device is released */ +static int sm_release(struct mtd_blktrans_dev *dev) +{ + struct sm_ftl *ftl = dev->priv; + + mutex_lock(&ftl->mutex); + del_timer_sync(&ftl->timer); + cancel_work_sync(&ftl->flush_work); + sm_cache_flush(ftl); + mutex_unlock(&ftl->mutex); + return 0; +} + +/* outside interface: get geometry */ +static int sm_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) +{ + struct sm_ftl *ftl = dev->priv; + geo->heads = ftl->heads; + geo->sectors = ftl->sectors; + geo->cylinders = ftl->cylinders; + return 0; +} + +/* external interface: main initialization function */ +static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) +{ + struct mtd_blktrans_dev *trans; + struct sm_ftl *ftl; + + /* Allocate & initialize our private structure */ + ftl = kzalloc(sizeof(struct sm_ftl), GFP_KERNEL); + if (!ftl) + goto error1; + + + mutex_init(&ftl->mutex); + setup_timer(&ftl->timer, sm_cache_flush_timer, (unsigned long)ftl); + INIT_WORK(&ftl->flush_work, sm_cache_flush_work); + init_completion(&ftl->erase_completion); + + /* Read media information */ + if (sm_get_media_info(ftl, mtd)) { + dbg("found unsupported mtd device, aborting"); + goto error2; + } + + + /* Allocate temporary CIS buffer for read retry support */ + ftl->cis_buffer = kzalloc(SM_SECTOR_SIZE, GFP_KERNEL); + if (!ftl->cis_buffer) + goto error2; + + /* Allocate zone array, it will be initialized on demand */ + ftl->zones = kzalloc(sizeof(struct ftl_zone) * ftl->zone_count, + GFP_KERNEL); + if (!ftl->zones) + goto error3; + + /* Allocate the cache*/ + ftl->cache_data = kzalloc(ftl->block_size, GFP_KERNEL); + + if (!ftl->cache_data) + goto error4; + + sm_cache_init(ftl); + + + /* Allocate upper layer structure and initialize it */ + trans = kzalloc(sizeof(struct mtd_blktrans_dev), GFP_KERNEL); + if (!trans) + goto error5; + + ftl->trans = trans; + trans->priv = ftl; + + trans->tr = tr; + trans->mtd = mtd; + trans->devnum = -1; + trans->size = (ftl->block_size * ftl->max_lba * ftl->zone_count) >> 9; + trans->readonly = ftl->readonly; + + if (sm_find_cis(ftl)) { + dbg("CIS not found on mtd device, aborting"); + goto error6; + } + + ftl->disk_attributes = sm_create_sysfs_attributes(ftl); + trans->disk_attributes = ftl->disk_attributes; + + sm_printk("Found %d MiB xD/SmartMedia FTL on mtd%d", + (int)(mtd->size / (1024 * 1024)), mtd->index); + + dbg("FTL layout:"); + dbg("%d zone(s), each consists of %d blocks (+%d spares)", + ftl->zone_count, ftl->max_lba, + ftl->zone_size - ftl->max_lba); + dbg("each block consists of %d bytes", + ftl->block_size); + + + /* Register device*/ + if (add_mtd_blktrans_dev(trans)) { + dbg("error in mtdblktrans layer"); + goto error6; + } + return; +error6: + kfree(trans); +error5: + kfree(ftl->cache_data); +error4: + kfree(ftl->zones); +error3: + kfree(ftl->cis_buffer); +error2: + kfree(ftl); +error1: + return; +} + +/* main interface: device {surprise,} removal */ +static void sm_remove_dev(struct mtd_blktrans_dev *dev) +{ + struct sm_ftl *ftl = dev->priv; + int i; + + del_mtd_blktrans_dev(dev); + ftl->trans = NULL; + + for (i = 0 ; i < ftl->zone_count; i++) { + + if (!ftl->zones[i].initialized) + continue; + + kfree(ftl->zones[i].lba_to_phys_table); + kfifo_free(&ftl->zones[i].free_sectors); + } + + sm_delete_sysfs_attributes(ftl); + kfree(ftl->cis_buffer); + kfree(ftl->zones); + kfree(ftl->cache_data); + kfree(ftl); +} + +static struct mtd_blktrans_ops sm_ftl_ops = { + .name = "smblk", + .major = -1, + .part_bits = SM_FTL_PARTN_BITS, + .blksize = SM_SECTOR_SIZE, + .getgeo = sm_getgeo, + + .add_mtd = sm_add_mtd, + .remove_dev = sm_remove_dev, + + .readsect = sm_read, + .writesect = sm_write, + + .flush = sm_flush, + .release = sm_release, + + .owner = THIS_MODULE, +}; + +static __init int sm_module_init(void) +{ + int error = 0; + cache_flush_workqueue = create_freezeable_workqueue("smflush"); + + if (IS_ERR(cache_flush_workqueue)) + return PTR_ERR(cache_flush_workqueue); + + error = register_mtd_blktrans(&sm_ftl_ops); + if (error) + destroy_workqueue(cache_flush_workqueue); + return error; + +} + +static void __exit sm_module_exit(void) +{ + destroy_workqueue(cache_flush_workqueue); + deregister_mtd_blktrans(&sm_ftl_ops); +} + +module_init(sm_module_init); +module_exit(sm_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Maxim Levitsky <maximlevitsky@gmail.com>"); +MODULE_DESCRIPTION("Smartmedia/xD mtd translation layer"); diff --git a/drivers/mtd/sm_ftl.h b/drivers/mtd/sm_ftl.h new file mode 100644 index 0000000..e30e48e --- /dev/null +++ b/drivers/mtd/sm_ftl.h @@ -0,0 +1,94 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * SmartMedia/xD translation layer + * + * Based loosly on ssfdc.c which is + * © 2005 Eptar srl + * Author: Claudio Lanconelli <lanconelli.claudio@eptar.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/mtd/blktrans.h> +#include <linux/kfifo.h> +#include <linux/sched.h> +#include <linux/completion.h> +#include <linux/mtd/mtd.h> + + + +struct ftl_zone { + int initialized; + int16_t *lba_to_phys_table; /* LBA to physical table */ + struct kfifo free_sectors; /* queue of free sectors */ +}; + +struct sm_ftl { + struct mtd_blktrans_dev *trans; + + struct mutex mutex; /* protects the structure */ + struct ftl_zone *zones; /* FTL tables for each zone */ + + /* Media information */ + int block_size; /* block size in bytes */ + int zone_size; /* zone size in blocks */ + int zone_count; /* number of zones */ + int max_lba; /* maximum lba in a zone */ + int smallpagenand; /* 256 bytes/page nand */ + int readonly; /* is FS readonly */ + int unstable; + int cis_block; /* CIS block location */ + int cis_boffset; /* CIS offset in the block */ + int cis_page_offset; /* CIS offset in the page */ + void *cis_buffer; /* tmp buffer for cis reads */ + + /* Cache */ + int cache_block; /* block number of cached block */ + int cache_zone; /* zone of cached block */ + unsigned char *cache_data; /* cached block data */ + long unsigned int cache_data_invalid_bitmap; + int cache_clean; + struct work_struct flush_work; + struct timer_list timer; + + /* Async erase stuff */ + struct completion erase_completion; + + /* Geometry stuff */ + int heads; + int sectors; + int cylinders; + + struct attribute_group *disk_attributes; +}; + +struct chs_entry { + unsigned long size; + unsigned short cyl; + unsigned char head; + unsigned char sec; +}; + + +#define SM_FTL_PARTN_BITS 3 + +#define sm_printk(format, ...) \ + printk(KERN_WARNING "sm_ftl" ": " format "\n", ## __VA_ARGS__) + +#define dbg(format, ...) \ + if (debug) \ + printk(KERN_DEBUG "sm_ftl" ": " format "\n", ## __VA_ARGS__) + +#define dbg_verbose(format, ...) \ + if (debug > 1) \ + printk(KERN_DEBUG "sm_ftl" ": " format "\n", ## __VA_ARGS__) + + +static void sm_erase_callback(struct erase_info *self); +static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block, + int put_free); +static void sm_mark_block_bad(struct sm_ftl *ftl, int zone_num, int block); + +static int sm_recheck_media(struct sm_ftl *ftl); diff --git a/drivers/mtd/ssfdc.c b/drivers/mtd/ssfdc.c index 3f67e00..81c4ecd 100644 --- a/drivers/mtd/ssfdc.c +++ b/drivers/mtd/ssfdc.c @@ -375,7 +375,6 @@ static void ssfdcr_remove_dev(struct mtd_blktrans_dev *dev) del_mtd_blktrans_dev(dev); kfree(ssfdc->logic_block_map); - kfree(ssfdc); } static int ssfdcr_readsect(struct mtd_blktrans_dev *dev, diff --git a/drivers/mtd/tests/mtd_pagetest.c b/drivers/mtd/tests/mtd_pagetest.c index 921a85d..6bc1b82 100644 --- a/drivers/mtd/tests/mtd_pagetest.c +++ b/drivers/mtd/tests/mtd_pagetest.c @@ -480,12 +480,11 @@ static int scan_for_bad_eraseblocks(void) { int i, bad = 0; - bbt = kmalloc(ebcnt, GFP_KERNEL); + bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { printk(PRINT_PREF "error: cannot allocate memory\n"); return -ENOMEM; } - memset(bbt, 0 , ebcnt); printk(PRINT_PREF "scanning for bad eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { diff --git a/drivers/mtd/tests/mtd_readtest.c b/drivers/mtd/tests/mtd_readtest.c index 7107fcc..afe71aa 100644 --- a/drivers/mtd/tests/mtd_readtest.c +++ b/drivers/mtd/tests/mtd_readtest.c @@ -141,12 +141,11 @@ static int scan_for_bad_eraseblocks(void) { int i, bad = 0; - bbt = kmalloc(ebcnt, GFP_KERNEL); + bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { printk(PRINT_PREF "error: cannot allocate memory\n"); return -ENOMEM; } - memset(bbt, 0 , ebcnt); /* NOR flash does not implement block_isbad */ if (mtd->block_isbad == NULL) diff --git a/drivers/mtd/tests/mtd_speedtest.c b/drivers/mtd/tests/mtd_speedtest.c index 56ca62b..161feeb 100644 --- a/drivers/mtd/tests/mtd_speedtest.c +++ b/drivers/mtd/tests/mtd_speedtest.c @@ -295,12 +295,11 @@ static int scan_for_bad_eraseblocks(void) { int i, bad = 0; - bbt = kmalloc(ebcnt, GFP_KERNEL); + bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { printk(PRINT_PREF "error: cannot allocate memory\n"); return -ENOMEM; } - memset(bbt, 0 , ebcnt); /* NOR flash does not implement block_isbad */ if (mtd->block_isbad == NULL) diff --git a/drivers/mtd/tests/mtd_stresstest.c b/drivers/mtd/tests/mtd_stresstest.c index 3854afe..531625f 100644 --- a/drivers/mtd/tests/mtd_stresstest.c +++ b/drivers/mtd/tests/mtd_stresstest.c @@ -221,12 +221,11 @@ static int scan_for_bad_eraseblocks(void) { int i, bad = 0; - bbt = kmalloc(ebcnt, GFP_KERNEL); + bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { printk(PRINT_PREF "error: cannot allocate memory\n"); return -ENOMEM; } - memset(bbt, 0 , ebcnt); /* NOR flash does not implement block_isbad */ if (mtd->block_isbad == NULL) diff --git a/drivers/mtd/tests/mtd_subpagetest.c b/drivers/mtd/tests/mtd_subpagetest.c index 700237a..11204e8 100644 --- a/drivers/mtd/tests/mtd_subpagetest.c +++ b/drivers/mtd/tests/mtd_subpagetest.c @@ -354,12 +354,11 @@ static int scan_for_bad_eraseblocks(void) { int i, bad = 0; - bbt = kmalloc(ebcnt, GFP_KERNEL); + bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { printk(PRINT_PREF "error: cannot allocate memory\n"); return -ENOMEM; } - memset(bbt, 0 , ebcnt); printk(PRINT_PREF "scanning for bad eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig index 0a8c7ea..f702a16 100644 --- a/drivers/mtd/ubi/Kconfig +++ b/drivers/mtd/ubi/Kconfig @@ -27,7 +27,7 @@ config MTD_UBI_WL_THRESHOLD The default value should be OK for SLC NAND flashes, NOR flashes and other flashes which have eraseblock life-cycle 100000 or more. However, in case of MLC NAND flashes which typically have eraseblock - life-cycle less then 10000, the threshold should be lessened (e.g., + life-cycle less than 10000, the threshold should be lessened (e.g., to 128 or 256, although it does not have to be power of 2). config MTD_UBI_BEB_RESERVE diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 55c726d..13b05cb 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -42,7 +42,6 @@ #include <linux/miscdevice.h> #include <linux/log2.h> #include <linux/kthread.h> -#include <linux/reboot.h> #include <linux/kernel.h> #include <linux/slab.h> #include "ubi.h" @@ -50,6 +49,12 @@ /* Maximum length of the 'mtd=' parameter */ #define MTD_PARAM_LEN_MAX 64 +#ifdef CONFIG_MTD_UBI_MODULE +#define ubi_is_module() 1 +#else +#define ubi_is_module() 0 +#endif + /** * struct mtd_dev_param - MTD device parameter description data structure. * @name: MTD character device node path, MTD device name, or MTD device number @@ -832,34 +837,6 @@ static int autoresize(struct ubi_device *ubi, int vol_id) } /** - * ubi_reboot_notifier - halt UBI transactions immediately prior to a reboot. - * @n: reboot notifier object - * @state: SYS_RESTART, SYS_HALT, or SYS_POWER_OFF - * @cmd: pointer to command string for RESTART2 - * - * This function stops the UBI background thread so that the flash device - * remains quiescent when Linux restarts the system. Any queued work will be - * discarded, but this function will block until do_work() finishes if an - * operation is already in progress. - * - * This function solves a real-life problem observed on NOR flashes when an - * PEB erase operation starts, then the system is rebooted before the erase is - * finishes, and the boot loader gets confused and dies. So we prefer to finish - * the ongoing operation before rebooting. - */ -static int ubi_reboot_notifier(struct notifier_block *n, unsigned long state, - void *cmd) -{ - struct ubi_device *ubi; - - ubi = container_of(n, struct ubi_device, reboot_notifier); - if (ubi->bgt_thread) - kthread_stop(ubi->bgt_thread); - ubi_sync(ubi->ubi_num); - return NOTIFY_DONE; -} - -/** * ubi_attach_mtd_dev - attach an MTD device. * @mtd: MTD device description object * @ubi_num: number to assign to the new UBI device @@ -1016,11 +993,6 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) wake_up_process(ubi->bgt_thread); spin_unlock(&ubi->wl_lock); - /* Flash device priority is 0 - UBI needs to shut down first */ - ubi->reboot_notifier.priority = 1; - ubi->reboot_notifier.notifier_call = ubi_reboot_notifier; - register_reboot_notifier(&ubi->reboot_notifier); - ubi_devices[ubi_num] = ubi; ubi_notify_all(ubi, UBI_VOLUME_ADDED, NULL); return ubi_num; @@ -1091,7 +1063,6 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) * Before freeing anything, we have to stop the background thread to * prevent it from doing anything on this device while we are freeing. */ - unregister_reboot_notifier(&ubi->reboot_notifier); if (ubi->bgt_thread) kthread_stop(ubi->bgt_thread); @@ -1241,9 +1212,24 @@ static int __init ubi_init(void) p->vid_hdr_offs); mutex_unlock(&ubi_devices_mutex); if (err < 0) { - put_mtd_device(mtd); ubi_err("cannot attach mtd%d", mtd->index); - goto out_detach; + put_mtd_device(mtd); + + /* + * Originally UBI stopped initializing on any error. + * However, later on it was found out that this + * behavior is not very good when UBI is compiled into + * the kernel and the MTD devices to attach are passed + * through the command line. Indeed, UBI failure + * stopped whole boot sequence. + * + * To fix this, we changed the behavior for the + * non-module case, but preserved the old behavior for + * the module case, just for compatibility. This is a + * little inconsistent, though. + */ + if (ubi_is_module()) + goto out_detach; } } diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 533b1a4..4b979e3 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -64,9 +64,9 @@ * device, e.g., make @ubi->min_io_size = 512 in the example above? * * A: because when writing a sub-page, MTD still writes a full 2K page but the - * bytes which are no relevant to the sub-page are 0xFF. So, basically, writing - * 4x512 sub-pages is 4 times slower then writing one 2KiB NAND page. Thus, we - * prefer to use sub-pages only for EV and VID headers. + * bytes which are not relevant to the sub-page are 0xFF. So, basically, + * writing 4x512 sub-pages is 4 times slower than writing one 2KiB NAND page. + * Thus, we prefer to use sub-pages only for EC and VID headers. * * As it was noted above, the VID header may start at a non-aligned offset. * For example, in case of a 2KiB page NAND flash with a 512 bytes sub-page, diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 17f287d..69fa4ef0 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -488,7 +488,7 @@ EXPORT_SYMBOL_GPL(ubi_leb_write); * * This function changes the contents of a logical eraseblock atomically. @buf * has to contain new logical eraseblock data, and @len - the length of the - * data, which has to be aligned. The length may be shorter then the logical + * data, which has to be aligned. The length may be shorter than the logical * eraseblock size, ant the logical eraseblock may be appended to more times * later on. This function guarantees that in case of an unclean reboot the old * contents is preserved. Returns zero in case of success and a negative error @@ -571,7 +571,7 @@ EXPORT_SYMBOL_GPL(ubi_leb_erase); * * This function un-maps logical eraseblock @lnum and schedules the * corresponding physical eraseblock for erasure, so that it will eventually be - * physically erased in background. This operation is much faster then the + * physically erased in background. This operation is much faster than the * erase operation. * * Unlike erase, the un-map operation does not guarantee that the logical @@ -590,7 +590,7 @@ EXPORT_SYMBOL_GPL(ubi_leb_erase); * * The main and obvious use-case of this function is when the contents of a * logical eraseblock has to be re-written. Then it is much more efficient to - * first un-map it, then write new data, rather then first erase it, then write + * first un-map it, then write new data, rather than first erase it, then write * new data. Note, once new data has been written to the logical eraseblock, * UBI guarantees that the old contents has gone forever. In other words, if an * unclean reboot happens after the logical eraseblock has been un-mapped and diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index dc5f688..aed19f3 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -231,7 +231,7 @@ static struct ubi_scan_volume *add_volume(struct ubi_scan_info *si, int vol_id, * case of success this function returns a positive value, in case of failure, a * negative error code is returned. The success return codes use the following * bits: - * o bit 0 is cleared: the first PEB (described by @seb) is newer then the + * o bit 0 is cleared: the first PEB (described by @seb) is newer than the * second PEB (described by @pnum and @vid_hdr); * o bit 0 is set: the second PEB is newer; * o bit 1 is cleared: no bit-flips were detected in the newer LEB; @@ -452,7 +452,7 @@ int ubi_scan_add_used(struct ubi_device *ubi, struct ubi_scan_info *si, if (cmp_res & 1) { /* - * This logical eraseblock is newer then the one + * This logical eraseblock is newer than the one * found earlier. */ err = validate_vid_hdr(vid_hdr, sv, pnum); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index 5176d48..a637f02 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -350,7 +350,6 @@ struct ubi_wl_entry; * @bgt_thread: background thread description object * @thread_enabled: if the background thread is enabled * @bgt_name: background thread name - * @reboot_notifier: notifier to terminate background thread before rebooting * * @flash_size: underlying MTD device size (in bytes) * @peb_count: count of physical eraseblocks on the MTD device @@ -436,7 +435,6 @@ struct ubi_device { struct task_struct *bgt_thread; int thread_enabled; char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2]; - struct notifier_block reboot_notifier; /* I/O sub-system's stuff */ long long flash_size; diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c index cd90ff3..14c10be 100644 --- a/drivers/mtd/ubi/vtbl.c +++ b/drivers/mtd/ubi/vtbl.c @@ -414,7 +414,7 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi, * 0 contains more recent information. * * So the plan is to first check LEB 0. Then - * a. if LEB 0 is OK, it must be containing the most resent data; then + * a. if LEB 0 is OK, it must be containing the most recent data; then * we compare it with LEB 1, and if they are different, we copy LEB * 0 to LEB 1; * b. if LEB 0 is corrupted, but LEB 1 has to be OK, and we copy LEB 1 @@ -848,7 +848,7 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si) goto out_free; /* - * Get sure that the scanning information is consistent to the + * Make sure that the scanning information is consistent to the * information stored in the volume table. */ err = check_scanning_info(ubi, si); diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index f64ddab..ee7b1d8 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -350,7 +350,7 @@ static void prot_queue_add(struct ubi_device *ubi, struct ubi_wl_entry *e) * @max: highest possible erase counter * * This function looks for a wear leveling entry with erase counter closest to - * @max and less then @max. + * @max and less than @max. */ static struct ubi_wl_entry *find_wl_entry(struct rb_root *root, int max) { |