diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2005-07-11 10:18:18 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-07-11 10:18:18 -0700 |
commit | 200d481f28be4522464bb849dd0eb5f8cb6be781 (patch) | |
tree | 8cd00ead1b202dfd377cf34000a5193959aa2e8b /drivers/mtd | |
parent | f43a64c5e1a65d12b9b53a35ed2d5db441fcb64c (diff) | |
parent | 97f927a4d7dbccde0a854a62c3ea54d90bae8679 (diff) | |
download | kernel_samsung_crespo-200d481f28be4522464bb849dd0eb5f8cb6be781.zip kernel_samsung_crespo-200d481f28be4522464bb849dd0eb5f8cb6be781.tar.gz kernel_samsung_crespo-200d481f28be4522464bb849dd0eb5f8cb6be781.tar.bz2 |
Merge master.kernel.org:/pub/scm/linux/kernel/git/tglx/mtd-2.6
Diffstat (limited to 'drivers/mtd')
48 files changed, 2767 insertions, 2877 deletions
diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig index d682dbc..b5dc593 100644 --- a/drivers/mtd/chips/Kconfig +++ b/drivers/mtd/chips/Kconfig @@ -1,5 +1,5 @@ # drivers/mtd/chips/Kconfig -# $Id: Kconfig,v 1.13 2004/12/01 15:49:10 nico Exp $ +# $Id: Kconfig,v 1.15 2005/06/06 23:04:35 tpoynor Exp $ menu "RAM/ROM/Flash chip drivers" depends on MTD!=n @@ -155,6 +155,31 @@ config MTD_CFI_I8 If your flash chips are interleaved in eights - i.e. you have eight flash chips addressed by each bus cycle, then say 'Y'. +config MTD_OTP + bool "Protection Registers aka one-time programmable (OTP) bits" + depends on MTD_CFI_ADV_OPTIONS + default n + help + This enables support for reading, writing and locking so called + "Protection Registers" present on some flash chips. + A subset of them are pre-programmed at the factory with a + unique set of values. The rest is user-programmable. + + The user-programmable Protection Registers contain one-time + programmable (OTP) bits; when programmed, register bits cannot be + erased. Each Protection Register can be accessed multiple times to + program individual bits, as long as the register remains unlocked. + + Each Protection Register has an associated Lock Register bit. When a + Lock Register bit is programmed, the associated Protection Register + can only be read; it can no longer be programmed. Additionally, + because the Lock Register bits themselves are OTP, when programmed, + Lock Register bits cannot be erased. Therefore, when a Protection + Register is locked, it cannot be unlocked. + + This feature should therefore be used with extreme care. Any mistake + in the programming of OTP bits will waste them. + config MTD_CFI_INTELEXT tristate "Support for Intel/Sharp flash chips" depends on MTD_GEN_PROBE @@ -275,7 +300,7 @@ config MTD_JEDEC config MTD_XIP bool "XIP aware MTD support" - depends on !SMP && MTD_CFI_INTELEXT && EXPERIMENTAL + depends on !SMP && (MTD_CFI_INTELEXT || MTD_CFI_AMDSTD) && EXPERIMENTAL default y if XIP_KERNEL help This allows MTD support to work with flash memory which is also diff --git a/drivers/mtd/chips/amd_flash.c b/drivers/mtd/chips/amd_flash.c index 41e2e3e..2dafeba 100644 --- a/drivers/mtd/chips/amd_flash.c +++ b/drivers/mtd/chips/amd_flash.c @@ -3,7 +3,7 @@ * * Author: Jonas Holmberg <jonas.holmberg@axis.com> * - * $Id: amd_flash.c,v 1.26 2004/11/20 12:49:04 dwmw2 Exp $ + * $Id: amd_flash.c,v 1.27 2005/02/04 07:43:09 jonashg Exp $ * * Copyright (c) 2001 Axis Communications AB * @@ -67,7 +67,6 @@ #define AM29LV160DT 0x22C4 #define AM29LV160DB 0x2249 #define AM29BDS323D 0x22D1 -#define AM29BDS643D 0x227E /* Atmel */ #define AT49xV16x 0x00C0 @@ -618,17 +617,6 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) { .offset = 0x3f0000, .erasesize = 0x02000, .numblocks = 8 }, } }, { - .mfr_id = MANUFACTURER_AMD, - .dev_id = AM29BDS643D, - .name = "AMD AM29BDS643D", - .size = 0x00800000, - .numeraseregions = 3, - .regions = { - { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 96 }, - { .offset = 0x600000, .erasesize = 0x10000, .numblocks = 31 }, - { .offset = 0x7f0000, .erasesize = 0x02000, .numblocks = 8 }, - } - }, { .mfr_id = MANUFACTURER_ATMEL, .dev_id = AT49xV16x, .name = "Atmel AT49xV16x", diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index c268bcd..0cfcd88 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -4,7 +4,7 @@ * * (C) 2000 Red Hat. GPL'd * - * $Id: cfi_cmdset_0001.c,v 1.164 2004/11/16 18:29:00 dwmw2 Exp $ + * $Id: cfi_cmdset_0001.c,v 1.178 2005/05/19 17:05:43 nico Exp $ * * * 10/10/2000 Nicolas Pitre <nico@cam.org> @@ -29,6 +29,7 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/reboot.h> #include <linux/mtd/xip.h> #include <linux/mtd/map.h> #include <linux/mtd/mtd.h> @@ -48,16 +49,25 @@ #define M50LPW080 0x002F static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); -//static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); -//static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *); static void cfi_intelext_sync (struct mtd_info *); static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len); static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len); +#ifdef CONFIG_MTD_OTP +static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_intelext_write_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_intelext_lock_user_prot_reg (struct mtd_info *, loff_t, size_t); +static int cfi_intelext_get_fact_prot_info (struct mtd_info *, + struct otp_info *, size_t); +static int cfi_intelext_get_user_prot_info (struct mtd_info *, + struct otp_info *, size_t); +#endif static int cfi_intelext_suspend (struct mtd_info *); static void cfi_intelext_resume (struct mtd_info *); +static int cfi_intelext_reboot (struct notifier_block *, unsigned long, void *); static void cfi_intelext_destroy(struct mtd_info *); @@ -252,7 +262,8 @@ read_pri_intelext(struct map_info *map, __u16 adr) int nb_parts, i; /* Protection Register info */ - extra_size += (extp->NumProtectionFields - 1) * (4 + 6); + extra_size += (extp->NumProtectionFields - 1) * + sizeof(struct cfi_intelext_otpinfo); /* Burst Read info */ extra_size += 6; @@ -324,7 +335,9 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) mtd->resume = cfi_intelext_resume; mtd->flags = MTD_CAP_NORFLASH; mtd->name = map->name; - + + mtd->reboot_notifier.notifier_call = cfi_intelext_reboot; + if (cfi->cfi_mode == CFI_MODE_CFI) { /* * It's a real CFI chip, not one for which the probe @@ -422,9 +435,13 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) mtd->eraseregions[i].numblocks); } -#if 0 - mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg; +#ifdef CONFIG_MTD_OTP mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg; + mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg; + mtd->write_user_prot_reg = cfi_intelext_write_user_prot_reg; + mtd->lock_user_prot_reg = cfi_intelext_lock_user_prot_reg; + mtd->get_fact_prot_info = cfi_intelext_get_fact_prot_info; + mtd->get_user_prot_info = cfi_intelext_get_user_prot_info; #endif /* This function has the potential to distort the reality @@ -433,6 +450,7 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) goto setup_err; __module_get(THIS_MODULE); + register_reboot_notifier(&mtd->reboot_notifier); return mtd; setup_err: @@ -471,7 +489,8 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, int offs, numregions, numparts, partshift, numvirtchips, i, j; /* Protection Register info */ - offs = (extp->NumProtectionFields - 1) * (4 + 6); + offs = (extp->NumProtectionFields - 1) * + sizeof(struct cfi_intelext_otpinfo); /* Burst Read info */ offs += 6; @@ -563,7 +582,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr resettime: timeo = jiffies + HZ; retry: - if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING)) { + if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE)) { /* * OK. We have possibility for contension on the write/erase * operations which are global to the real chip and not per @@ -807,10 +826,6 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad * assembly to make sure inline functions were actually inlined and that gcc * didn't emit calls to its own support functions). Also configuring MTD CFI * support to a single buswidth and a single interleave is also recommended. - * Note that not only IRQs are disabled but the preemption count is also - * increased to prevent other locking primitives (namely spin_unlock) from - * decrementing the preempt count to zero and scheduling the CPU away while - * not in array mode. */ static void xip_disable(struct map_info *map, struct flchip *chip, @@ -818,7 +833,6 @@ static void xip_disable(struct map_info *map, struct flchip *chip, { /* TODO: chips with no XIP use should ignore and return */ (void) map_read(map, adr); /* ensure mmu mapping is up to date */ - preempt_disable(); local_irq_disable(); } @@ -831,9 +845,8 @@ static void __xipram xip_enable(struct map_info *map, struct flchip *chip, chip->state = FL_READY; } (void) map_read(map, adr); - asm volatile (".rep 8; nop; .endr"); /* fill instruction prefetch */ + xip_iprefetch(); local_irq_enable(); - preempt_enable(); } /* @@ -909,7 +922,7 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, (void) map_read(map, adr); asm volatile (".rep 8; nop; .endr"); local_irq_enable(); - preempt_enable(); + spin_unlock(chip->mutex); asm volatile (".rep 8; nop; .endr"); cond_resched(); @@ -919,15 +932,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. */ - preempt_disable(); + spin_lock(chip->mutex); while (chip->state != newstate) { DECLARE_WAITQUEUE(wait, current); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - preempt_enable(); + spin_unlock(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - preempt_disable(); + spin_lock(chip->mutex); } /* Disallow XIP again */ local_irq_disable(); @@ -956,12 +969,14 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, * The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while * the flash is actively programming or erasing since we have to poll for * the operation to complete anyway. We can't do that in a generic way with - * a XIP setup so do it before the actual flash operation in this case. + * a XIP setup so do it before the actual flash operation in this case + * and stub it out from INVALIDATE_CACHE_UDELAY. */ -#undef INVALIDATE_CACHED_RANGE -#define INVALIDATE_CACHED_RANGE(x...) -#define XIP_INVAL_CACHED_RANGE(map, from, size) \ - do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0) +#define XIP_INVAL_CACHED_RANGE(map, from, size) \ + INVALIDATE_CACHED_RANGE(map, from, size) + +#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \ + UDELAY(map, chip, adr, usec) /* * Extra notes: @@ -984,11 +999,23 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, #define xip_disable(map, chip, adr) #define xip_enable(map, chip, adr) - -#define UDELAY(map, chip, adr, usec) cfi_udelay(usec) - #define XIP_INVAL_CACHED_RANGE(x...) +#define UDELAY(map, chip, adr, usec) \ +do { \ + spin_unlock(chip->mutex); \ + cfi_udelay(usec); \ + spin_lock(chip->mutex); \ +} while (0) + +#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \ +do { \ + spin_unlock(chip->mutex); \ + INVALIDATE_CACHED_RANGE(map, adr, len); \ + cfi_udelay(usec); \ + spin_lock(chip->mutex); \ +} while (0) + #endif static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len) @@ -1176,111 +1203,11 @@ static int cfi_intelext_read (struct mtd_info *mtd, loff_t from, size_t len, siz return ret; } -#if 0 -static int __xipram cfi_intelext_read_prot_reg (struct mtd_info *mtd, - loff_t from, size_t len, - size_t *retlen, - u_char *buf, - int base_offst, int reg_sz) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - struct cfi_pri_intelext *extp = cfi->cmdset_priv; - struct flchip *chip; - int ofs_factor = cfi->interleave * cfi->device_type; - int count = len; - int chip_num, offst; - int ret; - - chip_num = ((unsigned int)from/reg_sz); - offst = from - (reg_sz*chip_num)+base_offst; - - while (count) { - /* Calculate which chip & protection register offset we need */ - - if (chip_num >= cfi->numchips) - goto out; - - chip = &cfi->chips[chip_num]; - - spin_lock(chip->mutex); - ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY); - if (ret) { - spin_unlock(chip->mutex); - return (len-count)?:ret; - } - - xip_disable(map, chip, chip->start); - - if (chip->state != FL_JEDEC_QUERY) { - map_write(map, CMD(0x90), chip->start); - chip->state = FL_JEDEC_QUERY; - } - - while (count && ((offst-base_offst) < reg_sz)) { - *buf = map_read8(map,(chip->start+((extp->ProtRegAddr+1)*ofs_factor)+offst)); - buf++; - offst++; - count--; - } - - xip_enable(map, chip, chip->start); - put_chip(map, chip, chip->start); - spin_unlock(chip->mutex); - - /* Move on to the next chip */ - chip_num++; - offst = base_offst; - } - - out: - return len-count; -} - -static int cfi_intelext_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - struct cfi_pri_intelext *extp=cfi->cmdset_priv; - int base_offst,reg_sz; - - /* Check that we actually have some protection registers */ - if(!extp || !(extp->FeatureSupport&64)){ - printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name); - return 0; - } - - base_offst=(1<<extp->FactProtRegSize); - reg_sz=(1<<extp->UserProtRegSize); - - return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz); -} - -static int cfi_intelext_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - struct cfi_pri_intelext *extp=cfi->cmdset_priv; - int base_offst,reg_sz; - - /* Check that we actually have some protection registers */ - if(!extp || !(extp->FeatureSupport&64)){ - printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name); - return 0; - } - - base_offst=0; - reg_sz=(1<<extp->FactProtRegSize); - - return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz); -} -#endif - static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, - unsigned long adr, map_word datum) + unsigned long adr, map_word datum, int mode) { struct cfi_private *cfi = map->fldrv_priv; - map_word status, status_OK; + map_word status, status_OK, write_cmd; unsigned long timeo; int z, ret=0; @@ -1288,9 +1215,14 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, /* Let's determine this according to the interleave only once */ status_OK = CMD(0x80); + switch (mode) { + case FL_WRITING: write_cmd = CMD(0x40); break; + case FL_OTP_WRITE: write_cmd = CMD(0xc0); break; + default: return -EINVAL; + } spin_lock(chip->mutex); - ret = get_chip(map, chip, adr, FL_WRITING); + ret = get_chip(map, chip, adr, mode); if (ret) { spin_unlock(chip->mutex); return ret; @@ -1299,19 +1231,18 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map)); ENABLE_VPP(map); xip_disable(map, chip, adr); - map_write(map, CMD(0x40), adr); + map_write(map, write_cmd, adr); map_write(map, datum, adr); - chip->state = FL_WRITING; + chip->state = mode; - spin_unlock(chip->mutex); - INVALIDATE_CACHED_RANGE(map, adr, map_bankwidth(map)); - UDELAY(map, chip, adr, chip->word_write_time); - spin_lock(chip->mutex); + INVALIDATE_CACHE_UDELAY(map, chip, + adr, map_bankwidth(map), + chip->word_write_time); timeo = jiffies + (HZ/2); z = 0; for (;;) { - if (chip->state != FL_WRITING) { + if (chip->state != mode) { /* Someone's suspended the write. Sleep */ DECLARE_WAITQUEUE(wait, current); @@ -1339,10 +1270,8 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock(chip->mutex); z++; UDELAY(map, chip, adr, 1); - spin_lock(chip->mutex); } if (!z) { chip->word_write_time--; @@ -1399,7 +1328,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le datum = map_word_load_partial(map, datum, buf, gap, n); ret = do_write_oneword(map, &cfi->chips[chipnum], - bus_ofs, datum); + bus_ofs, datum, FL_WRITING); if (ret) return ret; @@ -1420,7 +1349,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le map_word datum = map_word_load(map, buf); ret = do_write_oneword(map, &cfi->chips[chipnum], - ofs, datum); + ofs, datum, FL_WRITING); if (ret) return ret; @@ -1444,7 +1373,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le datum = map_word_load_partial(map, datum, buf, 0, len); ret = do_write_oneword(map, &cfi->chips[chipnum], - ofs, datum); + ofs, datum, FL_WRITING); if (ret) return ret; @@ -1506,9 +1435,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, if (map_word_andequal(map, status, status_OK, status_OK)) break; - spin_unlock(chip->mutex); UDELAY(map, chip, cmd_adr, 1); - spin_lock(chip->mutex); if (++z > 20) { /* Argh. Not ready for write to buffer */ @@ -1554,10 +1481,9 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, map_write(map, CMD(0xd0), cmd_adr); chip->state = FL_WRITING; - spin_unlock(chip->mutex); - INVALIDATE_CACHED_RANGE(map, adr, len); - UDELAY(map, chip, cmd_adr, chip->buffer_write_time); - spin_lock(chip->mutex); + INVALIDATE_CACHE_UDELAY(map, chip, + cmd_adr, len, + chip->buffer_write_time); timeo = jiffies + (HZ/2); z = 0; @@ -1589,10 +1515,8 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock(chip->mutex); - UDELAY(map, chip, cmd_adr, 1); z++; - spin_lock(chip->mutex); + UDELAY(map, chip, cmd_adr, 1); } if (!z) { chip->buffer_write_time--; @@ -1720,10 +1644,9 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, chip->state = FL_ERASING; chip->erase_suspended = 0; - spin_unlock(chip->mutex); - INVALIDATE_CACHED_RANGE(map, adr, len); - UDELAY(map, chip, adr, chip->erase_time*1000/2); - spin_lock(chip->mutex); + INVALIDATE_CACHE_UDELAY(map, chip, + adr, len, + chip->erase_time*1000/2); /* FIXME. Use a timer to check this, and return immediately. */ /* Once the state machine's known to be working I'll do that */ @@ -1768,9 +1691,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock(chip->mutex); UDELAY(map, chip, adr, 1000000/HZ); - spin_lock(chip->mutex); } /* We've broken this before. It doesn't hurt to be safe */ @@ -1780,44 +1701,34 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, /* check for lock bit */ if (map_word_bitsset(map, status, CMD(0x3a))) { - unsigned char chipstatus; + unsigned long chipstatus; /* Reset the error bits */ map_write(map, CMD(0x50), adr); map_write(map, CMD(0x70), adr); xip_enable(map, chip, adr); - chipstatus = status.x[0]; - if (!map_word_equal(map, status, CMD(chipstatus))) { - int i, w; - for (w=0; w<map_words(map); w++) { - for (i = 0; i<cfi_interleave(cfi); i++) { - chipstatus |= status.x[w] >> (cfi->device_type * 8); - } - } - printk(KERN_WARNING "Status is not identical for all chips: 0x%lx. Merging to give 0x%02x\n", - status.x[0], chipstatus); - } + chipstatus = MERGESTATUS(status); if ((chipstatus & 0x30) == 0x30) { - printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", chipstatus); + printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%lx\n", chipstatus); ret = -EIO; } else if (chipstatus & 0x02) { /* Protection bit set */ ret = -EROFS; } else if (chipstatus & 0x8) { /* Voltage */ - printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", chipstatus); + printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%lx\n", chipstatus); ret = -EIO; } else if (chipstatus & 0x20) { if (retries--) { - printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus); + printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%lx. Retrying...\n", adr, chipstatus); timeo = jiffies + HZ; put_chip(map, chip, adr); spin_unlock(chip->mutex); goto retry; } - printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus); + printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%lx\n", adr, chipstatus); ret = -EIO; } } else { @@ -1882,6 +1793,7 @@ static void cfi_intelext_sync (struct mtd_info *mtd) if (chip->state == FL_SYNCING) { chip->state = chip->oldstate; + chip->oldstate = FL_READY; wake_up(&chip->wq); } spin_unlock(chip->mutex); @@ -1897,8 +1809,9 @@ static int __xipram do_printlockstatus_oneblock(struct map_info *map, struct cfi_private *cfi = map->fldrv_priv; int status, ofs_factor = cfi->interleave * cfi->device_type; + adr += chip->start; xip_disable(map, chip, adr+(2*ofs_factor)); - cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); + map_write(map, CMD(0x90), adr+(2*ofs_factor)); chip->state = FL_JEDEC_QUERY; status = cfi_read_query(map, adr+(2*ofs_factor)); xip_enable(map, chip, 0); @@ -1915,6 +1828,7 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip unsigned long adr, int len, void *thunk) { struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp = cfi->cmdset_priv; map_word status, status_OK; unsigned long timeo = jiffies + HZ; int ret; @@ -1944,9 +1858,13 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip } else BUG(); - spin_unlock(chip->mutex); - UDELAY(map, chip, adr, 1000000/HZ); - spin_lock(chip->mutex); + /* + * If Instant Individual Block Locking supported then no need + * to delay. + */ + + if (!extp || !(extp->FeatureSupport & (1 << 5))) + UDELAY(map, chip, adr, 1000000/HZ); /* FIXME. Use a timer to check this, and return immediately. */ /* Once the state machine's known to be working I'll do that */ @@ -1973,9 +1891,7 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock(chip->mutex); UDELAY(map, chip, adr, 1); - spin_lock(chip->mutex); } /* Done and happy. */ @@ -2034,6 +1950,274 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) return ret; } +#ifdef CONFIG_MTD_OTP + +typedef int (*otp_op_t)(struct map_info *map, struct flchip *chip, + u_long data_offset, u_char *buf, u_int size, + u_long prot_offset, u_int groupno, u_int groupsize); + +static int __xipram +do_otp_read(struct map_info *map, struct flchip *chip, u_long offset, + u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz) +{ + struct cfi_private *cfi = map->fldrv_priv; + int ret; + + spin_lock(chip->mutex); + ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY); + if (ret) { + spin_unlock(chip->mutex); + return ret; + } + + /* let's ensure we're not reading back cached data from array mode */ + INVALIDATE_CACHED_RANGE(map, chip->start + offset, size); + + xip_disable(map, chip, chip->start); + if (chip->state != FL_JEDEC_QUERY) { + map_write(map, CMD(0x90), chip->start); + chip->state = FL_JEDEC_QUERY; + } + map_copy_from(map, buf, chip->start + offset, size); + xip_enable(map, chip, chip->start); + + /* then ensure we don't keep OTP data in the cache */ + INVALIDATE_CACHED_RANGE(map, chip->start + offset, size); + + put_chip(map, chip, chip->start); + spin_unlock(chip->mutex); + return 0; +} + +static int +do_otp_write(struct map_info *map, struct flchip *chip, u_long offset, + u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz) +{ + int ret; + + while (size) { + unsigned long bus_ofs = offset & ~(map_bankwidth(map)-1); + int gap = offset - bus_ofs; + int n = min_t(int, size, map_bankwidth(map)-gap); + map_word datum = map_word_ff(map); + + datum = map_word_load_partial(map, datum, buf, gap, n); + ret = do_write_oneword(map, chip, bus_ofs, datum, FL_OTP_WRITE); + if (ret) + return ret; + + offset += n; + buf += n; + size -= n; + } + + return 0; +} + +static int +do_otp_lock(struct map_info *map, struct flchip *chip, u_long offset, + u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz) +{ + struct cfi_private *cfi = map->fldrv_priv; + map_word datum; + + /* make sure area matches group boundaries */ + if (size != grpsz) + return -EXDEV; + + datum = map_word_ff(map); + datum = map_word_clr(map, datum, CMD(1 << grpno)); + return do_write_oneword(map, chip, prot, datum, FL_OTP_WRITE); +} + +static int cfi_intelext_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf, + otp_op_t action, int user_regs) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp = cfi->cmdset_priv; + struct flchip *chip; + struct cfi_intelext_otpinfo *otp; + u_long devsize, reg_prot_offset, data_offset; + u_int chip_num, chip_step, field, reg_fact_size, reg_user_size; + u_int groups, groupno, groupsize, reg_fact_groups, reg_user_groups; + int ret; + + *retlen = 0; + + /* Check that we actually have some OTP registers */ + if (!extp || !(extp->FeatureSupport & 64) || !extp->NumProtectionFields) + return -ENODATA; + + /* we need real chips here not virtual ones */ + devsize = (1 << cfi->cfiq->DevSize) * cfi->interleave; + chip_step = devsize >> cfi->chipshift; + chip_num = 0; + + /* Some chips have OTP located in the _top_ partition only. + For example: Intel 28F256L18T (T means top-parameter device) */ + if (cfi->mfr == MANUFACTURER_INTEL) { + switch (cfi->id) { + case 0x880b: + case 0x880c: + case 0x880d: + chip_num = chip_step - 1; + } + } + + for ( ; chip_num < cfi->numchips; chip_num += chip_step) { + chip = &cfi->chips[chip_num]; + otp = (struct cfi_intelext_otpinfo *)&extp->extra[0]; + + /* first OTP region */ + field = 0; + reg_prot_offset = extp->ProtRegAddr; + reg_fact_groups = 1; + reg_fact_size = 1 << extp->FactProtRegSize; + reg_user_groups = 1; + reg_user_size = 1 << extp->UserProtRegSize; + + while (len > 0) { + /* flash geometry fixup */ + data_offset = reg_prot_offset + 1; + data_offset *= cfi->interleave * cfi->device_type; + reg_prot_offset *= cfi->interleave * cfi->device_type; + reg_fact_size *= cfi->interleave; + reg_user_size *= cfi->interleave; + + if (user_regs) { + groups = reg_user_groups; + groupsize = reg_user_size; + /* skip over factory reg area */ + groupno = reg_fact_groups; + data_offset += reg_fact_groups * reg_fact_size; + } else { + groups = reg_fact_groups; + groupsize = reg_fact_size; + groupno = 0; + } + + while (len > 0 && groups > 0) { + if (!action) { + /* + * Special case: if action is NULL + * we fill buf with otp_info records. + */ + struct otp_info *otpinfo; + map_word lockword; + len -= sizeof(struct otp_info); + if (len <= 0) + return -ENOSPC; + ret = do_otp_read(map, chip, + reg_prot_offset, + (u_char *)&lockword, + map_bankwidth(map), + 0, 0, 0); + if (ret) + return ret; + otpinfo = (struct otp_info *)buf; + otpinfo->start = from; + otpinfo->length = groupsize; + otpinfo->locked = + !map_word_bitsset(map, lockword, + CMD(1 << groupno)); + from += groupsize; + buf += sizeof(*otpinfo); + *retlen += sizeof(*otpinfo); + } else if (from >= groupsize) { + from -= groupsize; + data_offset += groupsize; + } else { + int size = groupsize; + data_offset += from; + size -= from; + from = 0; + if (size > len) + size = len; + ret = action(map, chip, data_offset, + buf, size, reg_prot_offset, + groupno, groupsize); + if (ret < 0) + return ret; + buf += size; + len -= size; + *retlen += size; + data_offset += size; + } + groupno++; + groups--; + } + + /* next OTP region */ + if (++field == extp->NumProtectionFields) + break; + reg_prot_offset = otp->ProtRegAddr; + reg_fact_groups = otp->FactGroups; + reg_fact_size = 1 << otp->FactProtRegSize; + reg_user_groups = otp->UserGroups; + reg_user_size = 1 << otp->UserProtRegSize; + otp++; + } + } + + return 0; +} + +static int cfi_intelext_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, + u_char *buf) +{ + return cfi_intelext_otp_walk(mtd, from, len, retlen, + buf, do_otp_read, 0); +} + +static int cfi_intelext_read_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, + u_char *buf) +{ + return cfi_intelext_otp_walk(mtd, from, len, retlen, + buf, do_otp_read, 1); +} + +static int cfi_intelext_write_user_prot_reg(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, + u_char *buf) +{ + return cfi_intelext_otp_walk(mtd, from, len, retlen, + buf, do_otp_write, 1); +} + +static int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd, + loff_t from, size_t len) +{ + size_t retlen; + return cfi_intelext_otp_walk(mtd, from, len, &retlen, + NULL, do_otp_lock, 1); +} + +static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd, + struct otp_info *buf, size_t len) +{ + size_t retlen; + int ret; + + ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 0); + return ret ? : retlen; +} + +static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd, + struct otp_info *buf, size_t len) +{ + size_t retlen; + int ret; + + ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 1); + return ret ? : retlen; +} + +#endif + static int cfi_intelext_suspend(struct mtd_info *mtd) { struct map_info *map = mtd->priv; @@ -2125,10 +2309,46 @@ static void cfi_intelext_resume(struct mtd_info *mtd) } } +static int cfi_intelext_reset(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int i, ret; + + for (i=0; i < cfi->numchips; i++) { + struct flchip *chip = &cfi->chips[i]; + + /* 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); + ret = get_chip(map, chip, chip->start, FL_SYNCING); + if (!ret) { + map_write(map, CMD(0xff), chip->start); + chip->state = FL_READY; + } + spin_unlock(chip->mutex); + } + + return 0; +} + +static int cfi_intelext_reboot(struct notifier_block *nb, unsigned long val, + void *v) +{ + struct mtd_info *mtd; + + mtd = container_of(nb, struct mtd_info, reboot_notifier); + cfi_intelext_reset(mtd); + return NOTIFY_DONE; +} + static void cfi_intelext_destroy(struct mtd_info *mtd) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; + cfi_intelext_reset(mtd); + unregister_reboot_notifier(&mtd->reboot_notifier); kfree(cfi->cmdset_priv); kfree(cfi->cfiq); kfree(cfi->chips[0].priv); diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index fca8ff6..8505f11 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -4,16 +4,20 @@ * * Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp> * Copyright (C) 2004 Arcom Control Systems Ltd <linux@arcom.com> + * Copyright (C) 2005 MontaVista Software Inc. <source@mvista.com> * * 2_by_8 routines added by Simon Munton * * 4_by_16 work by Carolyn J. Smith * + * XIP support hooks by Vitaly Wool (based on code for Intel flash + * by Nicolas Pitre) + * * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com * * This code is GPL * - * $Id: cfi_cmdset_0002.c,v 1.114 2004/12/11 15:43:53 dedekind Exp $ + * $Id: cfi_cmdset_0002.c,v 1.118 2005/07/04 22:34:29 gleixner Exp $ * */ @@ -34,6 +38,7 @@ #include <linux/mtd/map.h> #include <linux/mtd/mtd.h> #include <linux/mtd/cfi.h> +#include <linux/mtd/xip.h> #define AMD_BOOTLOC_BUG #define FORCE_WORD_WRITE 0 @@ -43,6 +48,7 @@ #define MANUFACTURER_AMD 0x0001 #define MANUFACTURER_SST 0x00BF #define SST49LF004B 0x0060 +#define SST49LF008A 0x005a static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); @@ -191,6 +197,7 @@ static struct cfi_fixup cfi_fixup_table[] = { }; static struct cfi_fixup jedec_fixup_table[] = { { MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, }, + { MANUFACTURER_SST, SST49LF008A, fixup_use_fwh_lock, NULL, }, { 0, 0, NULL, NULL } }; @@ -391,7 +398,7 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) * correctly and is therefore not done (particulary with interleaved chips * as each chip must be checked independantly of the others). */ -static int chip_ready(struct map_info *map, unsigned long addr) +static int __xipram chip_ready(struct map_info *map, unsigned long addr) { map_word d, t; @@ -401,6 +408,32 @@ static int chip_ready(struct map_info *map, unsigned long addr) return map_word_equal(map, d, t); } +/* + * Return true if the chip is ready and has the correct value. + * + * Ready is one of: read mode, query mode, erase-suspend-read mode (in any + * non-suspended sector) and it is indicated by no bits toggling. + * + * Error are indicated by toggling bits or bits held with the wrong value, + * or with bits toggling. + * + * Note that anything more complicated than checking if no bits are toggling + * (including checking DQ5 for an error status) is tricky to get working + * correctly and is therefore not done (particulary with interleaved chips + * as each chip must be checked independantly of the others). + * + */ +static int __xipram chip_good(struct map_info *map, unsigned long addr, map_word expected) +{ + map_word oldd, curd; + + oldd = map_read(map, addr); + curd = map_read(map, addr); + + return map_word_equal(map, oldd, curd) && + map_word_equal(map, curd, expected); +} + static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode) { DECLARE_WAITQUEUE(wait, current); @@ -420,12 +453,12 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr if (time_after(jiffies, timeo)) { printk(KERN_ERR "Waiting for chip to be ready timed out.\n"); - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); return -EIO; } - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); cfi_udelay(1); - cfi_spin_lock(chip->mutex); + spin_lock(chip->mutex); /* Someone else might have been playing with it. */ goto retry; } @@ -473,15 +506,23 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr return -EIO; } - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); cfi_udelay(1); - cfi_spin_lock(chip->mutex); + spin_lock(chip->mutex); /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. So we can just loop here. */ } chip->state = FL_READY; return 0; + case FL_XIP_WHILE_ERASING: + if (mode != FL_READY && mode != FL_POINT && + (!cfip || !(cfip->EraseSuspend&2))) + goto sleep; + chip->oldstate = chip->state; + chip->state = FL_READY; + return 0; + case FL_POINT: /* Only if there's no operation suspended... */ if (mode == FL_READY && chip->oldstate == FL_READY) @@ -491,10 +532,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); - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - cfi_spin_lock(chip->mutex); + spin_lock(chip->mutex); goto resettime; } } @@ -512,6 +553,11 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad chip->state = FL_ERASING; break; + case FL_XIP_WHILE_ERASING: + chip->state = chip->oldstate; + chip->oldstate = FL_READY; + break; + case FL_READY: case FL_STATUS: /* We should really make set_vpp() count, rather than doing this */ @@ -523,6 +569,198 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad wake_up(&chip->wq); } +#ifdef CONFIG_MTD_XIP + +/* + * No interrupt what so ever can be serviced while the flash isn't in array + * mode. This is ensured by the xip_disable() and xip_enable() functions + * enclosing any code path where the flash is known not to be in array mode. + * And within a XIP disabled code path, only functions marked with __xipram + * may be called and nothing else (it's a good thing to inspect generated + * assembly to make sure inline functions were actually inlined and that gcc + * didn't emit calls to its own support functions). Also configuring MTD CFI + * support to a single buswidth and a single interleave is also recommended. + */ + +static void xip_disable(struct map_info *map, struct flchip *chip, + unsigned long adr) +{ + /* TODO: chips with no XIP use should ignore and return */ + (void) map_read(map, adr); /* ensure mmu mapping is up to date */ + local_irq_disable(); +} + +static void __xipram xip_enable(struct map_info *map, struct flchip *chip, + unsigned long adr) +{ + struct cfi_private *cfi = map->fldrv_priv; + + if (chip->state != FL_POINT && chip->state != FL_READY) { + map_write(map, CMD(0xf0), adr); + chip->state = FL_READY; + } + (void) map_read(map, adr); + xip_iprefetch(); + local_irq_enable(); +} + +/* + * When a delay is required for the flash operation to complete, the + * xip_udelay() function is polling for both the given timeout and pending + * (but still masked) hardware interrupts. Whenever there is an interrupt + * pending then the flash erase operation is suspended, array mode restored + * and interrupts unmasked. Task scheduling might also happen at that + * point. The CPU eventually returns from the interrupt or the call to + * schedule() and the suspended flash operation is resumed for the remaining + * of the delay period. + * + * Warning: this function _will_ fool interrupt latency tracing tools. + */ + +static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, + unsigned long adr, int usec) +{ + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_amdstd *extp = cfi->cmdset_priv; + map_word status, OK = CMD(0x80); + unsigned long suspended, start = xip_currtime(); + flstate_t oldstate; + + do { + cpu_relax(); + if (xip_irqpending() && extp && + ((chip->state == FL_ERASING && (extp->EraseSuspend & 2))) && + (cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) { + /* + * Let's suspend the erase operation when supported. + * Note that we currently don't try to suspend + * interleaved chips if there is already another + * operation suspended (imagine what happens + * when one chip was already done with the current + * operation while another chip suspended it, then + * we resume the whole thing at once). Yes, it + * can happen! + */ + map_write(map, CMD(0xb0), adr); + usec -= xip_elapsed_since(start); + suspended = xip_currtime(); + do { + if (xip_elapsed_since(suspended) > 100000) { + /* + * The chip doesn't want to suspend + * after waiting for 100 msecs. + * This is a critical error but there + * is not much we can do here. + */ + return; + } + status = map_read(map, adr); + } while (!map_word_andequal(map, status, OK, OK)); + + /* Suspend succeeded */ + oldstate = chip->state; + if (!map_word_bitsset(map, status, CMD(0x40))) + break; + chip->state = FL_XIP_WHILE_ERASING; + chip->erase_suspended = 1; + map_write(map, CMD(0xf0), adr); + (void) map_read(map, adr); + asm volatile (".rep 8; nop; .endr"); + local_irq_enable(); + spin_unlock(chip->mutex); + asm volatile (".rep 8; nop; .endr"); + cond_resched(); + + /* + * We're back. However someone else might have + * decided to go write to the chip if we are in + * a suspended erase state. If so let's wait + * until it's done. + */ + spin_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); + schedule(); + remove_wait_queue(&chip->wq, &wait); + spin_lock(chip->mutex); + } + /* Disallow XIP again */ + local_irq_disable(); + + /* Resume the write or erase operation */ + map_write(map, CMD(0x30), adr); + chip->state = oldstate; + start = xip_currtime(); + } else if (usec >= 1000000/HZ) { + /* + * Try to save on CPU power when waiting delay + * is at least a system timer tick period. + * No need to be extremely accurate here. + */ + xip_cpu_idle(); + } + status = map_read(map, adr); + } while (!map_word_andequal(map, status, OK, OK) + && xip_elapsed_since(start) < usec); +} + +#define UDELAY(map, chip, adr, usec) xip_udelay(map, chip, adr, usec) + +/* + * The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while + * the flash is actively programming or erasing since we have to poll for + * the operation to complete anyway. We can't do that in a generic way with + * a XIP setup so do it before the actual flash operation in this case + * and stub it out from INVALIDATE_CACHE_UDELAY. + */ +#define XIP_INVAL_CACHED_RANGE(map, from, size) \ + INVALIDATE_CACHED_RANGE(map, from, size) + +#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \ + UDELAY(map, chip, adr, usec) + +/* + * Extra notes: + * + * Activating this XIP support changes the way the code works a bit. For + * example the code to suspend the current process when concurrent access + * happens is never executed because xip_udelay() will always return with the + * same chip state as it was entered with. This is why there is no care for + * the presence of add_wait_queue() or schedule() calls from within a couple + * xip_disable()'d areas of code, like in do_erase_oneblock for example. + * The queueing and scheduling are always happening within xip_udelay(). + * + * Similarly, get_chip() and put_chip() just happen to always be executed + * with chip->state set to FL_READY (or FL_XIP_WHILE_*) where flash state + * is in array mode, therefore never executing many cases therein and not + * causing any problem with XIP. + */ + +#else + +#define xip_disable(map, chip, adr) +#define xip_enable(map, chip, adr) +#define XIP_INVAL_CACHED_RANGE(x...) + +#define UDELAY(map, chip, adr, usec) \ +do { \ + spin_unlock(chip->mutex); \ + cfi_udelay(usec); \ + spin_lock(chip->mutex); \ +} while (0) + +#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \ +do { \ + spin_unlock(chip->mutex); \ + INVALIDATE_CACHED_RANGE(map, adr, len); \ + cfi_udelay(usec); \ + spin_lock(chip->mutex); \ +} while (0) + +#endif static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) { @@ -535,10 +773,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); - cfi_spin_lock(chip->mutex); + spin_lock(chip->mutex); ret = get_chip(map, chip, cmd_addr, FL_READY); if (ret) { - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); return ret; } @@ -551,7 +789,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof put_chip(map, chip, cmd_addr); - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); return 0; } @@ -605,7 +843,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi struct cfi_private *cfi = map->fldrv_priv; retry: - cfi_spin_lock(chip->mutex); + spin_lock(chip->mutex); if (chip->state != FL_READY){ #if 0 @@ -614,7 +852,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); - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); @@ -643,7 +881,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); - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); return 0; } @@ -692,7 +930,7 @@ static int cfi_amdstd_secsi_read (struct mtd_info *mtd, loff_t from, size_t len, } -static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum) +static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum) { struct cfi_private *cfi = map->fldrv_priv; unsigned long timeo = jiffies + HZ; @@ -712,10 +950,10 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned adr += chip->start; - cfi_spin_lock(chip->mutex); + spin_lock(chip->mutex); ret = get_chip(map, chip, adr, FL_WRITING); if (ret) { - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); return ret; } @@ -735,7 +973,9 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned goto op_done; } + XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map)); ENABLE_VPP(map); + xip_disable(map, chip, adr); retry: cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); @@ -743,9 +983,9 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned map_write(map, datum, adr); chip->state = FL_WRITING; - cfi_spin_unlock(chip->mutex); - cfi_udelay(chip->word_write_time); - cfi_spin_lock(chip->mutex); + INVALIDATE_CACHE_UDELAY(map, chip, + adr, map_bankwidth(map), + chip->word_write_time); /* See comment above for timeout value. */ timeo = jiffies + uWriteTimeout; @@ -756,39 +996,43 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + (HZ / 2); /* FIXME */ - cfi_spin_lock(chip->mutex); + spin_lock(chip->mutex); continue; } if (chip_ready(map, adr)) - goto op_done; + break; - if (time_after(jiffies, timeo)) + if (time_after(jiffies, timeo)) { + xip_enable(map, chip, adr); + printk(KERN_WARNING "MTD %s(): software timeout\n", __func__); + xip_disable(map, chip, adr); break; + } /* Latency issues. Drop the lock, wait a while and retry */ - cfi_spin_unlock(chip->mutex); - cfi_udelay(1); - cfi_spin_lock(chip->mutex); + UDELAY(map, chip, adr, 1); } + /* Did we succeed? */ + if (!chip_good(map, adr, datum)) { + /* reset on all failures. */ + map_write( map, CMD(0xF0), chip->start ); + /* FIXME - should have reset delay before continuing */ - printk(KERN_WARNING "MTD %s(): software timeout\n", __func__); - - /* reset on all failures. */ - map_write( map, CMD(0xF0), chip->start ); - /* FIXME - should have reset delay before continuing */ - if (++retry_cnt <= MAX_WORD_RETRIES) - goto retry; + if (++retry_cnt <= MAX_WORD_RETRIES) + goto retry; - ret = -EIO; + ret = -EIO; + } + xip_enable(map, chip, adr); op_done: chip->state = FL_READY; put_chip(map, chip, adr); - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); return ret; } @@ -820,7 +1064,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, map_word tmp_buf; retry: - cfi_spin_lock(cfi->chips[chipnum].mutex); + spin_lock(cfi->chips[chipnum].mutex); if (cfi->chips[chipnum].state != FL_READY) { #if 0 @@ -829,7 +1073,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); - cfi_spin_unlock(cfi->chips[chipnum].mutex); + spin_unlock(cfi->chips[chipnum].mutex); schedule(); remove_wait_queue(&cfi->chips[chipnum].wq, &wait); @@ -843,7 +1087,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); - cfi_spin_unlock(cfi->chips[chipnum].mutex); + spin_unlock(cfi->chips[chipnum].mutex); /* Number of bytes to copy from buffer */ n = min_t(int, len, map_bankwidth(map)-i); @@ -898,7 +1142,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, map_word tmp_buf; retry1: - cfi_spin_lock(cfi->chips[chipnum].mutex); + spin_lock(cfi->chips[chipnum].mutex); if (cfi->chips[chipnum].state != FL_READY) { #if 0 @@ -907,7 +1151,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); - cfi_spin_unlock(cfi->chips[chipnum].mutex); + spin_unlock(cfi->chips[chipnum].mutex); schedule(); remove_wait_queue(&cfi->chips[chipnum].wq, &wait); @@ -920,7 +1164,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, tmp_buf = map_read(map, ofs + chipstart); - cfi_spin_unlock(cfi->chips[chipnum].mutex); + spin_unlock(cfi->chips[chipnum].mutex); tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len); @@ -939,8 +1183,9 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, /* * FIXME: interleaved mode not tested, and probably not supported! */ -static inline int do_write_buffer(struct map_info *map, struct flchip *chip, - unsigned long adr, const u_char *buf, int len) +static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, + unsigned long adr, const u_char *buf, + int len) { struct cfi_private *cfi = map->fldrv_priv; unsigned long timeo = jiffies + HZ; @@ -954,10 +1199,10 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, adr += chip->start; cmd_adr = adr; - cfi_spin_lock(chip->mutex); + spin_lock(chip->mutex); ret = get_chip(map, chip, adr, FL_WRITING); if (ret) { - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); return ret; } @@ -966,7 +1211,10 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n", __func__, adr, datum.x[0] ); + XIP_INVAL_CACHED_RANGE(map, adr, len); ENABLE_VPP(map); + xip_disable(map, chip, cmd_adr); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); //cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); @@ -996,9 +1244,9 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, map_write(map, CMD(0x29), cmd_adr); chip->state = FL_WRITING; - cfi_spin_unlock(chip->mutex); - cfi_udelay(chip->buffer_write_time); - cfi_spin_lock(chip->mutex); + INVALIDATE_CACHE_UDELAY(map, chip, + adr, map_bankwidth(map), + chip->word_write_time); timeo = jiffies + uWriteTimeout; @@ -1009,38 +1257,39 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + (HZ / 2); /* FIXME */ - cfi_spin_lock(chip->mutex); + spin_lock(chip->mutex); continue; } - if (chip_ready(map, adr)) + if (chip_ready(map, adr)) { + xip_enable(map, chip, adr); goto op_done; + } if( time_after(jiffies, timeo)) break; /* Latency issues. Drop the lock, wait a while and retry */ - cfi_spin_unlock(chip->mutex); - cfi_udelay(1); - cfi_spin_lock(chip->mutex); + UDELAY(map, chip, adr, 1); } - printk(KERN_WARNING "MTD %s(): software timeout\n", - __func__ ); - /* reset on all failures. */ map_write( map, CMD(0xF0), chip->start ); + xip_enable(map, chip, adr); /* FIXME - should have reset delay before continuing */ + printk(KERN_WARNING "MTD %s(): software timeout\n", + __func__ ); + ret = -EIO; op_done: chip->state = FL_READY; put_chip(map, chip, adr); - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); return ret; } @@ -1130,7 +1379,7 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len, * Handle devices with one erase region, that only implement * the chip erase command. */ -static inline int do_erase_chip(struct map_info *map, struct flchip *chip) +static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) { struct cfi_private *cfi = map->fldrv_priv; unsigned long timeo = jiffies + HZ; @@ -1140,17 +1389,20 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip) adr = cfi->addr_unlock1; - cfi_spin_lock(chip->mutex); + spin_lock(chip->mutex); ret = get_chip(map, chip, adr, FL_WRITING); if (ret) { - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); return ret; } DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n", __func__, chip->start ); + XIP_INVAL_CACHED_RANGE(map, adr, map->size); ENABLE_VPP(map); + xip_disable(map, chip, adr); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); @@ -1162,9 +1414,9 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip) chip->erase_suspended = 0; chip->in_progress_block_addr = adr; - cfi_spin_unlock(chip->mutex); - msleep(chip->erase_time/2); - cfi_spin_lock(chip->mutex); + INVALIDATE_CACHE_UDELAY(map, chip, + adr, map->size, + chip->erase_time*500); timeo = jiffies + (HZ*20); @@ -1173,10 +1425,10 @@ static inline int 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); - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - cfi_spin_lock(chip->mutex); + spin_lock(chip->mutex); continue; } if (chip->erase_suspended) { @@ -1187,36 +1439,36 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip) } if (chip_ready(map, adr)) - goto op_done; + break; - if (time_after(jiffies, timeo)) + if (time_after(jiffies, timeo)) { + printk(KERN_WARNING "MTD %s(): software timeout\n", + __func__ ); break; + } /* Latency issues. Drop the lock, wait a while and retry */ - cfi_spin_unlock(chip->mutex); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - cfi_spin_lock(chip->mutex); + UDELAY(map, chip, adr, 1000000/HZ); } + /* Did we succeed? */ + if (!chip_good(map, adr, map_word_ff(map))) { + /* reset on all failures. */ + map_write( map, CMD(0xF0), chip->start ); + /* FIXME - should have reset delay before continuing */ - printk(KERN_WARNING "MTD %s(): software timeout\n", - __func__ ); - - /* reset on all failures. */ - map_write( map, CMD(0xF0), chip->start ); - /* FIXME - should have reset delay before continuing */ + ret = -EIO; + } - ret = -EIO; - op_done: chip->state = FL_READY; + xip_enable(map, chip, adr); put_chip(map, chip, adr); - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); return ret; } -static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk) +static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk) { struct cfi_private *cfi = map->fldrv_priv; unsigned long timeo = jiffies + HZ; @@ -1225,17 +1477,20 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u adr += chip->start; - cfi_spin_lock(chip->mutex); + spin_lock(chip->mutex); ret = get_chip(map, chip, adr, FL_ERASING); if (ret) { - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); return ret; } DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n", __func__, adr ); + XIP_INVAL_CACHED_RANGE(map, adr, len); ENABLE_VPP(map); + xip_disable(map, chip, adr); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); @@ -1246,10 +1501,10 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u chip->state = FL_ERASING; chip->erase_suspended = 0; chip->in_progress_block_addr = adr; - - cfi_spin_unlock(chip->mutex); - msleep(chip->erase_time/2); - cfi_spin_lock(chip->mutex); + + INVALIDATE_CACHE_UDELAY(map, chip, + adr, len, + chip->erase_time*500); timeo = jiffies + (HZ*20); @@ -1258,10 +1513,10 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u /* Someone's suspended the erase. Sleep */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - cfi_spin_lock(chip->mutex); + spin_lock(chip->mutex); continue; } if (chip->erase_suspended) { @@ -1271,31 +1526,33 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u chip->erase_suspended = 0; } - if (chip_ready(map, adr)) - goto op_done; + if (chip_ready(map, adr)) { + xip_enable(map, chip, adr); + break; + } - if (time_after(jiffies, timeo)) + if (time_after(jiffies, timeo)) { + xip_enable(map, chip, adr); + printk(KERN_WARNING "MTD %s(): software timeout\n", + __func__ ); break; + } /* Latency issues. Drop the lock, wait a while and retry */ - cfi_spin_unlock(chip->mutex); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - cfi_spin_lock(chip->mutex); + UDELAY(map, chip, adr, 1000000/HZ); + } + /* Did we succeed? */ + if (!chip_good(map, adr, map_word_ff(map))) { + /* reset on all failures. */ + map_write( map, CMD(0xF0), chip->start ); + /* FIXME - should have reset delay before continuing */ + + ret = -EIO; } - - printk(KERN_WARNING "MTD %s(): software timeout\n", - __func__ ); - - /* reset on all failures. */ - map_write( map, CMD(0xF0), chip->start ); - /* FIXME - should have reset delay before continuing */ - ret = -EIO; - op_done: chip->state = FL_READY; put_chip(map, chip, adr); - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); return ret; } @@ -1355,7 +1612,7 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) chip = &cfi->chips[i]; retry: - cfi_spin_lock(chip->mutex); + spin_lock(chip->mutex); switch(chip->state) { case FL_READY: @@ -1369,14 +1626,14 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) * with the chip now anyway. */ case FL_SYNCING: - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); break; default: /* Not an idle state */ add_wait_queue(&chip->wq, &wait); - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); schedule(); @@ -1391,13 +1648,13 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - cfi_spin_lock(chip->mutex); + spin_lock(chip->mutex); if (chip->state == FL_SYNCING) { chip->state = chip->oldstate; wake_up(&chip->wq); } - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); } } @@ -1413,7 +1670,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd) for (i=0; !ret && i<cfi->numchips; i++) { chip = &cfi->chips[i]; - cfi_spin_lock(chip->mutex); + spin_lock(chip->mutex); switch(chip->state) { case FL_READY: @@ -1433,7 +1690,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd) ret = -EAGAIN; break; } - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); } /* Unlock the chips again */ @@ -1442,13 +1699,13 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - cfi_spin_lock(chip->mutex); + spin_lock(chip->mutex); if (chip->state == FL_PM_SUSPENDED) { chip->state = chip->oldstate; wake_up(&chip->wq); } - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); } } @@ -1467,7 +1724,7 @@ static void cfi_amdstd_resume(struct mtd_info *mtd) chip = &cfi->chips[i]; - cfi_spin_lock(chip->mutex); + spin_lock(chip->mutex); if (chip->state == FL_PM_SUSPENDED) { chip->state = FL_READY; @@ -1477,7 +1734,7 @@ static void cfi_amdstd_resume(struct mtd_info *mtd) else printk(KERN_ERR "Argh. Chip not in PM_SUSPENDED state upon resume()\n"); - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); } } diff --git a/drivers/mtd/chips/fwh_lock.h b/drivers/mtd/chips/fwh_lock.h index fbf4470..e1a5b76 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. */ - cfi_spin_lock(chip->mutex); + spin_lock(chip->mutex); ret = get_chip(map, chip, adr, FL_LOCKING); if (ret) { - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); return ret; } @@ -71,7 +71,7 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, /* Done and happy. */ chip->state = FL_READY; put_chip(map, chip, adr); - cfi_spin_unlock(chip->mutex); + spin_unlock(chip->mutex); return 0; } diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index fc982c4..dc065b2 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -2,7 +2,7 @@ * Routines common to all CFI-type probes. * (C) 2001-2003 Red Hat, Inc. * GPL'd - * $Id: gen_probe.c,v 1.21 2004/08/14 15:14:05 dwmw2 Exp $ + * $Id: gen_probe.c,v 1.22 2005/01/24 23:49:50 rmk Exp $ */ #include <linux/kernel.h> @@ -162,7 +162,7 @@ static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp, int max_chips = map_bankwidth(map); /* And minimum 1 */ int nr_chips, type; - for (nr_chips = min_chips; nr_chips <= max_chips; nr_chips <<= 1) { + for (nr_chips = max_chips; nr_chips >= min_chips; nr_chips >>= 1) { if (!cfi_interleave_supported(nr_chips)) continue; diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 30325a2..30da428 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -1,7 +1,7 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: jedec_probe.c,v 1.61 2004/11/19 20:52:16 thayne Exp $ + $Id: jedec_probe.c,v 1.63 2005/02/14 16:30:32 bjd Exp $ See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5) for the standard this probe goes back to. @@ -142,6 +142,7 @@ #define SST29LE512 0x003d #define SST39LF800 0x2781 #define SST39LF160 0x2782 +#define SST39VF1601 0x234b #define SST39LF512 0x00D4 #define SST39LF010 0x00D5 #define SST39LF020 0x00D6 @@ -1448,6 +1449,21 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,256), ERASEINFO(0x1000,256) } + }, { + .mfr_id = MANUFACTURER_SST, /* should be CFI */ + .dev_id = SST39VF1601, + .name = "SST 39VF1601", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ + [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 2, + .regions = { + ERASEINFO(0x1000,256), + ERASEINFO(0x1000,256) + } }, { .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ @@ -1856,6 +1872,16 @@ static inline int jedec_match( __u32 base, case CFI_DEVICETYPE_X8: mfr = (__u8)finfo->mfr_id; id = (__u8)finfo->dev_id; + + /* bjd: it seems that if we do this, we can end up + * detecting 16bit flashes as an 8bit device, even though + * there aren't. + */ + if (finfo->dev_id > 0xff) { + DEBUG( MTD_DEBUG_LEVEL3, "%s(): ID is not 8bit\n", + __func__); + goto match_done; + } break; case CFI_DEVICETYPE_X16: mfr = (__u16)finfo->mfr_id; diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index 60ab4b8..ef24837 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -1,5 +1,5 @@ /* - * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $ + * $Id: cmdlinepart.c,v 1.18 2005/06/07 15:04:26 joern Exp $ * * Read flash partition table from command line * @@ -239,7 +239,8 @@ static int mtdpart_setup_real(char *s) &num_parts, /* out: number of parts */ 0, /* first partition */ (unsigned char**)&this_mtd, /* out: extra mem */ - mtd_id_len + 1 + sizeof(*this_mtd)); + mtd_id_len + 1 + sizeof(*this_mtd) + + sizeof(void*)-1 /*alignment*/); if(!parts) { /* @@ -252,6 +253,9 @@ static int mtdpart_setup_real(char *s) return 0; } + /* align this_mtd */ + this_mtd = (struct cmdline_mtd_partition *) + ALIGN((unsigned long)this_mtd, sizeof(void*)); /* enter results */ this_mtd->parts = parts; this_mtd->num_parts = num_parts; diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index cfe6ccf..4a7a805 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -1,10 +1,9 @@ /* - * $Id: block2mtd.c,v 1.23 2005/01/05 17:05:46 dwmw2 Exp $ + * $Id: block2mtd.c,v 1.28 2005/03/19 22:40:44 gleixner Exp $ * * block2mtd.c - create an mtd from a block device * * Copyright (C) 2001,2002 Simon Evans <spse@secret.org.uk> - * Copyright (C) 2004 Gareth Bult <Gareth@Encryptec.net> * Copyright (C) 2004,2005 Jörn Engel <joern@wh.fh-wedel.de> * * Licence: GPL @@ -20,7 +19,7 @@ #include <linux/mtd/mtd.h> #include <linux/buffer_head.h> -#define VERSION "$Revision: 1.23 $" +#define VERSION "$Revision: 1.28 $" #define ERROR(fmt, args...) printk(KERN_ERR "block2mtd: " fmt "\n" , ## args) @@ -89,7 +88,6 @@ void cache_readahead(struct address_space *mapping, int index) static struct page* page_readahead(struct address_space *mapping, int index) { filler_t *filler = (filler_t*)mapping->a_ops->readpage; - //do_page_cache_readahead(mapping, index, XXX, 64); cache_readahead(mapping, index); return read_cache_page(mapping, index, filler, NULL); } @@ -157,7 +155,7 @@ static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len, struct block2mtd_dev *dev = mtd->priv; struct page *page; int index = from >> PAGE_SHIFT; - int offset = from & (PAGE_SHIFT-1); + int offset = from & (PAGE_SIZE-1); int cpylen; if (from > mtd->size) @@ -370,16 +368,16 @@ static int ustrtoul(const char *cp, char **endp, unsigned int base) } -static int parse_num32(u32 *num32, const char *token) +static int parse_num(size_t *num, const char *token) { char *endp; - unsigned long n; + size_t n; - n = ustrtoul(token, &endp, 0); + n = (size_t) ustrtoul(token, &endp, 0); if (*endp) return -EINVAL; - *num32 = n; + *num = n; return 0; } @@ -422,7 +420,7 @@ static int block2mtd_setup(const char *val, struct kernel_param *kp) char buf[80+12], *str=buf; /* 80 for device, 12 for erase size */ char *token[2]; char *name; - u32 erase_size = PAGE_SIZE; + size_t erase_size = PAGE_SIZE; int i, ret; if (strnlen(val, sizeof(buf)) >= sizeof(buf)) @@ -449,7 +447,7 @@ static int block2mtd_setup(const char *val, struct kernel_param *kp) return 0; if (token[1]) { - ret = parse_num32(&erase_size, token[1]); + ret = parse_num(&erase_size, token[1]); if (ret) parse_err("illegal erase size"); } diff --git a/drivers/mtd/devices/ms02-nv.c b/drivers/mtd/devices/ms02-nv.c index 380ff08..f5026ce 100644 --- a/drivers/mtd/devices/ms02-nv.c +++ b/drivers/mtd/devices/ms02-nv.c @@ -6,7 +6,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * $Id: ms02-nv.c,v 1.8 2005/01/05 18:05:12 dwmw2 Exp $ + * $Id: ms02-nv.c,v 1.10 2005/06/20 12:24:41 macro Exp $ */ #include <linux/init.h> @@ -99,8 +99,8 @@ static inline uint ms02nv_probe_one(ulong addr) * The firmware writes MS02NV_ID at MS02NV_MAGIC and also * a diagnostic status at MS02NV_DIAG. */ - ms02nv_diagp = (ms02nv_uint *)(KSEG1ADDR(addr + MS02NV_DIAG)); - ms02nv_magicp = (ms02nv_uint *)(KSEG1ADDR(addr + MS02NV_MAGIC)); + ms02nv_diagp = (ms02nv_uint *)(CKSEG1ADDR(addr + MS02NV_DIAG)); + ms02nv_magicp = (ms02nv_uint *)(CKSEG1ADDR(addr + MS02NV_MAGIC)); err = get_dbe(ms02nv_magic, ms02nv_magicp); if (err) return 0; @@ -233,7 +233,7 @@ static int __init ms02nv_init_one(ulong addr) goto err_out_csr_res; } - printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %uMiB.\n", + printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %zuMiB.\n", mtd->index, ms02nv_name, addr, size >> 20); mp->next = root_ms02nv_mtd; diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index edac415..bb713fe 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -1,9 +1,10 @@ /* * mtdram - a test mtd device - * $Id: mtdram.c,v 1.35 2005/01/05 18:05:12 dwmw2 Exp $ + * $Id: mtdram.c,v 1.37 2005/04/21 03:42:11 joern Exp $ * Author: Alexander Larsson <alex@cendio.se> * * Copyright (c) 1999 Alexander Larsson <alex@cendio.se> + * Copyright (c) 2005 Joern Engel <joern@wh.fh-wedel.de> * * This code is GPL * @@ -18,213 +19,140 @@ #include <linux/mtd/compatmac.h> #include <linux/mtd/mtd.h> -#ifndef CONFIG_MTDRAM_ABS_POS - #define CONFIG_MTDRAM_ABS_POS 0 -#endif - -#if CONFIG_MTDRAM_ABS_POS > 0 - #include <asm/io.h> -#endif - -#ifdef MODULE static unsigned long total_size = CONFIG_MTDRAM_TOTAL_SIZE; static unsigned long erase_size = CONFIG_MTDRAM_ERASE_SIZE; -module_param(total_size,ulong,0); -MODULE_PARM_DESC(total_size, "Total device size in KiB"); -module_param(erase_size,ulong,0); -MODULE_PARM_DESC(erase_size, "Device erase block size in KiB"); #define MTDRAM_TOTAL_SIZE (total_size * 1024) #define MTDRAM_ERASE_SIZE (erase_size * 1024) -#else -#define MTDRAM_TOTAL_SIZE (CONFIG_MTDRAM_TOTAL_SIZE * 1024) -#define MTDRAM_ERASE_SIZE (CONFIG_MTDRAM_ERASE_SIZE * 1024) -#endif +#ifdef MODULE +module_param(total_size, ulong, 0); +MODULE_PARM_DESC(total_size, "Total device size in KiB"); +module_param(erase_size, ulong, 0); +MODULE_PARM_DESC(erase_size, "Device erase block size in KiB"); +#endif // We could store these in the mtd structure, but we only support 1 device.. static struct mtd_info *mtd_info; - -static int -ram_erase(struct mtd_info *mtd, struct erase_info *instr) +static int ram_erase(struct mtd_info *mtd, struct erase_info *instr) { - DEBUG(MTD_DEBUG_LEVEL2, "ram_erase(pos:%ld, len:%ld)\n", (long)instr->addr, (long)instr->len); - if (instr->addr + instr->len > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL1, "ram_erase() out of bounds (%ld > %ld)\n", (long)(instr->addr + instr->len), (long)mtd->size); - return -EINVAL; - } - - memset((char *)mtd->priv + instr->addr, 0xff, instr->len); - - instr->state = MTD_ERASE_DONE; - mtd_erase_callback(instr); - - return 0; + if (instr->addr + instr->len > mtd->size) + return -EINVAL; + + memset((char *)mtd->priv + instr->addr, 0xff, instr->len); + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return 0; } -static int ram_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) +static int ram_point(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char **mtdbuf) { - if (from + len > mtd->size) - return -EINVAL; - - *mtdbuf = mtd->priv + from; - *retlen = len; - return 0; + if (from + len > mtd->size) + return -EINVAL; + + *mtdbuf = mtd->priv + from; + *retlen = len; + return 0; } -static void ram_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, - size_t len) +static void ram_unpoint(struct mtd_info *mtd, u_char * addr, loff_t from, + size_t len) { - DEBUG(MTD_DEBUG_LEVEL2, "ram_unpoint\n"); } static int ram_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) + size_t *retlen, u_char *buf) { - DEBUG(MTD_DEBUG_LEVEL2, "ram_read(pos:%ld, len:%ld)\n", (long)from, (long)len); - if (from + len > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL1, "ram_read() out of bounds (%ld > %ld)\n", (long)(from + len), (long)mtd->size); - return -EINVAL; - } + if (from + len > mtd->size) + return -EINVAL; - memcpy(buf, mtd->priv + from, len); + memcpy(buf, mtd->priv + from, len); - *retlen=len; - return 0; + *retlen = len; + return 0; } static int ram_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) + size_t *retlen, const u_char *buf) { - DEBUG(MTD_DEBUG_LEVEL2, "ram_write(pos:%ld, len:%ld)\n", (long)to, (long)len); - if (to + len > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL1, "ram_write() out of bounds (%ld > %ld)\n", (long)(to + len), (long)mtd->size); - return -EINVAL; - } + if (to + len > mtd->size) + return -EINVAL; - memcpy ((char *)mtd->priv + to, buf, len); + memcpy((char *)mtd->priv + to, buf, len); - *retlen=len; - return 0; + *retlen = len; + return 0; } static void __exit cleanup_mtdram(void) { - if (mtd_info) { - del_mtd_device(mtd_info); -#if CONFIG_MTDRAM_TOTAL_SIZE > 0 - if (mtd_info->priv) -#if CONFIG_MTDRAM_ABS_POS > 0 - iounmap(mtd_info->priv); -#else - vfree(mtd_info->priv); -#endif -#endif - kfree(mtd_info); - } -} - -int mtdram_init_device(struct mtd_info *mtd, void *mapped_address, - unsigned long size, char *name) -{ - memset(mtd, 0, sizeof(*mtd)); - - /* Setup the MTD structure */ - mtd->name = name; - mtd->type = MTD_RAM; - mtd->flags = MTD_CAP_RAM; - mtd->size = size; - mtd->erasesize = MTDRAM_ERASE_SIZE; - mtd->priv = mapped_address; - - mtd->owner = THIS_MODULE; - mtd->erase = ram_erase; - mtd->point = ram_point; - mtd->unpoint = ram_unpoint; - mtd->read = ram_read; - mtd->write = ram_write; - - if (add_mtd_device(mtd)) { - return -EIO; - } - - return 0; -} - -#if CONFIG_MTDRAM_TOTAL_SIZE > 0 -#if CONFIG_MTDRAM_ABS_POS > 0 -static int __init init_mtdram(void) -{ - void *addr; - int err; - /* Allocate some memory */ - mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); - if (!mtd_info) - return -ENOMEM; - - addr = ioremap(CONFIG_MTDRAM_ABS_POS, MTDRAM_TOTAL_SIZE); - if (!addr) { - DEBUG(MTD_DEBUG_LEVEL1, - "Failed to ioremap) memory region of size %ld at ABS_POS:%ld\n", - (long)MTDRAM_TOTAL_SIZE, (long)CONFIG_MTDRAM_ABS_POS); - kfree(mtd_info); - mtd_info = NULL; - return -ENOMEM; - } - err = mtdram_init_device(mtd_info, addr, - MTDRAM_TOTAL_SIZE, "mtdram test device"); - if (err) - { - iounmap(addr); - kfree(mtd_info); - mtd_info = NULL; - return err; - } - memset(mtd_info->priv, 0xff, MTDRAM_TOTAL_SIZE); - return err; + if (mtd_info) { + del_mtd_device(mtd_info); + if (mtd_info->priv) + vfree(mtd_info->priv); + kfree(mtd_info); + } } -#else /* CONFIG_MTDRAM_ABS_POS > 0 */ - -static int __init init_mtdram(void) +int mtdram_init_device(struct mtd_info *mtd, void *mapped_address, + unsigned long size, char *name) { - void *addr; - int err; - /* Allocate some memory */ - mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); - if (!mtd_info) - return -ENOMEM; - - addr = vmalloc(MTDRAM_TOTAL_SIZE); - if (!addr) { - DEBUG(MTD_DEBUG_LEVEL1, - "Failed to vmalloc memory region of size %ld\n", - (long)MTDRAM_TOTAL_SIZE); - kfree(mtd_info); - mtd_info = NULL; - return -ENOMEM; - } - err = mtdram_init_device(mtd_info, addr, - MTDRAM_TOTAL_SIZE, "mtdram test device"); - if (err) - { - vfree(addr); - kfree(mtd_info); - mtd_info = NULL; - return err; - } - memset(mtd_info->priv, 0xff, MTDRAM_TOTAL_SIZE); - return err; + memset(mtd, 0, sizeof(*mtd)); + + /* Setup the MTD structure */ + mtd->name = name; + mtd->type = MTD_RAM; + mtd->flags = MTD_CAP_RAM; + mtd->size = size; + mtd->erasesize = MTDRAM_ERASE_SIZE; + mtd->priv = mapped_address; + + mtd->owner = THIS_MODULE; + mtd->erase = ram_erase; + mtd->point = ram_point; + mtd->unpoint = ram_unpoint; + mtd->read = ram_read; + mtd->write = ram_write; + + if (add_mtd_device(mtd)) { + return -EIO; + } + + return 0; } -#endif /* !(CONFIG_MTDRAM_ABS_POS > 0) */ - -#else /* CONFIG_MTDRAM_TOTAL_SIZE > 0 */ static int __init init_mtdram(void) { - return 0; + void *addr; + int err; + + if (!total_size) + return -EINVAL; + + /* Allocate some memory */ + mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + if (!mtd_info) + return -ENOMEM; + + addr = vmalloc(MTDRAM_TOTAL_SIZE); + if (!addr) { + kfree(mtd_info); + mtd_info = NULL; + return -ENOMEM; + } + err = mtdram_init_device(mtd_info, addr, MTDRAM_TOTAL_SIZE, "mtdram test device"); + if (err) { + vfree(addr); + kfree(mtd_info); + mtd_info = NULL; + return err; + } + memset(mtd_info->priv, 0xff, MTDRAM_TOTAL_SIZE); + return err; } -#endif /* !(CONFIG_MTDRAM_TOTAL_SIZE > 0) */ module_init(init_mtdram); module_exit(cleanup_mtdram); @@ -232,4 +160,3 @@ module_exit(cleanup_mtdram); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Alexander Larsson <alexl@redhat.com>"); MODULE_DESCRIPTION("Simulated MTD driver for testing"); - diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 5f8e164..a423a38 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -1,5 +1,5 @@ /** - * $Id: phram.c,v 1.11 2005/01/05 18:05:13 dwmw2 Exp $ + * $Id: phram.c,v 1.14 2005/03/07 21:43:38 joern Exp $ * * Copyright (c) ???? Jochen Schäuble <psionic@psionic.de> * Copyright (c) 2003-2004 Jörn Engel <joern@wh.fh-wedel.de> @@ -15,9 +15,7 @@ * * Example: * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi - * */ - #include <asm/io.h> #include <linux/init.h> #include <linux/kernel.h> @@ -36,7 +34,6 @@ struct phram_mtd_list { static LIST_HEAD(phram_list); - static int phram_erase(struct mtd_info *mtd, struct erase_info *instr) { u_char *start = mtd->priv; @@ -71,7 +68,8 @@ static int phram_point(struct mtd_info *mtd, loff_t from, size_t len, return 0; } -static void phram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) +static void phram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, + size_t len) { } @@ -80,8 +78,11 @@ static int phram_read(struct mtd_info *mtd, loff_t from, size_t len, { u_char *start = mtd->priv; - if (from + len > mtd->size) + if (from >= mtd->size) return -EINVAL; + + if (len > mtd->size - from) + len = mtd->size - from; memcpy(buf, start + from, len); @@ -94,8 +95,11 @@ static int phram_write(struct mtd_info *mtd, loff_t to, size_t len, { u_char *start = mtd->priv; - if (to + len > mtd->size) + if (to >= mtd->size) return -EINVAL; + + if (len > mtd->size - to) + len = mtd->size - to; memcpy(start + to, buf, len); @@ -107,9 +111,9 @@ static int phram_write(struct mtd_info *mtd, loff_t to, size_t len, static void unregister_devices(void) { - struct phram_mtd_list *this; + struct phram_mtd_list *this, *safe; - list_for_each_entry(this, &phram_list, list) { + list_for_each_entry_safe(this, safe, &phram_list, list) { del_mtd_device(&this->mtd); iounmap(this->mtd.priv); kfree(this); @@ -145,7 +149,7 @@ static int register_device(char *name, unsigned long start, unsigned long len) new->mtd.write = phram_write; new->mtd.owner = THIS_MODULE; new->mtd.type = MTD_RAM; - new->mtd.erasesize = 0; + new->mtd.erasesize = PAGE_SIZE; ret = -EAGAIN; if (add_mtd_device(&new->mtd)) { @@ -214,6 +218,15 @@ static int parse_name(char **pname, const char *token) return 0; } + +static inline void kill_final_newline(char *str) +{ + char *newline = strrchr(str, '\n'); + if (newline && !newline[1]) + *newline = 0; +} + + #define parse_err(fmt, args...) do { \ ERROR(fmt , ## args); \ return 0; \ @@ -232,6 +245,7 @@ static int phram_setup(const char *val, struct kernel_param *kp) parse_err("parameter too long\n"); strcpy(str, val); + kill_final_newline(str); for (i=0; i<3; i++) token[i] = strsep(&str, ","); diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index 5ab15e6..84fa913 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -1,6 +1,6 @@ /*====================================================================== - $Id: slram.c,v 1.33 2005/01/05 18:05:13 dwmw2 Exp $ + $Id: slram.c,v 1.34 2005/01/06 21:16:42 jwboyer Exp $ This driver provides a method to access memory not used by the kernel itself (i.e. if the kernel commandline mem=xxx is used). To actually @@ -50,6 +50,7 @@ #include <linux/mtd/mtd.h> #define SLRAM_MAX_DEVICES_PARAMS 6 /* 3 parameters / device */ +#define SLRAM_BLK_SZ 0x4000 #define T(fmt, args...) printk(KERN_DEBUG fmt, ## args) #define E(fmt, args...) printk(KERN_NOTICE fmt, ## args) @@ -108,6 +109,9 @@ static int slram_point(struct mtd_info *mtd, loff_t from, size_t len, { slram_priv_t *priv = mtd->priv; + if (from + len > mtd->size) + return -EINVAL; + *mtdbuf = priv->start + from; *retlen = len; return(0); @@ -121,7 +125,13 @@ static int slram_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { slram_priv_t *priv = mtd->priv; - + + if (from > mtd->size) + return -EINVAL; + + if (from + len > mtd->size) + len = mtd->size - from; + memcpy(buf, priv->start + from, len); *retlen = len; @@ -133,6 +143,9 @@ static int slram_write(struct mtd_info *mtd, loff_t to, size_t len, { slram_priv_t *priv = mtd->priv; + if (to + len > mtd->size) + return -EINVAL; + memcpy(priv->start + to, buf, len); *retlen = len; @@ -188,7 +201,7 @@ static int register_device(char *name, unsigned long start, unsigned long length (*curmtd)->mtdinfo->name = name; (*curmtd)->mtdinfo->size = length; (*curmtd)->mtdinfo->flags = MTD_CLEAR_BITS | MTD_SET_BITS | - MTD_WRITEB_WRITEABLE | MTD_VOLATILE; + MTD_WRITEB_WRITEABLE | MTD_VOLATILE | MTD_CAP_RAM; (*curmtd)->mtdinfo->erase = slram_erase; (*curmtd)->mtdinfo->point = slram_point; (*curmtd)->mtdinfo->unpoint = slram_unpoint; @@ -196,7 +209,7 @@ static int register_device(char *name, unsigned long start, unsigned long length (*curmtd)->mtdinfo->write = slram_write; (*curmtd)->mtdinfo->owner = THIS_MODULE; (*curmtd)->mtdinfo->type = MTD_RAM; - (*curmtd)->mtdinfo->erasesize = 0x0; + (*curmtd)->mtdinfo->erasesize = SLRAM_BLK_SZ; if (add_mtd_device((*curmtd)->mtdinfo)) { E("slram: Failed to register new device\n"); @@ -261,7 +274,7 @@ static int parse_cmdline(char *devname, char *szstart, char *szlength) } T("slram: devname=%s, devstart=0x%lx, devlength=0x%lx\n", devname, devstart, devlength); - if ((devstart < 0) || (devlength < 0)) { + if ((devstart < 0) || (devlength < 0) || (devlength % SLRAM_BLK_SZ != 0)) { E("slram: Illegal start / length parameter.\n"); return(-EINVAL); } diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index 18cc884..d9ab60b 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -1,5 +1,5 @@ /* This version ported to the Linux-MTD system by dwmw2@infradead.org - * $Id: ftl.c,v 1.54 2004/11/16 18:33:15 dwmw2 Exp $ + * $Id: ftl.c,v 1.55 2005/01/17 13:47:21 hvr Exp $ * * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br> * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups @@ -357,6 +357,7 @@ static int erase_xfer(partition_t *part, if (!erase) return -ENOMEM; + erase->mtd = part->mbd.mtd; erase->callback = ftl_erase_callback; erase->addr = xfer->Offset; erase->len = 1 << part->header.EraseUnitSize; @@ -1096,7 +1097,7 @@ struct mtd_blktrans_ops ftl_tr = { int init_ftl(void) { - DEBUG(0, "$Id: ftl.c,v 1.54 2004/11/16 18:33:15 dwmw2 Exp $\n"); + DEBUG(0, "$Id: ftl.c,v 1.55 2005/01/17 13:47:21 hvr Exp $\n"); return register_mtd_blktrans(&ftl_tr); } diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 2bea2e0..44781a8 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -1,5 +1,5 @@ # drivers/mtd/maps/Kconfig -# $Id: Kconfig,v 1.42 2005/01/05 16:59:50 dwmw2 Exp $ +# $Id: Kconfig,v 1.55 2005/07/02 01:53:24 tpoynor Exp $ menu "Mapping drivers for chip access" depends on MTD!=n @@ -122,16 +122,6 @@ config MTD_SBC_GXX More info at <http://www.arcomcontrols.com/products/icp/pc104/processors/SBC_GX1.htm>. -config MTD_ELAN_104NC - tristate "CFI Flash device mapped on Arcom ELAN-104NC" - depends on X86 && MTD_CFI_INTELEXT && MTD_PARTITIONS && MTD_COMPLEX_MAPPINGS - help - This provides a driver for the on-board flash of the Arcom Control - System's ELAN-104NC development board. By default the flash - is split into 3 partitions which are accessed as separate MTD - devices. This board utilizes Intel StrataFlash. More info at - <http://www.arcomcontrols.com/products/icp/pc104/processors/ELAN104NC.htm>. - config MTD_LUBBOCK tristate "CFI Flash device mapped on Intel Lubbock XScale eval board" depends on ARCH_LUBBOCK && MTD_CFI_INTELEXT && MTD_PARTITIONS @@ -139,6 +129,14 @@ config MTD_LUBBOCK This provides a driver for the on-board flash of the Intel 'Lubbock' XScale evaluation board. +config MTD_MAINSTONE + tristate "CFI Flash device mapped on Intel Mainstone XScale eval board" + depends on MACH_MAINSTONE && MTD_CFI_INTELEXT + select MTD_PARTITIONS + help + This provides a driver for the on-board flash of the Intel + 'Mainstone PXA27x evaluation board. + config MTD_OCTAGON tristate "JEDEC Flash device mapped on Octagon 5066 SBC" depends on X86 && MTD_JEDEC && MTD_COMPLEX_MAPPINGS @@ -213,74 +211,11 @@ config MTD_NETtel help Support for flash chips on NETtel/SecureEdge/SnapGear boards. -config MTD_PB1XXX - tristate "Flash devices on Alchemy PB1xxx boards" - depends on MIPS && ( MIPS_PB1000 || MIPS_PB1100 || MIPS_PB1500 ) - help - Flash memory access on Alchemy Pb1000/Pb1100/Pb1500 boards - -config MTD_PB1XXX_BOOT - bool "PB1x00 boot flash device" - depends on MTD_PB1XXX && ( MIPS_PB1100 || MIPS_PB1500 ) - help - Use the first of the two 32MiB flash banks on Pb1100/Pb1500 board. - You can say 'Y' to both this and 'MTD_PB1XXX_USER' below, to use - both banks. - -config MTD_PB1XXX_USER - bool "PB1x00 user flash device" - depends on MTD_PB1XXX && ( MIPS_PB1100 || MIPS_PB1500 ) - default y if MTD_PB1XX_BOOT = n - help - Use the second of the two 32MiB flash banks on Pb1100/Pb1500 board. - You can say 'Y' to both this and 'MTD_PB1XXX_BOOT' above, to use - both banks. - -config MTD_PB1550 - tristate "Flash devices on Alchemy PB1550 board" - depends on MIPS && MIPS_PB1550 - help - Flash memory access on Alchemy Pb1550 board - -config MTD_PB1550_BOOT - bool "PB1550 boot flash device" - depends on MTD_PB1550 +config MTD_ALCHEMY + tristate ' AMD Alchemy Pb1xxx/Db1xxx/RDK MTD support' + depends on MIPS && SOC_AU1X00 help - Use the first of the two 64MiB flash banks on Pb1550 board. - You can say 'Y' to both this and 'MTD_PB1550_USER' below, to use - both banks. - -config MTD_PB1550_USER - bool "PB1550 user flash device" - depends on MTD_PB1550 - default y if MTD_PB1550_BOOT = n - help - Use the second of the two 64MiB flash banks on Pb1550 board. - You can say 'Y' to both this and 'MTD_PB1550_BOOT' above, to use - both banks. - -config MTD_DB1550 - tristate "Flash devices on Alchemy DB1550 board" - depends on MIPS && MIPS_DB1550 - help - Flash memory access on Alchemy Db1550 board - -config MTD_DB1550_BOOT - bool "DB1550 boot flash device" - depends on MTD_DB1550 - help - Use the first of the two 64MiB flash banks on Db1550 board. - You can say 'Y' to both this and 'MTD_DB1550_USER' below, to use - both banks. - -config MTD_DB1550_USER - bool "DB1550 user flash device" - depends on MTD_DB1550 - default y if MTD_DB1550_BOOT = n - help - Use the second of the two 64MiB flash banks on Db1550 board. - You can say 'Y' to both this and 'MTD_DB1550_BOOT' above, to use - both banks. + Flash memory access on AMD Alchemy Pb/Db/RDK Reference Boards config MTD_DILNETPC tristate "CFI Flash device mapped on DIL/Net PC" @@ -588,6 +523,15 @@ config MTD_MPC1211 This enables access to the flash chips on the Interface MPC-1211(CTP/PCI/MPC-SH02). If you have such a board, say 'Y'. +config MTD_OMAP_NOR + tristate "TI OMAP board mappings" + depends on MTD_CFI && ARCH_OMAP + help + This enables access to the NOR flash chips on TI OMAP-based + boards defining flash platform devices and flash platform data. + These boards include the Innovator, H2, H3, OSK, Perseus2, and + more. If you have such a board, say 'Y'. + # This needs CFI or JEDEC, depending on the cards found. config MTD_PCI tristate "PCI MTD driver" @@ -647,13 +591,14 @@ config MTD_DMV182 Map driver for Dy-4 SVME/DMV-182 board. config MTD_BAST - tristate "Map driver for Simtec BAST (EB2410ITX)" - depends on ARCH_BAST + tristate "Map driver for Simtec BAST (EB2410ITX) or Thorcom VR1000" + depends on ARCH_BAST || MACH_VR1000 select MTD_PARTITIONS select MTD_MAP_BANK_WIDTH_16 select MTD_JEDECPROBE help - Map driver for NOR flash on the Simtec BAST (EB2410ITX). + Map driver for NOR flash on the Simtec BAST (EB2410ITX), or the + Thorcom VR1000 Note, this driver *cannot* over-ride the WP link on the board, or currently detect the state of the link. @@ -669,5 +614,15 @@ config MTD_SHARP_SL help This enables access to the flash chip on the Sharp SL Series of PDAs. +config MTD_PLATRAM + tristate "Map driver for platform device RAM (mtd-ram)" + depends on MTD + select MTD_RAM + help + Map driver for RAM areas described via the platform device + system. + + This selection automatically selects the map_ram driver. + endmenu diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 7ffe02b..7bcbc49 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -1,7 +1,7 @@ # # linux/drivers/maps/Makefile # -# $Id: Makefile.common,v 1.23 2005/01/05 17:06:36 dwmw2 Exp $ +# $Id: Makefile.common,v 1.30 2005/07/02 01:53:24 tpoynor Exp $ ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y) obj-$(CONFIG_MTD) += map_funcs.o @@ -15,7 +15,6 @@ obj-$(CONFIG_MTD_CFI_FLAGADM) += cfi_flagadm.o obj-$(CONFIG_MTD_CSTM_MIPS_IXX) += cstm_mips_ixx.o obj-$(CONFIG_MTD_DC21285) += dc21285.o obj-$(CONFIG_MTD_DILNETPC) += dilnetpc.o -obj-$(CONFIG_MTD_ELAN_104NC) += elan-104nc.o obj-$(CONFIG_MTD_EPXA10DB) += epxa10db-flash.o obj-$(CONFIG_MTD_IQ80310) += iq80310.o obj-$(CONFIG_MTD_L440GX) += l440gx.o @@ -23,6 +22,7 @@ obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o +obj-$(CONFIG_MTD_MAINSTONE) += mainstone-flash.o obj-$(CONFIG_MTD_MBX860) += mbx860.o obj-$(CONFIG_MTD_CEIVA) += ceiva.o obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o @@ -44,10 +44,7 @@ obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o obj-$(CONFIG_MTD_OCELOT) += ocelot.o obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o obj-$(CONFIG_MTD_PCI) += pci.o -obj-$(CONFIG_MTD_PB1XXX) += pb1xxx-flash.o -obj-$(CONFIG_MTD_DB1X00) += db1x00-flash.o -obj-$(CONFIG_MTD_PB1550) += pb1550-flash.o -obj-$(CONFIG_MTD_DB1550) += db1550-flash.o +obj-$(CONFIG_MTD_ALCHEMY) += alchemy-flash.o obj-$(CONFIG_MTD_LASAT) += lasat.o obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o obj-$(CONFIG_MTD_EDB7312) += edb7312.o @@ -71,3 +68,5 @@ obj-$(CONFIG_MTD_IXP2000) += ixp2000.o obj-$(CONFIG_MTD_WRSBC8260) += wr_sbc82xx_flash.o obj-$(CONFIG_MTD_DMV182) += dmv182.o obj-$(CONFIG_MTD_SHARP_SL) += sharpsl-flash.o +obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o +obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o diff --git a/drivers/mtd/maps/alchemy-flash.c b/drivers/mtd/maps/alchemy-flash.c new file mode 100644 index 0000000..27fd2a3 --- /dev/null +++ b/drivers/mtd/maps/alchemy-flash.c @@ -0,0 +1,192 @@ +/* + * Flash memory access on AMD Alchemy evaluation boards + * + * $Id: alchemy-flash.c,v 1.1 2005/02/27 21:50:21 ppopov Exp $ + * + * (C) 2003, 2004 Pete Popov <ppopov@embeddedalley.com> + * + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> + +#include <asm/io.h> + +#ifdef DEBUG_RW +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#ifdef CONFIG_MIPS_PB1000 +#define BOARD_MAP_NAME "Pb1000 Flash" +#define BOARD_FLASH_SIZE 0x00800000 /* 8MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#endif + +#ifdef CONFIG_MIPS_PB1500 +#define BOARD_MAP_NAME "Pb1500 Flash" +#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#endif + +#ifdef CONFIG_MIPS_PB1100 +#define BOARD_MAP_NAME "Pb1100 Flash" +#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#endif + +#ifdef CONFIG_MIPS_PB1550 +#define BOARD_MAP_NAME "Pb1550 Flash" +#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#endif + +#ifdef CONFIG_MIPS_PB1200 +#define BOARD_MAP_NAME "Pb1200 Flash" +#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */ +#define BOARD_FLASH_WIDTH 2 /* 16-bits */ +#endif + +#ifdef CONFIG_MIPS_DB1000 +#define BOARD_MAP_NAME "Db1000 Flash" +#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#endif + +#ifdef CONFIG_MIPS_DB1500 +#define BOARD_MAP_NAME "Db1500 Flash" +#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#endif + +#ifdef CONFIG_MIPS_DB1100 +#define BOARD_MAP_NAME "Db1100 Flash" +#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#endif + +#ifdef CONFIG_MIPS_DB1550 +#define BOARD_MAP_NAME "Db1550 Flash" +#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#endif + +#ifdef CONFIG_MIPS_DB1200 +#define BOARD_MAP_NAME "Db1200 Flash" +#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */ +#define BOARD_FLASH_WIDTH 2 /* 16-bits */ +#endif + +#ifdef CONFIG_MIPS_HYDROGEN3 +#define BOARD_MAP_NAME "Hydrogen3 Flash" +#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#define USE_LOCAL_ACCESSORS /* why? */ +#endif + +#ifdef CONFIG_MIPS_BOSPORUS +#define BOARD_MAP_NAME "Bosporus Flash" +#define BOARD_FLASH_SIZE 0x01000000 /* 16MB */ +#define BOARD_FLASH_WIDTH 2 /* 16-bits */ +#endif + +#ifdef CONFIG_MIPS_MIRAGE +#define BOARD_MAP_NAME "Mirage Flash" +#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */ +#define BOARD_FLASH_WIDTH 4 /* 32-bits */ +#define USE_LOCAL_ACCESSORS /* why? */ +#endif + +static struct map_info alchemy_map = { + .name = BOARD_MAP_NAME, +}; + +static struct mtd_partition alchemy_partitions[] = { + { + .name = "User FS", + .size = BOARD_FLASH_SIZE - 0x00400000, + .offset = 0x0000000 + },{ + .name = "YAMON", + .size = 0x0100000, + .offset = MTDPART_OFS_APPEND, + .mask_flags = MTD_WRITEABLE + },{ + .name = "raw kernel", + .size = (0x300000 - 0x40000), /* last 256KB is yamon env */ + .offset = MTDPART_OFS_APPEND, + } +}; + +#define NB_OF(x) (sizeof(x)/sizeof(x[0])) + +static struct mtd_info *mymtd; + +int __init alchemy_mtd_init(void) +{ + struct mtd_partition *parts; + int nb_parts = 0; + unsigned long window_addr; + unsigned long window_size; + + /* Default flash buswidth */ + alchemy_map.bankwidth = BOARD_FLASH_WIDTH; + + window_addr = 0x20000000 - BOARD_FLASH_SIZE; + window_size = BOARD_FLASH_SIZE; +#ifdef CONFIG_MIPS_MIRAGE_WHY + /* Boot ROM flash bank only; no user bank */ + window_addr = 0x1C000000; + window_size = 0x04000000; + /* USERFS from 0x1C00 0000 to 0x1FC00000 */ + alchemy_partitions[0].size = 0x03C00000; +#endif + + /* + * Static partition definition selection + */ + parts = alchemy_partitions; + nb_parts = NB_OF(alchemy_partitions); + alchemy_map.size = window_size; + + /* + * Now let's probe for the actual flash. Do it here since + * specific machine settings might have been set above. + */ + printk(KERN_NOTICE BOARD_MAP_NAME ": probing %d-bit flash bus\n", + alchemy_map.bankwidth*8); + alchemy_map.virt = ioremap(window_addr, window_size); + mymtd = do_map_probe("cfi_probe", &alchemy_map); + if (!mymtd) { + iounmap(alchemy_map.virt); + return -ENXIO; + } + mymtd->owner = THIS_MODULE; + + add_mtd_partitions(mymtd, parts, nb_parts); + return 0; +} + +static void __exit alchemy_mtd_cleanup(void) +{ + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); + iounmap(alchemy_map.virt); + } +} + +module_init(alchemy_mtd_init); +module_exit(alchemy_mtd_cleanup); + +MODULE_AUTHOR("Embedded Alley Solutions, Inc"); +MODULE_DESCRIPTION(BOARD_MAP_NAME " MTD driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index 51e97b0..e8a900a 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c @@ -2,7 +2,7 @@ * amd76xrom.c * * Normal mappings of chips in physical memory - * $Id: amd76xrom.c,v 1.19 2004/11/28 09:40:39 dwmw2 Exp $ + * $Id: amd76xrom.c,v 1.20 2005/03/18 14:04:35 gleixner Exp $ */ #include <linux/module.h> @@ -314,7 +314,7 @@ static int __init init_amd76xrom(void) } return -ENXIO; #if 0 - return pci_module_init(&amd76xrom_driver); + return pci_register_driver(&amd76xrom_driver); #endif } diff --git a/drivers/mtd/maps/bast-flash.c b/drivers/mtd/maps/bast-flash.c index 44de3a8..0c45464 100644 --- a/drivers/mtd/maps/bast-flash.c +++ b/drivers/mtd/maps/bast-flash.c @@ -1,14 +1,15 @@ /* linux/drivers/mtd/maps/bast_flash.c * - * Copyright (c) 2004 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> + * Copyright (c) 2004-2005 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> * * Simtec Bast (EB2410ITX) NOR MTD Mapping driver * * Changelog: * 20-Sep-2004 BJD Initial version + * 17-Jan-2005 BJD Add whole device if no partitions found * - * $Id: bast-flash.c,v 1.1 2004/09/21 14:29:04 bjd Exp $ + * $Id: bast-flash.c,v 1.2 2005/01/18 11:13:47 bjd Exp $ * * 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 @@ -46,9 +47,9 @@ #include <asm/arch/bast-cpld.h> #ifdef CONFIG_MTD_BAST_MAXSIZE -#define AREA_MAXSIZE (CONFIG_MTD_BAST_MAXSIZE * (1024*1024)) +#define AREA_MAXSIZE (CONFIG_MTD_BAST_MAXSIZE * SZ_1M) #else -#define AREA_MAXSIZE (32*1024*1024) +#define AREA_MAXSIZE (32 * SZ_1M) #endif #define PFX "bast-flash: " @@ -189,6 +190,8 @@ static int bast_flash_probe(struct device *dev) err = add_mtd_partitions(info->mtd, info->partitions, err); if (err) printk(KERN_ERR PFX "cannot add/parse partitions\n"); + } else { + err = add_mtd_device(info->mtd); } if (err == 0) diff --git a/drivers/mtd/maps/db1550-flash.c b/drivers/mtd/maps/db1550-flash.c deleted file mode 100644 index d213888..0000000 --- a/drivers/mtd/maps/db1550-flash.c +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Flash memory access on Alchemy Db1550 board - * - * $Id: db1550-flash.c,v 1.7 2004/11/04 13:24:14 gleixner Exp $ - * - * (C) 2004 Embedded Edge, LLC, based on db1550-flash.c: - * (C) 2003, 2004 Pete Popov <ppopov@embeddedalley.com> - * - */ - -#include <linux/config.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kernel.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> -#include <linux/mtd/partitions.h> - -#include <asm/io.h> - -#ifdef DEBUG_RW -#define DBG(x...) printk(x) -#else -#define DBG(x...) -#endif - -static unsigned long window_addr; -static unsigned long window_size; - - -static struct map_info db1550_map = { - .name = "Db1550 flash", -}; - -static unsigned char flash_bankwidth = 4; - -/* - * Support only 64MB NOR Flash parts - */ - -#if defined(CONFIG_MTD_DB1550_BOOT) && defined(CONFIG_MTD_DB1550_USER) -#define DB1550_BOTH_BANKS -#elif defined(CONFIG_MTD_DB1550_BOOT) && !defined(CONFIG_MTD_DB1550_USER) -#define DB1550_BOOT_ONLY -#elif !defined(CONFIG_MTD_DB1550_BOOT) && defined(CONFIG_MTD_DB1550_USER) -#define DB1550_USER_ONLY -#endif - -#ifdef DB1550_BOTH_BANKS -/* both banks will be used. Combine the first bank and the first - * part of the second bank together into a single jffs/jffs2 - * partition. - */ -static struct mtd_partition db1550_partitions[] = { - /* assume boot[2:0]:swap is '0000' or '1000', which translates to: - * 1C00 0000 1FFF FFFF CE0 64MB Boot NOR Flash - * 1800 0000 1BFF FFFF CE0 64MB Param NOR Flash - */ - { - .name = "User FS", - .size = (0x1FC00000 - 0x18000000), - .offset = 0x0000000 - },{ - .name = "yamon", - .size = 0x0100000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE - },{ - .name = "raw kernel", - .size = (0x300000 - 0x40000), /* last 256KB is yamon env */ - .offset = MTDPART_OFS_APPEND, - } -}; -#elif defined(DB1550_BOOT_ONLY) -static struct mtd_partition db1550_partitions[] = { - /* assume boot[2:0]:swap is '0000' or '1000', which translates to: - * 1C00 0000 1FFF FFFF CE0 64MB Boot NOR Flash - */ - { - .name = "User FS", - .size = 0x03c00000, - .offset = 0x0000000 - },{ - .name = "yamon", - .size = 0x0100000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE - },{ - .name = "raw kernel", - .size = (0x300000-0x40000), /* last 256KB is yamon env */ - .offset = MTDPART_OFS_APPEND, - } -}; -#elif defined(DB1550_USER_ONLY) -static struct mtd_partition db1550_partitions[] = { - /* assume boot[2:0]:swap is '0000' or '1000', which translates to: - * 1800 0000 1BFF FFFF CE0 64MB Param NOR Flash - */ - { - .name = "User FS", - .size = (0x4000000 - 0x200000), /* reserve 2MB for raw kernel */ - .offset = 0x0000000 - },{ - .name = "raw kernel", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND, - } -}; -#else -#error MTD_DB1550 define combo error /* should never happen */ -#endif - -#define NB_OF(x) (sizeof(x)/sizeof(x[0])) - -static struct mtd_info *mymtd; - -/* - * Probe the flash density and setup window address and size - * based on user CONFIG options. There are times when we don't - * want the MTD driver to be probing the boot or user flash, - * so having the option to enable only one bank is important. - */ -int setup_flash_params(void) -{ -#if defined(DB1550_BOTH_BANKS) - window_addr = 0x18000000; - window_size = 0x8000000; -#elif defined(DB1550_BOOT_ONLY) - window_addr = 0x1C000000; - window_size = 0x4000000; -#else /* USER ONLY */ - window_addr = 0x18000000; - window_size = 0x4000000; -#endif - return 0; -} - -int __init db1550_mtd_init(void) -{ - struct mtd_partition *parts; - int nb_parts = 0; - - /* Default flash bankwidth */ - db1550_map.bankwidth = flash_bankwidth; - - if (setup_flash_params()) - return -ENXIO; - - /* - * Static partition definition selection - */ - parts = db1550_partitions; - nb_parts = NB_OF(db1550_partitions); - db1550_map.size = window_size; - - /* - * Now let's probe for the actual flash. Do it here since - * specific machine settings might have been set above. - */ - printk(KERN_NOTICE "Db1550 flash: probing %d-bit flash bus\n", - db1550_map.bankwidth*8); - db1550_map.virt = ioremap(window_addr, window_size); - mymtd = do_map_probe("cfi_probe", &db1550_map); - if (!mymtd) return -ENXIO; - mymtd->owner = THIS_MODULE; - - add_mtd_partitions(mymtd, parts, nb_parts); - return 0; -} - -static void __exit db1550_mtd_cleanup(void) -{ - if (mymtd) { - del_mtd_partitions(mymtd); - map_destroy(mymtd); - iounmap((void *) db1550_map.virt); - } -} - -module_init(db1550_mtd_init); -module_exit(db1550_mtd_cleanup); - -MODULE_AUTHOR("Embedded Edge, LLC"); -MODULE_DESCRIPTION("Db1550 mtd map driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/maps/db1x00-flash.c b/drivers/mtd/maps/db1x00-flash.c deleted file mode 100644 index faa68ec..0000000 --- a/drivers/mtd/maps/db1x00-flash.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Flash memory access on Alchemy Db1xxx boards - * - * $Id: db1x00-flash.c,v 1.6 2004/11/04 13:24:14 gleixner Exp $ - * - * (C) 2003 Pete Popov <ppopov@embeddedalley.com> - * - */ - -#include <linux/config.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/init.h> -#include <linux/kernel.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> -#include <linux/mtd/partitions.h> - -#include <asm/io.h> - -#ifdef DEBUG_RW -#define DBG(x...) printk(x) -#else -#define DBG(x...) -#endif - -/* MTD CONFIG OPTIONS */ -#if defined(CONFIG_MTD_DB1X00_BOOT) && defined(CONFIG_MTD_DB1X00_USER) -#define DB1X00_BOTH_BANKS -#elif defined(CONFIG_MTD_DB1X00_BOOT) && !defined(CONFIG_MTD_DB1X00_USER) -#define DB1X00_BOOT_ONLY -#elif !defined(CONFIG_MTD_DB1X00_BOOT) && defined(CONFIG_MTD_DB1X00_USER) -#define DB1X00_USER_ONLY -#endif - -static unsigned long window_addr; -static unsigned long window_size; -static unsigned long flash_size; - -static unsigned short *bcsr = (unsigned short *)0xAE000000; -static unsigned char flash_bankwidth = 4; - -/* - * The Db1x boards support different flash densities. We setup - * the mtd_partition structures below for default of 64Mbit - * flash densities, and override the partitions sizes, if - * necessary, after we check the board status register. - */ - -#ifdef DB1X00_BOTH_BANKS -/* both banks will be used. Combine the first bank and the first - * part of the second bank together into a single jffs/jffs2 - * partition. - */ -static struct mtd_partition db1x00_partitions[] = { - { - .name = "User FS", - .size = 0x1c00000, - .offset = 0x0000000 - },{ - .name = "yamon", - .size = 0x0100000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE - },{ - .name = "raw kernel", - .size = (0x300000-0x40000), /* last 256KB is env */ - .offset = MTDPART_OFS_APPEND, - } -}; -#elif defined(DB1X00_BOOT_ONLY) -static struct mtd_partition db1x00_partitions[] = { - { - .name = "User FS", - .size = 0x00c00000, - .offset = 0x0000000 - },{ - .name = "yamon", - .size = 0x0100000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE - },{ - .name = "raw kernel", - .size = (0x300000-0x40000), /* last 256KB is env */ - .offset = MTDPART_OFS_APPEND, - } -}; -#elif defined(DB1X00_USER_ONLY) -static struct mtd_partition db1x00_partitions[] = { - { - .name = "User FS", - .size = 0x0e00000, - .offset = 0x0000000 - },{ - .name = "raw kernel", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND, - } -}; -#else -#error MTD_DB1X00 define combo error /* should never happen */ -#endif -#define NB_OF(x) (sizeof(x)/sizeof(x[0])) - -#define NAME "Db1x00 Linux Flash" - -static struct map_info db1xxx_mtd_map = { - .name = NAME, -}; - -static struct mtd_partition *parsed_parts; -static struct mtd_info *db1xxx_mtd; - -/* - * Probe the flash density and setup window address and size - * based on user CONFIG options. There are times when we don't - * want the MTD driver to be probing the boot or user flash, - * so having the option to enable only one bank is important. - */ -int setup_flash_params(void) -{ - switch ((bcsr[2] >> 14) & 0x3) { - case 0: /* 64Mbit devices */ - flash_size = 0x800000; /* 8MB per part */ -#if defined(DB1X00_BOTH_BANKS) - window_addr = 0x1E000000; - window_size = 0x2000000; -#elif defined(DB1X00_BOOT_ONLY) - window_addr = 0x1F000000; - window_size = 0x1000000; -#else /* USER ONLY */ - window_addr = 0x1E000000; - window_size = 0x1000000; -#endif - break; - case 1: - /* 128 Mbit devices */ - flash_size = 0x1000000; /* 16MB per part */ -#if defined(DB1X00_BOTH_BANKS) - window_addr = 0x1C000000; - window_size = 0x4000000; - /* USERFS from 0x1C00 0000 to 0x1FC0 0000 */ - db1x00_partitions[0].size = 0x3C00000; -#elif defined(DB1X00_BOOT_ONLY) - window_addr = 0x1E000000; - window_size = 0x2000000; - /* USERFS from 0x1E00 0000 to 0x1FC0 0000 */ - db1x00_partitions[0].size = 0x1C00000; -#else /* USER ONLY */ - window_addr = 0x1C000000; - window_size = 0x2000000; - /* USERFS from 0x1C00 0000 to 0x1DE00000 */ - db1x00_partitions[0].size = 0x1DE0000; -#endif - break; - case 2: - /* 256 Mbit devices */ - flash_size = 0x4000000; /* 64MB per part */ -#if defined(DB1X00_BOTH_BANKS) - return 1; -#elif defined(DB1X00_BOOT_ONLY) - /* Boot ROM flash bank only; no user bank */ - window_addr = 0x1C000000; - window_size = 0x4000000; - /* USERFS from 0x1C00 0000 to 0x1FC00000 */ - db1x00_partitions[0].size = 0x3C00000; -#else /* USER ONLY */ - return 1; -#endif - break; - default: - return 1; - } - db1xxx_mtd_map.size = window_size; - db1xxx_mtd_map.bankwidth = flash_bankwidth; - db1xxx_mtd_map.phys = window_addr; - db1xxx_mtd_map.bankwidth = flash_bankwidth; - return 0; -} - -int __init db1x00_mtd_init(void) -{ - struct mtd_partition *parts; - int nb_parts = 0; - - if (setup_flash_params()) - return -ENXIO; - - /* - * Static partition definition selection - */ - parts = db1x00_partitions; - nb_parts = NB_OF(db1x00_partitions); - - /* - * Now let's probe for the actual flash. Do it here since - * specific machine settings might have been set above. - */ - printk(KERN_NOTICE "Db1xxx flash: probing %d-bit flash bus\n", - db1xxx_mtd_map.bankwidth*8); - db1xxx_mtd_map.virt = ioremap(window_addr, window_size); - db1xxx_mtd = do_map_probe("cfi_probe", &db1xxx_mtd_map); - if (!db1xxx_mtd) return -ENXIO; - db1xxx_mtd->owner = THIS_MODULE; - - add_mtd_partitions(db1xxx_mtd, parts, nb_parts); - return 0; -} - -static void __exit db1x00_mtd_cleanup(void) -{ - if (db1xxx_mtd) { - del_mtd_partitions(db1xxx_mtd); - map_destroy(db1xxx_mtd); - if (parsed_parts) - kfree(parsed_parts); - } -} - -module_init(db1x00_mtd_init); -module_exit(db1x00_mtd_cleanup); - -MODULE_AUTHOR("Pete Popov"); -MODULE_DESCRIPTION("Db1x00 mtd map driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/maps/elan-104nc.c b/drivers/mtd/maps/elan-104nc.c deleted file mode 100644 index e9465f5..0000000 --- a/drivers/mtd/maps/elan-104nc.c +++ /dev/null @@ -1,228 +0,0 @@ -/* elan-104nc.c -- MTD map driver for Arcom Control Systems ELAN-104NC - - Copyright (C) 2000 Arcom Control System Ltd - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - - $Id: elan-104nc.c,v 1.25 2004/11/28 09:40:39 dwmw2 Exp $ - -The ELAN-104NC has up to 8 Mibyte of Intel StrataFlash (28F320/28F640) in x16 -mode. This drivers uses the CFI probe and Intel Extended Command Set drivers. - -The flash is accessed as follows: - - 32 kbyte memory window at 0xb0000-0xb7fff - - 16 bit I/O port (0x22) for some sort of paging. - -The single flash device is divided into 3 partition which appear as separate -MTD devices. - -Linux thinks that the I/O port is used by the PIC and hence check_region() will -always fail. So we don't do it. I just hope it doesn't break anything. -*/ -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/ioport.h> -#include <linux/init.h> -#include <asm/io.h> - -#include <linux/mtd/map.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/partitions.h> - -#define WINDOW_START 0xb0000 -/* Number of bits in offset. */ -#define WINDOW_SHIFT 15 -#define WINDOW_LENGTH (1 << WINDOW_SHIFT) -/* The bits for the offset into the window. */ -#define WINDOW_MASK (WINDOW_LENGTH-1) -#define PAGE_IO 0x22 -#define PAGE_IO_SIZE 2 - -static volatile int page_in_window = -1; // Current page in window. -static void __iomem *iomapadr; -static DEFINE_SPINLOCK(elan_104nc_spin); - -/* partition_info gives details on the logical partitions that the split the - * single flash device into. If the size if zero we use up to the end of the - * device. */ -static struct mtd_partition partition_info[]={ - { .name = "ELAN-104NC flash boot partition", - .offset = 0, - .size = 640*1024 }, - { .name = "ELAN-104NC flash partition 1", - .offset = 640*1024, - .size = 896*1024 }, - { .name = "ELAN-104NC flash partition 2", - .offset = (640+896)*1024 } -}; -#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0])) - -/* - * If no idea what is going on here. This is taken from the FlashFX stuff. - */ -#define ROMCS 1 - -static inline void elan_104nc_setup(void) -{ - u16 t; - - outw( 0x0023 + ROMCS*2, PAGE_IO ); - t=inb( PAGE_IO+1 ); - - t=(t & 0xf9) | 0x04; - - outw( ((0x0023 + ROMCS*2) | (t << 8)), PAGE_IO ); -} - -static inline void elan_104nc_page(struct map_info *map, unsigned long ofs) -{ - unsigned long page = ofs >> WINDOW_SHIFT; - - if( page!=page_in_window ) { - int cmd1; - int cmd2; - - cmd1=(page & 0x700) + 0x0833 + ROMCS*0x4000; - cmd2=((page & 0xff) << 8) + 0x0032; - - outw( cmd1, PAGE_IO ); - outw( cmd2, PAGE_IO ); - - page_in_window = page; - } -} - - -static map_word elan_104nc_read16(struct map_info *map, unsigned long ofs) -{ - map_word ret; - spin_lock(&elan_104nc_spin); - elan_104nc_page(map, ofs); - ret.x[0] = readw(iomapadr + (ofs & WINDOW_MASK)); - spin_unlock(&elan_104nc_spin); - return ret; -} - -static void elan_104nc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - while (len) { - unsigned long thislen = len; - if (len > (WINDOW_LENGTH - (from & WINDOW_MASK))) - thislen = WINDOW_LENGTH-(from & WINDOW_MASK); - - spin_lock(&elan_104nc_spin); - elan_104nc_page(map, from); - memcpy_fromio(to, iomapadr + (from & WINDOW_MASK), thislen); - spin_unlock(&elan_104nc_spin); - to += thislen; - from += thislen; - len -= thislen; - } -} - -static void elan_104nc_write16(struct map_info *map, map_word d, unsigned long adr) -{ - spin_lock(&elan_104nc_spin); - elan_104nc_page(map, adr); - writew(d.x[0], iomapadr + (adr & WINDOW_MASK)); - spin_unlock(&elan_104nc_spin); -} - -static void elan_104nc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - while(len) { - unsigned long thislen = len; - if (len > (WINDOW_LENGTH - (to & WINDOW_MASK))) - thislen = WINDOW_LENGTH-(to & WINDOW_MASK); - - spin_lock(&elan_104nc_spin); - elan_104nc_page(map, to); - memcpy_toio(iomapadr + (to & WINDOW_MASK), from, thislen); - spin_unlock(&elan_104nc_spin); - to += thislen; - from += thislen; - len -= thislen; - } -} - -static struct map_info elan_104nc_map = { - .name = "ELAN-104NC flash", - .phys = NO_XIP, - .size = 8*1024*1024, /* this must be set to a maximum possible amount - of flash so the cfi probe routines find all - the chips */ - .bankwidth = 2, - .read = elan_104nc_read16, - .copy_from = elan_104nc_copy_from, - .write = elan_104nc_write16, - .copy_to = elan_104nc_copy_to -}; - -/* MTD device for all of the flash. */ -static struct mtd_info *all_mtd; - -static void cleanup_elan_104nc(void) -{ - if( all_mtd ) { - del_mtd_partitions( all_mtd ); - map_destroy( all_mtd ); - } - - iounmap(iomapadr); -} - -static int __init init_elan_104nc(void) -{ - /* Urg! We use I/O port 0x22 without request_region()ing it, - because it's already allocated to the PIC. */ - - iomapadr = ioremap(WINDOW_START, WINDOW_LENGTH); - if (!iomapadr) { - printk( KERN_ERR"%s: failed to ioremap memory region\n", - elan_104nc_map.name ); - return -EIO; - } - - printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n", - elan_104nc_map.name, - PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1, - WINDOW_START, WINDOW_START+WINDOW_LENGTH-1 ); - - elan_104nc_setup(); - - /* Probe for chip. */ - all_mtd = do_map_probe("cfi_probe", &elan_104nc_map ); - if( !all_mtd ) { - cleanup_elan_104nc(); - return -ENXIO; - } - - all_mtd->owner = THIS_MODULE; - - /* Create MTD devices for each partition. */ - add_mtd_partitions( all_mtd, partition_info, NUM_PARTITIONS ); - - return 0; -} - -module_init(init_elan_104nc); -module_exit(cleanup_elan_104nc); - - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Arcom Control Systems Ltd."); -MODULE_DESCRIPTION("MTD map driver for Arcom Control Systems ELAN-104NC"); diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c index 29d1cc1..e505207 100644 --- a/drivers/mtd/maps/ichxrom.c +++ b/drivers/mtd/maps/ichxrom.c @@ -2,7 +2,7 @@ * ichxrom.c * * Normal mappings of chips in physical memory - * $Id: ichxrom.c,v 1.16 2004/11/28 09:40:39 dwmw2 Exp $ + * $Id: ichxrom.c,v 1.18 2005/07/07 10:26:20 dwmw2 Exp $ */ #include <linux/module.h> @@ -338,9 +338,9 @@ static struct pci_device_id ichxrom_pci_tbl[] __devinitdata = { { 0, }, }; +#if 0 MODULE_DEVICE_TABLE(pci, ichxrom_pci_tbl); -#if 0 static struct pci_driver ichxrom_driver = { .name = MOD_NAME, .id_table = ichxrom_pci_tbl, @@ -366,7 +366,7 @@ static int __init init_ichxrom(void) } return -ENXIO; #if 0 - return pci_module_init(&ichxrom_driver); + return pci_register_driver(&ichxrom_driver); #endif } diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c index c5b5f44..3e94b61 100644 --- a/drivers/mtd/maps/ixp2000.c +++ b/drivers/mtd/maps/ixp2000.c @@ -1,5 +1,5 @@ /* - * $Id: ixp2000.c,v 1.5 2004/11/16 17:15:48 dsaxena Exp $ + * $Id: ixp2000.c,v 1.6 2005/03/18 14:07:46 gleixner Exp $ * * drivers/mtd/maps/ixp2000.c * @@ -216,11 +216,6 @@ static int ixp2000_flash_probe(struct device *_dev) goto Error; } - /* - * Setup read mode for FLASH - */ - *IXP2000_SLOWPORT_FRM = 1; - #if defined(__ARMEB__) /* * Enable erratum 44 workaround for NPUs with broken slowport diff --git a/drivers/mtd/maps/mainstone-flash.c b/drivers/mtd/maps/mainstone-flash.c new file mode 100644 index 0000000..87e93fa --- /dev/null +++ b/drivers/mtd/maps/mainstone-flash.c @@ -0,0 +1,178 @@ +/* + * $Id: $ + * + * Map driver for the Mainstone developer platform. + * + * Author: Nicolas Pitre + * Copyright: (C) 2001 MontaVista Software Inc. + * + * 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/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/dma-mapping.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> +#include <asm/io.h> +#include <asm/hardware.h> +#include <asm/arch/pxa-regs.h> +#include <asm/arch/mainstone.h> + + +#define ROM_ADDR 0x00000000 +#define FLASH_ADDR 0x04000000 + +#define WINDOW_SIZE 0x04000000 + +static void mainstone_map_inval_cache(struct map_info *map, unsigned long from, + ssize_t len) +{ + consistent_sync((char *)map->cached + from, len, DMA_FROM_DEVICE); +} + +static struct map_info mainstone_maps[2] = { { + .size = WINDOW_SIZE, + .phys = PXA_CS0_PHYS, + .inval_cache = mainstone_map_inval_cache, +}, { + .size = WINDOW_SIZE, + .phys = PXA_CS1_PHYS, + .inval_cache = mainstone_map_inval_cache, +} }; + +static struct mtd_partition mainstone_partitions[] = { + { + .name = "Bootloader", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE /* force read-only */ + },{ + .name = "Kernel", + .size = 0x00400000, + .offset = 0x00040000, + },{ + .name = "Filesystem", + .size = MTDPART_SIZ_FULL, + .offset = 0x00440000 + } +}; + +static struct mtd_info *mymtds[2]; +static struct mtd_partition *parsed_parts[2]; +static int nr_parsed_parts[2]; + +static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; + +static int __init init_mainstone(void) +{ + int SW7 = 0; /* FIXME: get from SCR (Mst doc section 3.2.1.1) */ + int ret = 0, i; + + mainstone_maps[0].bankwidth = (BOOT_DEF & 1) ? 2 : 4; + mainstone_maps[1].bankwidth = 4; + + /* Compensate for SW7 which swaps the flash banks */ + mainstone_maps[SW7].name = "processor flash"; + mainstone_maps[SW7 ^ 1].name = "main board flash"; + + printk(KERN_NOTICE "Mainstone configured to boot from %s\n", + mainstone_maps[0].name); + + for (i = 0; i < 2; i++) { + mainstone_maps[i].virt = ioremap(mainstone_maps[i].phys, + WINDOW_SIZE); + if (!mainstone_maps[i].virt) { + printk(KERN_WARNING "Failed to ioremap %s\n", + mainstone_maps[i].name); + if (!ret) + ret = -ENOMEM; + continue; + } + mainstone_maps[i].cached = + ioremap_cached(mainstone_maps[i].phys, WINDOW_SIZE); + if (!mainstone_maps[i].cached) + printk(KERN_WARNING "Failed to ioremap cached %s\n", + mainstone_maps[i].name); + simple_map_init(&mainstone_maps[i]); + + printk(KERN_NOTICE + "Probing %s at physical address 0x%08lx" + " (%d-bit bankwidth)\n", + mainstone_maps[i].name, mainstone_maps[i].phys, + mainstone_maps[i].bankwidth * 8); + + mymtds[i] = do_map_probe("cfi_probe", &mainstone_maps[i]); + + if (!mymtds[i]) { + iounmap((void *)mainstone_maps[i].virt); + if (mainstone_maps[i].cached) + iounmap(mainstone_maps[i].cached); + if (!ret) + ret = -EIO; + continue; + } + mymtds[i]->owner = THIS_MODULE; + + ret = parse_mtd_partitions(mymtds[i], probes, + &parsed_parts[i], 0); + + if (ret > 0) + nr_parsed_parts[i] = ret; + } + + if (!mymtds[0] && !mymtds[1]) + return ret; + + for (i = 0; i < 2; i++) { + if (!mymtds[i]) { + printk(KERN_WARNING "%s is absent. Skipping\n", + mainstone_maps[i].name); + } else if (nr_parsed_parts[i]) { + add_mtd_partitions(mymtds[i], parsed_parts[i], + nr_parsed_parts[i]); + } else if (!i) { + printk("Using static partitions on %s\n", + mainstone_maps[i].name); + add_mtd_partitions(mymtds[i], mainstone_partitions, + ARRAY_SIZE(mainstone_partitions)); + } else { + printk("Registering %s as whole device\n", + mainstone_maps[i].name); + add_mtd_device(mymtds[i]); + } + } + return 0; +} + +static void __exit cleanup_mainstone(void) +{ + int i; + for (i = 0; i < 2; i++) { + if (!mymtds[i]) + continue; + + if (nr_parsed_parts[i] || !i) + del_mtd_partitions(mymtds[i]); + else + del_mtd_device(mymtds[i]); + + map_destroy(mymtds[i]); + iounmap((void *)mainstone_maps[i].virt); + if (mainstone_maps[i].cached) + iounmap(mainstone_maps[i].cached); + kfree(parsed_parts[i]); + } +} + +module_init(init_mainstone); +module_exit(cleanup_mainstone); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>"); +MODULE_DESCRIPTION("MTD map driver for Intel Mainstone"); diff --git a/drivers/mtd/maps/map_funcs.c b/drivers/mtd/maps/map_funcs.c index 38f6a7a..9105e6c 100644 --- a/drivers/mtd/maps/map_funcs.c +++ b/drivers/mtd/maps/map_funcs.c @@ -1,5 +1,5 @@ /* - * $Id: map_funcs.c,v 1.9 2004/07/13 22:33:15 dwmw2 Exp $ + * $Id: map_funcs.c,v 1.10 2005/06/06 23:04:36 tpoynor Exp $ * * Out-of-line map I/O functions for simple maps when CONFIG_COMPLEX_MAPPINGS * is enabled. @@ -9,23 +9,24 @@ #include <linux/module.h> #include <linux/mtd/map.h> +#include <linux/mtd/xip.h> -static map_word simple_map_read(struct map_info *map, unsigned long ofs) +static map_word __xipram simple_map_read(struct map_info *map, unsigned long ofs) { return inline_map_read(map, ofs); } -static void simple_map_write(struct map_info *map, const map_word datum, unsigned long ofs) +static void __xipram simple_map_write(struct map_info *map, const map_word datum, unsigned long ofs) { inline_map_write(map, datum, ofs); } -static void simple_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +static void __xipram simple_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) { inline_map_copy_from(map, to, from, len); } -static void simple_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +static void __xipram simple_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) { inline_map_copy_to(map, to, from, len); } diff --git a/drivers/mtd/maps/omap_nor.c b/drivers/mtd/maps/omap_nor.c new file mode 100644 index 0000000..8cc7140 --- /dev/null +++ b/drivers/mtd/maps/omap_nor.c @@ -0,0 +1,179 @@ +/* + * Flash memory support for various TI OMAP boards + * + * Copyright (C) 2001-2002 MontaVista Software Inc. + * Copyright (C) 2003-2004 Texas Instruments + * Copyright (C) 2004 Nokia Corporation + * + * Assembled using driver code copyright the companies above + * and written by David Brownell, Jian Zhang <jzhang@ti.com>, + * Tony Lindgren <tony@atomide.com> and others. + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> + +#include <asm/io.h> +#include <asm/hardware.h> +#include <asm/mach-types.h> +#include <asm/mach/flash.h> +#include <asm/arch/tc.h> + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probes[] = { /* "RedBoot", */ "cmdlinepart", NULL }; +#endif + +struct omapflash_info { + struct mtd_partition *parts; + struct mtd_info *mtd; + struct map_info map; +}; + +static void omap_set_vpp(struct map_info *map, int enable) +{ + static int count; + + if (enable) { + if (count++ == 0) + OMAP_EMIFS_CONFIG_REG |= OMAP_EMIFS_CONFIG_WP; + } else { + if (count && (--count == 0)) + OMAP_EMIFS_CONFIG_REG &= ~OMAP_EMIFS_CONFIG_WP; + } +} + +static int __devinit omapflash_probe(struct device *dev) +{ + int err; + struct omapflash_info *info; + struct platform_device *pdev = to_platform_device(dev); + struct flash_platform_data *pdata = pdev->dev.platform_data; + struct resource *res = pdev->resource; + unsigned long size = res->end - res->start + 1; + + info = kmalloc(sizeof(struct omapflash_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + memset(info, 0, sizeof(struct omapflash_info)); + + if (!request_mem_region(res->start, size, "flash")) { + err = -EBUSY; + goto out_free_info; + } + + info->map.virt = ioremap(res->start, size); + if (!info->map.virt) { + err = -ENOMEM; + goto out_release_mem_region; + } + info->map.name = pdev->dev.bus_id; + info->map.phys = res->start; + info->map.size = size; + info->map.bankwidth = pdata->width; + info->map.set_vpp = omap_set_vpp; + + simple_map_init(&info->map); + info->mtd = do_map_probe(pdata->map_name, &info->map); + if (!info->mtd) { + err = -EIO; + goto out_iounmap; + } + info->mtd->owner = THIS_MODULE; + +#ifdef CONFIG_MTD_PARTITIONS + err = parse_mtd_partitions(info->mtd, part_probes, &info->parts, 0); + if (err > 0) + add_mtd_partitions(info->mtd, info->parts, err); + else if (err < 0 && pdata->parts) + add_mtd_partitions(info->mtd, pdata->parts, pdata->nr_parts); + else +#endif + add_mtd_device(info->mtd); + + dev_set_drvdata(&pdev->dev, info); + + return 0; + +out_iounmap: + iounmap(info->map.virt); +out_release_mem_region: + release_mem_region(res->start, size); +out_free_info: + kfree(info); + + return err; +} + +static int __devexit omapflash_remove(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct omapflash_info *info = dev_get_drvdata(&pdev->dev); + + dev_set_drvdata(&pdev->dev, NULL); + + if (info) { + if (info->parts) { + del_mtd_partitions(info->mtd); + kfree(info->parts); + } else + del_mtd_device(info->mtd); + map_destroy(info->mtd); + release_mem_region(info->map.phys, info->map.size); + iounmap((void __iomem *) info->map.virt); + kfree(info); + } + + return 0; +} + +static struct device_driver omapflash_driver = { + .name = "omapflash", + .bus = &platform_bus_type, + .probe = omapflash_probe, + .remove = __devexit_p(omapflash_remove), +}; + +static int __init omapflash_init(void) +{ + return driver_register(&omapflash_driver); +} + +static void __exit omapflash_exit(void) +{ + driver_unregister(&omapflash_driver); +} + +module_init(omapflash_init); +module_exit(omapflash_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MTD NOR map driver for TI OMAP boards"); + diff --git a/drivers/mtd/maps/pb1550-flash.c b/drivers/mtd/maps/pb1550-flash.c deleted file mode 100644 index 1424726..0000000 --- a/drivers/mtd/maps/pb1550-flash.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Flash memory access on Alchemy Pb1550 board - * - * $Id: pb1550-flash.c,v 1.6 2004/11/04 13:24:15 gleixner Exp $ - * - * (C) 2004 Embedded Edge, LLC, based on pb1550-flash.c: - * (C) 2003 Pete Popov <ppopov@pacbell.net> - * - */ - -#include <linux/config.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kernel.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> -#include <linux/mtd/partitions.h> - -#include <asm/io.h> -#include <asm/au1000.h> -#include <asm/pb1550.h> - -#ifdef DEBUG_RW -#define DBG(x...) printk(x) -#else -#define DBG(x...) -#endif - -static unsigned long window_addr; -static unsigned long window_size; - - -static struct map_info pb1550_map = { - .name = "Pb1550 flash", -}; - -static unsigned char flash_bankwidth = 4; - -/* - * Support only 64MB NOR Flash parts - */ - -#ifdef PB1550_BOTH_BANKS -/* both banks will be used. Combine the first bank and the first - * part of the second bank together into a single jffs/jffs2 - * partition. - */ -static struct mtd_partition pb1550_partitions[] = { - /* assume boot[2:0]:swap is '0000' or '1000', which translates to: - * 1C00 0000 1FFF FFFF CE0 64MB Boot NOR Flash - * 1800 0000 1BFF FFFF CE0 64MB Param NOR Flash - */ - { - .name = "User FS", - .size = (0x1FC00000 - 0x18000000), - .offset = 0x0000000 - },{ - .name = "yamon", - .size = 0x0100000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE - },{ - .name = "raw kernel", - .size = (0x300000 - 0x40000), /* last 256KB is yamon env */ - .offset = MTDPART_OFS_APPEND, - } -}; -#elif defined(PB1550_BOOT_ONLY) -static struct mtd_partition pb1550_partitions[] = { - /* assume boot[2:0]:swap is '0000' or '1000', which translates to: - * 1C00 0000 1FFF FFFF CE0 64MB Boot NOR Flash - */ - { - .name = "User FS", - .size = 0x03c00000, - .offset = 0x0000000 - },{ - .name = "yamon", - .size = 0x0100000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE - },{ - .name = "raw kernel", - .size = (0x300000-0x40000), /* last 256KB is yamon env */ - .offset = MTDPART_OFS_APPEND, - } -}; -#elif defined(PB1550_USER_ONLY) -static struct mtd_partition pb1550_partitions[] = { - /* assume boot[2:0]:swap is '0000' or '1000', which translates to: - * 1800 0000 1BFF FFFF CE0 64MB Param NOR Flash - */ - { - .name = "User FS", - .size = (0x4000000 - 0x200000), /* reserve 2MB for raw kernel */ - .offset = 0x0000000 - },{ - .name = "raw kernel", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND, - } -}; -#else -#error MTD_PB1550 define combo error /* should never happen */ -#endif - -#define NB_OF(x) (sizeof(x)/sizeof(x[0])) - -static struct mtd_info *mymtd; - -/* - * Probe the flash density and setup window address and size - * based on user CONFIG options. There are times when we don't - * want the MTD driver to be probing the boot or user flash, - * so having the option to enable only one bank is important. - */ -int setup_flash_params(void) -{ - u16 boot_swapboot; - boot_swapboot = (au_readl(MEM_STSTAT) & (0x7<<1)) | - ((bcsr->status >> 6) & 0x1); - printk("Pb1550 MTD: boot:swap %d\n", boot_swapboot); - - switch (boot_swapboot) { - case 0: /* 512Mbit devices, both enabled */ - case 1: - case 8: - case 9: -#if defined(PB1550_BOTH_BANKS) - window_addr = 0x18000000; - window_size = 0x8000000; -#elif defined(PB1550_BOOT_ONLY) - window_addr = 0x1C000000; - window_size = 0x4000000; -#else /* USER ONLY */ - window_addr = 0x1E000000; - window_size = 0x4000000; -#endif - break; - case 0xC: - case 0xD: - case 0xE: - case 0xF: - /* 64 MB Boot NOR Flash is disabled */ - /* and the start address is moved to 0x0C00000 */ - window_addr = 0x0C000000; - window_size = 0x4000000; - default: - printk("Pb1550 MTD: unsupported boot:swap setting\n"); - return 1; - } - return 0; -} - -int __init pb1550_mtd_init(void) -{ - struct mtd_partition *parts; - int nb_parts = 0; - - /* Default flash bankwidth */ - pb1550_map.bankwidth = flash_bankwidth; - - if (setup_flash_params()) - return -ENXIO; - - /* - * Static partition definition selection - */ - parts = pb1550_partitions; - nb_parts = NB_OF(pb1550_partitions); - pb1550_map.size = window_size; - - /* - * Now let's probe for the actual flash. Do it here since - * specific machine settings might have been set above. - */ - printk(KERN_NOTICE "Pb1550 flash: probing %d-bit flash bus\n", - pb1550_map.bankwidth*8); - pb1550_map.virt = ioremap(window_addr, window_size); - mymtd = do_map_probe("cfi_probe", &pb1550_map); - if (!mymtd) return -ENXIO; - mymtd->owner = THIS_MODULE; - - add_mtd_partitions(mymtd, parts, nb_parts); - return 0; -} - -static void __exit pb1550_mtd_cleanup(void) -{ - if (mymtd) { - del_mtd_partitions(mymtd); - map_destroy(mymtd); - } -} - -module_init(pb1550_mtd_init); -module_exit(pb1550_mtd_cleanup); - -MODULE_AUTHOR("Embedded Edge, LLC"); -MODULE_DESCRIPTION("Pb1550 mtd map driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/maps/pb1xxx-flash.c b/drivers/mtd/maps/pb1xxx-flash.c deleted file mode 100644 index 06e7315..0000000 --- a/drivers/mtd/maps/pb1xxx-flash.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Flash memory access on Alchemy Pb1xxx boards - * - * (C) 2001 Pete Popov <ppopov@mvista.com> - * - * $Id: pb1xxx-flash.c,v 1.14 2004/11/04 13:24:15 gleixner Exp $ - */ - -#include <linux/config.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/init.h> -#include <linux/kernel.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> -#include <linux/mtd/partitions.h> - -#include <asm/io.h> - -#ifdef DEBUG_RW -#define DBG(x...) printk(x) -#else -#define DBG(x...) -#endif - -#ifdef CONFIG_MIPS_PB1000 - -#define WINDOW_ADDR 0x1F800000 -#define WINDOW_SIZE 0x800000 - -static struct mtd_partition pb1xxx_partitions[] = { - { - .name = "yamon env", - .size = 0x00020000, - .offset = 0, - .mask_flags = MTD_WRITEABLE}, - { - .name = "User FS", - .size = 0x003e0000, - .offset = 0x20000,}, - { - .name = "boot code", - .size = 0x100000, - .offset = 0x400000, - .mask_flags = MTD_WRITEABLE}, - { - .name = "raw/kernel", - .size = 0x300000, - .offset = 0x500000} -}; - -#elif defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) - -#if defined(CONFIG_MTD_PB1500_BOOT) && defined(CONFIG_MTD_PB1500_USER) -/* both 32MB banks will be used. Combine the first 32MB bank and the - * first 28MB of the second bank together into a single jffs/jffs2 - * partition. - */ -#define WINDOW_ADDR 0x1C000000 -#define WINDOW_SIZE 0x4000000 -static struct mtd_partition pb1xxx_partitions[] = { - { - .name = "User FS", - .size = 0x3c00000, - .offset = 0x0000000 - },{ - .name = "yamon", - .size = 0x0100000, - .offset = 0x3c00000, - .mask_flags = MTD_WRITEABLE - },{ - .name = "raw kernel", - .size = 0x02c0000, - .offset = 0x3d00000 - } -}; -#elif defined(CONFIG_MTD_PB1500_BOOT) && !defined(CONFIG_MTD_PB1500_USER) -#define WINDOW_ADDR 0x1E000000 -#define WINDOW_SIZE 0x2000000 -static struct mtd_partition pb1xxx_partitions[] = { - { - .name = "User FS", - .size = 0x1c00000, - .offset = 0x0000000 - },{ - .name = "yamon", - .size = 0x0100000, - .offset = 0x1c00000, - .mask_flags = MTD_WRITEABLE - },{ - .name = "raw kernel", - .size = 0x02c0000, - .offset = 0x1d00000 - } -}; -#elif !defined(CONFIG_MTD_PB1500_BOOT) && defined(CONFIG_MTD_PB1500_USER) -#define WINDOW_ADDR 0x1C000000 -#define WINDOW_SIZE 0x2000000 -static struct mtd_partition pb1xxx_partitions[] = { - { - .name = "User FS", - .size = 0x1e00000, - .offset = 0x0000000 - },{ - .name = "raw kernel", - .size = 0x0200000, - .offset = 0x1e00000, - } -}; -#else -#error MTD_PB1500 define combo error /* should never happen */ -#endif -#else -#error Unsupported board -#endif - -#define NAME "Pb1x00 Linux Flash" -#define PADDR WINDOW_ADDR -#define BUSWIDTH 4 -#define SIZE WINDOW_SIZE -#define PARTITIONS 4 - -static struct map_info pb1xxx_mtd_map = { - .name = NAME, - .size = SIZE, - .bankwidth = BUSWIDTH, - .phys = PADDR, -}; - -static struct mtd_info *pb1xxx_mtd; - -int __init pb1xxx_mtd_init(void) -{ - struct mtd_partition *parts; - int nb_parts = 0; - char *part_type; - - /* - * Static partition definition selection - */ - part_type = "static"; - parts = pb1xxx_partitions; - nb_parts = ARRAY_SIZE(pb1xxx_partitions); - - /* - * Now let's probe for the actual flash. Do it here since - * specific machine settings might have been set above. - */ - printk(KERN_NOTICE "Pb1xxx flash: probing %d-bit flash bus\n", - BUSWIDTH*8); - pb1xxx_mtd_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); - - simple_map_init(&pb1xxx_mtd_map); - - pb1xxx_mtd = do_map_probe("cfi_probe", &pb1xxx_mtd_map); - if (!pb1xxx_mtd) return -ENXIO; - pb1xxx_mtd->owner = THIS_MODULE; - - add_mtd_partitions(pb1xxx_mtd, parts, nb_parts); - return 0; -} - -static void __exit pb1xxx_mtd_cleanup(void) -{ - if (pb1xxx_mtd) { - del_mtd_partitions(pb1xxx_mtd); - map_destroy(pb1xxx_mtd); - iounmap((void *) pb1xxx_mtd_map.virt); - } -} - -module_init(pb1xxx_mtd_init); -module_exit(pb1xxx_mtd_cleanup); - -MODULE_AUTHOR("Pete Popov"); -MODULE_DESCRIPTION("Pb1xxx CFI map driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c index 08b60bd..18dbd3a 100644 --- a/drivers/mtd/maps/pci.c +++ b/drivers/mtd/maps/pci.c @@ -7,7 +7,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * $Id: pci.c,v 1.9 2004/11/28 09:40:40 dwmw2 Exp $ + * $Id: pci.c,v 1.10 2005/03/18 14:04:35 gleixner Exp $ * * Generic PCI memory map driver. We support the following boards: * - Intel IQ80310 ATU. @@ -370,7 +370,7 @@ static struct pci_driver mtd_pci_driver = { static int __init mtd_pci_maps_init(void) { - return pci_module_init(&mtd_pci_driver); + return pci_register_driver(&mtd_pci_driver); } static void __exit mtd_pci_maps_exit(void) diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c new file mode 100644 index 0000000..118b045 --- /dev/null +++ b/drivers/mtd/maps/plat-ram.c @@ -0,0 +1,278 @@ +/* drivers/mtd/maps/plat-ram.c + * + * (c) 2004-2005 Simtec Electronics + * http://www.simtec.co.uk/products/SWLINUX/ + * Ben Dooks <ben@simtec.co.uk> + * + * Generic platfrom device based RAM map + * + * $Id: plat-ram.c,v 1.3 2005/03/19 22:41:27 gleixner Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/device.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/plat-ram.h> + +#include <asm/io.h> + +/* private structure for each mtd platform ram device created */ + +struct platram_info { + struct device *dev; + struct mtd_info *mtd; + struct map_info map; + struct mtd_partition *partitions; + struct resource *area; + struct platdata_mtd_ram *pdata; +}; + +/* to_platram_info() + * + * device private data to struct platram_info conversion +*/ + +static inline struct platram_info *to_platram_info(struct device *dev) +{ + return (struct platram_info *)dev_get_drvdata(dev); +} + +/* platram_setrw + * + * call the platform device's set rw/ro control + * + * to = 0 => read-only + * = 1 => read-write +*/ + +static inline void platram_setrw(struct platram_info *info, int to) +{ + if (info->pdata == NULL) + return; + + if (info->pdata->set_rw != NULL) + (info->pdata->set_rw)(info->dev, to); +} + +/* platram_remove + * + * called to remove the device from the driver's control +*/ + +static int platram_remove(struct device *dev) +{ + struct platram_info *info = to_platram_info(dev); + + dev_set_drvdata(dev, NULL); + + dev_dbg(dev, "removing device\n"); + + if (info == NULL) + return 0; + + if (info->mtd) { +#ifdef CONFIG_MTD_PARTITIONS + if (info->partitions) { + del_mtd_partitions(info->mtd); + kfree(info->partitions); + } +#endif + del_mtd_device(info->mtd); + map_destroy(info->mtd); + } + + /* ensure ram is left read-only */ + + platram_setrw(info, PLATRAM_RO); + + /* release resources */ + + if (info->area) { + release_resource(info->area); + kfree(info->area); + } + + if (info->map.virt != NULL) + iounmap(info->map.virt); + + kfree(info); + + return 0; +} + +/* platram_probe + * + * called from device drive system when a device matching our + * driver is found. +*/ + +static int platram_probe(struct device *dev) +{ + struct platform_device *pd = to_platform_device(dev); + struct platdata_mtd_ram *pdata; + struct platram_info *info; + struct resource *res; + int err = 0; + + dev_dbg(dev, "probe entered\n"); + + if (dev->platform_data == NULL) { + dev_err(dev, "no platform data supplied\n"); + err = -ENOENT; + goto exit_error; + } + + pdata = dev->platform_data; + + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (info == NULL) { + dev_err(dev, "no memory for flash info\n"); + err = -ENOMEM; + goto exit_error; + } + + memset(info, 0, sizeof(*info)); + dev_set_drvdata(dev, info); + + info->dev = dev; + info->pdata = pdata; + + /* get the resource for the memory mapping */ + + res = platform_get_resource(pd, IORESOURCE_MEM, 0); + + if (res == NULL) { + dev_err(dev, "no memory resource specified\n"); + err = -ENOENT; + goto exit_free; + } + + dev_dbg(dev, "got platform resource %p (0x%lx)\n", res, res->start); + + /* setup map parameters */ + + info->map.phys = res->start; + info->map.size = (res->end - res->start) + 1; + info->map.name = pdata->mapname != NULL ? pdata->mapname : pd->name; + info->map.bankwidth = pdata->bankwidth; + + /* register our usage of the memory area */ + + info->area = request_mem_region(res->start, info->map.size, pd->name); + if (info->area == NULL) { + dev_err(dev, "failed to request memory region\n"); + err = -EIO; + goto exit_free; + } + + /* remap the memory area */ + + info->map.virt = ioremap(res->start, info->map.size); + dev_dbg(dev, "virt %p, %lu bytes\n", info->map.virt, info->map.size); + + if (info->map.virt == NULL) { + dev_err(dev, "failed to ioremap() region\n"); + err = -EIO; + goto exit_free; + } + + simple_map_init(&info->map); + + dev_dbg(dev, "initialised map, probing for mtd\n"); + + /* probe for the right mtd map driver */ + + info->mtd = do_map_probe("map_ram" , &info->map); + if (info->mtd == NULL) { + dev_err(dev, "failed to probe for map_ram\n"); + err = -ENOMEM; + goto exit_free; + } + + info->mtd->owner = THIS_MODULE; + + platram_setrw(info, PLATRAM_RW); + + /* check to see if there are any available partitions, or wether + * to add this device whole */ + +#ifdef CONFIG_MTD_PARTITIONS + if (pdata->nr_partitions > 0) { + const char **probes = { NULL }; + + if (pdata->probes) + probes = (const char **)pdata->probes; + + err = parse_mtd_partitions(info->mtd, probes, + &info->partitions, 0); + if (err > 0) { + err = add_mtd_partitions(info->mtd, info->partitions, + err); + } + } +#endif /* CONFIG_MTD_PARTITIONS */ + + if (add_mtd_device(info->mtd)) { + dev_err(dev, "add_mtd_device() failed\n"); + err = -ENOMEM; + } + + dev_info(dev, "registered mtd device\n"); + return err; + + exit_free: + platram_remove(dev); + exit_error: + return err; +} + +/* device driver info */ + +static struct device_driver platram_driver = { + .name = "mtd-ram", + .bus = &platform_bus_type, + .probe = platram_probe, + .remove = platram_remove, +}; + +/* module init/exit */ + +static int __init platram_init(void) +{ + printk("Generic platform RAM MTD, (c) 2004 Simtec Electronics\n"); + return driver_register(&platram_driver); +} + +static void __exit platram_exit(void) +{ + driver_unregister(&platram_driver); +} + +module_init(platram_init); +module_exit(platram_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); +MODULE_DESCRIPTION("MTD platform RAM map driver"); diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c index 5bb3b60..97a8dfd 100644 --- a/drivers/mtd/maps/scb2_flash.c +++ b/drivers/mtd/maps/scb2_flash.c @@ -1,6 +1,6 @@ /* * MTD map driver for BIOS Flash on Intel SCB2 boards - * $Id: scb2_flash.c,v 1.11 2004/11/28 09:40:40 dwmw2 Exp $ + * $Id: scb2_flash.c,v 1.12 2005/03/18 14:04:35 gleixner Exp $ * Copyright (C) 2002 Sun Microsystems, Inc. * Tim Hockin <thockin@sun.com> * @@ -238,7 +238,7 @@ static struct pci_driver scb2_flash_driver = { static int __init scb2_flash_init(void) { - return pci_module_init(&scb2_flash_driver); + return pci_register_driver(&scb2_flash_driver); } static void __exit diff --git a/drivers/mtd/maps/sharpsl-flash.c b/drivers/mtd/maps/sharpsl-flash.c index b3b39cb..d15da6f 100644 --- a/drivers/mtd/maps/sharpsl-flash.c +++ b/drivers/mtd/maps/sharpsl-flash.c @@ -4,7 +4,7 @@ * Copyright (C) 2001 Lineo Japan, Inc. * Copyright (C) 2002 SHARP * - * $Id: sharpsl-flash.c,v 1.2 2004/11/24 20:38:06 rpurdie Exp $ + * $Id: sharpsl-flash.c,v 1.5 2005/03/21 08:42:11 rpurdie Exp $ * * based on rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp * Handle mapping of the flash on the RPX Lite and CLLF boards @@ -24,13 +24,14 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> -#include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> #include <linux/mtd/partitions.h> +#include <asm/io.h> +#include <asm/mach-types.h> #define WINDOW_ADDR 0x00000000 -#define WINDOW_SIZE 0x01000000 +#define WINDOW_SIZE 0x00800000 #define BANK_WIDTH 2 static struct mtd_info *mymtd; @@ -44,9 +45,7 @@ struct map_info sharpsl_map = { static struct mtd_partition sharpsl_partitions[1] = { { - name: "Filesystem", - size: 0x006d0000, - offset: 0x00120000 + name: "Boot PROM Filesystem", } }; @@ -58,12 +57,16 @@ int __init init_sharpsl(void) int nb_parts = 0; char *part_type = "static"; - printk(KERN_NOTICE "Sharp SL series flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); + printk(KERN_NOTICE "Sharp SL series flash device: %x at %x\n", + WINDOW_SIZE, WINDOW_ADDR); sharpsl_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE); if (!sharpsl_map.virt) { printk("Failed to ioremap\n"); return -EIO; } + + simple_map_init(&sharpsl_map); + mymtd = do_map_probe("map_rom", &sharpsl_map); if (!mymtd) { iounmap(sharpsl_map.virt); @@ -72,6 +75,22 @@ int __init init_sharpsl(void) mymtd->owner = THIS_MODULE; + if (machine_is_corgi() || machine_is_shepherd() || machine_is_husky() + || machine_is_poodle()) { + sharpsl_partitions[0].size=0x006d0000; + sharpsl_partitions[0].offset=0x00120000; + } else if (machine_is_tosa()) { + sharpsl_partitions[0].size=0x006a0000; + sharpsl_partitions[0].offset=0x00160000; + } else if (machine_is_spitz()) { + sharpsl_partitions[0].size=0x006b0000; + sharpsl_partitions[0].offset=0x00140000; + } else { + map_destroy(mymtd); + iounmap(sharpsl_map.virt); + return -ENODEV; + } + parts = sharpsl_partitions; nb_parts = NB_OF(sharpsl_partitions); diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 510ad78..1ed602a 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -1,5 +1,5 @@ /* - * $Id: mtdchar.c,v 1.66 2005/01/05 18:05:11 dwmw2 Exp $ + * $Id: mtdchar.c,v 1.73 2005/07/04 17:36:41 gleixner Exp $ * * Character-device access to raw MTD devices. * @@ -15,27 +15,30 @@ #include <linux/fs.h> #include <asm/uaccess.h> -#ifdef CONFIG_DEVFS_FS -#include <linux/devfs_fs_kernel.h> +#include <linux/device.h> + +static struct class *mtd_class; static void mtd_notify_add(struct mtd_info* mtd) { if (!mtd) return; - devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2), - S_IFCHR | S_IRUGO | S_IWUGO, "mtd/%d", mtd->index); - - devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), - S_IFCHR | S_IRUGO, "mtd/%dro", mtd->index); + class_device_create(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2), + NULL, "mtd%d", mtd->index); + + class_device_create(mtd_class, + MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), + NULL, "mtd%dro", mtd->index); } static void mtd_notify_remove(struct mtd_info* mtd) { if (!mtd) return; - devfs_remove("mtd/%d", mtd->index); - devfs_remove("mtd/%dro", mtd->index); + + class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2)); + class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1)); } static struct mtd_notifier notifier = { @@ -43,25 +46,25 @@ static struct mtd_notifier notifier = { .remove = mtd_notify_remove, }; -static inline void mtdchar_devfs_init(void) -{ - devfs_mk_dir("mtd"); - register_mtd_user(¬ifier); -} +/* + * We use file->private_data to store a pointer to the MTDdevice. + * Since alighment is at least 32 bits, we have 2 bits free for OTP + * modes as well. + */ -static inline void mtdchar_devfs_exit(void) -{ - unregister_mtd_user(¬ifier); - devfs_remove("mtd"); -} -#else /* !DEVFS */ -#define mtdchar_devfs_init() do { } while(0) -#define mtdchar_devfs_exit() do { } while(0) -#endif +#define TO_MTD(file) (struct mtd_info *)((long)((file)->private_data) & ~3L) + +#define MTD_MODE_OTP_FACT 1 +#define MTD_MODE_OTP_USER 2 +#define MTD_MODE(file) ((long)((file)->private_data) & 3) + +#define SET_MTD_MODE(file, mode) \ + do { long __p = (long)((file)->private_data); \ + (file)->private_data = (void *)((__p & ~3L) | mode); } while (0) static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) { - struct mtd_info *mtd = file->private_data; + struct mtd_info *mtd = TO_MTD(file); switch (orig) { case 0: @@ -134,7 +137,7 @@ static int mtd_close(struct inode *inode, struct file *file) DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); - mtd = file->private_data; + mtd = TO_MTD(file); if (mtd->sync) mtd->sync(mtd); @@ -151,7 +154,7 @@ static int mtd_close(struct inode *inode, struct file *file) static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) { - struct mtd_info *mtd = file->private_data; + struct mtd_info *mtd = TO_MTD(file); size_t retlen=0; size_t total_retlen=0; int ret=0; @@ -178,7 +181,16 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t if (!kbuf) return -ENOMEM; - ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf); + switch (MTD_MODE(file)) { + case MTD_MODE_OTP_FACT: + ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf); + break; + case MTD_MODE_OTP_USER: + ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); + break; + default: + ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf); + } /* Nand returns -EBADMSG on ecc errors, but it returns * the data. For our userspace tools it is important * to dump areas with ecc errors ! @@ -196,6 +208,8 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t count -= retlen; buf += retlen; + if (retlen == 0) + count = 0; } else { kfree(kbuf); @@ -210,7 +224,7 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos) { - struct mtd_info *mtd = file->private_data; + struct mtd_info *mtd = TO_MTD(file); char *kbuf; size_t retlen; size_t total_retlen=0; @@ -245,7 +259,20 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count return -EFAULT; } - ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); + switch (MTD_MODE(file)) { + case MTD_MODE_OTP_FACT: + ret = -EROFS; + break; + case MTD_MODE_OTP_USER: + if (!mtd->write_user_prot_reg) { + ret = -EOPNOTSUPP; + break; + } + ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); + break; + default: + ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); + } if (!ret) { *ppos += retlen; total_retlen += retlen; @@ -276,7 +303,7 @@ static void mtdchar_erase_callback (struct erase_info *instr) static int mtd_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) { - struct mtd_info *mtd = file->private_data; + struct mtd_info *mtd = TO_MTD(file); void __user *argp = (void __user *)arg; int ret = 0; u_long size; @@ -518,6 +545,80 @@ static int mtd_ioctl(struct inode *inode, struct file *file, break; } +#ifdef CONFIG_MTD_OTP + case OTPSELECT: + { + int mode; + if (copy_from_user(&mode, argp, sizeof(int))) + return -EFAULT; + SET_MTD_MODE(file, 0); + switch (mode) { + case MTD_OTP_FACTORY: + if (!mtd->read_fact_prot_reg) + ret = -EOPNOTSUPP; + else + SET_MTD_MODE(file, MTD_MODE_OTP_FACT); + break; + case MTD_OTP_USER: + if (!mtd->read_fact_prot_reg) + ret = -EOPNOTSUPP; + else + SET_MTD_MODE(file, MTD_MODE_OTP_USER); + break; + default: + ret = -EINVAL; + case MTD_OTP_OFF: + break; + } + file->f_pos = 0; + break; + } + + case OTPGETREGIONCOUNT: + case OTPGETREGIONINFO: + { + struct otp_info *buf = kmalloc(4096, GFP_KERNEL); + if (!buf) + return -ENOMEM; + ret = -EOPNOTSUPP; + switch (MTD_MODE(file)) { + case MTD_MODE_OTP_FACT: + if (mtd->get_fact_prot_info) + ret = mtd->get_fact_prot_info(mtd, buf, 4096); + break; + case MTD_MODE_OTP_USER: + if (mtd->get_user_prot_info) + ret = mtd->get_user_prot_info(mtd, buf, 4096); + break; + } + if (ret >= 0) { + if (cmd == OTPGETREGIONCOUNT) { + int nbr = ret / sizeof(struct otp_info); + ret = copy_to_user(argp, &nbr, sizeof(int)); + } else + ret = copy_to_user(argp, buf, ret); + if (ret) + ret = -EFAULT; + } + kfree(buf); + break; + } + + case OTPLOCK: + { + struct otp_info info; + + if (MTD_MODE(file) != MTD_MODE_OTP_USER) + return -EINVAL; + if (copy_from_user(&info, argp, sizeof(info))) + return -EFAULT; + if (!mtd->lock_user_prot_reg) + return -EOPNOTSUPP; + ret = mtd->lock_user_prot_reg(mtd, info.start, info.length); + break; + } +#endif + default: ret = -ENOTTY; } @@ -543,13 +644,22 @@ static int __init init_mtdchar(void) return -EAGAIN; } - mtdchar_devfs_init(); + mtd_class = class_create(THIS_MODULE, "mtd"); + + if (IS_ERR(mtd_class)) { + printk(KERN_ERR "Error creating mtd class.\n"); + unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); + return PTR_ERR(mtd_class); + } + + register_mtd_user(¬ifier); return 0; } static void __exit cleanup_mtdchar(void) { - mtdchar_devfs_exit(); + unregister_mtd_user(¬ifier); + class_destroy(mtd_class); unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); } diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 9c0315d..dc86df1 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1,5 +1,5 @@ /* - * $Id: mtdcore.c,v 1.44 2004/11/16 18:28:59 dwmw2 Exp $ + * $Id: mtdcore.c,v 1.45 2005/02/18 14:34:50 dedekind Exp $ * * Core registration and callback routines for MTD * drivers and users. @@ -149,8 +149,8 @@ void register_mtd_user (struct mtd_notifier *new) } /** - * register_mtd_user - unregister a 'user' of MTD devices. - * @new: pointer to notifier info structure + * unregister_mtd_user - unregister a 'user' of MTD devices. + * @old: pointer to notifier info structure * * Removes a callback function pair from the list of 'users' to be * notified upon addition or removal of MTD devices. Causes the diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 96ebb52..b92e6bf 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -5,7 +5,7 @@ * * This code is GPL * - * $Id: mtdpart.c,v 1.51 2004/11/16 18:28:59 dwmw2 Exp $ + * $Id: mtdpart.c,v 1.53 2005/02/08 17:11:13 nico Exp $ * * 02-21-2002 Thomas Gleixner <gleixner@autronix.de> * added support for read_oob, write_oob @@ -116,6 +116,13 @@ static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t le len, retlen, buf); } +static int part_get_user_prot_info (struct mtd_info *mtd, + struct otp_info *buf, size_t len) +{ + struct mtd_part *part = PART(mtd); + return part->master->get_user_prot_info (part->master, buf, len); +} + static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { @@ -124,6 +131,13 @@ static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t le len, retlen, buf); } +static int part_get_fact_prot_info (struct mtd_info *mtd, + struct otp_info *buf, size_t len) +{ + struct mtd_part *part = PART(mtd); + return part->master->get_fact_prot_info (part->master, buf, len); +} + static int part_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { @@ -182,6 +196,12 @@ static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t l len, retlen, buf); } +static int part_lock_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len) +{ + struct mtd_part *part = PART(mtd); + return part->master->lock_user_prot_reg (part->master, from, len); +} + static int part_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen) { @@ -409,6 +429,12 @@ int add_mtd_partitions(struct mtd_info *master, slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; if(master->write_user_prot_reg) slave->mtd.write_user_prot_reg = part_write_user_prot_reg; + if(master->lock_user_prot_reg) + slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; + if(master->get_user_prot_info) + slave->mtd.get_user_prot_info = part_get_user_prot_info; + if(master->get_fact_prot_info) + slave->mtd.get_fact_prot_info = part_get_fact_prot_info; if (master->sync) slave->mtd.sync = part_sync; if (!i && master->suspend && master->resume) { diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index f7801eb..36d34e5 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,5 +1,5 @@ # drivers/mtd/nand/Kconfig -# $Id: Kconfig,v 1.26 2005/01/05 12:42:24 dwmw2 Exp $ +# $Id: Kconfig,v 1.31 2005/06/20 12:03:21 bjd Exp $ menu "NAND Flash Device Drivers" depends on MTD!=n @@ -58,20 +58,6 @@ config MTD_NAND_TOTO config MTD_NAND_IDS tristate -config MTD_NAND_TX4925NDFMC - tristate "SmartMedia Card on Toshiba RBTX4925 reference board" - depends on TOSHIBA_RBTX4925 && MTD_NAND && TOSHIBA_RBTX4925_MPLEX_NAND - help - This enables the driver for the NAND flash device found on the - Toshiba RBTX4925 reference board, which is a SmartMediaCard. - -config MTD_NAND_TX4938NDFMC - tristate "NAND Flash device on Toshiba RBTX4938 reference board" - depends on TOSHIBA_RBTX4938 && MTD_NAND && TOSHIBA_RBTX4938_MPLEX_NAND - help - This enables the driver for the NAND flash device found on the - Toshiba RBTX4938 reference board. - config MTD_NAND_AU1550 tristate "Au1550 NAND support" depends on SOC_AU1550 && MTD_NAND @@ -95,10 +81,11 @@ config MTD_NAND_PPCHAMELEONEVB This enables the NAND flash driver on the PPChameleon EVB Board. config MTD_NAND_S3C2410 - tristate "NAND Flash support for S3C2410 SoC" + tristate "NAND Flash support for S3C2410/S3C2440 SoC" depends on ARCH_S3C2410 && MTD_NAND help - This enables the NAND flash controller on the S3C2410. + This enables the NAND flash controller on the S3C2410 and S3C2440 + SoCs No board specfic support is done by this driver, each board must advertise a platform_device for the driver to attach. diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index d9dc8cc..4174202 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -10,8 +10,6 @@ obj-$(CONFIG_MTD_NAND_SPIA) += spia.o obj-$(CONFIG_MTD_NAND_TOTO) += toto.o obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o -obj-$(CONFIG_MTD_NAND_TX4925NDFMC) += tx4925ndfmc.o -obj-$(CONFIG_MTD_NAND_TX4938NDFMC) += tx4938ndfmc.o obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 02135c3..fdb5d4a 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -16,7 +16,7 @@ * * Interface to generic NAND code for M-Systems DiskOnChip devices * - * $Id: diskonchip.c,v 1.45 2005/01/05 18:05:14 dwmw2 Exp $ + * $Id: diskonchip.c,v 1.54 2005/04/07 14:22:55 dbrown Exp $ */ #include <linux/kernel.h> @@ -35,13 +35,13 @@ #include <linux/mtd/inftl.h> /* Where to look for the devices? */ -#ifndef CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS -#define CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS 0 +#ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS +#define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0 #endif static unsigned long __initdata doc_locations[] = { #if defined (__alpha__) || defined(__i386__) || defined(__x86_64__) -#ifdef CONFIG_MTD_DISKONCHIP_PROBE_HIGH +#ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000, 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, @@ -81,11 +81,6 @@ struct doc_priv { struct mtd_info *nextdoc; }; -/* Max number of eraseblocks to scan (from start of device) for the (I)NFTL - MediaHeader. The spec says to just keep going, I think, but that's just - silly. */ -#define MAX_MEDIAHEADER_SCAN 8 - /* This is the syndrome computed by the HW ecc generator upon reading an empty page, one with all 0xff for data and stored ecc code. */ static u_char empty_read_syndrome[6] = { 0x26, 0xff, 0x6d, 0x47, 0x73, 0x7a }; @@ -111,10 +106,11 @@ module_param(try_dword, int, 0); static int no_ecc_failures=0; module_param(no_ecc_failures, int, 0); -#ifdef CONFIG_MTD_PARTITIONS static int no_autopart=0; module_param(no_autopart, int, 0); -#endif + +static int show_firmware_partition=0; +module_param(show_firmware_partition, int, 0); #ifdef MTD_NAND_DISKONCHIP_BBTWRITE static int inftl_bbt_write=1; @@ -123,7 +119,7 @@ static int inftl_bbt_write=0; #endif module_param(inftl_bbt_write, int, 0); -static unsigned long doc_config_location = CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS; +static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS; module_param(doc_config_location, ulong, 0); MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip"); @@ -410,7 +406,12 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) doc200x_hwcontrol(mtd, NAND_CTL_SETALE); this->write_byte(mtd, 0); doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); - + + /* We cant' use dev_ready here, but at least we wait for the + * command to complete + */ + udelay(50); + ret = this->read_byte(mtd) << 8; ret |= this->read_byte(mtd); @@ -429,6 +430,8 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) doc2000_write_byte(mtd, 0); doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); + udelay(50); + ident.dword = readl(docptr + DoC_2k_CDSN_IO); if (((ident.byte[0] << 8) | ident.byte[1]) == ret) { printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n"); @@ -1046,11 +1049,21 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ //u_char mydatabuf[528]; +/* The strange out-of-order .oobfree list below is a (possibly unneeded) + * attempt to retain compatibility. It used to read: + * .oobfree = { {8, 8} } + * Since that leaves two bytes unusable, it was changed. But the following + * scheme might affect existing jffs2 installs by moving the cleanmarker: + * .oobfree = { {6, 10} } + * jffs2 seems to handle the above gracefully, but the current scheme seems + * safer. The only problem with it is that any code that parses oobfree must + * be able to handle out-of-order segments. + */ static struct nand_oobinfo doc200x_oobinfo = { .useecc = MTD_NANDECC_AUTOPLACE, .eccbytes = 6, .eccpos = {0, 1, 2, 3, 4, 5}, - .oobfree = { {8, 8} } + .oobfree = { {8, 8}, {6, 2} } }; /* Find the (I)NFTL Media Header, and optionally also the mirror media header. @@ -1064,12 +1077,11 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, { struct nand_chip *this = mtd->priv; struct doc_priv *doc = this->priv; - unsigned offs, end = (MAX_MEDIAHEADER_SCAN << this->phys_erase_shift); + unsigned offs; int ret; size_t retlen; - end = min(end, mtd->size); // paranoia - for (offs = 0; offs < end; offs += mtd->erasesize) { + for (offs = 0; offs < mtd->size; offs += mtd->erasesize) { ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf); if (retlen != mtd->oobblock) continue; if (ret) { @@ -1111,6 +1123,7 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, u_char *buf; struct NFTLMediaHeader *mh; const unsigned psize = 1 << this->page_shift; + int numparts = 0; unsigned blocks, maxblocks; int offs, numheaders; @@ -1122,8 +1135,10 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, if (!(numheaders=find_media_headers(mtd, buf, "ANAND", 1))) goto out; mh = (struct NFTLMediaHeader *) buf; -//#ifdef CONFIG_MTD_DEBUG_VERBOSE -// if (CONFIG_MTD_DEBUG_VERBOSE >= 2) + mh->NumEraseUnits = le16_to_cpu(mh->NumEraseUnits); + mh->FirstPhysicalEUN = le16_to_cpu(mh->FirstPhysicalEUN); + mh->FormattedSize = le32_to_cpu(mh->FormattedSize); + printk(KERN_INFO " DataOrgID = %s\n" " NumEraseUnits = %d\n" " FirstPhysicalEUN = %d\n" @@ -1132,7 +1147,6 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, mh->DataOrgID, mh->NumEraseUnits, mh->FirstPhysicalEUN, mh->FormattedSize, mh->UnitSizeFactor); -//#endif blocks = mtd->size >> this->phys_erase_shift; maxblocks = min(32768U, mtd->erasesize - psize); @@ -1175,23 +1189,28 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, offs <<= this->page_shift; offs += mtd->erasesize; - //parts[0].name = " DiskOnChip Boot / Media Header partition"; - //parts[0].offset = 0; - //parts[0].size = offs; + if (show_firmware_partition == 1) { + parts[0].name = " DiskOnChip Firmware / Media Header partition"; + parts[0].offset = 0; + parts[0].size = offs; + numparts = 1; + } - parts[0].name = " DiskOnChip BDTL partition"; - parts[0].offset = offs; - parts[0].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift; + parts[numparts].name = " DiskOnChip BDTL partition"; + parts[numparts].offset = offs; + parts[numparts].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift; + + offs += parts[numparts].size; + numparts++; - offs += parts[0].size; if (offs < mtd->size) { - parts[1].name = " DiskOnChip Remainder partition"; - parts[1].offset = offs; - parts[1].size = mtd->size - offs; - ret = 2; - goto out; + parts[numparts].name = " DiskOnChip Remainder partition"; + parts[numparts].offset = offs; + parts[numparts].size = mtd->size - offs; + numparts++; } - ret = 1; + + ret = numparts; out: kfree(buf); return ret; @@ -1233,8 +1252,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, mh->FormatFlags = le32_to_cpu(mh->FormatFlags); mh->PercentUsed = le32_to_cpu(mh->PercentUsed); -//#ifdef CONFIG_MTD_DEBUG_VERBOSE -// if (CONFIG_MTD_DEBUG_VERBOSE >= 2) printk(KERN_INFO " bootRecordID = %s\n" " NoOfBootImageBlocks = %d\n" " NoOfBinaryPartitions = %d\n" @@ -1252,7 +1269,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, ((unsigned char *) &mh->OsakVersion)[2] & 0xf, ((unsigned char *) &mh->OsakVersion)[3] & 0xf, mh->PercentUsed); -//#endif vshift = this->phys_erase_shift + mh->BlockMultiplierBits; @@ -1278,8 +1294,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, ip->spareUnits = le32_to_cpu(ip->spareUnits); ip->Reserved0 = le32_to_cpu(ip->Reserved0); -//#ifdef CONFIG_MTD_DEBUG_VERBOSE -// if (CONFIG_MTD_DEBUG_VERBOSE >= 2) printk(KERN_INFO " PARTITION[%d] ->\n" " virtualUnits = %d\n" " firstUnit = %d\n" @@ -1289,16 +1303,14 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, i, ip->virtualUnits, ip->firstUnit, ip->lastUnit, ip->flags, ip->spareUnits); -//#endif -/* - if ((i == 0) && (ip->firstUnit > 0)) { + if ((show_firmware_partition == 1) && + (i == 0) && (ip->firstUnit > 0)) { parts[0].name = " DiskOnChip IPL / Media Header partition"; parts[0].offset = 0; parts[0].size = mtd->erasesize * ip->firstUnit; numparts = 1; } -*/ if (ip->flags & INFTL_BINARY) parts[numparts].name = " DiskOnChip BDK partition"; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 44d5b12..1bd71a5 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -28,6 +28,24 @@ * among multiple independend devices. Suggestions and initial patch * from Ben Dooks <ben-mtd@fluff.org> * + * 12-05-2004 dmarlin: add workaround for Renesas AG-AND chips "disturb" issue. + * Basically, any block not rewritten may lose data when surrounding blocks + * are rewritten many times. JFFS2 ensures this doesn't happen for blocks + * it uses, but the Bad Block Table(s) may not be rewritten. To ensure they + * do not lose data, force them to be rewritten when some of the surrounding + * blocks are erased. Rather than tracking a specific nearby block (which + * could itself go bad), use a page address 'mask' to select several blocks + * in the same area, and rewrite the BBT when any of them are erased. + * + * 01-03-2005 dmarlin: added support for the device recovery command sequence for Renesas + * AG-AND chips. If there was a sudden loss of power during an erase operation, + * a "device recovery" operation must be performed when power is restored + * to ensure correct operation. + * + * 01-20-2005 dmarlin: added support for optional hardware specific callback routine to + * perform extra error status checks on erase and write failures. This required + * adding a wrapper function for nand_read_ecc. + * * Credits: * David Woodhouse for adding multichip support * @@ -41,7 +59,7 @@ * The AG-AND chips have nice features for speed improvement, * which are not supported yet. Read / program 4 pages in one go. * - * $Id: nand_base.c,v 1.126 2004/12/13 11:22:25 lavinen Exp $ + * $Id: nand_base.c,v 1.146 2005/06/17 15:02:06 gleixner Exp $ * * 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 @@ -149,17 +167,21 @@ static void nand_release_device (struct mtd_info *mtd) /* De-select the NAND device */ this->select_chip(mtd, -1); - /* Do we have a hardware controller ? */ + if (this->controller) { + /* Release the controller and the chip */ spin_lock(&this->controller->lock); this->controller->active = NULL; + this->state = FL_READY; + wake_up(&this->controller->wq); spin_unlock(&this->controller->lock); + } else { + /* Release the chip */ + spin_lock(&this->chip_lock); + this->state = FL_READY; + wake_up(&this->wq); + spin_unlock(&this->chip_lock); } - /* Release the chip */ - spin_lock (&this->chip_lock); - this->state = FL_READY; - wake_up (&this->wq); - spin_unlock (&this->chip_lock); } /** @@ -443,7 +465,8 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) /* Get block number */ block = ((int) ofs) >> this->bbt_erase_shift; - this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); + if (this->bbt) + this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); /* Do we have a flash based bad block table ? */ if (this->options & NAND_USE_FLASH_BBT) @@ -466,7 +489,7 @@ static int nand_check_wp (struct mtd_info *mtd) struct nand_chip *this = mtd->priv; /* Check the WP bit */ this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); - return (this->read_byte(mtd) & 0x80) ? 0 : 1; + return (this->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; } /** @@ -490,6 +513,22 @@ static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, i return nand_isbad_bbt (mtd, ofs, allowbbt); } +/* + * Wait for the ready pin, after a command + * The timeout is catched later. + */ +static void nand_wait_ready(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + unsigned long timeo = jiffies + 2; + + /* wait until command is processed or timeout occures */ + do { + if (this->dev_ready(mtd)) + return; + } while (time_before(jiffies, timeo)); +} + /** * nand_command - [DEFAULT] Send command to NAND device * @mtd: MTD device structure @@ -571,7 +610,7 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in this->hwcontrol(mtd, NAND_CTL_SETCLE); this->write_byte(mtd, NAND_CMD_STATUS); this->hwcontrol(mtd, NAND_CTL_CLRCLE); - while ( !(this->read_byte(mtd) & 0x40)); + while ( !(this->read_byte(mtd) & NAND_STATUS_READY)); return; /* This applies to read commands */ @@ -585,12 +624,11 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in return; } } - /* Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */ ndelay (100); - /* wait until command is processed */ - while (!this->dev_ready(mtd)); + + nand_wait_ready(mtd); } /** @@ -619,7 +657,7 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, /* Begin command latch cycle */ this->hwcontrol(mtd, NAND_CTL_SETCLE); /* Write out the command to the device. */ - this->write_byte(mtd, command); + this->write_byte(mtd, (command & 0xff)); /* End command latch cycle */ this->hwcontrol(mtd, NAND_CTL_CLRCLE); @@ -647,8 +685,8 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, /* * program and erase have their own busy handlers - * status and sequential in needs no delay - */ + * status, sequential in, and deplete1 need no delay + */ switch (command) { case NAND_CMD_CACHEDPROG: @@ -657,8 +695,19 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, case NAND_CMD_ERASE2: case NAND_CMD_SEQIN: case NAND_CMD_STATUS: + case NAND_CMD_DEPLETE1: return; + /* + * read error status commands require only a short delay + */ + case NAND_CMD_STATUS_ERROR: + case NAND_CMD_STATUS_ERROR0: + case NAND_CMD_STATUS_ERROR1: + case NAND_CMD_STATUS_ERROR2: + case NAND_CMD_STATUS_ERROR3: + udelay(this->chip_delay); + return; case NAND_CMD_RESET: if (this->dev_ready) @@ -667,7 +716,7 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, this->hwcontrol(mtd, NAND_CTL_SETCLE); this->write_byte(mtd, NAND_CMD_STATUS); this->hwcontrol(mtd, NAND_CTL_CLRCLE); - while ( !(this->read_byte(mtd) & 0x40)); + while ( !(this->read_byte(mtd) & NAND_STATUS_READY)); return; case NAND_CMD_READ0: @@ -690,12 +739,12 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, return; } } - + /* Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */ ndelay (100); - /* wait until command is processed */ - while (!this->dev_ready(mtd)); + + nand_wait_ready(mtd); } /** @@ -708,37 +757,34 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, */ static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state) { - struct nand_chip *active = this; - + struct nand_chip *active; + spinlock_t *lock; + wait_queue_head_t *wq; DECLARE_WAITQUEUE (wait, current); - /* - * Grab the lock and see if the device is available - */ + lock = (this->controller) ? &this->controller->lock : &this->chip_lock; + wq = (this->controller) ? &this->controller->wq : &this->wq; retry: + active = this; + spin_lock(lock); + /* Hardware controller shared among independend devices */ if (this->controller) { - spin_lock (&this->controller->lock); if (this->controller->active) active = this->controller->active; else this->controller->active = this; - spin_unlock (&this->controller->lock); } - - if (active == this) { - spin_lock (&this->chip_lock); - if (this->state == FL_READY) { - this->state = new_state; - spin_unlock (&this->chip_lock); - return; - } - } - set_current_state (TASK_UNINTERRUPTIBLE); - add_wait_queue (&active->wq, &wait); - spin_unlock (&active->chip_lock); - schedule (); - remove_wait_queue (&active->wq, &wait); + if (active == this && this->state == FL_READY) { + this->state = new_state; + spin_unlock(lock); + return; + } + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(wq, &wait); + spin_unlock(lock); + schedule(); + remove_wait_queue(wq, &wait); goto retry; } @@ -785,7 +831,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) if (this->read_byte(mtd) & NAND_STATUS_READY) break; } - yield (); + cond_resched(); } status = (int) this->read_byte(mtd); return status; @@ -871,8 +917,14 @@ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int pa if (!cached) { /* call wait ready function */ status = this->waitfunc (mtd, this, FL_WRITING); + + /* See if operation failed and additional status checks are available */ + if ((status & NAND_STATUS_FAIL) && (this->errstat)) { + status = this->errstat(mtd, this, FL_WRITING, status, page); + } + /* See if device thinks it succeeded */ - if (status & 0x01) { + if (status & NAND_STATUS_FAIL) { DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page); return -EIO; } @@ -975,7 +1027,7 @@ static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int if (!this->dev_ready) udelay (this->chip_delay); else - while (!this->dev_ready(mtd)); + nand_wait_ready(mtd); /* All done, return happy */ if (!numpages) @@ -997,23 +1049,24 @@ out: #endif /** - * nand_read - [MTD Interface] MTD compability function for nand_read_ecc + * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc * @mtd: MTD device structure * @from: offset to read from * @len: number of bytes to read * @retlen: pointer to variable to store the number of read bytes * @buf: the databuffer to put data * - * This function simply calls nand_read_ecc with oob buffer and oobsel = NULL -*/ + * This function simply calls nand_do_read_ecc with oob buffer and oobsel = NULL + * and flags = 0xff + */ static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) { - return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL); -} + return nand_do_read_ecc (mtd, from, len, retlen, buf, NULL, &mtd->oobinfo, 0xff); +} /** - * nand_read_ecc - [MTD Interface] Read data with ECC + * nand_read_ecc - [MTD Interface] MTD compability function for nand_do_read_ecc * @mtd: MTD device structure * @from: offset to read from * @len: number of bytes to read @@ -1022,11 +1075,39 @@ static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * re * @oob_buf: filesystem supplied oob data buffer * @oobsel: oob selection structure * - * NAND read with ECC + * This function simply calls nand_do_read_ecc with flags = 0xff */ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel) { + /* use userspace supplied oobinfo, if zero */ + if (oobsel == NULL) + oobsel = &mtd->oobinfo; + return nand_do_read_ecc(mtd, from, len, retlen, buf, oob_buf, oobsel, 0xff); +} + + +/** + * nand_do_read_ecc - [MTD Interface] Read data with ECC + * @mtd: MTD device structure + * @from: offset to read from + * @len: number of bytes to read + * @retlen: pointer to variable to store the number of read bytes + * @buf: the databuffer to put data + * @oob_buf: filesystem supplied oob data buffer (can be NULL) + * @oobsel: oob selection structure + * @flags: flag to indicate if nand_get_device/nand_release_device should be preformed + * and how many corrected error bits are acceptable: + * bits 0..7 - number of tolerable errors + * bit 8 - 0 == do not get/release chip, 1 == get/release chip + * + * NAND read with ECC + */ +int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, + size_t * retlen, u_char * buf, u_char * oob_buf, + struct nand_oobinfo *oobsel, int flags) +{ + int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1; int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0; struct nand_chip *this = mtd->priv; @@ -1051,12 +1132,9 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, } /* Grab the lock and see if the device is available */ - nand_get_device (this, mtd ,FL_READING); + if (flags & NAND_GET_DEVICE) + nand_get_device (this, mtd, FL_READING); - /* use userspace supplied oobinfo, if zero */ - if (oobsel == NULL) - oobsel = &mtd->oobinfo; - /* Autoplace of oob data ? Use the default placement scheme */ if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) oobsel = this->autooob; @@ -1118,7 +1196,8 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, } /* get oob area, if we have no oob buffer from fs-driver */ - if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE) + if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE || + oobsel->useecc == MTD_NANDECC_AUTOPL_USR) oob_data = &this->data_buf[end]; eccsteps = this->eccsteps; @@ -1155,7 +1234,8 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, /* We calc error correction directly, it checks the hw * generator for an error, reads back the syndrome and * does the error correction on the fly */ - if (this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]) == -1) { + ecc_status = this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]); + if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr); ecc_failed++; @@ -1194,7 +1274,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, p[i] = ecc_status; } - if (ecc_status == -1) { + if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) { DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page); ecc_failed++; } @@ -1206,14 +1286,14 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, /* without autoplace. Legacy mode used by YAFFS1 */ switch(oobsel->useecc) { case MTD_NANDECC_AUTOPLACE: + case MTD_NANDECC_AUTOPL_USR: /* Walk through the autoplace chunks */ - for (i = 0, j = 0; j < mtd->oobavail; i++) { + for (i = 0; oobsel->oobfree[i][1]; i++) { int from = oobsel->oobfree[i][0]; int num = oobsel->oobfree[i][1]; memcpy(&oob_buf[oob], &oob_data[from], num); - j+= num; + oob += num; } - oob += mtd->oobavail; break; case MTD_NANDECC_PLACE: /* YAFFS1 legacy mode */ @@ -1239,7 +1319,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, if (!this->dev_ready) udelay (this->chip_delay); else - while (!this->dev_ready(mtd)); + nand_wait_ready(mtd); if (read == len) break; @@ -1264,7 +1344,8 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, } /* Deselect and wake up anyone waiting on the device */ - nand_release_device(mtd); + if (flags & NAND_GET_DEVICE) + nand_release_device(mtd); /* * Return success, if no ECC failures, else -EBADMSG @@ -1337,7 +1418,7 @@ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t if (!this->dev_ready) udelay (this->chip_delay); else - while (!this->dev_ready(mtd)); + nand_wait_ready(mtd); /* Read more ? */ if (i < len) { @@ -1417,7 +1498,7 @@ int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, if (!this->dev_ready) udelay (this->chip_delay); else - while (!this->dev_ready(mtd)); + nand_wait_ready(mtd); /* Check, if the chip supports auto page increment */ if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) @@ -1567,6 +1648,8 @@ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, oobsel = this->autooob; autoplace = 1; } + if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR) + autoplace = 1; /* Setup variables and oob buffer */ totalpages = len >> this->page_shift; @@ -1733,7 +1816,7 @@ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * status = this->waitfunc (mtd, this, FL_WRITING); /* See if device thinks it succeeded */ - if (status & 0x01) { + if (status & NAND_STATUS_FAIL) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page); ret = -EIO; goto out; @@ -1841,6 +1924,8 @@ static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsig oobsel = this->autooob; autoplace = 1; } + if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR) + autoplace = 1; /* Setup start page */ page = (int) (to >> this->page_shift); @@ -1987,6 +2072,7 @@ static int nand_erase (struct mtd_info *mtd, struct erase_info *instr) return nand_erase_nand (mtd, instr, 0); } +#define BBT_PAGE_MASK 0xffffff3f /** * nand_erase_intern - [NAND Interface] erase block(s) * @mtd: MTD device structure @@ -1999,6 +2085,10 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb { int page, len, status, pages_per_block, ret, chipnr; struct nand_chip *this = mtd->priv; + int rewrite_bbt[NAND_MAX_CHIPS]={0}; /* flags to indicate the page, if bbt needs to be rewritten. */ + unsigned int bbt_masked_page; /* bbt mask to compare to page being erased. */ + /* It is used to see if the current page is in the same */ + /* 256 block group and the same bank as the bbt. */ DEBUG (MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); @@ -2044,6 +2134,13 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb goto erase_exit; } + /* if BBT requires refresh, set the BBT page mask to see if the BBT should be rewritten */ + if (this->options & BBT_AUTO_REFRESH) { + bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK; + } else { + bbt_masked_page = 0xffffffff; /* should not match anything */ + } + /* Loop through the pages */ len = instr->len; @@ -2066,13 +2163,26 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb status = this->waitfunc (mtd, this, FL_ERASING); + /* See if operation failed and additional status checks are available */ + if ((status & NAND_STATUS_FAIL) && (this->errstat)) { + status = this->errstat(mtd, this, FL_ERASING, status, page); + } + /* See if block erase succeeded */ - if (status & 0x01) { + if (status & NAND_STATUS_FAIL) { DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); instr->state = MTD_ERASE_FAILED; instr->fail_addr = (page << this->page_shift); goto erase_exit; } + + /* if BBT requires refresh, set the BBT rewrite flag to the page being erased */ + if (this->options & BBT_AUTO_REFRESH) { + if (((page & BBT_PAGE_MASK) == bbt_masked_page) && + (page != this->bbt_td->pages[chipnr])) { + rewrite_bbt[chipnr] = (page << this->page_shift); + } + } /* Increment page address and decrement length */ len -= (1 << this->phys_erase_shift); @@ -2083,6 +2193,13 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb chipnr++; this->select_chip(mtd, -1); this->select_chip(mtd, chipnr); + + /* if BBT requires refresh and BBT-PERCHIP, + * set the BBT page mask to see if this BBT should be rewritten */ + if ((this->options & BBT_AUTO_REFRESH) && (this->bbt_td->options & NAND_BBT_PERCHIP)) { + bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK; + } + } } instr->state = MTD_ERASE_DONE; @@ -2097,6 +2214,18 @@ erase_exit: /* Deselect and wake up anyone waiting on the device */ nand_release_device(mtd); + /* if BBT requires refresh and erase was successful, rewrite any selected bad block tables */ + if ((this->options & BBT_AUTO_REFRESH) && (!ret)) { + for (chipnr = 0; chipnr < this->numchips; chipnr++) { + if (rewrite_bbt[chipnr]) { + /* update the BBT for chip */ + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt (%d:0x%0x 0x%0x)\n", + chipnr, rewrite_bbt[chipnr], this->bbt_td->pages[chipnr]); + nand_update_bbt (mtd, rewrite_bbt[chipnr]); + } + } + } + /* Return more or less happy */ return ret; } @@ -2168,7 +2297,7 @@ static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs) */ int nand_scan (struct mtd_info *mtd, int maxchips) { - int i, j, nand_maf_id, nand_dev_id, busw; + int i, nand_maf_id, nand_dev_id, busw, maf_id; struct nand_chip *this = mtd->priv; /* Get buswidth to select the correct functions*/ @@ -2256,12 +2385,18 @@ int nand_scan (struct mtd_info *mtd, int maxchips) busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16; } + /* Try to identify manufacturer */ + for (maf_id = 0; nand_manuf_ids[maf_id].id != 0x0; maf_id++) { + if (nand_manuf_ids[maf_id].id == nand_maf_id) + break; + } + /* Check, if buswidth is correct. Hardware drivers should set * this correct ! */ if (busw != (this->options & NAND_BUSWIDTH_16)) { printk (KERN_INFO "NAND device: Manufacturer ID:" " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, - nand_manuf_ids[i].name , mtd->name); + nand_manuf_ids[maf_id].name , mtd->name); printk (KERN_WARNING "NAND bus width %d instead %d bit\n", (this->options & NAND_BUSWIDTH_16) ? 16 : 8, @@ -2300,14 +2435,9 @@ int nand_scan (struct mtd_info *mtd, int maxchips) if (mtd->oobblock > 512 && this->cmdfunc == nand_command) this->cmdfunc = nand_command_lp; - /* Try to identify manufacturer */ - for (j = 0; nand_manuf_ids[j].id != 0x0; j++) { - if (nand_manuf_ids[j].id == nand_maf_id) - break; - } printk (KERN_INFO "NAND device: Manufacturer ID:" " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, - nand_manuf_ids[j].name , nand_flash_ids[i].name); + nand_manuf_ids[maf_id].name , nand_flash_ids[i].name); break; } @@ -2388,12 +2518,9 @@ int nand_scan (struct mtd_info *mtd, int maxchips) /* The number of bytes available for the filesystem to place fs dependend * oob data */ - if (this->options & NAND_BUSWIDTH_16) { - mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 2); - if (this->autooob->eccbytes & 0x01) - mtd->oobavail--; - } else - mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 1); + mtd->oobavail = 0; + for (i = 0; this->autooob->oobfree[i][1]; i++) + mtd->oobavail += this->autooob->oobfree[i][1]; /* * check ECC mode, default to software @@ -2524,6 +2651,10 @@ int nand_scan (struct mtd_info *mtd, int maxchips) memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo)); mtd->owner = THIS_MODULE; + + /* Check, if we should skip the bad block table scan */ + if (this->options & NAND_SKIP_BBTSCAN) + return 0; /* Build bad block table */ return this->scan_bbt (mtd); @@ -2555,8 +2686,8 @@ void nand_release (struct mtd_info *mtd) kfree (this->data_buf); } -EXPORT_SYMBOL (nand_scan); -EXPORT_SYMBOL (nand_release); +EXPORT_SYMBOL_GPL (nand_scan); +EXPORT_SYMBOL_GPL (nand_release); MODULE_LICENSE ("GPL"); MODULE_AUTHOR ("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>"); diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 9a19497..5ac2d29 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -6,7 +6,7 @@ * * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) * - * $Id: nand_bbt.c,v 1.28 2004/11/13 10:19:09 gleixner Exp $ + * $Id: nand_bbt.c,v 1.33 2005/06/14 15:47:56 gleixner Exp $ * * 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 @@ -77,7 +77,7 @@ */ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) { - int i, end; + int i, end = 0; uint8_t *p = buf; end = paglen + td->offs; @@ -95,9 +95,9 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des return -1; } - p += td->len; - end += td->len; if (td->options & NAND_BBT_SCANEMPTY) { + p += td->len; + end += td->len; for (i = end; i < len; i++) { if (*p++ != 0xff) return -1; @@ -106,6 +106,32 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des return 0; } +/** + * check_short_pattern - [GENERIC] check if a pattern is in the buffer + * @buf: the buffer to search + * @len: the length of buffer to search + * @paglen: the pagelength + * @td: search pattern descriptor + * + * Check for a pattern at the given place. Used to search bad block + * tables and good / bad block identifiers. Same as check_pattern, but + * no optional empty check and the pattern is expected to start + * at offset 0. + * +*/ +static int check_short_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) +{ + int i; + uint8_t *p = buf; + + /* Compare the pattern */ + for (i = 0; i < td->len; i++) { + if (p[i] != td->pattern[i]) + return -1; + } + return 0; +} + /** * read_bbt - [GENERIC] Read the bad block table starting from page * @mtd: MTD device structure @@ -252,7 +278,7 @@ static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_de * Create a bad block table by scanning the device * for the given good/bad block identify pattern */ -static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) +static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) { struct nand_chip *this = mtd->priv; int i, j, numblocks, len, scanlen; @@ -270,9 +296,17 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc else len = 1; } - scanlen = mtd->oobblock + mtd->oobsize; - readlen = len * mtd->oobblock; - ooblen = len * mtd->oobsize; + + if (!(bd->options & NAND_BBT_SCANEMPTY)) { + /* We need only read few bytes from the OOB area */ + scanlen = ooblen = 0; + readlen = bd->len; + } else { + /* Full page content should be read */ + scanlen = mtd->oobblock + mtd->oobsize; + readlen = len * mtd->oobblock; + ooblen = len * mtd->oobsize; + } if (chip == -1) { /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it @@ -284,7 +318,7 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc if (chip >= this->numchips) { printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", chip + 1, this->numchips); - return; + return -EINVAL; } numblocks = this->chipsize >> (this->bbt_erase_shift - 1); startblock = chip * numblocks; @@ -293,18 +327,41 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc } for (i = startblock; i < numblocks;) { - nand_read_raw (mtd, buf, from, readlen, ooblen); + int ret; + + if (bd->options & NAND_BBT_SCANEMPTY) + if ((ret = nand_read_raw (mtd, buf, from, readlen, ooblen))) + return ret; + for (j = 0; j < len; j++) { - if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { - this->bbt[i >> 3] |= 0x03 << (i & 0x6); - printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n", - i >> 1, (unsigned int) from); - break; + if (!(bd->options & NAND_BBT_SCANEMPTY)) { + size_t retlen; + + /* No need to read pages fully, just read required OOB bytes */ + ret = mtd->read_oob(mtd, from + j * mtd->oobblock + bd->offs, + readlen, &retlen, &buf[0]); + if (ret) + return ret; + + if (check_short_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { + this->bbt[i >> 3] |= 0x03 << (i & 0x6); + printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n", + i >> 1, (unsigned int) from); + break; + } + } else { + if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { + this->bbt[i >> 3] |= 0x03 << (i & 0x6); + printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n", + i >> 1, (unsigned int) from); + break; + } } } i += 2; from += (1 << this->bbt_erase_shift); } + return 0; } /** @@ -589,14 +646,12 @@ write: * The function creates a memory based bbt by scanning the device * for manufacturer / software marked good / bad blocks */ -static int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) +static inline int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) { struct nand_chip *this = mtd->priv; - /* Ensure that we only scan for the pattern and nothing else */ - bd->options = 0; - create_bbt (mtd, this->data_buf, bd, -1); - return 0; + bd->options &= ~NAND_BBT_SCANEMPTY; + return create_bbt (mtd, this->data_buf, bd, -1); } /** @@ -808,8 +863,14 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) /* If no primary table decriptor is given, scan the device * to build a memory based bad block table */ - if (!td) - return nand_memory_bbt(mtd, bd); + if (!td) { + if ((res = nand_memory_bbt(mtd, bd))) { + printk (KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n"); + kfree (this->bbt); + this->bbt = NULL; + } + return res; + } /* Allocate a temporary buffer for one eraseblock incl. oob */ len = (1 << this->bbt_erase_shift); @@ -904,14 +965,11 @@ out: } /* Define some generic bad / good block scan pattern which are used - * while scanning a device for factory marked good / bad blocks - * - * The memory based patterns just - */ + * while scanning a device for factory marked good / bad blocks. */ static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; static struct nand_bbt_descr smallpage_memorybased = { - .options = 0, + .options = NAND_BBT_SCAN2NDPAGE, .offs = 5, .len = 1, .pattern = scan_ff_pattern @@ -1042,7 +1100,7 @@ int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt) res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03; DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", - (unsigned int)offs, res, block >> 1); + (unsigned int)offs, block >> 1, res); switch ((int)res) { case 0x00: return 0; diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 2d8c432..efe2469 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -2,8 +2,8 @@ * drivers/mtd/nandids.c * * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) - * - * $Id: nand_ids.c,v 1.10 2004/05/26 13:40:12 gleixner Exp $ + * + * $Id: nand_ids.c,v 1.14 2005/06/23 09:38:50 gleixner Exp $ * * 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 @@ -56,17 +56,24 @@ struct nand_flash_dev nand_flash_ids[] = { {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0}, + {"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0}, {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0}, {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16}, {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, + {"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16}, {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0}, - {"NAND 512MiB 3,3V 8-bit", 0xDC, 512, 512, 0x4000, 0}, - /* These are the new chips with large page size. The pagesize * and the erasesize is determined from the extended id bytes */ + /*512 Megabit */ + {"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, + {"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + {"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, + /* 1 Gigabit */ {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, @@ -103,7 +110,7 @@ struct nand_flash_dev nand_flash_ids[] = { * Anyway JFFS2 would increase the eraseblock size so we chose a combined one which can be erased in one go * There are more speed improvements for reads and writes possible, but not implemented now */ - {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY}, + {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH}, {NULL,} }; @@ -118,6 +125,7 @@ struct nand_manufacturers nand_manuf_ids[] = { {NAND_MFR_NATIONAL, "National"}, {NAND_MFR_RENESAS, "Renesas"}, {NAND_MFR_STMICRO, "ST Micro"}, + {NAND_MFR_HYNIX, "Hynix"}, {0x0, "Unknown"} }; diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 13feefd..754b6ed 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -22,7 +22,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA * - * $Id: nandsim.c,v 1.7 2004/12/06 11:53:06 dedekind Exp $ + * $Id: nandsim.c,v 1.8 2005/03/19 15:33:56 dedekind Exp $ */ #include <linux/config.h> @@ -1484,33 +1484,6 @@ ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) } /* - * Having only NAND chip IDs we call nand_scan which detects NAND flash - * parameters and then calls scan_bbt in order to scan/find/build the - * NAND flash bad block table. But since at that moment the NAND flash - * image isn't allocated in the simulator, errors arise. To avoid this - * we redefine the scan_bbt callback and initialize the nandsim structure - * before the flash media scanning. - */ -int ns_scan_bbt(struct mtd_info *mtd) -{ - struct nand_chip *chip = (struct nand_chip *)mtd->priv; - struct nandsim *ns = (struct nandsim *)(chip->priv); - int retval; - - if (!NS_IS_INITIALIZED(ns)) - if ((retval = init_nandsim(mtd)) != 0) { - NS_ERR("scan_bbt: can't initialize the nandsim structure\n"); - return retval; - } - if ((retval = nand_default_bbt(mtd)) != 0) { - free_nandsim(ns); - return retval; - } - - return 0; -} - -/* * Module initialization function */ int __init ns_init_module(void) @@ -1544,7 +1517,6 @@ int __init ns_init_module(void) chip->hwcontrol = ns_hwcontrol; chip->read_byte = ns_nand_read_byte; chip->dev_ready = ns_device_ready; - chip->scan_bbt = ns_scan_bbt; chip->write_byte = ns_nand_write_byte; chip->write_buf = ns_nand_write_buf; chip->read_buf = ns_nand_read_buf; @@ -1552,6 +1524,7 @@ int __init ns_init_module(void) chip->write_word = ns_nand_write_word; chip->read_word = ns_nand_read_word; chip->eccmode = NAND_ECC_SOFT; + chip->options |= NAND_SKIP_BBTSCAN; /* * Perform minimum nandsim structure initialization to handle @@ -1580,6 +1553,16 @@ int __init ns_init_module(void) goto error; } + if ((retval = init_nandsim(nsmtd)) != 0) { + NS_ERR("scan_bbt: can't initialize the nandsim structure\n"); + goto error; + } + + if ((retval = nand_default_bbt(nsmtd)) != 0) { + free_nandsim(nand); + goto error; + } + /* Register NAND as one big partition */ add_mtd_partitions(nsmtd, &nand->part, 1); diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index 02305a2..031051c 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -6,7 +6,7 @@ * Derived from drivers/mtd/nand/spia.c * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * - * $Id: rtc_from4.c,v 1.7 2004/11/04 12:53:10 gleixner Exp $ + * $Id: rtc_from4.c,v 1.9 2005/01/24 20:40:11 dmarlin Exp $ * * 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 @@ -83,13 +83,18 @@ static struct mtd_info *rtc_from4_mtd = NULL; #define RTC_FROM4_RS_ECC_CHK (RTC_FROM4_NAND_ADDR_FPGA | 0x00000070) #define RTC_FROM4_RS_ECC_CHK_ERROR (1 << 7) +#define ERR_STAT_ECC_AVAILABLE 0x20 + /* Undefine for software ECC */ #define RTC_FROM4_HWECC 1 +/* Define as 1 for no virtual erase blocks (in JFFS2) */ +#define RTC_FROM4_NO_VIRTBLOCKS 0 + /* * Module stuff */ -static void __iomem *rtc_from4_fio_base = P2SEGADDR(RTC_FROM4_FIO_BASE); +static void __iomem *rtc_from4_fio_base = (void *)P2SEGADDR(RTC_FROM4_FIO_BASE); const static struct mtd_partition partition_info[] = { { @@ -267,7 +272,6 @@ static void rtc_from4_nand_select_chip(struct mtd_info *mtd, int chip) } - /* * rtc_from4_nand_device_ready - hardware specific ready/busy check * @mtd: MTD device structure @@ -286,6 +290,40 @@ static int rtc_from4_nand_device_ready(struct mtd_info *mtd) } + +/* + * deplete - code to perform device recovery in case there was a power loss + * @mtd: MTD device structure + * @chip: Chip to select (0 == slot 3, 1 == slot 4) + * + * If there was a sudden loss of power during an erase operation, a + * "device recovery" operation must be performed when power is restored + * to ensure correct operation. This routine performs the required steps + * for the requested chip. + * + * See page 86 of the data sheet for details. + * + */ +static void deplete(struct mtd_info *mtd, int chip) +{ + struct nand_chip *this = mtd->priv; + + /* wait until device is ready */ + while (!this->dev_ready(mtd)); + + this->select_chip(mtd, chip); + + /* Send the commands for device recovery, phase 1 */ + this->cmdfunc (mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0000); + this->cmdfunc (mtd, NAND_CMD_DEPLETE2, -1, -1); + + /* Send the commands for device recovery, phase 2 */ + this->cmdfunc (mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0004); + this->cmdfunc (mtd, NAND_CMD_DEPLETE2, -1, -1); + +} + + #ifdef RTC_FROM4_HWECC /* * rtc_from4_enable_hwecc - hardware specific hardware ECC enable function @@ -329,6 +367,7 @@ static void rtc_from4_enable_hwecc(struct mtd_info *mtd, int mode) } + /* * rtc_from4_calculate_ecc - hardware specific code to read ECC code * @mtd: MTD device structure @@ -356,6 +395,7 @@ static void rtc_from4_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_c ecc_code[7] |= 0x0f; /* set the last four bits (not used) */ } + /* * rtc_from4_correct_data - hardware specific code to correct data using ECC code * @mtd: MTD device structure @@ -365,16 +405,14 @@ static void rtc_from4_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_c * * The FPGA tells us fast, if there's an error or not. If no, we go back happy * else we read the ecc results from the fpga and call the rs library to decode - * and hopefully correct the error + * and hopefully correct the error. * - * For now I use the code, which we read from the FLASH to use the RS lib, - * as the syndrom conversion has a unresolved issue. */ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_char *ecc1, u_char *ecc2) { int i, j, res; unsigned short status; - uint16_t par[6], syn[6], tmp; + uint16_t par[6], syn[6]; uint8_t ecc[8]; volatile unsigned short *rs_ecc; @@ -416,15 +454,86 @@ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_cha } /* Let the library code do its magic.*/ - res = decode_rs8(rs_decoder, buf, par, 512, syn, 0, NULL, 0xff, NULL); + res = decode_rs8(rs_decoder, (uint8_t *)buf, par, 512, syn, 0, NULL, 0xff, NULL); if (res > 0) { DEBUG (MTD_DEBUG_LEVEL0, "rtc_from4_correct_data: " "ECC corrected %d errors on read\n", res); } return res; } + + +/** + * rtc_from4_errstat - perform additional error status checks + * @mtd: MTD device structure + * @this: NAND chip structure + * @state: state or the operation + * @status: status code returned from read status + * @page: startpage inside the chip, must be called with (page & this->pagemask) + * + * Perform additional error status checks on erase and write failures + * to determine if errors are correctable. For this device, correctable + * 1-bit errors on erase and write are considered acceptable. + * + * note: see pages 34..37 of data sheet for details. + * + */ +static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page) +{ + int er_stat=0; + int rtn, retlen; + size_t len; + uint8_t *buf; + int i; + + this->cmdfunc (mtd, NAND_CMD_STATUS_CLEAR, -1, -1); + + if (state == FL_ERASING) { + for (i=0; i<4; i++) { + if (status & 1<<(i+1)) { + this->cmdfunc (mtd, (NAND_CMD_STATUS_ERROR + i + 1), -1, -1); + rtn = this->read_byte(mtd); + this->cmdfunc (mtd, NAND_CMD_STATUS_RESET, -1, -1); + if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { + er_stat |= 1<<(i+1); /* err_ecc_not_avail */ + } + } + } + } else if (state == FL_WRITING) { + /* single bank write logic */ + this->cmdfunc (mtd, NAND_CMD_STATUS_ERROR, -1, -1); + rtn = this->read_byte(mtd); + this->cmdfunc (mtd, NAND_CMD_STATUS_RESET, -1, -1); + if (!(rtn & ERR_STAT_ECC_AVAILABLE)) { + er_stat |= 1<<1; /* err_ecc_not_avail */ + } else { + len = mtd->oobblock; + buf = kmalloc (len, GFP_KERNEL); + if (!buf) { + printk (KERN_ERR "rtc_from4_errstat: Out of memory!\n"); + er_stat = 1; /* if we can't check, assume failed */ + } else { + /* recovery read */ + /* page read */ + rtn = nand_do_read_ecc (mtd, page, len, &retlen, buf, NULL, this->autooob, 1); + if (rtn) { /* if read failed or > 1-bit error corrected */ + er_stat |= 1<<1; /* ECC read failed */ + } + kfree(buf); + } + } + } + + rtn = status; + if (er_stat == 0) { /* if ECC is available */ + rtn = (status & ~NAND_STATUS_FAIL); /* clear the error bit */ + } + + return rtn; +} #endif + /* * Main initialization routine */ @@ -432,6 +541,7 @@ int __init rtc_from4_init (void) { struct nand_chip *this; unsigned short bcr1, bcr2, wcr2; + int i; /* Allocate memory for MTD device structure and private data */ rtc_from4_mtd = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip), @@ -483,6 +593,8 @@ int __init rtc_from4_init (void) this->eccmode = NAND_ECC_HW8_512; this->options |= NAND_HWECC_SYNDROME; + /* return the status of extra status and ECC checks */ + this->errstat = rtc_from4_errstat; /* set the nand_oobinfo to support FPGA H/W error detection */ this->autooob = &rtc_from4_nand_oobinfo; this->enable_hwecc = rtc_from4_enable_hwecc; @@ -504,6 +616,18 @@ int __init rtc_from4_init (void) return -ENXIO; } + /* Perform 'device recovery' for each chip in case there was a power loss. */ + for (i=0; i < this->numchips; i++) { + deplete(rtc_from4_mtd, i); + } + +#if RTC_FROM4_NO_VIRTBLOCKS + /* use a smaller erase block to minimize wasted space when a block is bad */ + /* note: this uses eight times as much RAM as using the default and makes */ + /* mounts take four times as long. */ + rtc_from4_mtd->flags |= MTD_NO_VIRTBLOCKS; +#endif + /* Register the partitions */ add_mtd_partitions(rtc_from4_mtd, partition_info, NUM_PARTITIONS); diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index d05e9b9..891e3a1 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -1,17 +1,24 @@ /* linux/drivers/mtd/nand/s3c2410.c * - * Copyright (c) 2004 Simtec Electronics - * Ben Dooks <ben@simtec.co.uk> + * Copyright (c) 2004,2005 Simtec Electronics + * http://www.simtec.co.uk/products/SWLINUX/ + * Ben Dooks <ben@simtec.co.uk> * - * Samsung S3C2410 NAND driver + * Samsung S3C2410/S3C240 NAND driver * * Changelog: * 21-Sep-2004 BJD Initial version * 23-Sep-2004 BJD Mulitple device support * 28-Sep-2004 BJD Fixed ECC placement for Hardware mode * 12-Oct-2004 BJD Fixed errors in use of platform data + * 18-Feb-2005 BJD Fix sparse errors + * 14-Mar-2005 BJD Applied tglx's code reduction patch + * 02-May-2005 BJD Fixed s3c2440 support + * 02-May-2005 BJD Reduced hwcontrol decode + * 20-Jun-2005 BJD Updated s3c2440 support, fixed timing bug + * 08-Jul-2005 BJD Fix OOPS when no platform data supplied * - * $Id: s3c2410.c,v 1.7 2005/01/05 18:05:14 dwmw2 Exp $ + * $Id: s3c2410.c,v 1.14 2005/07/06 20:05:06 bjd Exp $ * * 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 @@ -69,10 +76,10 @@ static int hardware_ecc = 0; */ static struct nand_oobinfo nand_hw_eccoob = { - .useecc = MTD_NANDECC_AUTOPLACE, - .eccbytes = 3, - .eccpos = {0, 1, 2 }, - .oobfree = { {8, 8} } + .useecc = MTD_NANDECC_AUTOPLACE, + .eccbytes = 3, + .eccpos = {0, 1, 2 }, + .oobfree = { {8, 8} } }; /* controller and mtd information */ @@ -99,8 +106,10 @@ struct s3c2410_nand_info { struct device *device; struct resource *area; struct clk *clk; - void *regs; + void __iomem *regs; int mtd_count; + + unsigned char is_s3c2440; }; /* conversion functions */ @@ -165,12 +174,12 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, /* calculate the timing information for the controller */ if (plat != NULL) { - tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 8); + tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 4); twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8); twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8); } else { /* default timings */ - tacls = 8; + tacls = 4; twrph0 = 8; twrph1 = 8; } @@ -185,10 +194,16 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, to_ns(twrph0, clkrate), to_ns(twrph1, clkrate)); - cfg = S3C2410_NFCONF_EN; - cfg |= S3C2410_NFCONF_TACLS(tacls-1); - cfg |= S3C2410_NFCONF_TWRPH0(twrph0-1); - cfg |= S3C2410_NFCONF_TWRPH1(twrph1-1); + if (!info->is_s3c2440) { + cfg = S3C2410_NFCONF_EN; + cfg |= S3C2410_NFCONF_TACLS(tacls-1); + cfg |= S3C2410_NFCONF_TWRPH0(twrph0-1); + cfg |= S3C2410_NFCONF_TWRPH1(twrph1-1); + } else { + cfg = S3C2440_NFCONF_TACLS(tacls-1); + cfg |= S3C2440_NFCONF_TWRPH0(twrph0-1); + cfg |= S3C2440_NFCONF_TWRPH1(twrph1-1); + } pr_debug(PFX "NF_CONF is 0x%lx\n", cfg); @@ -203,17 +218,22 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) struct s3c2410_nand_info *info; struct s3c2410_nand_mtd *nmtd; struct nand_chip *this = mtd->priv; + void __iomem *reg; unsigned long cur; + unsigned long bit; nmtd = this->priv; info = nmtd->info; - cur = readl(info->regs + S3C2410_NFCONF); + bit = (info->is_s3c2440) ? S3C2440_NFCONT_nFCE : S3C2410_NFCONF_nFCE; + reg = info->regs+((info->is_s3c2440) ? S3C2440_NFCONT:S3C2410_NFCONF); + + cur = readl(reg); if (chip == -1) { - cur |= S3C2410_NFCONF_nFCE; + cur |= bit; } else { - if (chip > nmtd->set->nr_chips) { + if (nmtd->set != NULL && chip > nmtd->set->nr_chips) { printk(KERN_ERR PFX "chip %d out of range\n", chip); return; } @@ -223,143 +243,76 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) (info->platform->select_chip)(nmtd->set, chip); } - cur &= ~S3C2410_NFCONF_nFCE; + cur &= ~bit; } - writel(cur, info->regs + S3C2410_NFCONF); + writel(cur, reg); } -/* command and control functions */ +/* command and control functions + * + * Note, these all use tglx's method of changing the IO_ADDR_W field + * to make the code simpler, and use the nand layer's code to issue the + * command and address sequences via the proper IO ports. + * +*/ static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd) { struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - unsigned long cur; + struct nand_chip *chip = mtd->priv; switch (cmd) { case NAND_CTL_SETNCE: - cur = readl(info->regs + S3C2410_NFCONF); - cur &= ~S3C2410_NFCONF_nFCE; - writel(cur, info->regs + S3C2410_NFCONF); - break; - case NAND_CTL_CLRNCE: - cur = readl(info->regs + S3C2410_NFCONF); - cur |= S3C2410_NFCONF_nFCE; - writel(cur, info->regs + S3C2410_NFCONF); + printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__); break; - /* we don't need to implement these */ case NAND_CTL_SETCLE: - case NAND_CTL_CLRCLE: + chip->IO_ADDR_W = info->regs + S3C2410_NFCMD; + break; + case NAND_CTL_SETALE: - case NAND_CTL_CLRALE: - pr_debug(PFX "s3c2410_nand_hwcontrol(%d) unusedn", cmd); + chip->IO_ADDR_W = info->regs + S3C2410_NFADDR; + break; + + /* NAND_CTL_CLRCLE: */ + /* NAND_CTL_CLRALE: */ + default: + chip->IO_ADDR_W = info->regs + S3C2410_NFDATA; break; } } -/* s3c2410_nand_command - * - * This function implements sending commands and the relevant address - * information to the chip, via the hardware controller. Since the - * S3C2410 generates the correct ALE/CLE signaling automatically, we - * do not need to use hwcontrol. -*/ +/* command and control functions */ -static void s3c2410_nand_command (struct mtd_info *mtd, unsigned command, - int column, int page_addr) +static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd) { - register struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - register struct nand_chip *this = mtd->priv; + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + struct nand_chip *chip = mtd->priv; - /* - * Write out the command to the device. - */ - if (command == NAND_CMD_SEQIN) { - int readcmd; - - if (column >= mtd->oobblock) { - /* OOB area */ - column -= mtd->oobblock; - readcmd = NAND_CMD_READOOB; - } else if (column < 256) { - /* First 256 bytes --> READ0 */ - readcmd = NAND_CMD_READ0; - } else { - column -= 256; - readcmd = NAND_CMD_READ1; - } - - writeb(readcmd, info->regs + S3C2410_NFCMD); - } - writeb(command, info->regs + S3C2410_NFCMD); + switch (cmd) { + case NAND_CTL_SETNCE: + case NAND_CTL_CLRNCE: + printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__); + break; - /* Set ALE and clear CLE to start address cycle */ + case NAND_CTL_SETCLE: + chip->IO_ADDR_W = info->regs + S3C2440_NFCMD; + break; - if (column != -1 || page_addr != -1) { + case NAND_CTL_SETALE: + chip->IO_ADDR_W = info->regs + S3C2440_NFADDR; + break; - /* Serially input address */ - if (column != -1) { - /* Adjust columns for 16 bit buswidth */ - if (this->options & NAND_BUSWIDTH_16) - column >>= 1; - writeb(column, info->regs + S3C2410_NFADDR); - } - if (page_addr != -1) { - writeb((unsigned char) (page_addr), info->regs + S3C2410_NFADDR); - writeb((unsigned char) (page_addr >> 8), info->regs + S3C2410_NFADDR); - /* One more address cycle for higher density devices */ - if (this->chipsize & 0x0c000000) - writeb((unsigned char) ((page_addr >> 16) & 0x0f), - info->regs + S3C2410_NFADDR); - } - /* Latch in address */ - } - - /* - * program and erase have their own busy handlers - * status and sequential in needs no delay - */ - switch (command) { - - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_SEQIN: - case NAND_CMD_STATUS: - return; - - case NAND_CMD_RESET: - if (this->dev_ready) - break; - - udelay(this->chip_delay); - writeb(NAND_CMD_STATUS, info->regs + S3C2410_NFCMD); - - while ( !(this->read_byte(mtd) & 0x40)); - return; - - /* This applies to read commands */ + /* NAND_CTL_CLRCLE: */ + /* NAND_CTL_CLRALE: */ default: - /* - * If we don't have access to the busy pin, we apply the given - * command delay - */ - if (!this->dev_ready) { - udelay (this->chip_delay); - return; - } + chip->IO_ADDR_W = info->regs + S3C2440_NFDATA; + break; } - - /* Apply this short delay always to ensure that we do wait tWB in - * any case on any machine. */ - ndelay (100); - /* wait until command is processed */ - while (!this->dev_ready(mtd)); } - /* s3c2410_nand_devready() * * returns 0 if the nand is busy, 1 if it is ready @@ -369,9 +322,12 @@ static int s3c2410_nand_devready(struct mtd_info *mtd) { struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + if (info->is_s3c2440) + return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY; return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY; } + /* ECC handling functions */ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, @@ -394,6 +350,12 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, return -1; } +/* ECC functions + * + * These allow the s3c2410 and s3c2440 to use the controller's ECC + * generator block to ECC the data as it passes through] +*/ + static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) { struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); @@ -404,6 +366,15 @@ static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) writel(ctrl, info->regs + S3C2410_NFCONF); } +static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned long ctrl; + + ctrl = readl(info->regs + S3C2440_NFCONT); + writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT); +} + static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) { @@ -420,7 +391,26 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, } -/* over-ride the standard functions for a little more speed? */ +static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_code) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned long ecc = readl(info->regs + S3C2440_NFMECC0); + + ecc_code[0] = ecc; + ecc_code[1] = ecc >> 8; + ecc_code[2] = ecc >> 16; + + pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", + ecc_code[0], ecc_code[1], ecc_code[2]); + + return 0; +} + + +/* over-ride the standard functions for a little more speed. We can + * use read/write block to move the data buffers to/from the controller +*/ static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) { @@ -523,11 +513,10 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, { struct nand_chip *chip = &nmtd->chip; - chip->IO_ADDR_R = (char *)info->regs + S3C2410_NFDATA; - chip->IO_ADDR_W = (char *)info->regs + S3C2410_NFDATA; + chip->IO_ADDR_R = info->regs + S3C2410_NFDATA; + chip->IO_ADDR_W = info->regs + S3C2410_NFDATA; chip->hwcontrol = s3c2410_nand_hwcontrol; chip->dev_ready = s3c2410_nand_devready; - chip->cmdfunc = s3c2410_nand_command; chip->write_buf = s3c2410_nand_write_buf; chip->read_buf = s3c2410_nand_read_buf; chip->select_chip = s3c2410_nand_select_chip; @@ -536,6 +525,12 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, chip->options = 0; chip->controller = &info->controller; + if (info->is_s3c2440) { + chip->IO_ADDR_R = info->regs + S3C2440_NFDATA; + chip->IO_ADDR_W = info->regs + S3C2440_NFDATA; + chip->hwcontrol = s3c2440_nand_hwcontrol; + } + nmtd->info = info; nmtd->mtd.priv = chip; nmtd->set = set; @@ -546,6 +541,11 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, chip->calculate_ecc = s3c2410_nand_calculate_ecc; chip->eccmode = NAND_ECC_HW3_512; chip->autooob = &nand_hw_eccoob; + + if (info->is_s3c2440) { + chip->enable_hwecc = s3c2440_nand_enable_hwecc; + chip->calculate_ecc = s3c2440_nand_calculate_ecc; + } } else { chip->eccmode = NAND_ECC_SOFT; } @@ -559,7 +559,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, * nand layer to look for devices */ -static int s3c2410_nand_probe(struct device *dev) +static int s3c24xx_nand_probe(struct device *dev, int is_s3c2440) { struct platform_device *pdev = to_platform_device(dev); struct s3c2410_platform_nand *plat = to_nand_plat(dev); @@ -585,6 +585,7 @@ static int s3c2410_nand_probe(struct device *dev) dev_set_drvdata(dev, info); spin_lock_init(&info->controller.lock); + init_waitqueue_head(&info->controller.wq); /* get the clock source and enable it */ @@ -600,7 +601,8 @@ static int s3c2410_nand_probe(struct device *dev) /* allocate and map the resource */ - res = pdev->resource; /* assume that the flash has one resource */ + /* currently we assume we have the one resource */ + res = pdev->resource; size = res->end - res->start + 1; info->area = request_mem_region(res->start, size, pdev->name); @@ -611,9 +613,10 @@ static int s3c2410_nand_probe(struct device *dev) goto exit_error; } - info->device = dev; - info->platform = plat; - info->regs = ioremap(res->start, size); + info->device = dev; + info->platform = plat; + info->regs = ioremap(res->start, size); + info->is_s3c2440 = is_s3c2440; if (info->regs == NULL) { printk(KERN_ERR PFX "cannot reserve register region\n"); @@ -678,6 +681,18 @@ static int s3c2410_nand_probe(struct device *dev) return err; } +/* driver device registration */ + +static int s3c2410_nand_probe(struct device *dev) +{ + return s3c24xx_nand_probe(dev, 0); +} + +static int s3c2440_nand_probe(struct device *dev) +{ + return s3c24xx_nand_probe(dev, 1); +} + static struct device_driver s3c2410_nand_driver = { .name = "s3c2410-nand", .bus = &platform_bus_type, @@ -685,14 +700,24 @@ static struct device_driver s3c2410_nand_driver = { .remove = s3c2410_nand_remove, }; +static struct device_driver s3c2440_nand_driver = { + .name = "s3c2440-nand", + .bus = &platform_bus_type, + .probe = s3c2440_nand_probe, + .remove = s3c2410_nand_remove, +}; + static int __init s3c2410_nand_init(void) { - printk("S3C2410 NAND Driver, (c) 2004 Simtec Electronics\n"); + printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n"); + + driver_register(&s3c2440_nand_driver); return driver_register(&s3c2410_nand_driver); } static void __exit s3c2410_nand_exit(void) { + driver_unregister(&s3c2440_nand_driver); driver_unregister(&s3c2410_nand_driver); } @@ -701,4 +726,4 @@ module_exit(s3c2410_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); -MODULE_DESCRIPTION("S3C2410 MTD NAND driver"); +MODULE_DESCRIPTION("S3C24XX MTD NAND driver"); diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 2957279..9853b87 100755..100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -3,7 +3,7 @@ * * Copyright (C) 2004 Richard Purdie * - * $Id: sharpsl.c,v 1.3 2005/01/03 14:53:50 rpurdie Exp $ + * $Id: sharpsl.c,v 1.4 2005/01/23 11:09:19 rpurdie Exp $ * * Based on Sharp's NAND driver sharp_sl.c * @@ -216,7 +216,7 @@ sharpsl_nand_init(void) nr_partitions = DEFAULT_NUM_PARTITIONS; sharpsl_partition_info = sharpsl_nand_default_partition_info; if (machine_is_poodle()) { - sharpsl_partition_info[1].size=22 * 1024 * 1024; + sharpsl_partition_info[1].size=30 * 1024 * 1024; } else if (machine_is_corgi() || machine_is_shepherd()) { sharpsl_partition_info[1].size=25 * 1024 * 1024; } else if (machine_is_husky()) { diff --git a/drivers/mtd/nand/tx4925ndfmc.c b/drivers/mtd/nand/tx4925ndfmc.c deleted file mode 100644 index bba6888..0000000 --- a/drivers/mtd/nand/tx4925ndfmc.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - * drivers/mtd/tx4925ndfmc.c - * - * Overview: - * This is a device driver for the NAND flash device found on the - * Toshiba RBTX4925 reference board, which is a SmartMediaCard. It supports - * 16MiB, 32MiB and 64MiB cards. - * - * Author: MontaVista Software, Inc. source@mvista.com - * - * Derived from drivers/mtd/autcpu12.c - * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) - * - * $Id: tx4925ndfmc.c,v 1.5 2004/10/05 13:50:20 gleixner Exp $ - * - * Copyright (C) 2001 Toshiba Corporation - * - * 2003 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - * - */ - -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/partitions.h> -#include <linux/delay.h> -#include <asm/io.h> -#include <asm/tx4925/tx4925_nand.h> - -extern struct nand_oobinfo jffs2_oobinfo; - -/* - * MTD structure for RBTX4925 board - */ -static struct mtd_info *tx4925ndfmc_mtd = NULL; - -/* - * Define partitions for flash devices - */ - -static struct mtd_partition partition_info16k[] = { - { .name = "RBTX4925 flash partition 1", - .offset = 0, - .size = 8 * 0x00100000 }, - { .name = "RBTX4925 flash partition 2", - .offset = 8 * 0x00100000, - .size = 8 * 0x00100000 }, -}; - -static struct mtd_partition partition_info32k[] = { - { .name = "RBTX4925 flash partition 1", - .offset = 0, - .size = 8 * 0x00100000 }, - { .name = "RBTX4925 flash partition 2", - .offset = 8 * 0x00100000, - .size = 24 * 0x00100000 }, -}; - -static struct mtd_partition partition_info64k[] = { - { .name = "User FS", - .offset = 0, - .size = 16 * 0x00100000 }, - { .name = "RBTX4925 flash partition 2", - .offset = 16 * 0x00100000, - .size = 48 * 0x00100000}, -}; - -static struct mtd_partition partition_info128k[] = { - { .name = "Skip bad section", - .offset = 0, - .size = 16 * 0x00100000 }, - { .name = "User FS", - .offset = 16 * 0x00100000, - .size = 112 * 0x00100000 }, -}; -#define NUM_PARTITIONS16K 2 -#define NUM_PARTITIONS32K 2 -#define NUM_PARTITIONS64K 2 -#define NUM_PARTITIONS128K 2 - -/* - * hardware specific access to control-lines -*/ -static void tx4925ndfmc_hwcontrol(struct mtd_info *mtd, int cmd) -{ - - switch(cmd){ - - case NAND_CTL_SETCLE: - tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CLE; - break; - case NAND_CTL_CLRCLE: - tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CLE; - break; - case NAND_CTL_SETALE: - tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ALE; - break; - case NAND_CTL_CLRALE: - tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ALE; - break; - case NAND_CTL_SETNCE: - tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CE; - break; - case NAND_CTL_CLRNCE: - tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CE; - break; - case NAND_CTL_SETWP: - tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_WE; - break; - case NAND_CTL_CLRWP: - tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_WE; - break; - } -} - -/* -* read device ready pin -*/ -static int tx4925ndfmc_device_ready(struct mtd_info *mtd) -{ - int ready; - ready = (tx4925_ndfmcptr->sr & TX4925_NDSFR_BUSY) ? 0 : 1; - return ready; -} -void tx4925ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) -{ - /* reset first */ - tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_MASK; - tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK; - tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_ENAB; -} -static void tx4925ndfmc_disable_ecc(void) -{ - tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK; -} -static void tx4925ndfmc_enable_read_ecc(void) -{ - tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK; - tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_READ; -} -void tx4925ndfmc_readecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code){ - int i; - u_char *ecc = ecc_code; - tx4925ndfmc_enable_read_ecc(); - for (i = 0;i < 6;i++,ecc++) - *ecc = tx4925_read_nfmc(&(tx4925_ndfmcptr->dtr)); - tx4925ndfmc_disable_ecc(); -} -void tx4925ndfmc_device_setup(void) -{ - - *(unsigned char *)0xbb005000 &= ~0x08; - - /* reset NDFMC */ - tx4925_ndfmcptr->rstr |= TX4925_NDFRSTR_RST; - while (tx4925_ndfmcptr->rstr & TX4925_NDFRSTR_RST); - - /* setup BusSeparete, Hold Time, Strobe Pulse Width */ - tx4925_ndfmcptr->mcr = TX4925_BSPRT ? TX4925_NDFMCR_BSPRT : 0; - tx4925_ndfmcptr->spr = TX4925_HOLD << 4 | TX4925_SPW; -} -static u_char tx4925ndfmc_nand_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd->priv; - return tx4925_read_nfmc(this->IO_ADDR_R); -} - -static void tx4925ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte) -{ - struct nand_chip *this = mtd->priv; - tx4925_write_nfmc(byte, this->IO_ADDR_W); -} - -static void tx4925ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) -{ - int i; - struct nand_chip *this = mtd->priv; - - for (i=0; i<len; i++) - tx4925_write_nfmc(buf[i], this->IO_ADDR_W); -} - -static void tx4925ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - int i; - struct nand_chip *this = mtd->priv; - - for (i=0; i<len; i++) - buf[i] = tx4925_read_nfmc(this->IO_ADDR_R); -} - -static int tx4925ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) -{ - int i; - struct nand_chip *this = mtd->priv; - - for (i=0; i<len; i++) - if (buf[i] != tx4925_read_nfmc(this->IO_ADDR_R)) - return -EFAULT; - - return 0; -} - -/* - * Send command to NAND device - */ -static void tx4925ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) -{ - register struct nand_chip *this = mtd->priv; - - /* Begin command latch cycle */ - this->hwcontrol(mtd, NAND_CTL_SETCLE); - /* - * Write out the command to the device. - */ - if (command == NAND_CMD_SEQIN) { - int readcmd; - - if (column >= mtd->oobblock) { - /* OOB area */ - column -= mtd->oobblock; - readcmd = NAND_CMD_READOOB; - } else if (column < 256) { - /* First 256 bytes --> READ0 */ - readcmd = NAND_CMD_READ0; - } else { - column -= 256; - readcmd = NAND_CMD_READ1; - } - this->write_byte(mtd, readcmd); - } - this->write_byte(mtd, command); - - /* Set ALE and clear CLE to start address cycle */ - this->hwcontrol(mtd, NAND_CTL_CLRCLE); - - if (column != -1 || page_addr != -1) { - this->hwcontrol(mtd, NAND_CTL_SETALE); - - /* Serially input address */ - if (column != -1) - this->write_byte(mtd, column); - if (page_addr != -1) { - this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); - this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); - /* One more address cycle for higher density devices */ - if (mtd->size & 0x0c000000) - this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); - } - /* Latch in address */ - this->hwcontrol(mtd, NAND_CTL_CLRALE); - } - - /* - * program and erase have their own busy handlers - * status and sequential in needs no delay - */ - switch (command) { - - case NAND_CMD_PAGEPROG: - /* Turn off WE */ - this->hwcontrol (mtd, NAND_CTL_CLRWP); - return; - - case NAND_CMD_SEQIN: - /* Turn on WE */ - this->hwcontrol (mtd, NAND_CTL_SETWP); - return; - - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_STATUS: - return; - - case NAND_CMD_RESET: - if (this->dev_ready) - break; - this->hwcontrol(mtd, NAND_CTL_SETCLE); - this->write_byte(mtd, NAND_CMD_STATUS); - this->hwcontrol(mtd, NAND_CTL_CLRCLE); - while ( !(this->read_byte(mtd) & 0x40)); - return; - - /* This applies to read commands */ - default: - /* - * If we don't have access to the busy pin, we apply the given - * command delay - */ - if (!this->dev_ready) { - udelay (this->chip_delay); - return; - } - } - - /* wait until command is processed */ - while (!this->dev_ready(mtd)); -} - -#ifdef CONFIG_MTD_CMDLINE_PARTS -extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partitio -n **pparts, char *); -#endif - -/* - * Main initialization routine - */ -extern int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); -int __init tx4925ndfmc_init (void) -{ - struct nand_chip *this; - int err = 0; - - /* Allocate memory for MTD device structure and private data */ - tx4925ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), - GFP_KERNEL); - if (!tx4925ndfmc_mtd) { - printk ("Unable to allocate RBTX4925 NAND MTD device structure.\n"); - err = -ENOMEM; - goto out; - } - - tx4925ndfmc_device_setup(); - - /* io is indirect via a register so don't need to ioremap address */ - - /* Get pointer to private data */ - this = (struct nand_chip *) (&tx4925ndfmc_mtd[1]); - - /* Initialize structures */ - memset((char *) tx4925ndfmc_mtd, 0, sizeof(struct mtd_info)); - memset((char *) this, 0, sizeof(struct nand_chip)); - - /* Link the private data with the MTD structure */ - tx4925ndfmc_mtd->priv = this; - - /* Set address of NAND IO lines */ - this->IO_ADDR_R = (void __iomem *)&(tx4925_ndfmcptr->dtr); - this->IO_ADDR_W = (void __iomem *)&(tx4925_ndfmcptr->dtr); - this->hwcontrol = tx4925ndfmc_hwcontrol; - this->enable_hwecc = tx4925ndfmc_enable_hwecc; - this->calculate_ecc = tx4925ndfmc_readecc; - this->correct_data = nand_correct_data; - this->eccmode = NAND_ECC_HW6_512; - this->dev_ready = tx4925ndfmc_device_ready; - /* 20 us command delay time */ - this->chip_delay = 20; - this->read_byte = tx4925ndfmc_nand_read_byte; - this->write_byte = tx4925ndfmc_nand_write_byte; - this->cmdfunc = tx4925ndfmc_nand_command; - this->write_buf = tx4925ndfmc_nand_write_buf; - this->read_buf = tx4925ndfmc_nand_read_buf; - this->verify_buf = tx4925ndfmc_nand_verify_buf; - - /* Scan to find existance of the device */ - if (nand_scan (tx4925ndfmc_mtd, 1)) { - err = -ENXIO; - goto out_ior; - } - - /* Register the partitions */ -#ifdef CONFIG_MTD_CMDLINE_PARTS - { - int mtd_parts_nb = 0; - struct mtd_partition *mtd_parts = 0; - mtd_parts_nb = parse_cmdline_partitions(tx4925ndfmc_mtd, &mtd_parts, "tx4925ndfmc"); - if (mtd_parts_nb > 0) - add_mtd_partitions(tx4925ndfmc_mtd, mtd_parts, mtd_parts_nb); - else - add_mtd_device(tx4925ndfmc_mtd); - } -#else /* ifdef CONFIG_MTD_CMDLINE_PARTS */ - switch(tx4925ndfmc_mtd->size){ - case 0x01000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info16k, NUM_PARTITIONS16K); break; - case 0x02000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info32k, NUM_PARTITIONS32K); break; - case 0x04000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info64k, NUM_PARTITIONS64K); break; - case 0x08000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info128k, NUM_PARTITIONS128K); break; - default: { - printk ("Unsupported SmartMedia device\n"); - err = -ENXIO; - goto out_ior; - } - } -#endif /* ifdef CONFIG_MTD_CMDLINE_PARTS */ - goto out; - -out_ior: -out: - return err; -} - -module_init(tx4925ndfmc_init); - -/* - * Clean up routine - */ -#ifdef MODULE -static void __exit tx4925ndfmc_cleanup (void) -{ - /* Release resources, unregister device */ - nand_release (tx4925ndfmc_mtd); - - /* Free the MTD device structure */ - kfree (tx4925ndfmc_mtd); -} -module_exit(tx4925ndfmc_cleanup); -#endif - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>"); -MODULE_DESCRIPTION("Glue layer for SmartMediaCard on Toshiba RBTX4925"); diff --git a/drivers/mtd/nand/tx4938ndfmc.c b/drivers/mtd/nand/tx4938ndfmc.c deleted file mode 100644 index df26e58..0000000 --- a/drivers/mtd/nand/tx4938ndfmc.c +++ /dev/null @@ -1,406 +0,0 @@ -/* - * drivers/mtd/nand/tx4938ndfmc.c - * - * Overview: - * This is a device driver for the NAND flash device connected to - * TX4938 internal NAND Memory Controller. - * TX4938 NDFMC is almost same as TX4925 NDFMC, but register size are 64 bit. - * - * Author: source@mvista.com - * - * Based on spia.c by Steven J. Hill - * - * $Id: tx4938ndfmc.c,v 1.4 2004/10/05 13:50:20 gleixner Exp $ - * - * Copyright (C) 2000-2001 Toshiba Corporation - * - * 2003 (c) MontaVista Software, Inc. This file is licensed under the - * terms of the GNU General Public License version 2. This program is - * licensed "as is" without any warranty of any kind, whether express - * or implied. - */ -#include <linux/config.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/nand_ecc.h> -#include <linux/mtd/partitions.h> -#include <asm/io.h> -#include <asm/bootinfo.h> -#include <linux/delay.h> -#include <asm/tx4938/rbtx4938.h> - -extern struct nand_oobinfo jffs2_oobinfo; - -/* - * MTD structure for TX4938 NDFMC - */ -static struct mtd_info *tx4938ndfmc_mtd; - -/* - * Define partitions for flash device - */ -#define flush_wb() (void)tx4938_ndfmcptr->mcr; - -#define NUM_PARTITIONS 3 -#define NUMBER_OF_CIS_BLOCKS 24 -#define SIZE_OF_BLOCK 0x00004000 -#define NUMBER_OF_BLOCK_PER_ZONE 1024 -#define SIZE_OF_ZONE (NUMBER_OF_BLOCK_PER_ZONE * SIZE_OF_BLOCK) -#ifndef CONFIG_MTD_CMDLINE_PARTS -/* - * You can use the following sample of MTD partitions - * on the NAND Flash Memory 32MB or more. - * - * The following figure shows the image of the sample partition on - * the 32MB NAND Flash Memory. - * - * Block No. - * 0 +-----------------------------+ ------ - * | CIS | ^ - * 24 +-----------------------------+ | - * | kernel image | | Zone 0 - * | | | - * +-----------------------------+ | - * 1023 | unused area | v - * +-----------------------------+ ------ - * 1024 | JFFS2 | ^ - * | | | - * | | | Zone 1 - * | | | - * | | | - * | | v - * 2047 +-----------------------------+ ------ - * - */ -static struct mtd_partition partition_info[NUM_PARTITIONS] = { - { - .name = "RBTX4938 CIS Area", - .offset = 0, - .size = (NUMBER_OF_CIS_BLOCKS * SIZE_OF_BLOCK), - .mask_flags = MTD_WRITEABLE /* This partition is NOT writable */ - }, - { - .name = "RBTX4938 kernel image", - .offset = MTDPART_OFS_APPEND, - .size = 8 * 0x00100000, /* 8MB (Depends on size of kernel image) */ - .mask_flags = MTD_WRITEABLE /* This partition is NOT writable */ - }, - { - .name = "Root FS (JFFS2)", - .offset = (0 + SIZE_OF_ZONE), /* start address of next zone */ - .size = MTDPART_SIZ_FULL - }, -}; -#endif - -static void tx4938ndfmc_hwcontrol(struct mtd_info *mtd, int cmd) -{ - switch (cmd) { - case NAND_CTL_SETCLE: - tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CLE; - break; - case NAND_CTL_CLRCLE: - tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CLE; - break; - case NAND_CTL_SETALE: - tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_ALE; - break; - case NAND_CTL_CLRALE: - tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_ALE; - break; - /* TX4938_NDFMCR_CE bit is 0:high 1:low */ - case NAND_CTL_SETNCE: - tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CE; - break; - case NAND_CTL_CLRNCE: - tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CE; - break; - case NAND_CTL_SETWP: - tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_WE; - break; - case NAND_CTL_CLRWP: - tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_WE; - break; - } -} -static int tx4938ndfmc_dev_ready(struct mtd_info *mtd) -{ - flush_wb(); - return !(tx4938_ndfmcptr->sr & TX4938_NDFSR_BUSY); -} -static void tx4938ndfmc_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) -{ - u32 mcr = tx4938_ndfmcptr->mcr; - mcr &= ~TX4938_NDFMCR_ECC_ALL; - tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF; - tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_READ; - ecc_code[1] = tx4938_ndfmcptr->dtr; - ecc_code[0] = tx4938_ndfmcptr->dtr; - ecc_code[2] = tx4938_ndfmcptr->dtr; - tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF; -} -static void tx4938ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) -{ - u32 mcr = tx4938_ndfmcptr->mcr; - mcr &= ~TX4938_NDFMCR_ECC_ALL; - tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_RESET; - tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF; - tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_ON; -} - -static u_char tx4938ndfmc_nand_read_byte(struct mtd_info *mtd) -{ - struct nand_chip *this = mtd->priv; - return tx4938_read_nfmc(this->IO_ADDR_R); -} - -static void tx4938ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte) -{ - struct nand_chip *this = mtd->priv; - tx4938_write_nfmc(byte, this->IO_ADDR_W); -} - -static void tx4938ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) -{ - int i; - struct nand_chip *this = mtd->priv; - - for (i=0; i<len; i++) - tx4938_write_nfmc(buf[i], this->IO_ADDR_W); -} - -static void tx4938ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) -{ - int i; - struct nand_chip *this = mtd->priv; - - for (i=0; i<len; i++) - buf[i] = tx4938_read_nfmc(this->IO_ADDR_R); -} - -static int tx4938ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) -{ - int i; - struct nand_chip *this = mtd->priv; - - for (i=0; i<len; i++) - if (buf[i] != tx4938_read_nfmc(this->IO_ADDR_R)) - return -EFAULT; - - return 0; -} - -/* - * Send command to NAND device - */ -static void tx4938ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) -{ - register struct nand_chip *this = mtd->priv; - - /* Begin command latch cycle */ - this->hwcontrol(mtd, NAND_CTL_SETCLE); - /* - * Write out the command to the device. - */ - if (command == NAND_CMD_SEQIN) { - int readcmd; - - if (column >= mtd->oobblock) { - /* OOB area */ - column -= mtd->oobblock; - readcmd = NAND_CMD_READOOB; - } else if (column < 256) { - /* First 256 bytes --> READ0 */ - readcmd = NAND_CMD_READ0; - } else { - column -= 256; - readcmd = NAND_CMD_READ1; - } - this->write_byte(mtd, readcmd); - } - this->write_byte(mtd, command); - - /* Set ALE and clear CLE to start address cycle */ - this->hwcontrol(mtd, NAND_CTL_CLRCLE); - - if (column != -1 || page_addr != -1) { - this->hwcontrol(mtd, NAND_CTL_SETALE); - - /* Serially input address */ - if (column != -1) - this->write_byte(mtd, column); - if (page_addr != -1) { - this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); - this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); - /* One more address cycle for higher density devices */ - if (mtd->size & 0x0c000000) - this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); - } - /* Latch in address */ - this->hwcontrol(mtd, NAND_CTL_CLRALE); - } - - /* - * program and erase have their own busy handlers - * status and sequential in needs no delay - */ - switch (command) { - - case NAND_CMD_PAGEPROG: - /* Turn off WE */ - this->hwcontrol (mtd, NAND_CTL_CLRWP); - return; - - case NAND_CMD_SEQIN: - /* Turn on WE */ - this->hwcontrol (mtd, NAND_CTL_SETWP); - return; - - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_STATUS: - return; - - case NAND_CMD_RESET: - if (this->dev_ready) - break; - this->hwcontrol(mtd, NAND_CTL_SETCLE); - this->write_byte(mtd, NAND_CMD_STATUS); - this->hwcontrol(mtd, NAND_CTL_CLRCLE); - while ( !(this->read_byte(mtd) & 0x40)); - return; - - /* This applies to read commands */ - default: - /* - * If we don't have access to the busy pin, we apply the given - * command delay - */ - if (!this->dev_ready) { - udelay (this->chip_delay); - return; - } - } - - /* wait until command is processed */ - while (!this->dev_ready(mtd)); -} - -#ifdef CONFIG_MTD_CMDLINE_PARTS -extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *); -#endif -/* - * Main initialization routine - */ -int __init tx4938ndfmc_init (void) -{ - struct nand_chip *this; - int bsprt = 0, hold = 0xf, spw = 0xf; - int protected = 0; - - if ((*rbtx4938_piosel_ptr & 0x0c) != 0x08) { - printk("TX4938 NDFMC: disabled by IOC PIOSEL\n"); - return -ENODEV; - } - bsprt = 1; - hold = 2; - spw = 9 - 1; /* 8 GBUSCLK = 80ns (@ GBUSCLK 100MHz) */ - - if ((tx4938_ccfgptr->pcfg & - (TX4938_PCFG_ATA_SEL|TX4938_PCFG_ISA_SEL|TX4938_PCFG_NDF_SEL)) - != TX4938_PCFG_NDF_SEL) { - printk("TX4938 NDFMC: disabled by PCFG.\n"); - return -ENODEV; - } - - /* reset NDFMC */ - tx4938_ndfmcptr->rstr |= TX4938_NDFRSTR_RST; - while (tx4938_ndfmcptr->rstr & TX4938_NDFRSTR_RST) - ; - /* setup BusSeparete, Hold Time, Strobe Pulse Width */ - tx4938_ndfmcptr->mcr = bsprt ? TX4938_NDFMCR_BSPRT : 0; - tx4938_ndfmcptr->spr = hold << 4 | spw; - - /* Allocate memory for MTD device structure and private data */ - tx4938ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), - GFP_KERNEL); - if (!tx4938ndfmc_mtd) { - printk ("Unable to allocate TX4938 NDFMC MTD device structure.\n"); - return -ENOMEM; - } - - /* Get pointer to private data */ - this = (struct nand_chip *) (&tx4938ndfmc_mtd[1]); - - /* Initialize structures */ - memset((char *) tx4938ndfmc_mtd, 0, sizeof(struct mtd_info)); - memset((char *) this, 0, sizeof(struct nand_chip)); - - /* Link the private data with the MTD structure */ - tx4938ndfmc_mtd->priv = this; - - /* Set address of NAND IO lines */ - this->IO_ADDR_R = (unsigned long)&tx4938_ndfmcptr->dtr; - this->IO_ADDR_W = (unsigned long)&tx4938_ndfmcptr->dtr; - this->hwcontrol = tx4938ndfmc_hwcontrol; - this->dev_ready = tx4938ndfmc_dev_ready; - this->calculate_ecc = tx4938ndfmc_calculate_ecc; - this->correct_data = nand_correct_data; - this->enable_hwecc = tx4938ndfmc_enable_hwecc; - this->eccmode = NAND_ECC_HW3_256; - this->chip_delay = 100; - this->read_byte = tx4938ndfmc_nand_read_byte; - this->write_byte = tx4938ndfmc_nand_write_byte; - this->cmdfunc = tx4938ndfmc_nand_command; - this->write_buf = tx4938ndfmc_nand_write_buf; - this->read_buf = tx4938ndfmc_nand_read_buf; - this->verify_buf = tx4938ndfmc_nand_verify_buf; - - /* Scan to find existance of the device */ - if (nand_scan (tx4938ndfmc_mtd, 1)) { - kfree (tx4938ndfmc_mtd); - return -ENXIO; - } - - if (protected) { - printk(KERN_INFO "TX4938 NDFMC: write protected.\n"); - tx4938ndfmc_mtd->flags &= ~(MTD_WRITEABLE | MTD_ERASEABLE); - } - -#ifdef CONFIG_MTD_CMDLINE_PARTS - { - int mtd_parts_nb = 0; - struct mtd_partition *mtd_parts = 0; - mtd_parts_nb = parse_cmdline_partitions(tx4938ndfmc_mtd, &mtd_parts, "tx4938ndfmc"); - if (mtd_parts_nb > 0) - add_mtd_partitions(tx4938ndfmc_mtd, mtd_parts, mtd_parts_nb); - else - add_mtd_device(tx4938ndfmc_mtd); - } -#else - add_mtd_partitions(tx4938ndfmc_mtd, partition_info, NUM_PARTITIONS ); -#endif - - return 0; -} -module_init(tx4938ndfmc_init); - -/* - * Clean up routine - */ -static void __exit tx4938ndfmc_cleanup (void) -{ - /* Release resources, unregister device */ - nand_release (tx4938ndfmc_mtd); - - /* Free the MTD device structure */ - kfree (tx4938ndfmc_mtd); -} -module_exit(tx4938ndfmc_cleanup); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>"); -MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on TX4938 NDFMC"); |