diff options
| author | David Woodhouse <dwmw2@shinybook.infradead.org> | 2005-01-05 21:11:47 +0100 |
|---|---|---|
| committer | David Woodhouse <dwmw2@shinybook.infradead.org> | 2005-01-05 21:11:47 +0100 |
| commit | ee7ce018c513404c68148688b4c692ede25719cf (patch) | |
| tree | 70552f5afa45ce908eecf2fb6e0663e42341d659 | |
| parent | 1db608d19a9479d0cae16a2cab6c77e7f1457453 (diff) | |
| parent | 956846c2ac0246da330c16720c9325442205f029 (diff) | |
Merge shinybook.infradead.org:/home/dwmw2/bk/linus-2.6
into shinybook.infradead.org:/home/dwmw2/bk/mtd-2.6
102 files changed, 3988 insertions, 1329 deletions
@@ -3578,7 +3578,6 @@ S: The Netherlands N: David Woodhouse E: dwmw2@infradead.org -E: dwmw2@redhat.com D: ARCnet stuff, Applicom board driver, SO_BINDTODEVICE, D: some Alpha platform porting from 2.0, Memory Technology Devices, D: Acquire watchdog timer, PC speaker driver maintenance, diff --git a/MAINTAINERS b/MAINTAINERS index 6db9dd666079..91730edf8599 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1469,7 +1469,7 @@ S: Maintained MEMORY TECHNOLOGY DEVICES P: David Woodhouse -M: dwmw2@redhat.com +M: dwmw2@infradead.org W: http://www.linux-mtd.infradead.org/ L: linux-mtd@lists.infradead.org S: Maintained diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c index 2d016858c601..fe9c7ea3496d 100644 --- a/drivers/char/applicom.c +++ b/drivers/char/applicom.c @@ -1,6 +1,6 @@ /* Derived from Applicom driver ac.c for SCO Unix */ /* Ported by David Woodhouse, Axiom (Cambridge) Ltd. */ -/* dwmw2@redhat.com 30/8/98 */ +/* dwmw2@infradead.org 30/8/98 */ /* $Id: ac.c,v 1.30 2000/03/22 16:03:57 dwmw2 Exp $ */ /* This module is for Linux 2.1 and 2.2 series kernels. */ /*****************************************************************************/ diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index f0aaaa225fb5..afd0b67c48e9 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -1,4 +1,4 @@ -# $Id: Kconfig,v 1.6 2004/08/09 13:19:42 dwmw2 Exp $ +# $Id: Kconfig,v 1.7 2004/11/22 11:33:56 ijc Exp $ menu "Memory Technology Devices (MTD)" @@ -54,8 +54,8 @@ config MTD_REDBOOT_PARTS depends on MTD_PARTITIONS ---help--- RedBoot is a ROM monitor and bootloader which deals with multiple - 'images' in flash devices by putting a table in the last erase - block of the device, similar to a partition table, which gives + 'images' in flash devices by putting a table one of the erase + blocks on the device, similar to a partition table, which gives the offsets, lengths and names of all the images stored in the flash. @@ -68,6 +68,23 @@ config MTD_REDBOOT_PARTS SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for example. +config MTD_REDBOOT_DIRECTORY_BLOCK + int "Location of RedBoot partition table" + depends on MTD_REDBOOT_PARTS + default "-1" + ---help--- + This option is the Linux counterpart to the + CYGNUM_REDBOOT_FIS_DIRECTORY_BLOCK RedBoot compile time + option. + + The option specifies which Flash sectors holds the RedBoot + partition table. A zero or positive value gives an absolete + erase block number. A negative value specifies a number of + sectors before the end of the device. + + For example "2" means block number 2, "-1" means the last + block and "-2" means the penultimate block. + config MTD_REDBOOT_PARTS_UNALLOCATED bool " Include unallocated flash regions" depends on MTD_REDBOOT_PARTS diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig index 2d2b7d467086..d682dbc8157e 100644 --- a/drivers/mtd/chips/Kconfig +++ b/drivers/mtd/chips/Kconfig @@ -1,5 +1,5 @@ # drivers/mtd/chips/Kconfig -# $Id: Kconfig,v 1.9 2004/07/16 15:32:14 dwmw2 Exp $ +# $Id: Kconfig,v 1.13 2004/12/01 15:49:10 nico Exp $ menu "RAM/ROM/Flash chip drivers" depends on MTD!=n @@ -7,6 +7,7 @@ menu "RAM/ROM/Flash chip drivers" config MTD_CFI tristate "Detect flash chips by Common Flash Interface (CFI) probe" depends on MTD + select MTD_GEN_PROBE help The Common Flash Interface specification was developed by Intel, AMD and other flash manufactures that provides a universal method @@ -18,6 +19,7 @@ config MTD_CFI config MTD_JEDECPROBE tristate "Detect non-CFI AMD/JEDEC-compatible flash chips" depends on MTD + select MTD_GEN_PROBE help This option enables JEDEC-style probing of flash chips which are not compatible with the Common Flash Interface, but will use the common @@ -29,8 +31,6 @@ config MTD_JEDECPROBE config MTD_GEN_PROBE tristate - default m if MTD_CFI!=y && !MTD_INTELPROBE && MTD_JEDECPROBE!=y && (MTD_CFI=m || MTD_JEDECPROBE=m) - default y if MTD_CFI=y || MTD_INTELPROBE || MTD_JEDECPROBE=y config MTD_CFI_ADV_OPTIONS bool "Flash chip driver advanced configuration options" @@ -158,6 +158,7 @@ config MTD_CFI_I8 config MTD_CFI_INTELEXT tristate "Support for Intel/Sharp flash chips" depends on MTD_GEN_PROBE + select MTD_CFI_UTIL help The Common Flash Interface defines a number of different command sets which a CFI-compliant chip may claim to implement. This code @@ -167,6 +168,7 @@ config MTD_CFI_INTELEXT config MTD_CFI_AMDSTD tristate "Support for AMD/Fujitsu flash chips" depends on MTD_GEN_PROBE + select MTD_CFI_UTIL help The Common Flash Interface defines a number of different command sets which a CFI-compliant chip may claim to implement. This code @@ -197,6 +199,7 @@ config MTD_CFI_AMDSTD_RETRY_MAX config MTD_CFI_STAA tristate "Support for ST (Advanced Architecture) flash chips" depends on MTD_GEN_PROBE + select MTD_CFI_UTIL help The Common Flash Interface defines a number of different command sets which a CFI-compliant chip may claim to implement. This code @@ -204,8 +207,6 @@ config MTD_CFI_STAA config MTD_CFI_UTIL tristate - default y if MTD_CFI_INTELEXT=y || MTD_CFI_AMDSTD=y || MTD_CFI_STAA=y - default m if MTD_CFI_INTELEXT=m || MTD_CFI_AMDSTD=m || MTD_CFI_STAA=m config MTD_RAM tristate "Support for RAM chips in bus mapping" @@ -272,5 +273,14 @@ config MTD_JEDEC <http://www.jedec.org/> distributes the identification codes for the chips. +config MTD_XIP + bool "XIP aware MTD support" + depends on !SMP && MTD_CFI_INTELEXT && EXPERIMENTAL + default y if XIP_KERNEL + help + This allows MTD support to work with flash memory which is also + used for XIP purposes. If you're not sure what this is all about + then say N. + endmenu diff --git a/drivers/mtd/chips/amd_flash.c b/drivers/mtd/chips/amd_flash.c index 1c1640ebecf2..41e2e3e31603 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.25 2004/08/09 13:19:43 dwmw2 Exp $ + * $Id: amd_flash.c,v 1.26 2004/11/20 12:49:04 dwmw2 Exp $ * * Copyright (c) 2001 Axis Communications AB * @@ -1122,7 +1122,7 @@ retry: timeo = jiffies + (HZ * 20); spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); + msleep(1000); spin_lock_bh(chip->mutex); while (flash_is_busy(map, adr, private->interleave)) { diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 9028cacedd1b..731102d0e028 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -4,9 +4,8 @@ * * (C) 2000 Red Hat. GPL'd * - * $Id: cfi_cmdset_0001.c,v 1.160 2004/11/01 06:02:24 nico Exp $ - * (+ suspend fix from v1.162) - * (+ partition detection fix from v1.163) + * $Id: cfi_cmdset_0001.c,v 1.164 2004/11/16 18:29:00 dwmw2 Exp $ + * * * 10/10/2000 Nicolas Pitre <nico@cam.org> * - completely revamped method functions so they are aware and @@ -30,6 +29,7 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/mtd/xip.h> #include <linux/mtd/map.h> #include <linux/mtd/mtd.h> #include <linux/mtd/compatmac.h> @@ -37,6 +37,10 @@ /* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */ +#ifdef CONFIG_MTD_XIP +#define CMDSET0001_DISABLE_WRITE_SUSPEND +#endif + // debugging, turns off buffer write mode if set to 1 #define FORCE_WORD_WRITE 0 @@ -147,6 +151,21 @@ static void fixup_intel_strataflash(struct mtd_info *mtd, void* param) } #endif +#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND +/* The XIP config appears to have problems using write suspend at the moment */ +static void fixup_no_write_suspend(struct mtd_info *mtd, void* param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *cfip = cfi->cmdset_priv; + + if (cfip && (cfip->FeatureSupport&4)) { + cfip->FeatureSupport &= ~4; + printk(KERN_WARNING "cfi_cmdset_0001: write suspend disabled\n"); + } +} +#endif + static void fixup_st_m28w320ct(struct mtd_info *mtd, void* param) { struct map_info *map = mtd->priv; @@ -189,6 +208,9 @@ static struct cfi_fixup cfi_fixup_table[] = { #ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE { CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL }, #endif +#ifdef CMDSET0001_DISABLE_WRITE_SUSPEND + { CFI_MFR_ANY, CFI_ID_ANY, fixup_no_write_suspend, NULL }, +#endif #if !FORCE_WORD_WRITE { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL }, #endif @@ -679,6 +701,14 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr chip->state = FL_STATUS; return 0; + case FL_XIP_WHILE_ERASING: + if (mode != FL_READY && mode != FL_POINT && + (mode != FL_WRITING || !cfip || !(cfip->SuspendCmdSupport&1))) + 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) @@ -746,6 +776,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: case FL_JEDEC_QUERY: @@ -758,6 +793,201 @@ 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. + * 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, + 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 */ + preempt_disable(); + 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(0xff), adr); + chip->state = FL_READY; + } + (void) map_read(map, adr); + asm volatile (".rep 8; nop; .endr"); /* fill instruction prefetch */ + local_irq_enable(); + preempt_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 or write 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_intelext *cfip = cfi->cmdset_priv; + map_word status, OK = CMD(0x80); + unsigned long suspended, start = xip_currtime(); + flstate_t oldstate, newstate; + + do { + cpu_relax(); + if (xip_irqpending() && cfip && + ((chip->state == FL_ERASING && (cfip->FeatureSupport&2)) || + (chip->state == FL_WRITING && (cfip->FeatureSupport&4))) && + (cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) { + /* + * Let's suspend the erase or write 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); + map_write(map, CMD(0x70), 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 (oldstate == FL_ERASING) { + if (!map_word_bitsset(map, status, CMD(0x40))) + break; + newstate = FL_XIP_WHILE_ERASING; + chip->erase_suspended = 1; + } else { + if (!map_word_bitsset(map, status, CMD(0x04))) + break; + newstate = FL_XIP_WHILE_WRITING; + chip->write_suspended = 1; + } + chip->state = newstate; + map_write(map, CMD(0xff), adr); + (void) map_read(map, adr); + asm volatile (".rep 8; nop; .endr"); + local_irq_enable(); + preempt_enable(); + 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. + */ + preempt_disable(); + while (chip->state != newstate) { + DECLARE_WAITQUEUE(wait, current); + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + preempt_enable(); + schedule(); + remove_wait_queue(&chip->wq, &wait); + preempt_disable(); + } + /* Disallow XIP again */ + local_irq_disable(); + + /* Resume the write or erase operation */ + map_write(map, CMD(0xd0), adr); + map_write(map, CMD(0x70), 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. + */ +#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) + +/* + * 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 UDELAY(map, chip, adr, usec) cfi_udelay(usec) + +#define XIP_INVAL_CACHED_RANGE(x...) + +#endif + static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len) { unsigned long cmd_addr; @@ -944,7 +1174,11 @@ static int cfi_intelext_read (struct mtd_info *mtd, loff_t from, size_t len, siz } #if 0 -static int 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) +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; @@ -973,6 +1207,8 @@ static int cfi_intelext_read_prot_reg (struct mtd_info *mtd, loff_t from, size_t 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; @@ -985,6 +1221,7 @@ static int cfi_intelext_read_prot_reg (struct mtd_info *mtd, loff_t from, size_t count--; } + xip_enable(map, chip, chip->start); put_chip(map, chip, chip->start); spin_unlock(chip->mutex); @@ -1036,7 +1273,8 @@ static int cfi_intelext_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, s } #endif -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; map_word status, status_OK; @@ -1055,14 +1293,16 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned return ret; } + 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, datum, adr); chip->state = FL_WRITING; spin_unlock(chip->mutex); INVALIDATE_CACHED_RANGE(map, adr, map_bankwidth(map)); - cfi_udelay(chip->word_write_time); + UDELAY(map, chip, adr, chip->word_write_time); spin_lock(chip->mutex); timeo = jiffies + (HZ/2); @@ -1089,6 +1329,7 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned /* OK Still waiting */ if (time_after(jiffies, timeo)) { chip->state = FL_STATUS; + xip_enable(map, chip, adr); printk(KERN_ERR "waiting for chip to be ready timed out in word write\n"); ret = -EIO; goto out; @@ -1097,7 +1338,7 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock(chip->mutex); z++; - cfi_udelay(1); + UDELAY(map, chip, adr, 1); spin_lock(chip->mutex); } if (!z) { @@ -1119,8 +1360,9 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned map_write(map, CMD(0x70), adr); ret = -EROFS; } - out: - put_chip(map, chip, adr); + + xip_enable(map, chip, adr); + out: put_chip(map, chip, adr); spin_unlock(chip->mutex); return ret; @@ -1210,8 +1452,8 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le } -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; map_word status, status_OK; @@ -1232,6 +1474,10 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, return ret; } + XIP_INVAL_CACHED_RANGE(map, adr, len); + ENABLE_VPP(map); + xip_disable(map, chip, cmd_adr); + /* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set [...], the device will not accept any more Write to Buffer commands". So we must check here and reset those bits if they're set. Otherwise @@ -1240,12 +1486,13 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, map_write(map, CMD(0x70), cmd_adr); status = map_read(map, cmd_adr); if (map_word_bitsset(map, status, CMD(0x30))) { + xip_enable(map, chip, cmd_adr); printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %lx). Clearing.\n", status.x[0]); + xip_disable(map, chip, cmd_adr); map_write(map, CMD(0x50), cmd_adr); map_write(map, CMD(0x70), cmd_adr); } - ENABLE_VPP(map); chip->state = FL_WRITING_TO_BUFFER; z = 0; @@ -1257,7 +1504,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, break; spin_unlock(chip->mutex); - cfi_udelay(1); + UDELAY(map, chip, cmd_adr, 1); spin_lock(chip->mutex); if (++z > 20) { @@ -1269,6 +1516,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, /* Odd. Clear status bits */ map_write(map, CMD(0x50), cmd_adr); map_write(map, CMD(0x70), cmd_adr); + xip_enable(map, chip, cmd_adr); printk(KERN_ERR "Chip not ready for buffer write. status = %lx, Xstatus = %lx\n", status.x[0], Xstatus.x[0]); ret = -EIO; @@ -1305,7 +1553,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, spin_unlock(chip->mutex); INVALIDATE_CACHED_RANGE(map, adr, len); - cfi_udelay(chip->buffer_write_time); + UDELAY(map, chip, cmd_adr, chip->buffer_write_time); spin_lock(chip->mutex); timeo = jiffies + (HZ/2); @@ -1331,6 +1579,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, /* OK Still waiting */ if (time_after(jiffies, timeo)) { chip->state = FL_STATUS; + xip_enable(map, chip, cmd_adr); printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n"); ret = -EIO; goto out; @@ -1338,7 +1587,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock(chip->mutex); - cfi_udelay(1); + UDELAY(map, chip, cmd_adr, 1); z++; spin_lock(chip->mutex); } @@ -1362,8 +1611,8 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, ret = -EROFS; } - out: - put_chip(map, chip, cmd_adr); + xip_enable(map, chip, cmd_adr); + out: put_chip(map, chip, cmd_adr); spin_unlock(chip->mutex); return ret; } @@ -1432,8 +1681,8 @@ static int cfi_intelext_write_buffers (struct mtd_info *mtd, loff_t to, return 0; } -static 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; map_word status, status_OK; @@ -1455,7 +1704,10 @@ static int do_erase_oneblock(struct map_info *map, struct flchip *chip, return ret; } + XIP_INVAL_CACHED_RANGE(map, adr, len); ENABLE_VPP(map); + xip_disable(map, chip, adr); + /* Clear the status register first */ map_write(map, CMD(0x50), adr); @@ -1467,7 +1719,7 @@ static int do_erase_oneblock(struct map_info *map, struct flchip *chip, spin_unlock(chip->mutex); INVALIDATE_CACHED_RANGE(map, adr, len); - msleep(chip->erase_time / 2); + UDELAY(map, chip, adr, chip->erase_time*1000/2); spin_lock(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ @@ -1505,6 +1757,7 @@ static int do_erase_oneblock(struct map_info *map, struct flchip *chip, /* Clear status bits */ map_write(map, CMD(0x50), adr); map_write(map, CMD(0x70), adr); + xip_enable(map, chip, adr); printk(KERN_ERR "waiting for erase at %08lx to complete timed out. status = %lx, Xstatus = %lx.\n", adr, status.x[0], Xstatus.x[0]); ret = -EIO; @@ -1513,8 +1766,7 @@ static int do_erase_oneblock(struct map_info *map, struct flchip *chip, /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock(chip->mutex); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); + UDELAY(map, chip, adr, 1000000/HZ); spin_lock(chip->mutex); } @@ -1530,6 +1782,7 @@ static int do_erase_oneblock(struct map_info *map, struct flchip *chip, /* 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))) { @@ -1565,6 +1818,7 @@ static int do_erase_oneblock(struct map_info *map, struct flchip *chip, ret = -EIO; } } else { + xip_enable(map, chip, adr); ret = 0; } @@ -1632,15 +1886,19 @@ static void cfi_intelext_sync (struct mtd_info *mtd) } #ifdef DEBUG_LOCK_BITS -static int do_printlockstatus_oneblock(struct map_info *map, struct flchip *chip, - unsigned long adr, int len, void *thunk) +static int __xipram do_printlockstatus_oneblock(struct map_info *map, + struct flchip *chip, + unsigned long adr, + int len, void *thunk) { struct cfi_private *cfi = map->fldrv_priv; int status, ofs_factor = cfi->interleave * cfi->device_type; + xip_disable(map, chip, adr+(2*ofs_factor)); cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); chip->state = FL_JEDEC_QUERY; status = cfi_read_query(map, adr+(2*ofs_factor)); + xip_enable(map, chip, 0); printk(KERN_DEBUG "block status register for 0x%08lx is %x\n", adr, status); return 0; @@ -1650,8 +1908,8 @@ static int do_printlockstatus_oneblock(struct map_info *map, struct flchip *chip #define DO_XXLOCK_ONEBLOCK_LOCK ((void *) 1) #define DO_XXLOCK_ONEBLOCK_UNLOCK ((void *) 2) -static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, - unsigned long adr, int len, void *thunk) +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; map_word status, status_OK; @@ -1671,8 +1929,9 @@ static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, } ENABLE_VPP(map); + xip_disable(map, chip, adr); + map_write(map, CMD(0x60), adr); - if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) { map_write(map, CMD(0x01), adr); chip->state = FL_LOCKING; @@ -1683,7 +1942,7 @@ static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, BUG(); spin_unlock(chip->mutex); - schedule_timeout(HZ); + UDELAY(map, chip, adr, 1000000/HZ); spin_lock(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ @@ -1702,6 +1961,7 @@ static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; Xstatus = map_read(map, adr); + xip_enable(map, chip, adr); printk(KERN_ERR "waiting for unlock to complete timed out. status = %lx, Xstatus = %lx.\n", status.x[0], Xstatus.x[0]); put_chip(map, chip, adr); @@ -1711,12 +1971,13 @@ static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, /* Latency issues. Drop the lock, wait a while and retry */ spin_unlock(chip->mutex); - cfi_udelay(1); + UDELAY(map, chip, adr, 1); spin_lock(chip->mutex); } /* Done and happy. */ chip->state = FL_STATUS; + xip_enable(map, chip, adr); put_chip(map, chip, adr); spin_unlock(chip->mutex); return 0; @@ -1875,7 +2136,7 @@ static void cfi_intelext_destroy(struct mtd_info *mtd) static char im_name_1[]="cfi_cmdset_0001"; static char im_name_3[]="cfi_cmdset_0003"; -int __init cfi_intelext_init(void) +static int __init cfi_intelext_init(void) { inter_module_register(im_name_1, THIS_MODULE, &cfi_cmdset_0001); inter_module_register(im_name_3, THIS_MODULE, &cfi_cmdset_0001); diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 69a612659813..fca8ff6f7e14 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -13,7 +13,7 @@ * * This code is GPL * - * $Id: cfi_cmdset_0002.c,v 1.111 2004/11/16 18:29:00 dwmw2 Exp $ + * $Id: cfi_cmdset_0002.c,v 1.114 2004/12/11 15:43:53 dedekind Exp $ * */ @@ -707,7 +707,7 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned */ unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; int ret = 0; - map_word oldd, curd; + map_word oldd; int retry_cnt = 0; adr += chip->start; @@ -764,23 +764,11 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned continue; } - /* Test to see if toggling has stopped. */ - oldd = map_read(map, adr); - curd = map_read(map, adr); - if (map_word_equal(map, curd, oldd)) { - /* Do we have the correct value? */ - if (map_word_equal(map, curd, datum)) { - goto op_done; - } - /* Nope something has gone wrong. */ - break; - } + if (chip_ready(map, adr)) + goto op_done; - if (time_after(jiffies, timeo)) { - printk(KERN_WARNING "MTD %s(): software timeout\n", - __func__ ); - break; - } + if (time_after(jiffies, timeo)) + break; /* Latency issues. Drop the lock, wait a while and retry */ cfi_spin_unlock(chip->mutex); @@ -788,6 +776,8 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned cfi_spin_lock(chip->mutex); } + 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 */ @@ -1173,8 +1163,7 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip) chip->in_progress_block_addr = adr; cfi_spin_unlock(chip->mutex); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((chip->erase_time*HZ)/(2*1000)); + msleep(chip->erase_time/2); cfi_spin_lock(chip->mutex); timeo = jiffies + (HZ*20); @@ -1259,8 +1248,7 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u chip->in_progress_block_addr = adr; cfi_spin_unlock(chip->mutex); - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((chip->erase_time*HZ)/(2*1000)); + msleep(chip->erase_time/2); cfi_spin_lock(chip->mutex); timeo = jiffies + (HZ*20); diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index e5957ceaa022..8c24e18db3b4 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -4,7 +4,7 @@ * * (C) 2000 Red Hat. GPL'd * - * $Id: cfi_cmdset_0020.c,v 1.16 2004/11/16 18:29:00 dwmw2 Exp $ + * $Id: cfi_cmdset_0020.c,v 1.17 2004/11/20 12:49:04 dwmw2 Exp $ * * 10/10/2000 Nicolas Pitre <nico@cam.org> * - completely revamped method functions so they are aware and @@ -788,7 +788,7 @@ retry: chip->state = FL_ERASING; spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); + msleep(1000); spin_lock_bh(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ @@ -1087,7 +1087,7 @@ retry: chip->state = FL_LOCKING; spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); + msleep(1000); spin_lock_bh(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ @@ -1236,7 +1236,7 @@ retry: chip->state = FL_UNLOCKING; spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); + msleep(1000); spin_lock_bh(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index c2a3894d5c91..cf750038ce6a 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -1,7 +1,7 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: cfi_probe.c,v 1.79 2004/10/20 23:04:01 dwmw2 Exp $ + $Id: cfi_probe.c,v 1.83 2004/11/16 18:19:02 nico Exp $ */ #include <linux/config.h> @@ -15,6 +15,7 @@ #include <linux/slab.h> #include <linux/interrupt.h> +#include <linux/mtd/xip.h> #include <linux/mtd/map.h> #include <linux/mtd/cfi.h> #include <linux/mtd/gen_probe.h> @@ -31,11 +32,47 @@ static int cfi_chip_setup(struct map_info *map, struct cfi_private *cfi); struct mtd_info *cfi_probe(struct map_info *map); +#ifdef CONFIG_MTD_XIP + +/* only needed for short periods, so this is rather simple */ +#define xip_disable() local_irq_disable() + +#define xip_allowed(base, map) \ +do { \ + (void) map_read(map, base); \ + asm volatile (".rep 8; nop; .endr"); \ + local_irq_enable(); \ +} while (0) + +#define xip_enable(base, map, cfi) \ +do { \ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); \ + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); \ + xip_allowed(base, map); \ +} while (0) + +#define xip_disable_qry(base, map, cfi) \ +do { \ + xip_disable(); \ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); \ + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); \ + cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); \ +} while (0) + +#else + +#define xip_disable() do { } while (0) +#define xip_allowed(base, map) do { } while (0) +#define xip_enable(base, map, cfi) do { } while (0) +#define xip_disable_qry(base, map, cfi) do { } while (0) + +#endif + /* check for QRY. in: interleave,type,mode ret: table index, <0 for error */ -static int qry_present(struct map_info *map, __u32 base, +static int __xipram qry_present(struct map_info *map, __u32 base, struct cfi_private *cfi) { int osf = cfi->interleave * cfi->device_type; // scale factor @@ -59,11 +96,11 @@ static int qry_present(struct map_info *map, __u32 base, if (!map_word_equal(map, qry[2], val[2])) return 0; - return 1; // nothing found + return 1; // "QRY" found } -static int cfi_probe_chip(struct map_info *map, __u32 base, - unsigned long *chip_map, struct cfi_private *cfi) +static int __xipram cfi_probe_chip(struct map_info *map, __u32 base, + unsigned long *chip_map, struct cfi_private *cfi) { int i; @@ -79,12 +116,16 @@ static int cfi_probe_chip(struct map_info *map, __u32 base, (unsigned long)base + 0x55, map->size -1); return 0; } + + xip_disable(); cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); - if (!qry_present(map,base,cfi)) + if (!qry_present(map,base,cfi)) { + xip_enable(base, map, cfi); return 0; + } if (!cfi->numchips) { /* This is the first time we're called. Set up the CFI @@ -110,6 +151,7 @@ static int cfi_probe_chip(struct map_info *map, __u32 base, /* If the QRY marker goes away, it's an alias */ if (!qry_present(map, start, cfi)) { + xip_allowed(base, map); printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", map->name, base, start); return 0; @@ -122,6 +164,7 @@ static int cfi_probe_chip(struct map_info *map, __u32 base, cfi_send_gen_cmd(0xFF, 0, start, map, cfi, cfi->device_type, NULL); if (qry_present(map, base, cfi)) { + xip_allowed(base, map); printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", map->name, base, start); return 0; @@ -137,6 +180,7 @@ static int cfi_probe_chip(struct map_info *map, __u32 base, /* Put it back into Read Mode */ cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); + xip_allowed(base, map); printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n", map->name, cfi->interleave, cfi->device_type*8, base, @@ -145,14 +189,15 @@ static int cfi_probe_chip(struct map_info *map, __u32 base, return 1; } -static int cfi_chip_setup(struct map_info *map, - struct cfi_private *cfi) +static int __xipram cfi_chip_setup(struct map_info *map, + struct cfi_private *cfi) { int ofs_factor = cfi->interleave*cfi->device_type; __u32 base = 0; int num_erase_regions = cfi_read_query(map, base + (0x10 + 28)*ofs_factor); int i; + xip_enable(base, map, cfi); #ifdef DEBUG_CFI printk("Number of erase regions: %d\n", num_erase_regions); #endif @@ -170,13 +215,33 @@ static int cfi_chip_setup(struct map_info *map, cfi->cfi_mode = CFI_MODE_CFI; /* Read the CFI info structure */ - for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) { + xip_disable_qry(base, map, cfi); + for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) ((unsigned char *)cfi->cfiq)[i] = cfi_read_query(map,base + (0x10 + i)*ofs_factor); - } - + + /* Note we put the device back into Read Mode BEFORE going into Auto + * Select Mode, as some devices support nesting of modes, others + * don't. This way should always work. + * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and + * so should be treated as nops or illegal (and so put the device + * back into Read Mode, which is a nop in this case). + */ + cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); + cfi->mfr = cfi_read_query(map, base); + cfi->id = cfi_read_query(map, base + ofs_factor); + + /* Put it back into Read Mode */ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + /* ... even if it's an Intel chip */ + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); + xip_allowed(base, map); + /* Do any necessary byteswapping */ cfi->cfiq->P_ID = le16_to_cpu(cfi->cfiq->P_ID); - + cfi->cfiq->P_ADR = le16_to_cpu(cfi->cfiq->P_ADR); cfi->cfiq->A_ID = le16_to_cpu(cfi->cfiq->A_ID); cfi->cfiq->A_ADR = le16_to_cpu(cfi->cfiq->A_ADR); @@ -198,25 +263,6 @@ static int cfi_chip_setup(struct map_info *map, #endif } - /* Note we put the device back into Read Mode BEFORE going into Auto - * Select Mode, as some devices support nesting of modes, others - * don't. This way should always work. - * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and - * so should be treated as nops or illegal (and so put the device - * back into Read Mode, which is a nop in this case). - */ - cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); - cfi->mfr = cfi_read_query(map, base); - cfi->id = cfi_read_query(map, base + ofs_factor); - - /* Put it back into Read Mode */ - cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); - /* ... even if it's an Intel chip */ - cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); - printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n", map->name, cfi->interleave, cfi->device_type*8, base, map->bankwidth*8); diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index 49c97bc96bba..2b2ede2bfcca 100644 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -7,7 +7,7 @@ * * This code is covered by the GPL. * - * $Id: cfi_util.c,v 1.5 2004/08/12 06:40:23 eric Exp $ + * $Id: cfi_util.c,v 1.8 2004/12/14 19:55:56 nico Exp $ * */ @@ -22,13 +22,14 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/interrupt.h> +#include <linux/mtd/xip.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> #include <linux/mtd/cfi.h> #include <linux/mtd/compatmac.h> struct cfi_extquery * -cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name) +__xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name) { struct cfi_private *cfi = map->fldrv_priv; __u32 base = 0; // cfi->chips[0].start; @@ -40,21 +41,35 @@ cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name) if (!adr) goto out; - /* Switch it into Query Mode */ - cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); - extp = kmalloc(size, GFP_KERNEL); if (!extp) { printk(KERN_ERR "Failed to allocate memory\n"); goto out; } - + +#ifdef CONFIG_MTD_XIP + local_irq_disable(); +#endif + + /* Switch it into Query Mode */ + cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); + /* Read in the Extended Query Table */ for (i=0; i<size; i++) { ((unsigned char *)extp)[i] = cfi_read_query(map, base+((adr+i)*ofs_factor)); } + /* Make sure it returns to read mode */ + cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL); + +#ifdef CONFIG_MTD_XIP + (void) map_read(map, base); + asm volatile (".rep 8; nop; .endr"); + local_irq_enable(); +#endif + if (extp->MajorVersion != '1' || (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { printk(KERN_WARNING " Unknown %s Extended Query " @@ -62,15 +77,9 @@ cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name) extp->MinorVersion); kfree(extp); extp = NULL; - goto out; } -out: - /* Make sure it's in read mode */ - cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL); - - return extp; + out: return extp; } EXPORT_SYMBOL(cfi_read_pri); @@ -156,7 +165,6 @@ int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, i=first; while(len) { - unsigned long chipmask; int size = regions[i].erasesize; ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk); @@ -165,10 +173,10 @@ int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, return ret; adr += size; + ofs += size; len -= size; - chipmask = (1 << cfi->chipshift) - 1; - if ((adr & chipmask) == ((regions[i].offset + size * regions[i].numblocks) & chipmask)) + if (ofs == regions[i].offset + size * regions[i].numblocks) i++; if (adr >> cfi->chipshift) { diff --git a/drivers/mtd/chips/jedec.c b/drivers/mtd/chips/jedec.c index 2dfd53ed39d2..62d235a9a4e2 100644 --- a/drivers/mtd/chips/jedec.c +++ b/drivers/mtd/chips/jedec.c @@ -11,7 +11,7 @@ * not going to guess how to send commands to them, plus I expect they will * all speak CFI.. * - * $Id: jedec.c,v 1.21 2004/08/09 13:19:43 dwmw2 Exp $ + * $Id: jedec.c,v 1.22 2005/01/05 18:05:11 dwmw2 Exp $ */ #include <linux/init.h> @@ -529,7 +529,7 @@ static int jedec_probe32(struct map_info *map,unsigned long base, static int jedec_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct map_info *map = (struct map_info *)mtd->priv; + struct map_info *map = mtd->priv; map_copy_from(map, buf, from, len); *retlen = len; @@ -541,8 +541,8 @@ static int jedec_read(struct mtd_info *mtd, loff_t from, size_t len, static int jedec_read_banked(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct map_info *map = (struct map_info *)mtd->priv; - struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; + struct map_info *map = mtd->priv; + struct jedec_private *priv = map->fldrv_priv; *retlen = 0; while (len > 0) @@ -593,8 +593,8 @@ static int flash_erase(struct mtd_info *mtd, struct erase_info *instr) unsigned long NoTime = 0; unsigned long start = instr->addr, len = instr->len; unsigned int I; - struct map_info *map = (struct map_info *)mtd->priv; - struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; + struct map_info *map = mtd->priv; + struct jedec_private *priv = map->fldrv_priv; // Verify the arguments.. if (start + len > mtd->size || @@ -800,8 +800,8 @@ static int flash_write(struct mtd_info *mtd, loff_t start, size_t len, #define flread(x) map_read8(map,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift)) #define flwrite(v,x) map_write8(map,v,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift)) - struct map_info *map = (struct map_info *)mtd->priv; - struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; + struct map_info *map = mtd->priv; + struct jedec_private *priv = map->fldrv_priv; unsigned long base; unsigned long off; size_t save_len = len; diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 3e232fd0f7c5..7df2e2185fe3 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.58 2004/11/16 18:29:00 dwmw2 Exp $ + $Id: jedec_probe.c,v 1.61 2004/11/19 20:52:16 thayne Exp $ See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5) for the standard this probe goes back to. @@ -227,6 +227,11 @@ static const struct unlock_addr unlock_addrs[] = { [MTD_UADDR_DONT_CARE] = { .addr1 = 0x0000, /* Doesn't matter which address */ .addr2 = 0x0000 /* is used - must be last entry */ + }, + + [MTD_UADDR_UNNECESSARY] = { + .addr1 = 0x0000, + .addr2 = 0x0000 } }; @@ -514,15 +519,20 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - mfr_id: MANUFACTURER_AMD, - dev_id: AM29F002T, - name: "AMD AM29F002T", - DevSize: SIZE_256KiB, - NumEraseRegions: 4, - regions: {ERASEINFO(0x10000,3), - ERASEINFO(0x08000,1), - ERASEINFO(0x02000,2), - ERASEINFO(0x04000,1) + .mfr_id = MANUFACTURER_AMD, + .dev_id = AM29F002T, + .name = "AMD AM29F002T", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,3), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1), } }, { .mfr_id = MANUFACTURER_ATMEL, @@ -770,15 +780,20 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - mfr_id: MANUFACTURER_HYUNDAI, - dev_id: HY29F002T, - name: "Hyundai HY29F002T", - DevSize: SIZE_256KiB, - NumEraseRegions: 4, - regions: {ERASEINFO(0x10000,3), - ERASEINFO(0x08000,1), - ERASEINFO(0x02000,2), - ERASEINFO(0x04000,1) + .mfr_id = MANUFACTURER_HYUNDAI, + .dev_id = HY29F002T, + .name = "Hyundai HY29F002T", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,3), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1), } }, { .mfr_id = MANUFACTURER_INTEL, @@ -1177,15 +1192,20 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,7), } }, { - mfr_id: MANUFACTURER_MACRONIX, - dev_id: MX29F002T, - name: "Macronix MX29F002T", - DevSize: SIZE_256KiB, - NumEraseRegions: 4, - regions: {ERASEINFO(0x10000,3), - ERASEINFO(0x08000,1), - ERASEINFO(0x02000,2), - ERASEINFO(0x04000,1) + .mfr_id = MANUFACTURER_MACRONIX, + .dev_id = MX29F002T, + .name = "Macronix MX29F002T", + .uaddr = { + [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ + }, + .DevSize = SIZE_256KiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 4, + .regions = { + ERASEINFO(0x10000,3), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1), } }, { .mfr_id = MANUFACTURER_PMC, @@ -1780,7 +1800,6 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) return 0; } - /* Mask out address bits which are smaller than the device type */ p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1; p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2; @@ -1923,7 +1942,6 @@ static int jedec_probe_chip(struct map_info *map, __u32 base, if (MTD_UADDR_UNNECESSARY == uaddr_idx) return 0; - /* Mask out address bits which are smaller than the device type */ cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1; cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2; } diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c index c5e8f520c60d..bd2e876a814b 100644 --- a/drivers/mtd/chips/map_ram.c +++ b/drivers/mtd/chips/map_ram.c @@ -1,7 +1,7 @@ /* * Common code to handle map devices which are simple RAM * (C) 2000 Red Hat. GPL'd. - * $Id: map_ram.c,v 1.21 2004/11/16 18:29:00 dwmw2 Exp $ + * $Id: map_ram.c,v 1.22 2005/01/05 18:05:12 dwmw2 Exp $ */ #include <linux/module.h> @@ -83,7 +83,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map) static int mapram_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct map_info *map = (struct map_info *)mtd->priv; + struct map_info *map = mtd->priv; map_copy_from(map, buf, from, len); *retlen = len; @@ -92,7 +92,7 @@ static int mapram_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *r static int mapram_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - struct map_info *map = (struct map_info *)mtd->priv; + struct map_info *map = mtd->priv; map_copy_to(map, to, buf, len); *retlen = len; @@ -103,7 +103,7 @@ static int mapram_erase (struct mtd_info *mtd, struct erase_info *instr) { /* Yeah, it's inefficient. Who cares? It's faster than a _real_ flash erase. */ - struct map_info *map = (struct map_info *)mtd->priv; + struct map_info *map = mtd->priv; map_word allff; unsigned long i; diff --git a/drivers/mtd/chips/map_rom.c b/drivers/mtd/chips/map_rom.c index 540f90348e4b..624c12c232c8 100644 --- a/drivers/mtd/chips/map_rom.c +++ b/drivers/mtd/chips/map_rom.c @@ -1,7 +1,7 @@ /* * Common code to handle map devices which are simple ROM * (C) 2000 Red Hat. GPL'd. - * $Id: map_rom.c,v 1.22 2004/11/16 18:29:00 dwmw2 Exp $ + * $Id: map_rom.c,v 1.23 2005/01/05 18:05:12 dwmw2 Exp $ */ #include <linux/module.h> @@ -57,7 +57,7 @@ static struct mtd_info *map_rom_probe(struct map_info *map) static int maprom_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct map_info *map = (struct map_info *)mtd->priv; + struct map_info *map = mtd->priv; map_copy_from(map, buf, from, len); *retlen = len; diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c index d5ef1e75b1bc..60ab4b89a2f9 100644 --- a/drivers/mtd/cmdlinepart.c +++ b/drivers/mtd/cmdlinepart.c @@ -1,5 +1,5 @@ /* - * $Id: cmdlinepart.c,v 1.16 2004/11/16 18:28:59 dwmw2 Exp $ + * $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $ * * Read flash partition table from command line * @@ -338,8 +338,10 @@ static int parse_cmdline_partitions(struct mtd_info *master, * This is the handler for our kernel parameter, called from * main.c::checksetup(). Note that we can not yet kmalloc() anything, * so we only save the commandline for later processing. + * + * This function needs to be visible for bootloaders. */ -static int mtdpart_setup(char *s) +int mtdpart_setup(char *s) { cmdline = s; return 1; diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 1613d73dc188..fb862d06e975 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -1,5 +1,5 @@ # drivers/mtd/maps/Kconfig -# $Id: Kconfig,v 1.13 2004/10/01 21:47:13 gleixner Exp $ +# $Id: Kconfig,v 1.15 2004/12/22 17:51:15 joern Exp $ menu "Self-contained MTD device drivers" depends on MTD!=n @@ -125,11 +125,22 @@ config MTD_BLKMTD Testing MTD users (eg JFFS2) on large media and media that might be removed during a write (using the floppy drive). +config MTD_BLOCK2MTD + tristate "MTD using block device (rewrite)" + depends on MTD || EXPERIMENTAL + help + This driver is basically the same at MTD_BLKMTD above, but + experienced some interface changes plus serious speedups. In + the long term, it should replace MTD_BLKMTD. Right now, you + shouldn't entrust important data to it yet. + comment "Disk-On-Chip Device Drivers" config MTD_DOC2000 tristate "M-Systems Disk-On-Chip 2000 and Millennium (DEPRECATED)" depends on MTD + select MTD_DOCPROBE + select MTD_NAND_IDS ---help--- This provides an MTD device driver for the M-Systems DiskOnChip 2000 and Millennium devices. Originally designed for the DiskOnChip @@ -151,6 +162,8 @@ config MTD_DOC2000 config MTD_DOC2001 tristate "M-Systems Disk-On-Chip Millennium-only alternative driver (DEPRECATED)" depends on MTD + select MTD_DOCPROBE + select MTD_NAND_IDS ---help--- This provides an alternative MTD device driver for the M-Systems DiskOnChip Millennium devices. Use this if you have problems with @@ -171,6 +184,8 @@ config MTD_DOC2001 config MTD_DOC2001PLUS tristate "M-Systems Disk-On-Chip Millennium Plus" depends on MTD + select MTD_DOCPROBE + select MTD_NAND_IDS ---help--- This provides an MTD device driver for the M-Systems DiskOnChip Millennium Plus devices. @@ -186,17 +201,10 @@ config MTD_DOC2001PLUS config MTD_DOCPROBE tristate - default m if MTD_DOC2001!=y && MTD_DOC2000!=y && MTD_DOC2001PLUS!=y && (MTD_DOC2001=m || MTD_DOC2000=m || MTD_DOC2001PLUS=m) - default y if MTD_DOC2001=y || MTD_DOC2000=y || MTD_DOC2001PLUS=y - help - This isn't a real config option; it's derived. + select MTD_DOCECC config MTD_DOCECC tristate - default m if MTD_DOCPROBE=m - default y if MTD_DOCPROBE=y - help - This isn't a real config option; it's derived. config MTD_DOCPROBE_ADVANCED bool "Advanced detection options for DiskOnChip" diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 3bb872edac59..e38db348057d 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -1,7 +1,7 @@ # # linux/drivers/devices/Makefile # -# $Id: Makefile.common,v 1.6 2004/07/12 16:07:30 dwmw2 Exp $ +# $Id: Makefile.common,v 1.7 2004/12/22 17:51:15 joern Exp $ # *** BIG UGLY NOTE *** # @@ -22,3 +22,4 @@ obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o obj-$(CONFIG_MTD_MTDRAM) += mtdram.o obj-$(CONFIG_MTD_LART) += lart.o obj-$(CONFIG_MTD_BLKMTD) += blkmtd.o +obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c new file mode 100644 index 000000000000..84e98e45bde2 --- /dev/null +++ b/drivers/mtd/devices/block2mtd.c @@ -0,0 +1,505 @@ +/* + * $Id: block2mtd.c,v 1.23 2005/01/05 17:05:46 dwmw2 Exp $ + * + * blockmtd.c - use a block device as a fake MTD + * + * Author: Simon Evans <spse@secret.org.uk> + * + * Copyright (C) 2001,2002 Simon Evans + * Copyright (C) 2004 + * Copyright (C) 2004 Jörn Engel <joern@wh.fh-wedel.de> + * + * Licence: GPL + * + * How it works: + * The driver uses raw/io to read/write the device and the page + * cache to cache access. Writes update the page cache with the + * new data and mark it dirty and add the page into a BIO which + * is then written out. + * + * It can be loaded Read-Only to prevent erases and writes to the + * medium. + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/blkdev.h> +#include <linux/bio.h> +#include <linux/pagemap.h> +#include <linux/list.h> +#include <linux/init.h> +#include <linux/mtd/mtd.h> +#include <linux/buffer_head.h> + +#define ERROR(fmt, args...) printk(KERN_ERR "blockmtd: " fmt "\n" , ## args) +#define INFO(fmt, args...) printk(KERN_INFO "blockmtd: " fmt "\n" , ## args) + + +/* Default erase size in K, always make it a multiple of PAGE_SIZE */ +#define VERSION "$Revision: 1.23 $" + +/* Info for the block device */ +struct blockmtd_dev { + struct list_head list; + struct block_device *blkdev; + struct mtd_info mtd; + struct semaphore write_mutex; +}; + + +/* Static info about the MTD, used in cleanup_module */ +static LIST_HEAD(blkmtd_device_list); + + +#define PAGE_READAHEAD 64 +void cache_readahead(struct address_space *mapping, int index) +{ + filler_t *filler = (filler_t*)mapping->a_ops->readpage; + int i, pagei; + unsigned ret = 0; + unsigned long end_index; + struct page *page; + LIST_HEAD(page_pool); + struct inode *inode = mapping->host; + loff_t isize = i_size_read(inode); + + if (!isize) { + printk(KERN_INFO "iSize=0 in cache_readahead\n"); + return; + } + + end_index = ((isize - 1) >> PAGE_CACHE_SHIFT); + + spin_lock_irq(&mapping->tree_lock); + for (i = 0; i < PAGE_READAHEAD; i++) { + pagei = index + i; + if (pagei > end_index) { + printk(KERN_INFO "Overrun end of disk in cache readahead\n"); + break; + } + page = radix_tree_lookup(&mapping->page_tree, pagei); + if (page && (!i)) + break; + if (page) + continue; + spin_unlock_irq(&mapping->tree_lock); + page = page_cache_alloc_cold(mapping); + spin_lock_irq(&mapping->tree_lock); + if (!page) + break; + page->index = pagei; + list_add(&page->lru, &page_pool); + ret++; + } + spin_unlock_irq(&mapping->tree_lock); + if (ret) + read_cache_pages(mapping, &page_pool, filler, NULL); +} + + +static struct page* page_readahead(struct address_space *mapping, int index) +{ + filler_t *filler = (filler_t*)mapping->a_ops->readpage; + cache_readahead(mapping, index); + return read_cache_page(mapping, index, filler, NULL); +} + + +/* erase a specified part of the device */ +static int _blockmtd_erase(struct blockmtd_dev *dev, loff_t to, size_t len) +{ + struct address_space *mapping = dev->blkdev->bd_inode->i_mapping; + struct page *page; + int index = to >> PAGE_SHIFT; // page index + int pages = len >> PAGE_SHIFT; + u_long *p; + u_long *max; + + while (pages) { + page = page_readahead(mapping, index); + if (!page) + return -ENOMEM; + if (IS_ERR(page)) + return PTR_ERR(page); + + max = (u_long*)page_address(page) + PAGE_SIZE; + for (p=(u_long*)page_address(page); p<max; p++) + if (*p != -1UL) { + lock_page(page); + memset(page_address(page), 0xff, PAGE_SIZE); + set_page_dirty(page); + unlock_page(page); + break; + } + + page_cache_release(page); + pages--; + index++; + } + return 0; +} +static int blockmtd_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct blockmtd_dev *dev = mtd->priv; + size_t from = instr->addr; + size_t len = instr->len; + int err; + + instr->state = MTD_ERASING; + down(&dev->write_mutex); + err = _blockmtd_erase(dev, from, len); + up(&dev->write_mutex); + if (err) { + ERROR("erase failed err = %d", err); + instr->state = MTD_ERASE_FAILED; + } else + instr->state = MTD_ERASE_DONE; + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + return err; +} + + +static int blockmtd_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct blockmtd_dev *dev = mtd->priv; + struct page *page; + int index = from >> PAGE_SHIFT; + int offset = from & (PAGE_SHIFT-1); + int cpylen; + + if (from > mtd->size) + return -EINVAL; + if (from + len > mtd->size) + len = mtd->size - from; + + if (retlen) + *retlen = 0; + + while (len) { + if ((offset + len) > PAGE_SIZE) + cpylen = PAGE_SIZE - offset; // multiple pages + else + cpylen = len; // this page + len = len - cpylen; + + // Get page + page = page_readahead(dev->blkdev->bd_inode->i_mapping, index); + if (!page) + return -ENOMEM; + if (IS_ERR(page)) + return PTR_ERR(page); + + memcpy(buf, page_address(page) + offset, cpylen); + page_cache_release(page); + + if (retlen) + *retlen += cpylen; + buf += cpylen; + offset = 0; + index++; + } + return 0; +} + + +/* write data to the underlying device */ +static int _blockmtd_write(struct blockmtd_dev *dev, const u_char *buf, + loff_t to, size_t len, size_t *retlen) +{ + struct page *page; + struct address_space *mapping = dev->blkdev->bd_inode->i_mapping; + int index = to >> PAGE_SHIFT; // page index + int offset = to & ~PAGE_MASK; // page offset + int cpylen; + + if (retlen) + *retlen = 0; + while (len) { + if ((offset+len) > PAGE_SIZE) + cpylen = PAGE_SIZE - offset; // multiple pages + else + cpylen = len; // this page + len = len - cpylen; + + // Get page + page = page_readahead(mapping, index); + if (!page) + return -ENOMEM; + if (IS_ERR(page)) + return PTR_ERR(page); + + if (memcmp(page_address(page)+offset, buf, cpylen)) { + lock_page(page); + memcpy(page_address(page) + offset, buf, cpylen); + set_page_dirty(page); + unlock_page(page); + } + page_cache_release(page); + + if (retlen) + *retlen += cpylen; + + buf += cpylen; + offset = 0; + index++; + } + return 0; +} +static int blockmtd_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct blockmtd_dev *dev = mtd->priv; + int err; + + if (!len) + return 0; + if (to >= mtd->size) + return -ENOSPC; + if (to + len > mtd->size) + len = mtd->size - to; + + down(&dev->write_mutex); + err = _blockmtd_write(dev, buf, to, len, retlen); + up(&dev->write_mutex); + if (err > 0) + err = 0; + return err; +} + + +/* sync the device - wait until the write queue is empty */ +static void blockmtd_sync(struct mtd_info *mtd) +{ + struct blockmtd_dev *dev = mtd->priv; + sync_blockdev(dev->blkdev); + return; +} + + +static void blockmtd_free_device(struct blockmtd_dev *dev) +{ + if (!dev) + return; + + kfree(dev->mtd.name); + + if (dev->blkdev) { + invalidate_inode_pages(dev->blkdev->bd_inode->i_mapping); + close_bdev_excl(dev->blkdev); + } + + kfree(dev); +} + + +/* FIXME: ensure that mtd->size % erase_size == 0 */ +static struct blockmtd_dev *add_device(char *devname, int erase_size) +{ + struct block_device *bdev; + struct blockmtd_dev *dev; + + if (!devname) + return NULL; + + dev = kmalloc(sizeof(struct blockmtd_dev), GFP_KERNEL); + if (!dev) + return NULL; + memset(dev, 0, sizeof(*dev)); + + /* Get a handle on the device */ + bdev = open_bdev_excl(devname, O_RDWR, NULL); + if (IS_ERR(bdev)) { + ERROR("error: cannot open device %s", devname); + goto devinit_err; + } + dev->blkdev = bdev; + + if (MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) { + ERROR("attempting to use an MTD device as a block device"); + goto devinit_err; + } + + atomic_set(&bdev->bd_inode->i_mapping->truncate_count, 0); + init_MUTEX(&dev->write_mutex); + + /* Setup the MTD structure */ + /* make the name contain the block device in */ + dev->mtd.name = kmalloc(sizeof("blockmtd: ") + strlen(devname), + GFP_KERNEL); + if (!dev->mtd.name) + goto devinit_err; + + sprintf(dev->mtd.name, "blockmtd: %s", devname); + + dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; + dev->mtd.erasesize = erase_size; + dev->mtd.type = MTD_RAM; + dev->mtd.flags = MTD_CAP_RAM; + dev->mtd.erase = blockmtd_erase; + dev->mtd.write = blockmtd_write; + dev->mtd.writev = default_mtd_writev; + dev->mtd.sync = blockmtd_sync; + dev->mtd.read = blockmtd_read; + dev->mtd.readv = default_mtd_readv; + dev->mtd.priv = dev; + dev->mtd.owner = THIS_MODULE; + + if (add_mtd_device(&dev->mtd)) { + /* Device didnt get added, so free the entry */ + goto devinit_err; + } + list_add(&dev->list, &blkmtd_device_list); + INFO("mtd%d: [%s] erase_size = %dKiB [%ld]", dev->mtd.index, + dev->mtd.name + strlen("blkmtd: "), + dev->mtd.erasesize >> 10, PAGE_SIZE); + return dev; + +devinit_err: + blockmtd_free_device(dev); + return NULL; +} + + +static int ustrtoul(const char *cp, char **endp, unsigned int base) +{ + unsigned long result = simple_strtoul(cp, endp, base); + switch (**endp) { + case 'G' : + result *= 1024; + case 'M': + result *= 1024; + case 'k': + result *= 1024; + /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */ + if ((*endp)[1] == 'i') + (*endp) += 2; + } + return result; +} + + +static int parse_num32(u32 *num32, const char *token) +{ + char *endp; + unsigned long n; + + n = ustrtoul(token, &endp, 0); + if (*endp) + return -EINVAL; + + *num32 = n; + return 0; +} + + +static int parse_name(char **pname, const char *token, size_t limit) +{ + size_t len; + char *name; + + len = strlen(token) + 1; + if (len > limit) + return -ENOSPC; + + name = kmalloc(len, GFP_KERNEL); + if (!name) + return -ENOMEM; + + strcpy(name, token); + + *pname = name; + return 0; +} + + +#define parse_err(fmt, args...) do { \ + ERROR("blockmtd: " fmt "\n", ## args); \ + return 0; \ +} while (0) + +static int blockmtd_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; + size_t erase_size = PAGE_SIZE; + int i, ret; + + if (strnlen(val, sizeof(buf)) >= sizeof(buf)) + parse_err("parameter too long"); + + strcpy(str, val); + + for (i=0; i<2; i++) + token[i] = strsep(&str, ","); + + { /* people dislike typing "echo -n". and it's simple enough */ + char *newline = strrchr(token[1], '\n'); + if (newline && !newline[1]) + *newline = 0; + } + + if (str) + parse_err("too many arguments"); + + if (!token[0]) + parse_err("no argument"); + + ret = parse_name(&name, token[0], 80); + if (ret == -ENOMEM) + parse_err("out of memory"); + if (ret == -ENOSPC) + parse_err("name too long"); + if (ret) + return 0; + + if (token[1]) { + ret = parse_num32(&erase_size, token[1]); + if (ret) + parse_err("illegal erase size"); + } + + add_device(name, erase_size); + + return 0; +} + + +module_param_call(blockmtd, blockmtd_setup, NULL, NULL, 0200); +MODULE_PARM_DESC(blockmtd, "Device to use. \"blockmtd=<dev>[,<erasesize>]\""); + +static int __init blockmtd_init(void) +{ + INFO("version " VERSION); + return 0; +} + + +static void __devexit blockmtd_exit(void) +{ + struct list_head *pos, *next; + + /* Remove the MTD devices */ + list_for_each_safe(pos, next, &blkmtd_device_list) { + struct blockmtd_dev *dev = list_entry(pos, typeof(*dev), list); + blockmtd_sync(&dev->mtd); + del_mtd_device(&dev->mtd); + INFO("mtd%d: [%s] removed", dev->mtd.index, + dev->mtd.name + strlen("blkmtd: ")); + list_del(&dev->list); + blockmtd_free_device(dev); + } +} + + +module_init(blockmtd_init); +module_exit(blockmtd_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Simon Evans <spse@secret.org.uk> and others"); +MODULE_DESCRIPTION("Emulate an MTD using a block device"); diff --git a/drivers/mtd/devices/doc1000.c b/drivers/mtd/devices/doc1000.c deleted file mode 100644 index ff8375fb6d7f..000000000000 --- a/drivers/mtd/devices/doc1000.c +++ /dev/null @@ -1,594 +0,0 @@ -/*====================================================================== - - $Id: doc1000.c,v 1.15 2001/10/02 15:05:13 dwmw2 Exp $ - -======================================================================*/ - - -#include <linux/config.h> -#include <linux/module.h> -#include <asm/uaccess.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/ptrace.h> -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/timer.h> -#include <linux/major.h> -#include <linux/fs.h> -#include <linux/ioctl.h> -#include <asm/io.h> -#include <asm/system.h> -#include <stdarg.h> -#include <linux/delay.h> -#include <linux/init.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/iflash.h> - -/* Parameters that can be set with 'insmod' */ - -static u_long base = 0xe0000; -static int erase_timeout = 10*HZ; /* in ticks */ -static int retry_limit = 4; /* write retries */ -static u_long max_tries = 4096; /* status polling */ - -MODULE_PARM(base,"l"); -MODULE_PARM(erase_timeout, "i"); -MODULE_PARM(retry_limit, "i"); -MODULE_PARM(max_tries, "i"); - -#define WINDOW_SIZE 0x2000 -#define WINDOW_MASK (WINDOW_SIZE - 1) -#define PAGEREG_LO (WINDOW_SIZE) -#define PAGEREG_HI (WINDOW_SIZE + 2) - -static struct mtd_info *mymtd; -static struct timer_list flashcard_timer; - -#define MAX_CELLS 32 -#define MAX_FLASH_DEVICES 8 - -/* A flash region is composed of one or more "cells", where we allow - simultaneous erases if they are in different cells */ - - - -struct mypriv { - u_char *baseaddr; - u_short curpage; - u_char locked; - u_short numdevices; - u_char interleave; - struct erase_info *cur_erases; - wait_queue_head_t wq; - u_char devstat[MAX_FLASH_DEVICES]; - u_long devshift; -}; - - -static void flashcard_periodic(u_long data); -static int flashcard_erase (struct mtd_info *mtd, struct erase_info *instr); -static int flashcard_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); -static int flashcard_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); -static void flashcard_sync (struct mtd_info *mtd); - -static inline void resume_erase(volatile u_char *addr); -static inline int suspend_erase(volatile u_char *addr); -static inline int byte_write (volatile u_char *addr, u_char byte); -static inline int word_write (volatile u_char *addr, __u16 word); -static inline int check_write(volatile u_char *addr); -static inline void block_erase (volatile u_char *addr); -static inline int check_erase(volatile u_char *addr); - -#ifdef CONFIG_SMP -#warning This is definitely not SMP safe. Lock the paging mechanism. -#endif - -static u_char *pagein(struct mtd_info *mtd, u_long addr) -{ - struct mypriv *priv=mtd->priv; - u_short page = addr >> 13; - - priv->baseaddr[PAGEREG_LO] = page & 0xff; - priv->baseaddr[PAGEREG_HI] = page >> 8; - priv->curpage = page; - - return &priv->baseaddr[addr & WINDOW_MASK]; -} - - -void flashcard_sync (struct mtd_info *mtd) -{ - struct mypriv *priv=mtd->priv; - - flashcard_periodic((u_long) mtd); - printk("sync..."); - if (priv->cur_erases) - interruptible_sleep_on(&priv->wq); - printk("Done.\n"); -} - -int flashcard_erase (struct mtd_info *mtd, struct erase_info *instr) -{ - u_char *pageaddr; - struct mypriv *priv=mtd->priv; - struct erase_info **tmp=&priv->cur_erases; - - if (instr->len != mtd->erasesize) - return -EINVAL; - if (instr->addr + instr->len > mtd->size) - return -EINVAL; - - pageaddr=pagein(mtd,instr->addr); - instr->mtd = mtd; - instr->dev = instr->addr >> priv->devshift; - instr->cell = (instr->addr - (instr->dev << priv->devshift)) / mtd->erasesize; - instr->next = NULL; - instr->state = MTD_ERASE_PENDING; - - while (*tmp) - { - tmp = &((*tmp) -> next); - } - - *tmp = instr; - flashcard_periodic((u_long)mtd); - return 0; -} - - -int flashcard_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) -{ - u_char *pageaddr=pagein(mtd,from); - struct mypriv *priv=mtd->priv; - u_char device = from >> priv->devshift; - u_char cell = (int) (from - (device << priv->devshift)) / mtd->erasesize; - int ret = 0, timeron = 0; - - if ((from & WINDOW_MASK) + len <= WINDOW_SIZE) - *retlen = len; - else - *retlen = WINDOW_SIZE - (from & WINDOW_MASK); - - if (priv->devstat[device]) - { - - /* There is an erase in progress or pending for this device. Stop it */ - timeron = del_timer(&flashcard_timer); - - if (priv->cur_erases && priv->cur_erases->cell == cell) - - { - /* The erase is on the current cell. Just return all 0xff */ - add_timer(&flashcard_timer); - - - printk("Cell %d currently erasing. Setting to all 0xff\n",cell); - memset(buf, 0xff, *retlen); - return 0; - } - if (priv->devstat[device] == MTD_ERASING) - { - ret = suspend_erase(pageaddr); - priv->devstat[device] = MTD_ERASE_SUSPEND; - - if (ret) - { - printk("flashcard: failed to suspend erase\n"); - add_timer (&flashcard_timer); - return ret; - } - } - - } - - writew(IF_READ_ARRAY, (u_long)pageaddr & ~1); - - ret = 0; - memcpy (buf, pageaddr, *retlen); - - writew(IF_READ_CSR, (u_long)pageaddr & ~1); - - - if (priv->devstat[device] & MTD_ERASE_SUSPEND) - { - resume_erase(pageaddr); - priv->devstat[device]=MTD_ERASING; - } - - - if (timeron) add_timer (&flashcard_timer); - - return ret; -} - - -int flashcard_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) -{ - struct mypriv *priv = (struct mypriv *)mtd->priv; - u_char *endaddr, *startaddr; - register u_char *pageaddr; - u_char device = to >> priv->devshift; -/* jiffies_t oldj=jiffies;*/ - int ret; - - while (priv->devstat[device]) - { - flashcard_sync(mtd); - } - - if ((to & WINDOW_MASK) + len <= WINDOW_SIZE) - *retlen = len; - else - *retlen = WINDOW_SIZE - (to & WINDOW_MASK); - - pageaddr = pagein(mtd, to); - startaddr = (u_char *)((u_long) pageaddr & ~1); - endaddr = pageaddr+(*retlen); - - - - /* Set up to read */ - writew(IF_READ_CSR, startaddr); - - /* Make sure it's aligned by reading the first byte if necessary */ - if (to & 1) - { - /* Unaligned access */ - - u_char cbuf; - - cbuf = *buf; - - if (!((u_long)pageaddr & 0xf)) - schedule(); - - ret = byte_write(pageaddr, cbuf); - if (ret) return ret; - - pageaddr++; buf++; - } - - - for ( ; pageaddr + 1 < endaddr; buf += 2, pageaddr += 2) - { - /* if ((u_long)pageaddr & 0xf) schedule();*/ - - ret = word_write(pageaddr, *(__u16 *)buf); - if (ret) - return ret; - } - - if (pageaddr != endaddr) - { - /* One more byte to write at the end. */ - u_char cbuf; - - cbuf = *buf; - - ret = byte_write(pageaddr, cbuf); - - if (ret) return ret; - } - - return check_write(startaddr); -/* printk("Time taken in flashcard_write: %lx jiffies\n",jiffies - oldj);*/ -} - - - - -/*====================================================================*/ - -static inline int byte_write (volatile u_char *addr, u_char byte) -{ - register u_char status; - register u_short i = 0; - - do { - status = readb(addr); - if (status & CSR_WR_READY) - { - writeb(IF_WRITE & 0xff, addr); - writeb(byte, addr); - return 0; - } - i++; - } while(i < max_tries); - - - printk(KERN_NOTICE "flashcard: byte_write timed out, status 0x%x\n",status); - return -EIO; -} - -static inline int word_write (volatile u_char *addr, __u16 word) -{ - register u_short status; - register u_short i = 0; - - do { - status = readw(addr); - if ((status & CSR_WR_READY) == CSR_WR_READY) - { - writew(IF_WRITE, addr); - writew(word, addr); - return 0; - } - i++; - } while(i < max_tries); - - printk(KERN_NOTICE "flashcard: word_write timed out at %p, status 0x%x\n", addr, status); - return -EIO; -} - -static inline void block_erase (volatile u_char *addr) -{ - writew(IF_BLOCK_ERASE, addr); - writew(IF_CONFIRM, addr); -} - - -static inline int check_erase(volatile u_char *addr) -{ - __u16 status; - -/* writew(IF_READ_CSR, addr);*/ - status = readw(addr); - - - if ((status & CSR_WR_READY) != CSR_WR_READY) - return -EBUSY; - - if (status & (CSR_ERA_ERR | CSR_VPP_LOW | CSR_WR_ERR)) - { - printk(KERN_NOTICE "flashcard: erase failed, status 0x%x\n", - status); - return -EIO; - } - - return 0; -} - -static inline int suspend_erase(volatile u_char *addr) -{ - __u16 status; - u_long i = 0; - - writew(IF_ERASE_SUSPEND, addr); - writew(IF_READ_CSR, addr); - - do { - status = readw(addr); - if ((status & CSR_WR_READY) == CSR_WR_READY) - return 0; - i++; - } while(i < max_tries); - - printk(KERN_NOTICE "flashcard: suspend_erase timed out, status 0x%x\n", status); - return -EIO; - -} - -static inline void resume_erase(volatile u_char *addr) -{ - __u16 status; - - writew(IF_READ_CSR, addr); - status = readw(addr); - - /* Only give resume signal if the erase is really suspended */ - if (status & CSR_ERA_SUSPEND) - writew(IF_CONFIRM, addr); -} - -static inline void reset_block(volatile u_char *addr) -{ - u_short i; - __u16 status; - - writew(IF_CLEAR_CSR, addr); - - for (i = 0; i < 100; i++) { - writew(IF_READ_CSR, addr); - status = readw(addr); - if (status != 0xffff) break; - udelay(1000); - } - - writew(IF_READ_CSR, addr); -} - -static inline int check_write(volatile u_char *addr) -{ - u_short status, i = 0; - - writew(IF_READ_CSR, addr); - - do { - status = readw(addr); - if (status & (CSR_WR_ERR | CSR_VPP_LOW)) - { - printk(KERN_NOTICE "flashcard: write failure at %p, status 0x%x\n", addr, status); - reset_block(addr); - return -EIO; - } - if ((status & CSR_WR_READY) == CSR_WR_READY) - return 0; - i++; - } while (i < max_tries); - - printk(KERN_NOTICE "flashcard: write timed out at %p, status 0x%x\n", addr, status); - return -EIO; -} - - -/*====================================================================*/ - - - -static void flashcard_periodic(unsigned long data) -{ - register struct mtd_info *mtd = (struct mtd_info *)data; - register struct mypriv *priv = mtd->priv; - struct erase_info *erase = priv->cur_erases; - u_char *pageaddr; - - del_timer (&flashcard_timer); - - if (!erase) - return; - - pageaddr = pagein(mtd, erase->addr); - - if (erase->state == MTD_ERASE_PENDING) - { - block_erase(pageaddr); - priv->devstat[erase->dev] = erase->state = MTD_ERASING; - erase->time = jiffies; - erase->retries = 0; - } - else if (erase->state == MTD_ERASING) - { - /* It's trying to erase. Check whether it's finished */ - - int ret = check_erase(pageaddr); - - if (!ret) - { - /* It's finished OK */ - priv->devstat[erase->dev] = 0; - priv->cur_erases = erase->next; - erase->state = MTD_ERASE_DONE; - if (erase->callback) - (*(erase->callback))(erase); - else - kfree(erase); - } - else if (ret == -EIO) - { - if (++erase->retries > retry_limit) - { - printk("Failed too many times. Giving up\n"); - priv->cur_erases = erase->next; - priv->devstat[erase->dev] = 0; - erase->state = MTD_ERASE_FAILED; - if (erase->callback) - (*(erase->callback))(erase); - else - kfree(erase); - } - else - priv->devstat[erase->dev] = erase->state = MTD_ERASE_PENDING; - } - else if (erase->time + erase_timeout < jiffies) - { - printk("Flash erase timed out. The world is broken.\n"); - - /* Just ignore and hope it goes away. For a while, read ops will give the CSR - and writes won't work. */ - - priv->cur_erases = erase->next; - priv->devstat[erase->dev] = 0; - erase->state = MTD_ERASE_FAILED; - if (erase->callback) - (*(erase->callback))(erase); - else - kfree(erase); - } - } - - if (priv->cur_erases) - { - flashcard_timer.expires = jiffies + HZ; - add_timer (&flashcard_timer); - } - else - wake_up_interruptible(&priv->wq); - -} - -int __init init_doc1000(void) -{ - struct mypriv *priv; - - if (!base) - { - printk(KERN_NOTICE "flashcard: No start address for memory device.\n"); - return -EINVAL; - } - - mymtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); - - if (!mymtd) - { - printk(KERN_NOTICE "physmem: Cannot allocate memory for new MTD device.\n"); - return -ENOMEM; - } - - memset(mymtd,0,sizeof(struct mtd_info)); - - mymtd->priv = (void *) kmalloc (sizeof(struct mypriv), GFP_KERNEL); - if (!mymtd->priv) - { - kfree(mymtd); - printk(KERN_NOTICE "physmem: Cannot allocate memory for new MTD device's private data.\n"); - return -ENOMEM; - } - - - - - priv=mymtd->priv; - init_waitqueue_head(&priv->wq); - - memset (priv,0,sizeof(struct mypriv)); - - priv->baseaddr = phys_to_virt(base); - priv->numdevices = 4; - - mymtd->name = "M-Systems DiskOnChip 1000"; - - mymtd->size = 0x100000; - mymtd->flags = MTD_CLEAR_BITS | MTD_ERASEABLE; - mymtd->erase = flashcard_erase; - mymtd->point = NULL; - mymtd->unpoint = NULL; - mymtd->read = flashcard_read; - mymtd->write = flashcard_write; - - mymtd->sync = flashcard_sync; - mymtd->erasesize = 0x10000; - // mymtd->interleave = 2; - priv->devshift = 24; - mymtd->type = MTD_NORFLASH; - - if (add_mtd_device(mymtd)) - { - printk(KERN_NOTICE "MTD device registration failed!\n"); - kfree(mymtd->priv); - kfree(mymtd); - return -EAGAIN; - } - - init_timer(&flashcard_timer); - flashcard_timer.function = flashcard_periodic; - flashcard_timer.data = (u_long)mymtd; - return 0; -} - -static void __init cleanup_doc1000(void) -{ - kfree (mymtd->priv); - del_mtd_device(mymtd); - kfree(mymtd); -} - -module_init(init_doc1000); -module_exit(cleanup_doc1000); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); -MODULE_DESCRIPTION("MTD driver for DiskOnChip 1000"); - diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index 9f7aa0b8fea7..5fc532895a24 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -4,7 +4,7 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> * - * $Id: doc2000.c,v 1.64 2004/11/16 18:29:01 dwmw2 Exp $ + * $Id: doc2000.c,v 1.66 2005/01/05 18:05:12 dwmw2 Exp $ */ #include <linux/kernel.h> @@ -527,14 +527,14 @@ static const char im_name[] = "DoC2k_init"; */ static void DoC2k_init(struct mtd_info *mtd) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; struct DiskOnChip *old = NULL; int maxchips; /* We must avoid being called twice for the same device. */ if (doc2klist) - old = (struct DiskOnChip *) doc2klist->priv; + old = doc2klist->priv; while (old) { if (DoC2k_is_alias(old, this)) { @@ -546,7 +546,7 @@ static void DoC2k_init(struct mtd_info *mtd) return; } if (old->nextdoc) - old = (struct DiskOnChip *) old->nextdoc->priv; + old = old->nextdoc->priv; else old = NULL; } @@ -633,7 +633,7 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem *docptr = this->virtadr; struct Nand *mychip; unsigned char syndrome[6]; @@ -790,7 +790,7 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; int di; /* Yes, DI is a hangover from when I was disassembling the binary driver */ void __iomem *docptr = this->virtadr; volatile char dummy; @@ -1033,7 +1033,7 @@ static int doc_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t * retlen, u_char * buf) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; int len256 = 0, ret; struct Nand *mychip; @@ -1091,7 +1091,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, size_t * retlen, const u_char * buf) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; int len256 = 0; void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; @@ -1194,7 +1194,7 @@ static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t * retlen, const u_char * buf) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; int ret; down(&this->lock); @@ -1206,7 +1206,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, static int doc_erase(struct mtd_info *mtd, struct erase_info *instr) { - struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *this = mtd->priv; __u32 ofs = instr->addr; __u32 len = instr->len; volatile int dummy; @@ -1288,7 +1288,7 @@ static void __exit cleanup_doc2000(void) struct DiskOnChip *this; while ((mtd = doc2klist)) { - this = (struct DiskOnChip *) mtd->priv; + this = mtd->priv; doc2klist = this->nextdoc; del_mtd_device(mtd); diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c index 8d9bc75015b3..1e704915ef08 100644 --- a/drivers/mtd/devices/doc2001.c +++ b/drivers/mtd/devices/doc2001.c @@ -4,7 +4,7 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> * - * $Id: doc2001.c,v 1.46 2004/11/16 18:29:01 dwmw2 Exp $ + * $Id: doc2001.c,v 1.48 2005/01/05 18:05:12 dwmw2 Exp $ */ #include <linux/kernel.h> @@ -335,12 +335,12 @@ static const char im_name[] = "DoCMil_init"; */ static void DoCMil_init(struct mtd_info *mtd) { - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; struct DiskOnChip *old = NULL; /* We must avoid being called twice for the same device. */ if (docmillist) - old = (struct DiskOnChip *)docmillist->priv; + old = docmillist->priv; while (old) { if (DoCMil_is_alias(this, old)) { @@ -351,7 +351,7 @@ static void DoCMil_init(struct mtd_info *mtd) return; } if (old->nextdoc) - old = (struct DiskOnChip *)old->nextdoc->priv; + old = old->nextdoc->priv; else old = NULL; } @@ -416,7 +416,7 @@ static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, int i, ret; volatile char dummy; unsigned char syndrome[6]; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[from >> (this->chipshift)]; @@ -542,7 +542,7 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, { int i,ret = 0; volatile char dummy; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[to >> (this->chipshift)]; @@ -677,7 +677,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, int i; #endif volatile char dummy; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; @@ -729,7 +729,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, #endif volatile char dummy; int ret = 0; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem *docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; @@ -796,7 +796,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, int doc_erase (struct mtd_info *mtd, struct erase_info *instr) { volatile char dummy; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; __u32 ofs = instr->addr; __u32 len = instr->len; void __iomem *docptr = this->virtadr; @@ -868,7 +868,7 @@ static void __exit cleanup_doc2001(void) struct DiskOnChip *this; while ((mtd=docmillist)) { - this = (struct DiskOnChip *)mtd->priv; + this = mtd->priv; docmillist = this->nextdoc; del_mtd_device(mtd); diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c index 39e343547897..ed47bafb2ce2 100644 --- a/drivers/mtd/devices/doc2001plus.c +++ b/drivers/mtd/devices/doc2001plus.c @@ -6,7 +6,7 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> * - * $Id: doc2001plus.c,v 1.11 2004/11/16 18:29:01 dwmw2 Exp $ + * $Id: doc2001plus.c,v 1.13 2005/01/05 18:05:12 dwmw2 Exp $ * * Released under GPL */ @@ -190,7 +190,7 @@ static int DoC_SelectFloor(void __iomem * docptr, int floor) may not want it */ static unsigned int DoC_GetDataOffset(struct mtd_info *mtd, loff_t *from) { - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; if (this->interleave) { unsigned int ofs = *from & 0x3ff; @@ -458,12 +458,12 @@ static const char im_name[] = "DoCMilPlus_init"; */ static void DoCMilPlus_init(struct mtd_info *mtd) { - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; struct DiskOnChip *old = NULL; /* We must avoid being called twice for the same device. */ if (docmilpluslist) - old = (struct DiskOnChip *)docmilpluslist->priv; + old = docmilpluslist->priv; while (old) { if (DoCMilPlus_is_alias(this, old)) { @@ -475,7 +475,7 @@ static void DoCMilPlus_init(struct mtd_info *mtd) return; } if (old->nextdoc) - old = (struct DiskOnChip *)old->nextdoc->priv; + old = old->nextdoc->priv; else old = NULL; } @@ -530,7 +530,7 @@ static int doc_dumpblk(struct mtd_info *mtd, loff_t from) { int i; loff_t fofs; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem * docptr = this->virtadr; struct Nand *mychip = &this->chips[from >> (this->chipshift)]; unsigned char *bp, buf[1056]; @@ -615,7 +615,7 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, volatile char dummy; loff_t fofs; unsigned char syndrome[6]; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem * docptr = this->virtadr; struct Nand *mychip = &this->chips[from >> (this->chipshift)]; @@ -754,7 +754,7 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, int i, before, ret = 0; loff_t fto; volatile char dummy; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem * docptr = this->virtadr; struct Nand *mychip = &this->chips[to >> (this->chipshift)]; @@ -880,7 +880,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, u_char *buf) { loff_t fofs, base; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem * docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; size_t i, size, got, want; @@ -958,7 +958,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, { volatile char dummy; loff_t fofs, base; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; void __iomem * docptr = this->virtadr; struct Nand *mychip = &this->chips[ofs >> this->chipshift]; size_t i, size, got, want; @@ -1058,7 +1058,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, int doc_erase(struct mtd_info *mtd, struct erase_info *instr) { volatile char dummy; - struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *this = mtd->priv; __u32 ofs = instr->addr; __u32 len = instr->len; void __iomem * docptr = this->virtadr; @@ -1134,7 +1134,7 @@ static void __exit cleanup_doc2001plus(void) struct DiskOnChip *this; while ((mtd=docmilpluslist)) { - this = (struct DiskOnChip *)mtd->priv; + this = mtd->priv; docmilpluslist = this->nextdoc; del_mtd_device(mtd); diff --git a/drivers/mtd/devices/docprobe.c b/drivers/mtd/devices/docprobe.c index d89dd6d0e579..e64efe338681 100644 --- a/drivers/mtd/devices/docprobe.c +++ b/drivers/mtd/devices/docprobe.c @@ -4,7 +4,7 @@ /* (C) 1999 Machine Vision Holdings, Inc. */ /* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> */ -/* $Id: docprobe.c,v 1.43 2004/11/16 18:29:01 dwmw2 Exp $ */ +/* $Id: docprobe.c,v 1.44 2005/01/05 12:40:36 dwmw2 Exp $ */ diff --git a/drivers/mtd/devices/ms02-nv.c b/drivers/mtd/devices/ms02-nv.c index 42417b05493c..380ff08d29e4 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.7 2004/07/29 14:16:45 macro Exp $ + * $Id: ms02-nv.c,v 1.8 2005/01/05 18:05:12 dwmw2 Exp $ */ #include <linux/init.h> @@ -59,7 +59,7 @@ static struct mtd_info *root_ms02nv_mtd; static int ms02nv_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct ms02nv_private *mp = (struct ms02nv_private *)mtd->priv; + struct ms02nv_private *mp = mtd->priv; if (from + len > mtd->size) return -EINVAL; @@ -73,7 +73,7 @@ static int ms02nv_read(struct mtd_info *mtd, loff_t from, static int ms02nv_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - struct ms02nv_private *mp = (struct ms02nv_private *)mtd->priv; + struct ms02nv_private *mp = mtd->priv; if (to + len > mtd->size) return -EINVAL; @@ -265,7 +265,7 @@ err_out_mod_res: static void __exit ms02nv_remove_one(void) { struct mtd_info *mtd = root_ms02nv_mtd; - struct ms02nv_private *mp = (struct ms02nv_private *)mtd->priv; + struct ms02nv_private *mp = mtd->priv; root_ms02nv_mtd = mp->next; diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index aafe29b9d52f..2f999368960e 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -1,6 +1,6 @@ /* * mtdram - a test mtd device - * $Id: mtdram.c,v 1.34 2004/11/16 18:29:01 dwmw2 Exp $ + * $Id: mtdram.c,v 1.35 2005/01/05 18:05:12 dwmw2 Exp $ * Author: Alexander Larsson <alex@cendio.se> * * Copyright (c) 1999 Alexander Larsson <alex@cendio.se> @@ -158,7 +158,7 @@ static int __init init_mtdram(void) void *addr; int err; /* Allocate some memory */ - mtd_info = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); if (!mtd_info) return -ENOMEM; @@ -191,7 +191,7 @@ static int __init init_mtdram(void) void *addr; int err; /* Allocate some memory */ - mtd_info = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); if (!mtd_info) return -ENOMEM; diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c index 8aab73e6f26a..5f8e164ddb71 100644 --- a/drivers/mtd/devices/phram.c +++ b/drivers/mtd/devices/phram.c @@ -1,13 +1,8 @@ /** + * $Id: phram.c,v 1.11 2005/01/05 18:05:13 dwmw2 Exp $ * - * $Id: phram.c,v 1.3 2004/11/16 18:29:01 dwmw2 Exp $ - * - * Copyright (c) Jochen Schaeuble <psionic@psionic.de> - * 07/2003 rewritten by Joern Engel <joern@wh.fh-wedel.de> - * - * DISCLAIMER: This driver makes use of Rusty's excellent module code, - * so it will not work for 2.4 without changes and it wont work for 2.4 - * as a module without major changes. Oh well! + * Copyright (c) ???? Jochen Schäuble <psionic@psionic.de> + * Copyright (c) 2003-2004 Jörn Engel <joern@wh.fh-wedel.de> * * Usage: * @@ -15,9 +10,12 @@ * phram=<name>,<start>,<len> * <name> may be up to 63 characters. * <start> and <len> can be octal, decimal or hexadecimal. If followed - * by "k", "M" or "G", the numbers will be interpreted as kilo, mega or + * by "ki", "Mi" or "Gi", the numbers will be interpreted as kilo, mega or * gigabytes. * + * Example: + * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi + * */ #include <asm/io.h> @@ -31,8 +29,8 @@ #define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args) struct phram_mtd_list { + struct mtd_info mtd; struct list_head list; - struct mtd_info *mtdinfo; }; static LIST_HEAD(phram_list); @@ -41,7 +39,7 @@ static LIST_HEAD(phram_list); static int phram_erase(struct mtd_info *mtd, struct erase_info *instr) { - u_char *start = (u_char *)mtd->priv; + u_char *start = mtd->priv; if (instr->addr + instr->len > mtd->size) return -EINVAL; @@ -63,7 +61,7 @@ static int phram_erase(struct mtd_info *mtd, struct erase_info *instr) static int phram_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) { - u_char *start = (u_char *)mtd->priv; + u_char *start = mtd->priv; if (from + len > mtd->size) return -EINVAL; @@ -80,7 +78,7 @@ static void phram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_ static int phram_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - u_char *start = (u_char *)mtd->priv; + u_char *start = mtd->priv; if (from + len > mtd->size) return -EINVAL; @@ -94,7 +92,7 @@ static int phram_read(struct mtd_info *mtd, loff_t from, size_t len, static int phram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - u_char *start = (u_char *)mtd->priv; + u_char *start = mtd->priv; if (to + len > mtd->size) return -EINVAL; @@ -112,9 +110,8 @@ static void unregister_devices(void) struct phram_mtd_list *this; list_for_each_entry(this, &phram_list, list) { - del_mtd_device(this->mtdinfo); - iounmap(this->mtdinfo->priv); - kfree(this->mtdinfo); + del_mtd_device(&this->mtd); + iounmap(this->mtd.priv); kfree(this); } } @@ -128,45 +125,39 @@ static int register_device(char *name, unsigned long start, unsigned long len) if (!new) goto out0; - new->mtdinfo = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); - if (!new->mtdinfo) - goto out1; - - memset(new->mtdinfo, 0, sizeof(struct mtd_info)); + memset(new, 0, sizeof(*new)); ret = -EIO; - new->mtdinfo->priv = ioremap(start, len); - if (!new->mtdinfo->priv) { + new->mtd.priv = ioremap(start, len); + if (!new->mtd.priv) { ERROR("ioremap failed\n"); - goto out2; + goto out1; } - new->mtdinfo->name = name; - new->mtdinfo->size = len; - new->mtdinfo->flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE; - new->mtdinfo->erase = phram_erase; - new->mtdinfo->point = phram_point; - new->mtdinfo->unpoint = phram_unpoint; - new->mtdinfo->read = phram_read; - new->mtdinfo->write = phram_write; - new->mtdinfo->owner = THIS_MODULE; - new->mtdinfo->type = MTD_RAM; - new->mtdinfo->erasesize = 0x0; + new->mtd.name = name; + new->mtd.size = len; + new->mtd.flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE; + new->mtd.erase = phram_erase; + new->mtd.point = phram_point; + new->mtd.unpoint = phram_unpoint; + new->mtd.read = phram_read; + new->mtd.write = phram_write; + new->mtd.owner = THIS_MODULE; + new->mtd.type = MTD_RAM; + new->mtd.erasesize = 0; ret = -EAGAIN; - if (add_mtd_device(new->mtdinfo)) { + if (add_mtd_device(&new->mtd)) { ERROR("Failed to register new device\n"); - goto out3; + goto out2; } list_add_tail(&new->list, &phram_list); return 0; -out3: - iounmap(new->mtdinfo->priv); out2: - kfree(new->mtdinfo); + iounmap(new->mtd.priv); out1: kfree(new); out0: @@ -184,7 +175,9 @@ static int ustrtoul(const char *cp, char **endp, unsigned int base) result *= 1024; case 'k': result *= 1024; - endp++; + /* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */ + if ((*endp)[1] == 'i') + (*endp) += 2; } return result; } @@ -235,7 +228,7 @@ static int phram_setup(const char *val, struct kernel_param *kp) uint32_t len; int i, ret; - if (strnlen(val, sizeof(str)) >= sizeof(str)) + if (strnlen(val, sizeof(buf)) >= sizeof(buf)) parse_err("parameter too long\n"); strcpy(str, val); @@ -271,78 +264,11 @@ static int phram_setup(const char *val, struct kernel_param *kp) } module_param_call(phram, phram_setup, NULL, NULL, 000); -MODULE_PARM_DESC(phram, "Memory region to map. \"map=<name>,<start><length>\""); - -/* - * Just for compatibility with slram, this is horrible and should go someday. - */ -static int __init slram_setup(const char *val, struct kernel_param *kp) -{ - char buf[256], *str = buf; - - if (!val || !val[0]) - parse_err("no arguments to \"slram=\"\n"); - - if (strnlen(val, sizeof(str)) >= sizeof(str)) - parse_err("parameter too long\n"); - - strcpy(str, val); - - while (str) { - char *token[3]; - char *name; - uint32_t start; - uint32_t len; - int i, ret; - - for (i=0; i<3; i++) { - token[i] = strsep(&str, ","); - if (token[i]) - continue; - parse_err("wrong number of arguments to \"slram=\"\n"); - } - - /* name */ - ret = parse_name(&name, token[0]); - if (ret == -ENOMEM) - parse_err("of memory\n"); - if (ret == -ENOSPC) - parse_err("too long\n"); - if (ret) - return 1; - - /* start */ - ret = parse_num32(&start, token[1]); - if (ret) - parse_err("illegal start address\n"); - - /* len */ - if (token[2][0] == '+') - ret = parse_num32(&len, token[2] + 1); - else - ret = parse_num32(&len, token[2]); - - if (ret) - parse_err("illegal device length\n"); - - if (token[2][0] != '+') { - if (len < start) - parse_err("end < start\n"); - len -= start; - } - - register_device(name, start, len); - } - return 1; -} - -module_param_call(slram, slram_setup, NULL, NULL, 000); -MODULE_PARM_DESC(slram, "List of memory regions to map. \"map=<name>,<start><length/end>\""); +MODULE_PARM_DESC(phram,"Memory region to map. \"map=<name>,<start>,<length>\""); static int __init init_phram(void) { - printk(KERN_ERR "phram loaded\n"); return 0; } diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index 01c78f0dc772..b00a67099157 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -1,5 +1,5 @@ /* - * $Id: pmc551.c,v 1.29 2004/11/16 18:29:01 dwmw2 Exp $ + * $Id: pmc551.c,v 1.30 2005/01/05 18:05:13 dwmw2 Exp $ * * PMC551 PCI Mezzanine Ram Device * @@ -113,7 +113,7 @@ static struct mtd_info *pmc551list; static int pmc551_erase (struct mtd_info *mtd, struct erase_info *instr) { - struct mypriv *priv = (struct mypriv *)mtd->priv; + struct mypriv *priv = mtd->priv; u32 soff_hi, soff_lo; /* start address offset hi/lo */ u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ unsigned long end; @@ -176,7 +176,7 @@ out: static int pmc551_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) { - struct mypriv *priv = (struct mypriv *)mtd->priv; + struct mypriv *priv = mtd->priv; u32 soff_hi; u32 soff_lo; @@ -217,7 +217,7 @@ static void pmc551_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, siz static int pmc551_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - struct mypriv *priv = (struct mypriv *)mtd->priv; + struct mypriv *priv = mtd->priv; u32 soff_hi, soff_lo; /* start address offset hi/lo */ u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ unsigned long end; @@ -279,7 +279,7 @@ out: static int pmc551_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - struct mypriv *priv = (struct mypriv *)mtd->priv; + struct mypriv *priv = mtd->priv; u32 soff_hi, soff_lo; /* start address offset hi/lo */ u32 eoff_hi, eoff_lo; /* end address offset hi/lo */ unsigned long end; @@ -820,7 +820,7 @@ static void __exit cleanup_pmc551(void) struct mypriv *priv; while((mtd=pmc551list)) { - priv = (struct mypriv *)mtd->priv; + priv = mtd->priv; pmc551list = priv->nextpmc551; if(priv->start) { diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index 1caf00b8480b..c6e81d1cf238 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -1,6 +1,6 @@ /*====================================================================== - $Id: slram.c,v 1.32 2004/11/16 18:29:01 dwmw2 Exp $ + $Id: slram.c,v 1.33 2005/01/05 18:05:13 dwmw2 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 @@ -106,7 +106,7 @@ static int slram_erase(struct mtd_info *mtd, struct erase_info *instr) static int slram_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) { - slram_priv_t *priv = (slram_priv_t *)mtd->priv; + slram_priv_t *priv = mtd->priv; *mtdbuf = priv->start + from; *retlen = len; @@ -120,7 +120,7 @@ static void slram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_ static int slram_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { - slram_priv_t *priv = (slram_priv_t *)mtd->priv; + slram_priv_t *priv = mtd->priv; memcpy(buf, priv->start + from, len); @@ -131,7 +131,7 @@ static int slram_read(struct mtd_info *mtd, loff_t from, size_t len, static int slram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - slram_priv_t *priv = (slram_priv_t *)mtd->priv; + slram_priv_t *priv = mtd->priv; memcpy(priv->start + to, buf, len); @@ -161,7 +161,7 @@ static int register_device(char *name, unsigned long start, unsigned long length if ((*curmtd)->mtdinfo) { memset((char *)(*curmtd)->mtdinfo, 0, sizeof(struct mtd_info)); (*curmtd)->mtdinfo->priv = - (void *)kmalloc(sizeof(slram_priv_t), GFP_KERNEL); + kmalloc(sizeof(slram_priv_t), GFP_KERNEL); if (!(*curmtd)->mtdinfo->priv) { kfree((*curmtd)->mtdinfo); diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index 52816aab7d73..b5dda47395a7 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -8,7 +8,7 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: inftlmount.c,v 1.15 2004/11/05 21:55:55 kalev Exp $ + * $Id: inftlmount.c,v 1.16 2004/11/22 13:50:53 kalev 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 @@ -41,7 +41,7 @@ #include <linux/mtd/inftl.h> #include <linux/mtd/compatmac.h> -char inftlmountrev[]="$Revision: 1.15 $"; +char inftlmountrev[]="$Revision: 1.16 $"; /* * find_boot_record: Find the INFTL Media Header and its Spare copy which @@ -389,8 +389,6 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block) struct erase_info *instr = &inftl->instr; int physblock; - instr->mtd = inftl->mbd.mtd; - DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=%p," "block=%d)\n", inftl, block); @@ -400,6 +398,7 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block) _first_? */ /* Use async erase interface, test return code */ + instr->mtd = inftl->mbd.mtd; instr->addr = block * inftl->EraseSize; instr->len = inftl->mbd.mtd->erasesize; /* Erase one physical eraseblock at a time, even though the NAND api diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index f8b7edab4f94..16f4997049b7 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -1,5 +1,5 @@ # drivers/mtd/maps/Kconfig -# $Id: Kconfig,v 1.37 2004/10/20 22:57:18 dwmw2 Exp $ +# $Id: Kconfig,v 1.42 2005/01/05 16:59:50 dwmw2 Exp $ menu "Mapping drivers for chip access" depends on MTD!=n @@ -159,7 +159,7 @@ config MTD_VMAX config MTD_SCx200_DOCFLASH tristate "Flash device mapped with DOCCS on NatSemi SCx200" - depends on X86 && MTD_CFI + depends on X86 && MTD_CFI && MTD_PARTITIONS help Enable support for a flash chip mapped using the DOCCS signal on a National Semiconductor SCx200 processor. @@ -373,9 +373,17 @@ config MTD_ARCTIC Arctic board. If you have one of these boards and would like to use the flash chips on it, say 'Y'. +config MTD_WALNUT + tristate "Flash device mapped on IBM 405GP Walnut" + depends on MTD_JEDECPROBE && PPC32 && 40x && WALNUT + help + This enables access routines for the flash chips on the IBM 405GP + Walnut board. If you have one of these boards and would like to + use the flash chips on it, say 'Y'. + config MTD_EBONY tristate "Flash devices mapped on IBM 440GP Ebony" - depends on MTD_CFI && PPC32 && 44x && EBONY + depends on MTD_JEDECPROBE && PPC32 && 44x && EBONY help This enables access routines for the flash chips on the IBM 440GP Ebony board. If you have one of these boards and would like to @@ -399,7 +407,7 @@ config MTD_REDWOOD config MTD_CHESTNUT tristate "CFI Flash devices mapped on IBM 750FX or IBM 750GX Eval Boards" - depends on MTD_CFI && PPC32 && CHESTNUT + depends on MTD_CFI && PPC32 && CHESTNUT && MTD_PARTITIONS help This enables access routines for the flash chips on the IBM 750FX and 750GX Eval Boards. If you have one of these boards and @@ -653,5 +661,11 @@ config MTD_BAST_MAXSIZE depends on MTD_BAST default "4" +config MTD_SHARP_SL + bool "ROM maped on Sharp SL Series" + depends on MTD && ARCH_PXA + help + This enables access to the flash chip on the Sharp SL Series of PDAs. + endmenu diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 55a4a4dcaed5..be797693039d 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -1,7 +1,7 @@ # # linux/drivers/maps/Makefile # -# $Id: Makefile.common,v 1.19 2004/09/21 14:27:16 bjd Exp $ +# $Id: Makefile.common,v 1.23 2005/01/05 17:06:36 dwmw2 Exp $ ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y) obj-$(CONFIG_MTD) += map_funcs.o @@ -54,7 +54,7 @@ obj-$(CONFIG_MTD_EDB7312) += edb7312.o obj-$(CONFIG_MTD_IMPA7) += impa7.o obj-$(CONFIG_MTD_FORTUNET) += fortunet.o obj-$(CONFIG_MTD_REDWOOD) += redwood.o -obj-$(CONFIG_CHESTNUT) += chestnut.o +obj-$(CONFIG_MTD_CHESTNUT) += chestnut.o obj-$(CONFIG_MTD_UCLINUX) += uclinux.o obj-$(CONFIG_MTD_NETtel) += nettel.o obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o @@ -62,6 +62,7 @@ obj-$(CONFIG_MTD_EBONY) += ebony.o obj-$(CONFIG_MTD_OCOTEA) += ocotea.o obj-$(CONFIG_MTD_BEECH) += beech-mtd.o obj-$(CONFIG_MTD_ARCTIC) += arctic-mtd.o +obj-$(CONFIG_MTD_WALNUT) += walnut.o obj-$(CONFIG_MTD_H720X) += h720x-flash.o obj-$(CONFIG_MTD_SBC8240) += sbc8240.o obj-$(CONFIG_MTD_NOR_TOTO) += omap-toto-flash.o @@ -70,3 +71,4 @@ obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o 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 diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index baf028c9c7b7..51e97b05304e 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.18 2004/11/16 18:29:02 dwmw2 Exp $ + * $Id: amd76xrom.c,v 1.19 2004/11/28 09:40:39 dwmw2 Exp $ */ #include <linux/module.h> diff --git a/drivers/mtd/maps/chestnut.c b/drivers/mtd/maps/chestnut.c index 084a24c614b7..1cb5f1527279 100644 --- a/drivers/mtd/maps/chestnut.c +++ b/drivers/mtd/maps/chestnut.c @@ -1,9 +1,11 @@ /* * drivers/mtd/maps/chestnut.c * + * $Id: chestnut.c,v 1.1 2005/01/05 16:59:50 dwmw2 Exp $ + * * Flash map driver for IBM Chestnut (750FXGX Eval) * - * Chose not to enable 8 bit flash as it contains the firware and board + * Chose not to enable 8 bit flash as it contains the firmware and board * info. Thus only the 32bit flash is supported. * * Author: <source@mvista.com> @@ -85,5 +87,5 @@ module_init(init_chestnut); module_exit(cleanup_chestnut); MODULE_DESCRIPTION("MTD map and partitions for IBM Chestnut (750fxgx Eval)"); -MODULE_AUTHOR("<mvista.com>"); +MODULE_AUTHOR("<source@mvista.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/maps/dilnetpc.c b/drivers/mtd/maps/dilnetpc.c index 69d0e7ceb5ec..2bac6a6bbde6 100644 --- a/drivers/mtd/maps/dilnetpc.c +++ b/drivers/mtd/maps/dilnetpc.c @@ -14,7 +14,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: dilnetpc.c,v 1.16 2004/11/04 13:24:14 gleixner Exp $ + * $Id: dilnetpc.c,v 1.17 2004/11/28 09:40:39 dwmw2 Exp $ * * The DIL/Net PC is a tiny embedded PC board made by SSV Embedded Systems * featuring the AMD Elan SC410 processor. There are two variants of this diff --git a/drivers/mtd/maps/ebony.c b/drivers/mtd/maps/ebony.c index b654c316ccd0..b9d9cf4854b6 100644 --- a/drivers/mtd/maps/ebony.c +++ b/drivers/mtd/maps/ebony.c @@ -1,5 +1,5 @@ /* - * $Id: ebony.c,v 1.13 2004/11/04 13:24:14 gleixner Exp $ + * $Id: ebony.c,v 1.15 2004/12/09 18:39:54 holindho Exp $ * * Mapping for Ebony user flash * @@ -103,7 +103,7 @@ int __init init_ebony(void) simple_map_init(&ebony_small_map); - flash = do_map_probe("map_rom", &ebony_small_map); + flash = do_map_probe("jedec_probe", &ebony_small_map); if (flash) { flash->owner = THIS_MODULE; add_mtd_partitions(flash, ebony_small_partitions, @@ -124,7 +124,7 @@ int __init init_ebony(void) simple_map_init(&ebony_large_map); - flash = do_map_probe("cfi_probe", &ebony_large_map); + flash = do_map_probe("jedec_probe", &ebony_large_map); if (flash) { flash->owner = THIS_MODULE; add_mtd_partitions(flash, ebony_large_partitions, diff --git a/drivers/mtd/maps/elan-104nc.c b/drivers/mtd/maps/elan-104nc.c index f36bddc65bb7..05d19bc01bbc 100644 --- a/drivers/mtd/maps/elan-104nc.c +++ b/drivers/mtd/maps/elan-104nc.c @@ -16,7 +16,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: elan-104nc.c,v 1.24 2004/11/16 18:29:02 dwmw2 Exp $ + $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. diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c index 9a8890d7f1df..29d1cc1bb426 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.15 2004/11/16 18:29:02 dwmw2 Exp $ + * $Id: ichxrom.c,v 1.16 2004/11/28 09:40:39 dwmw2 Exp $ */ #include <linux/module.h> diff --git a/drivers/mtd/maps/l440gx.c b/drivers/mtd/maps/l440gx.c index 620ef1c381b9..b08668212ab7 100644 --- a/drivers/mtd/maps/l440gx.c +++ b/drivers/mtd/maps/l440gx.c @@ -1,5 +1,5 @@ /* - * $Id: l440gx.c,v 1.16 2004/11/16 18:29:02 dwmw2 Exp $ + * $Id: l440gx.c,v 1.17 2004/11/28 09:40:39 dwmw2 Exp $ * * BIOS Flash chip on Intel 440GX board. * diff --git a/drivers/mtd/maps/netsc520.c b/drivers/mtd/maps/netsc520.c index 0c7799f7fb96..ab7e6358d281 100644 --- a/drivers/mtd/maps/netsc520.c +++ b/drivers/mtd/maps/netsc520.c @@ -3,7 +3,7 @@ * Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com) * based on sc520cdp.c by Sysgo Real-Time Solutions GmbH * - * $Id: netsc520.c,v 1.12 2004/11/04 13:24:15 gleixner Exp $ + * $Id: netsc520.c,v 1.13 2004/11/28 09:40:40 dwmw2 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 diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index a6014b66b2fc..61be5a4148c9 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -6,7 +6,7 @@ * (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com) * (C) Copyright 2001-2002, SnapGear (www.snapgear.com) * - * $Id: nettel.c,v 1.8 2004/11/04 13:24:15 gleixner Exp $ + * $Id: nettel.c,v 1.10 2005/01/05 17:11:29 dwmw2 Exp $ */ /****************************************************************************/ @@ -332,8 +332,8 @@ int __init nettel_init(void) /* Destroy useless AMD MTD mapping */ amd_mtd = NULL; - iounmap((void *) nettel_amd_map.virt); - nettel_amd_map.virt = (unsigned long) NULL; + iounmap(nettel_amd_map.virt); + nettel_amd_map.virt = NULL; #else /* Only AMD flash supported */ return(-ENXIO); @@ -357,8 +357,7 @@ int __init nettel_init(void) /* Probe for the the size of the first Intel flash */ nettel_intel_map.size = maxsize; nettel_intel_map.phys = intel0addr; - nettel_intel_map.virt = (unsigned long) - ioremap_nocache(intel0addr, maxsize); + nettel_intel_map.virt = ioremap_nocache(intel0addr, maxsize); if (!nettel_intel_map.virt) { printk("SNAPGEAR: failed to ioremap() ROMCS1\n"); return(-EIO); @@ -366,8 +365,8 @@ int __init nettel_init(void) simple_map_init(&nettel_intel_map); intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map); - if (! intel_mtd) { - iounmap((void *) nettel_intel_map.virt); + if (!intel_mtd) { + iounmap(nettel_intel_map.virt); return(-ENXIO); } @@ -388,11 +387,10 @@ int __init nettel_init(void) /* Delete the old map and probe again to do both chips */ map_destroy(intel_mtd); intel_mtd = NULL; - iounmap((void *) nettel_intel_map.virt); + iounmap(nettel_intel_map.virt); nettel_intel_map.size = maxsize; - nettel_intel_map.virt = (unsigned long) - ioremap_nocache(intel0addr, maxsize); + nettel_intel_map.virt = ioremap_nocache(intel0addr, maxsize); if (!nettel_intel_map.virt) { printk("SNAPGEAR: failed to ioremap() ROMCS1/2\n"); return(-EIO); @@ -480,7 +478,7 @@ void __exit nettel_cleanup(void) map_destroy(intel_mtd); } if (nettel_intel_map.virt) { - iounmap((void *)nettel_intel_map.virt); + iounmap(nettel_intel_map.virt); nettel_intel_map.virt = 0; } #endif diff --git a/drivers/mtd/maps/ocelot.c b/drivers/mtd/maps/ocelot.c index beb0b7bea800..82c3070678c5 100644 --- a/drivers/mtd/maps/ocelot.c +++ b/drivers/mtd/maps/ocelot.c @@ -1,5 +1,5 @@ /* - * $Id: ocelot.c,v 1.15 2004/11/04 13:24:15 gleixner Exp $ + * $Id: ocelot.c,v 1.16 2005/01/05 18:05:13 dwmw2 Exp $ * * Flash on Momenco Ocelot */ @@ -28,7 +28,7 @@ static struct mtd_info *nvram_mtd; static void ocelot_ram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - struct map_info *map = (struct map_info *)mtd->priv; + struct map_info *map = mtd->priv; size_t done = 0; /* If we use memcpy, it does word-wide writes. Even though we told the diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c index fa29999fb410..3dee11edede7 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.8 2004/07/12 22:38:29 dwmw2 Exp $ + * $Id: pci.c,v 1.9 2004/11/28 09:40:40 dwmw2 Exp $ * * Generic PCI memory map driver. We support the following boards: * - Intel IQ80310 ATU. diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 0b605af8bd82..b853670bfb81 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -1,5 +1,5 @@ /* - * $Id: physmap.c,v 1.36 2004/11/04 13:24:15 gleixner Exp $ + * $Id: physmap.c,v 1.37 2004/11/28 09:40:40 dwmw2 Exp $ * * Normal mappings of chips in physical memory * diff --git a/drivers/mtd/maps/sbc_gxx.c b/drivers/mtd/maps/sbc_gxx.c index 176602839fe9..064c14635e62 100644 --- a/drivers/mtd/maps/sbc_gxx.c +++ b/drivers/mtd/maps/sbc_gxx.c @@ -17,7 +17,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: sbc_gxx.c,v 1.32 2004/11/16 18:29:02 dwmw2 Exp $ + $Id: sbc_gxx.c,v 1.33 2004/11/28 09:40:40 dwmw2 Exp $ The SBC-MediaGX / SBC-GXx has up to 16 MiB of Intel StrataFlash (28F320/28F640) in x8 mode. diff --git a/drivers/mtd/maps/sc520cdp.c b/drivers/mtd/maps/sc520cdp.c index fc5a5553a060..a06ed21e7ed1 100644 --- a/drivers/mtd/maps/sc520cdp.c +++ b/drivers/mtd/maps/sc520cdp.c @@ -16,7 +16,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: sc520cdp.c,v 1.18 2004/11/04 13:24:15 gleixner Exp $ + * $Id: sc520cdp.c,v 1.21 2004/12/13 10:27:08 dedekind Exp $ * * * The SC520CDP is an evaluation board for the Elan SC520 processor available diff --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c index 1854cd3a43fb..5bb3b600e5d0 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.10 2004/11/16 18:29:02 dwmw2 Exp $ + * $Id: scb2_flash.c,v 1.11 2004/11/28 09:40:40 dwmw2 Exp $ * Copyright (C) 2002 Sun Microsystems, Inc. * Tim Hockin <thockin@sun.com> * diff --git a/drivers/mtd/maps/scx200_docflash.c b/drivers/mtd/maps/scx200_docflash.c index d07ddf660789..1f738e165c4b 100644 --- a/drivers/mtd/maps/scx200_docflash.c +++ b/drivers/mtd/maps/scx200_docflash.c @@ -2,7 +2,7 @@ Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> - $Id: scx200_docflash.c,v 1.9 2004/11/16 18:29:02 dwmw2 Exp $ + $Id: scx200_docflash.c,v 1.10 2004/11/28 09:40:40 dwmw2 Exp $ National Semiconductor SCx200 flash mapped with DOCCS */ diff --git a/drivers/mtd/maps/sharpsl-flash.c b/drivers/mtd/maps/sharpsl-flash.c new file mode 100644 index 000000000000..b3b39cb7c608 --- /dev/null +++ b/drivers/mtd/maps/sharpsl-flash.c @@ -0,0 +1,101 @@ +/* + * sharpsl-flash.c + * + * 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 $ + * + * 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 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/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> + +#define WINDOW_ADDR 0x00000000 +#define WINDOW_SIZE 0x01000000 +#define BANK_WIDTH 2 + +static struct mtd_info *mymtd; + +struct map_info sharpsl_map = { + .name = "sharpsl-flash", + .size = WINDOW_SIZE, + .bankwidth = BANK_WIDTH, + .phys = WINDOW_ADDR +}; + +static struct mtd_partition sharpsl_partitions[1] = { + { + name: "Filesystem", + size: 0x006d0000, + offset: 0x00120000 + } +}; + +#define NB_OF(x) (sizeof(x)/sizeof(x[0])) + +int __init init_sharpsl(void) +{ + struct mtd_partition *parts; + int nb_parts = 0; + char *part_type = "static"; + + 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; + } + mymtd = do_map_probe("map_rom", &sharpsl_map); + if (!mymtd) { + iounmap(sharpsl_map.virt); + return -ENXIO; + } + + mymtd->owner = THIS_MODULE; + + parts = sharpsl_partitions; + nb_parts = NB_OF(sharpsl_partitions); + + printk(KERN_NOTICE "Using %s partision definition\n", part_type); + add_mtd_partitions(mymtd, parts, nb_parts); + + return 0; +} + +static void __exit cleanup_sharpsl(void) +{ + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } + if (sharpsl_map.virt) { + iounmap(sharpsl_map.virt); + sharpsl_map.virt = 0; + } +} + +module_init(init_sharpsl); +module_exit(cleanup_sharpsl); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("SHARP (Original: Arnold Christensen <AKC@pel.dk>)"); +MODULE_DESCRIPTION("MTD map driver for SHARP SL series"); diff --git a/drivers/mtd/maps/ts5500_flash.c b/drivers/mtd/maps/ts5500_flash.c index f90998b27979..3ebd90f56503 100644 --- a/drivers/mtd/maps/ts5500_flash.c +++ b/drivers/mtd/maps/ts5500_flash.c @@ -25,7 +25,7 @@ * - If you have created your own jffs file system and the bios overwrites * it during boot, try disabling Drive A: and B: in the boot order. * - * $Id: ts5500_flash.c,v 1.1 2004/09/20 15:33:26 sean Exp $ + * $Id: ts5500_flash.c,v 1.2 2004/11/28 09:40:40 dwmw2 Exp $ */ #include <linux/config.h> diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index 32ea86024137..811d92e5f5b1 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c @@ -5,7 +5,7 @@ * * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) * - * $Id: uclinux.c,v 1.9 2004/11/04 13:24:15 gleixner Exp $ + * $Id: uclinux.c,v 1.10 2005/01/05 18:05:13 dwmw2 Exp $ */ /****************************************************************************/ @@ -47,7 +47,7 @@ struct mtd_partition uclinux_romfs[] = { int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) { - struct map_info *map = (struct map_info *) mtd->priv; + struct map_info *map = mtd->priv; *mtdbuf = (u_char *) (map->virt + ((int) from)); *retlen = len; return(0); @@ -81,7 +81,7 @@ int __init uclinux_mtd_init(void) mtd = do_map_probe("map_ram", mapp); if (!mtd) { printk("uclinux[mtd]: failed to find a mapping?\n"); - iounmap((void *) mapp->virt); + iounmap(mapp->virt); return(-ENXIO); } diff --git a/drivers/mtd/maps/walnut.c b/drivers/mtd/maps/walnut.c new file mode 100644 index 000000000000..d6137b1b5670 --- /dev/null +++ b/drivers/mtd/maps/walnut.c @@ -0,0 +1,122 @@ +/* + * $Id: walnut.c,v 1.2 2004/12/10 12:07:42 holindho Exp $ + * + * Mapping for Walnut flash + * (used ebony.c as a "framework") + * + * Heikki Lindholm <holindho@infradead.org> + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> +#include <linux/config.h> +#include <linux/version.h> +#include <asm/io.h> +#include <asm/ibm4xx.h> +#include <platforms/4xx/walnut.h> + +/* these should be in platforms/4xx/walnut.h ? */ +#define WALNUT_FLASH_ONBD_N(x) (x & 0x02) +#define WALNUT_FLASH_SRAM_SEL(x) (x & 0x01) +#define WALNUT_FLASH_LOW 0xFFF00000 +#define WALNUT_FLASH_HIGH 0xFFF80000 +#define WALNUT_FLASH_SIZE 0x80000 + +static struct mtd_info *flash; + +static struct map_info walnut_map = { + .name = "Walnut flash", + .size = WALNUT_FLASH_SIZE, + .bankwidth = 1, +}; + +/* Actually, OpenBIOS is the last 128 KiB of the flash - better + * partitioning could be made */ +static struct mtd_partition walnut_partitions[] = { + { + .name = "OpenBIOS", + .offset = 0x0, + .size = WALNUT_FLASH_SIZE, + /*.mask_flags = MTD_WRITEABLE, */ /* force read-only */ + } +}; + +int __init init_walnut(void) +{ + u8 fpga_brds1; + void *fpga_brds1_adr; + void *fpga_status_adr; + unsigned long flash_base; + + /* this should already be mapped (platform/4xx/walnut.c) */ + fpga_status_adr = ioremap(WALNUT_FPGA_BASE, 8); + if (!fpga_status_adr) + return -ENOMEM; + + fpga_brds1_adr = fpga_status_adr+5; + fpga_brds1 = readb(fpga_brds1_adr); + /* iounmap(fpga_status_adr); */ + + if (WALNUT_FLASH_ONBD_N(fpga_brds1)) { + printk("The on-board flash is disabled (U79 sw 5)!"); + return -EIO; + } + if (WALNUT_FLASH_SRAM_SEL(fpga_brds1)) + flash_base = WALNUT_FLASH_LOW; + else + flash_base = WALNUT_FLASH_HIGH; + + walnut_map.phys = flash_base; + walnut_map.virt = + (void __iomem *)ioremap(flash_base, walnut_map.size); + + if (!walnut_map.virt) { + printk("Failed to ioremap flash.\n"); + return -EIO; + } + + simple_map_init(&walnut_map); + + flash = do_map_probe("jedec_probe", &walnut_map); + if (flash) { + flash->owner = THIS_MODULE; + add_mtd_partitions(flash, walnut_partitions, + ARRAY_SIZE(walnut_partitions)); + } else { + printk("map probe failed for flash\n"); + return -ENXIO; + } + + return 0; +} + +static void __exit cleanup_walnut(void) +{ + if (flash) { + del_mtd_partitions(flash); + map_destroy(flash); + } + + if (walnut_map.virt) { + iounmap((void *)walnut_map.virt); + walnut_map.virt = 0; + } +} + +module_init(init_walnut); +module_exit(cleanup_walnut); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Heikki Lindholm <holindho@infradead.org>"); +MODULE_DESCRIPTION("MTD map and partitions for IBM 405GP Walnut boards"); diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index d6b9207863ff..b7c32c242bc7 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -1,7 +1,7 @@ /* * Direct MTD block device access * - * $Id: mtdblock.c,v 1.65 2004/11/16 18:28:59 dwmw2 Exp $ + * $Id: mtdblock.c,v 1.66 2004/11/25 13:52:52 joern Exp $ * * (C) 2000-2003 Nicolas Pitre <nico@cam.org> * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> @@ -248,7 +248,7 @@ static int mtdblock_writesect(struct mtd_blktrans_dev *dev, unsigned long block, char *buf) { struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; - if (unlikely(!mtdblk->cache_data)) { + if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) { mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize); if (!mtdblk->cache_data) return -EINTR; diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index fafae6e58dbe..379214005417 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -1,5 +1,5 @@ /* - * $Id: mtdchar.c,v 1.65 2004/09/23 23:45:47 gleixner Exp $ + * $Id: mtdchar.c,v 1.66 2005/01/05 18:05:11 dwmw2 Exp $ * * Character-device access to raw MTD devices. * @@ -61,7 +61,7 @@ static inline void mtdchar_devfs_exit(void) static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) { - struct mtd_info *mtd=(struct mtd_info *)file->private_data; + struct mtd_info *mtd = file->private_data; switch (orig) { case 0: @@ -134,7 +134,7 @@ static int mtd_close(struct inode *inode, struct file *file) DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); - mtd = (struct mtd_info *)file->private_data; + mtd = file->private_data; if (mtd->sync) mtd->sync(mtd); @@ -151,7 +151,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 = (struct mtd_info *)file->private_data; + struct mtd_info *mtd = file->private_data; size_t retlen=0; size_t total_retlen=0; int ret=0; @@ -210,7 +210,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 = (struct mtd_info *)file->private_data; + struct mtd_info *mtd = file->private_data; char *kbuf; size_t retlen; size_t total_retlen=0; @@ -276,7 +276,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 = (struct mtd_info *)file->private_data; + struct mtd_info *mtd = file->private_data; void __user *argp = (void __user *)arg; int ret = 0; u_long size; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 89ab7da160f1..f7801eb730ce 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,5 +1,5 @@ # drivers/mtd/nand/Kconfig -# $Id: Kconfig,v 1.22 2004/10/05 22:11:46 gleixner Exp $ +# $Id: Kconfig,v 1.26 2005/01/05 12:42:24 dwmw2 Exp $ menu "NAND Flash Device Drivers" depends on MTD!=n @@ -7,6 +7,7 @@ menu "NAND Flash Device Drivers" config MTD_NAND tristate "NAND Device Support" depends on MTD + select MTD_NAND_IDS help This enables support for accessing all type of NAND flash devices. For further information see @@ -56,8 +57,6 @@ config MTD_NAND_TOTO config MTD_NAND_IDS tristate - default y if MTD_NAND = y || MTD_DOC2000 = y || MTD_DOC2001 = y || MTD_DOC2001PLUS = y - default m if MTD_NAND = m || MTD_DOC2000 = m || MTD_DOC2001 = m || MTD_DOC2001PLUS = m config MTD_NAND_TX4925NDFMC tristate "SmartMedia Card on Toshiba RBTX4925 reference board" @@ -192,4 +191,17 @@ config MTD_NAND_DISKONCHIP_BBTWRITE Even if you leave this disabled, you can enable BBT writes at module load time (assuming you build diskonchip as a module) with the module parameter "inftl_bbt_write=1". + + config MTD_NAND_SHARPSL + bool "Support for NAND Flash on Sharp SL Series (C7xx + others)" + depends on MTD_NAND && ARCH_PXA + + config MTD_NAND_NANDSIM + bool "Support for NAND Flash Simulator" + depends on MTD_NAND && MTD_PARTITIONS + + help + The simulator may simulate verious NAND flash chips for the + MTD nand layer. + endmenu diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index a0b6c60528da..d9dc8cc2da8c 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -1,7 +1,7 @@ # # linux/drivers/nand/Makefile # -# $Id: Makefile.common,v 1.13 2004/09/28 22:04:23 bjd Exp $ +# $Id: Makefile.common,v 1.15 2004/11/26 12:28:22 dedekind Exp $ obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o @@ -18,5 +18,7 @@ obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o obj-$(CONFIG_MTD_NAND_H1900) += h1910.o obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o +obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o +obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o nand-objs = nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 78883c9d52ff..02135c3ac29a 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.42 2004/11/16 18:29:03 dwmw2 Exp $ + * $Id: diskonchip.c,v 1.45 2005/01/05 18:05:14 dwmw2 Exp $ */ #include <linux/kernel.h> @@ -24,6 +24,7 @@ #include <linux/sched.h> #include <linux/delay.h> #include <linux/rslib.h> +#include <linux/moduleparam.h> #include <asm/io.h> #include <linux/mtd/mtd.h> @@ -308,7 +309,7 @@ static inline int DoC_WaitReady(struct doc_priv *doc) static void doc2000_write_byte(struct mtd_info *mtd, u_char datum) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; if(debug)printk("write_byte %02x\n", datum); @@ -319,7 +320,7 @@ static void doc2000_write_byte(struct mtd_info *mtd, u_char datum) static u_char doc2000_read_byte(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; u_char ret; @@ -334,7 +335,7 @@ static void doc2000_writebuf(struct mtd_info *mtd, const u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; if (debug)printk("writebuf of %d bytes: ", len); @@ -350,7 +351,7 @@ static void doc2000_readbuf(struct mtd_info *mtd, u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; @@ -365,7 +366,7 @@ static void doc2000_readbuf_dword(struct mtd_info *mtd, u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; @@ -386,7 +387,7 @@ static int doc2000_verifybuf(struct mtd_info *mtd, const u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; @@ -399,7 +400,7 @@ static int doc2000_verifybuf(struct mtd_info *mtd, static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; uint16_t ret; doc200x_select_chip(mtd, nr); @@ -441,7 +442,7 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr) static void __init doc2000_count_chips(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; uint16_t mfrid; int i; @@ -462,7 +463,7 @@ static void __init doc2000_count_chips(struct mtd_info *mtd) static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state) { - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; int status; @@ -477,7 +478,7 @@ static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state) static void doc2001_write_byte(struct mtd_info *mtd, u_char datum) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; WriteDOC(datum, docptr, CDSNSlowIO); @@ -488,7 +489,7 @@ static void doc2001_write_byte(struct mtd_info *mtd, u_char datum) static u_char doc2001_read_byte(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; //ReadDOC(docptr, CDSNSlowIO); @@ -503,7 +504,7 @@ static void doc2001_writebuf(struct mtd_info *mtd, const u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; @@ -517,7 +518,7 @@ static void doc2001_readbuf(struct mtd_info *mtd, u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; @@ -535,7 +536,7 @@ static int doc2001_verifybuf(struct mtd_info *mtd, const u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; @@ -555,7 +556,7 @@ static int doc2001_verifybuf(struct mtd_info *mtd, static u_char doc2001plus_read_byte(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; u_char ret; @@ -570,7 +571,7 @@ static void doc2001plus_writebuf(struct mtd_info *mtd, const u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; @@ -587,7 +588,7 @@ static void doc2001plus_readbuf(struct mtd_info *mtd, u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; @@ -617,7 +618,7 @@ static int doc2001plus_verifybuf(struct mtd_info *mtd, const u_char *buf, int len) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; @@ -643,7 +644,7 @@ static int doc2001plus_verifybuf(struct mtd_info *mtd, static void doc2001plus_select_chip(struct mtd_info *mtd, int chip) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int floor = 0; @@ -669,7 +670,7 @@ static void doc2001plus_select_chip(struct mtd_info *mtd, int chip) static void doc200x_select_chip(struct mtd_info *mtd, int chip) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int floor = 0; @@ -696,7 +697,7 @@ static void doc200x_select_chip(struct mtd_info *mtd, int chip) static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; switch(cmd) { @@ -734,7 +735,7 @@ static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd) static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; /* @@ -838,7 +839,7 @@ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int col static int doc200x_dev_ready(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; if (DoC_is_MillenniumPlus(doc)) { @@ -876,7 +877,7 @@ static int doc200x_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; /* Prime the ECC engine */ @@ -895,7 +896,7 @@ static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode) static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; /* Prime the ECC engine */ @@ -916,7 +917,7 @@ static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, unsigned char *ecc_code) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; int i; int emptymatch = 1; @@ -974,7 +975,7 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ { int i, ret = 0; struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; void __iomem *docptr = doc->virtadr; volatile u_char dummy; int emptymatch = 1; @@ -1062,7 +1063,7 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const char *id, int findmirror) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; unsigned offs, end = (MAX_MEDIAHEADER_SCAN << this->phys_erase_shift); int ret; size_t retlen; @@ -1105,7 +1106,7 @@ static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; int ret = 0; u_char *buf; struct NFTLMediaHeader *mh; @@ -1201,7 +1202,7 @@ static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; int ret = 0; u_char *buf; struct INFTLMediaHeader *mh; @@ -1326,7 +1327,7 @@ static int __init nftl_scan_bbt(struct mtd_info *mtd) { int ret, numparts; struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; struct mtd_partition parts[2]; memset((char *) parts, 0, sizeof(parts)); @@ -1365,7 +1366,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd) { int ret, numparts; struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; struct mtd_partition parts[5]; if (this->numchips > doc->chips_per_floor) { @@ -1424,7 +1425,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd) static inline int __init doc2000_init(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; this->write_byte = doc2000_write_byte; this->read_byte = doc2000_read_byte; @@ -1442,7 +1443,7 @@ static inline int __init doc2000_init(struct mtd_info *mtd) static inline int __init doc2001_init(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; this->write_byte = doc2001_write_byte; this->read_byte = doc2001_read_byte; @@ -1474,7 +1475,7 @@ static inline int __init doc2001_init(struct mtd_info *mtd) static inline int __init doc2001plus_init(struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - struct doc_priv *doc = (void *)this->priv; + struct doc_priv *doc = this->priv; this->write_byte = NULL; this->read_byte = doc2001plus_read_byte; @@ -1596,7 +1597,7 @@ static inline int __init doc_probe(unsigned long physadr) unsigned char oldval; unsigned char newval; nand = mtd->priv; - doc = (void *)nand->priv; + doc = nand->priv; /* Use the alias resolution register to determine if this is in fact the same DOC aliased to a new address. If writes to one chip's alias resolution register change the value on @@ -1645,10 +1646,10 @@ static inline int __init doc_probe(unsigned long physadr) nand->bbt_td = (struct nand_bbt_descr *) (doc + 1); nand->bbt_md = nand->bbt_td + 1; - mtd->priv = (void *) nand; + mtd->priv = nand; mtd->owner = THIS_MODULE; - nand->priv = (void *) doc; + nand->priv = doc; nand->select_chip = doc200x_select_chip; nand->hwcontrol = doc200x_hwcontrol; nand->dev_ready = doc200x_dev_ready; @@ -1711,7 +1712,7 @@ static void release_nanddoc(void) for (mtd = doclist; mtd; mtd = nextmtd) { nand = mtd->priv; - doc = (void *)nand->priv; + doc = nand->priv; nextmtd = doc->nextdoc; nand_release(mtd); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 181b95275352..44d5b128911f 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -41,7 +41,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.121 2004/10/06 19:53:11 gleixner Exp $ + * $Id: nand_base.c,v 1.126 2004/12/13 11:22:25 lavinen 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 @@ -810,7 +810,7 @@ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int pa u_char *oob_buf, struct nand_oobinfo *oobsel, int cached) { int i, status; - u_char ecc_code[8]; + u_char ecc_code[32]; int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; int *oob_config = oobsel->eccpos; int datidx = 0, eccidx = 0, eccsteps = this->eccsteps; @@ -840,18 +840,8 @@ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int pa } this->write_buf(mtd, this->data_poi, mtd->oobblock); break; - - /* Hardware ecc 8 byte / 512 byte data */ - case NAND_ECC_HW8_512: - eccbytes += 2; - /* Hardware ecc 6 byte / 512 byte data */ - case NAND_ECC_HW6_512: - eccbytes += 3; - /* Hardware ecc 3 byte / 256 data */ - /* Hardware ecc 3 byte / 512 byte data */ - case NAND_ECC_HW3_256: - case NAND_ECC_HW3_512: - eccbytes += 3; + default: + eccbytes = this->eccbytes; for (; eccsteps; eccsteps--) { /* enable hardware ecc logic for write */ this->enable_hwecc(mtd, NAND_ECC_WRITE); @@ -864,14 +854,9 @@ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int pa * the data bytes (words) */ if (this->options & NAND_HWECC_SYNDROME) this->write_buf(mtd, ecc_code, eccbytes); - datidx += this->eccsize; } break; - - default: - printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); - BUG(); } /* Write out OOB data */ @@ -1051,7 +1036,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, int eccmode, eccsteps; int *oob_config, datidx; int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; - int eccbytes = 3; + int eccbytes; int compareecc = 1; int oobreadlen; @@ -1092,19 +1077,9 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, end = mtd->oobblock; ecc = this->eccsize; - switch (eccmode) { - case NAND_ECC_HW6_512: /* Hardware ECC 6 byte / 512 byte data */ - eccbytes = 6; - break; - case NAND_ECC_HW8_512: /* Hardware ECC 8 byte / 512 byte data */ - eccbytes = 8; - break; - case NAND_ECC_NONE: - compareecc = 0; - break; - } - - if (this->options & NAND_HWECC_SYNDROME) + eccbytes = this->eccbytes; + + if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME)) compareecc = 0; oobreadlen = mtd->oobsize; @@ -1164,13 +1139,10 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); break; - - case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data */ - case NAND_ECC_HW3_512: /* Hardware ECC 3 byte /512 byte data */ - case NAND_ECC_HW6_512: /* Hardware ECC 6 byte / 512 byte data */ - case NAND_ECC_HW8_512: /* Hardware ECC 8 byte / 512 byte data */ + + default: for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) { - this->enable_hwecc(mtd, NAND_ECC_READ); + this->enable_hwecc(mtd, NAND_ECC_READ); this->read_buf(mtd, &data_poi[datidx], ecc); /* HW ecc with syndrome calculation must read the @@ -1193,10 +1165,6 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, } } break; - - default: - printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); - BUG(); } /* read oobdata */ @@ -2433,8 +2401,19 @@ int nand_scan (struct mtd_info *mtd, int maxchips) * fallback to software ECC */ this->eccsize = 256; /* set default eccsize */ + this->eccbytes = 3; switch (this->eccmode) { + case NAND_ECC_HW12_2048: + if (mtd->oobblock < 2048) { + printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n", + mtd->oobblock); + this->eccmode = NAND_ECC_SOFT; + this->calculate_ecc = nand_calculate_ecc; + this->correct_data = nand_correct_data; + } else + this->eccsize = 2048; + break; case NAND_ECC_HW3_512: case NAND_ECC_HW6_512: @@ -2444,16 +2423,13 @@ int nand_scan (struct mtd_info *mtd, int maxchips) this->eccmode = NAND_ECC_SOFT; this->calculate_ecc = nand_calculate_ecc; this->correct_data = nand_correct_data; - break; } else - this->eccsize = 512; /* set eccsize to 512 and fall through for function check */ - + this->eccsize = 512; /* set eccsize to 512 */ + break; + case NAND_ECC_HW3_256: - if (this->calculate_ecc && this->correct_data && this->enable_hwecc) - break; - printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n"); - BUG(); - + break; + case NAND_ECC_NONE: printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n"); this->eccmode = NAND_ECC_NONE; @@ -2468,11 +2444,32 @@ int nand_scan (struct mtd_info *mtd, int maxchips) printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); BUG(); } - + + /* Check hardware ecc function availability and adjust number of ecc bytes per + * calculation step + */ + switch (this->eccmode) { + case NAND_ECC_HW12_2048: + this->eccbytes += 4; + case NAND_ECC_HW8_512: + this->eccbytes += 2; + case NAND_ECC_HW6_512: + this->eccbytes += 3; + case NAND_ECC_HW3_512: + case NAND_ECC_HW3_256: + if (this->calculate_ecc && this->correct_data && this->enable_hwecc) + break; + printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n"); + BUG(); + } + mtd->eccsize = this->eccsize; /* Set the number of read / write steps for one page to ensure ECC generation */ switch (this->eccmode) { + case NAND_ECC_HW12_2048: + this->eccsteps = mtd->oobblock / 2048; + break; case NAND_ECC_HW3_512: case NAND_ECC_HW6_512: case NAND_ECC_HW8_512: diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 77f08d1ee14f..9a1949751c1f 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.26 2004/10/05 13:50:20 gleixner Exp $ + * $Id: nand_bbt.c,v 1.28 2004/11/13 10:19:09 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 @@ -1001,25 +1001,27 @@ int nand_default_bbt (struct mtd_info *mtd) return nand_scan_bbt (mtd, &agand_flashbased); } + /* Is a flash based bad block table requested ? */ if (this->options & NAND_USE_FLASH_BBT) { /* Use the default pattern descriptors */ if (!this->bbt_td) { this->bbt_td = &bbt_main_descr; this->bbt_md = &bbt_mirror_descr; - } - if (mtd->oobblock > 512) - return nand_scan_bbt (mtd, &largepage_flashbased); - else - return nand_scan_bbt (mtd, &smallpage_flashbased); + } + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->oobblock > 512) ? + &largepage_flashbased : &smallpage_flashbased; + } } else { this->bbt_td = NULL; this->bbt_md = NULL; - if (mtd->oobblock > 512) - return nand_scan_bbt (mtd, &largepage_memorybased); - else - return nand_scan_bbt (mtd, &smallpage_memorybased); + if (!this->badblock_pattern) { + this->badblock_pattern = (mtd->oobblock > 512) ? + &largepage_memorybased : &smallpage_memorybased; + } } + return nand_scan_bbt (mtd, this->badblock_pattern); } /** diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c new file mode 100644 index 000000000000..13feefd7d8ca --- /dev/null +++ b/drivers/mtd/nand/nandsim.c @@ -0,0 +1,1613 @@ +/* + * NAND flash simulator. + * + * Author: Artem B. Bityuckiy <dedekind@oktetlabs.ru>, <dedekind@infradead.org> + * + * Copyright (C) 2004 Nokia Corporation + * + * Note: NS means "NAND Simulator". + * Note: Input means input TO flash chip, output means output FROM chip. + * + * 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, 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: nandsim.c,v 1.7 2004/12/06 11:53:06 dedekind Exp $ + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/delay.h> +#ifdef CONFIG_NS_ABS_POS +#include <asm/io.h> +#endif + + +/* Default simulator parameters values */ +#if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ + !defined(CONFIG_NANDSIM_SECOND_ID_BYTE) || \ + !defined(CONFIG_NANDSIM_THIRD_ID_BYTE) || \ + !defined(CONFIG_NANDSIM_FOURTH_ID_BYTE) +#define CONFIG_NANDSIM_FIRST_ID_BYTE 0x98 +#define CONFIG_NANDSIM_SECOND_ID_BYTE 0x39 +#define CONFIG_NANDSIM_THIRD_ID_BYTE 0xFF /* No byte */ +#define CONFIG_NANDSIM_FOURTH_ID_BYTE 0xFF /* No byte */ +#endif + +#ifndef CONFIG_NANDSIM_ACCESS_DELAY +#define CONFIG_NANDSIM_ACCESS_DELAY 25 +#endif +#ifndef CONFIG_NANDSIM_PROGRAMM_DELAY +#define CONFIG_NANDSIM_PROGRAMM_DELAY 200 +#endif +#ifndef CONFIG_NANDSIM_ERASE_DELAY +#define CONFIG_NANDSIM_ERASE_DELAY 2 +#endif +#ifndef CONFIG_NANDSIM_OUTPUT_CYCLE +#define CONFIG_NANDSIM_OUTPUT_CYCLE 40 +#endif +#ifndef CONFIG_NANDSIM_INPUT_CYCLE +#define CONFIG_NANDSIM_INPUT_CYCLE 50 +#endif +#ifndef CONFIG_NANDSIM_BUS_WIDTH +#define CONFIG_NANDSIM_BUS_WIDTH 8 +#endif +#ifndef CONFIG_NANDSIM_DO_DELAYS +#define CONFIG_NANDSIM_DO_DELAYS 0 +#endif +#ifndef CONFIG_NANDSIM_LOG +#define CONFIG_NANDSIM_LOG 0 +#endif +#ifndef CONFIG_NANDSIM_DBG +#define CONFIG_NANDSIM_DBG 0 +#endif + +static uint first_id_byte = CONFIG_NANDSIM_FIRST_ID_BYTE; +static uint second_id_byte = CONFIG_NANDSIM_SECOND_ID_BYTE; +static uint third_id_byte = CONFIG_NANDSIM_THIRD_ID_BYTE; +static uint fourth_id_byte = CONFIG_NANDSIM_FOURTH_ID_BYTE; +static uint access_delay = CONFIG_NANDSIM_ACCESS_DELAY; +static uint programm_delay = CONFIG_NANDSIM_PROGRAMM_DELAY; +static uint erase_delay = CONFIG_NANDSIM_ERASE_DELAY; +static uint output_cycle = CONFIG_NANDSIM_OUTPUT_CYCLE; +static uint input_cycle = CONFIG_NANDSIM_INPUT_CYCLE; +static uint bus_width = CONFIG_NANDSIM_BUS_WIDTH; +static uint do_delays = CONFIG_NANDSIM_DO_DELAYS; +static uint log = CONFIG_NANDSIM_LOG; +static uint dbg = CONFIG_NANDSIM_DBG; + +module_param(first_id_byte, uint, 0400); +module_param(second_id_byte, uint, 0400); +module_param(third_id_byte, uint, 0400); +module_param(fourth_id_byte, uint, 0400); +module_param(access_delay, uint, 0400); +module_param(programm_delay, uint, 0400); +module_param(erase_delay, uint, 0400); +module_param(output_cycle, uint, 0400); +module_param(input_cycle, uint, 0400); +module_param(bus_width, uint, 0400); +module_param(do_delays, uint, 0400); +module_param(log, uint, 0400); +module_param(dbg, uint, 0400); + +MODULE_PARM_DESC(first_id_byte, "The fist byte returned by NAND Flash 'read ID' command (manufaturer ID)"); +MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)"); +MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command"); +MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command"); +MODULE_PARM_DESC(access_delay, "Initial page access delay (microiseconds)"); +MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds"); +MODULE_PARM_DESC(erase_delay, "Sector erase delay (milliseconds)"); +MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanodeconds)"); +MODULE_PARM_DESC(input_cycle, "Word input (to flash) time (nanodeconds)"); +MODULE_PARM_DESC(bus_width, "Chip's bus width (8- or 16-bit)"); +MODULE_PARM_DESC(do_delays, "Simulate NAND delays using busy-waits if not zero"); +MODULE_PARM_DESC(log, "Perform logging if not zero"); +MODULE_PARM_DESC(dbg, "Output debug information if not zero"); + +/* The largest possible page size */ +#define NS_LARGEST_PAGE_SIZE 2048 + +/* The prefix for simulator output */ +#define NS_OUTPUT_PREFIX "[nandsim]" + +/* Simulator's output macros (logging, debugging, warning, error) */ +#define NS_LOG(args...) \ + do { if (log) printk(KERN_DEBUG NS_OUTPUT_PREFIX " log: " args); } while(0) +#define NS_DBG(args...) \ + do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0) +#define NS_WARN(args...) \ + do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warnig: " args); } while(0) +#define NS_ERR(args...) \ + do { printk(KERN_ERR NS_OUTPUT_PREFIX " errorr: " args); } while(0) + +/* Busy-wait delay macros (microseconds, milliseconds) */ +#define NS_UDELAY(us) \ + do { if (do_delays) udelay(us); } while(0) +#define NS_MDELAY(us) \ + do { if (do_delays) mdelay(us); } while(0) + +/* Is the nandsim structure initialized ? */ +#define NS_IS_INITIALIZED(ns) ((ns)->geom.totsz != 0) + +/* Good operation completion status */ +#define NS_STATUS_OK(ns) (NAND_STATUS_READY | (NAND_STATUS_WP * ((ns)->lines.wp == 0))) + +/* Operation failed completion status */ +#define NS_STATUS_FAILED(ns) (NAND_STATUS_FAIL | NS_STATUS_OK(ns)) + +/* Calculate the page offset in flash RAM image by (row, column) address */ +#define NS_RAW_OFFSET(ns) \ + (((ns)->regs.row << (ns)->geom.pgshift) + ((ns)->regs.row * (ns)->geom.oobsz) + (ns)->regs.column) + +/* Calculate the OOB offset in flash RAM image by (row, column) address */ +#define NS_RAW_OFFSET_OOB(ns) (NS_RAW_OFFSET(ns) + ns->geom.pgsz) + +/* After a command is input, the simulator goes to one of the following states */ +#define STATE_CMD_READ0 0x00000001 /* read data from the beginning of page */ +#define STATE_CMD_READ1 0x00000002 /* read data from the second half of page */ +#define STATE_CMD_READSTART 0x00000003 /* read data second command (large page devices) */ +#define STATE_CMD_PAGEPROG 0x00000004 /* start page programm */ +#define STATE_CMD_READOOB 0x00000005 /* read OOB area */ +#define STATE_CMD_ERASE1 0x00000006 /* sector erase first command */ +#define STATE_CMD_STATUS 0x00000007 /* read status */ +#define STATE_CMD_STATUS_M 0x00000008 /* read multi-plane status (isn't implemented) */ +#define STATE_CMD_SEQIN 0x00000009 /* sequential data imput */ +#define STATE_CMD_READID 0x0000000A /* read ID */ +#define STATE_CMD_ERASE2 0x0000000B /* sector erase second command */ +#define STATE_CMD_RESET 0x0000000C /* reset */ +#define STATE_CMD_MASK 0x0000000F /* command states mask */ + +/* After an addres is input, the simulator goes to one of these states */ +#define STATE_ADDR_PAGE 0x00000010 /* full (row, column) address is accepted */ +#define STATE_ADDR_SEC 0x00000020 /* sector address was accepted */ +#define STATE_ADDR_ZERO 0x00000030 /* one byte zero address was accepted */ +#define STATE_ADDR_MASK 0x00000030 /* address states mask */ + +/* Durind data input/output the simulator is in these states */ +#define STATE_DATAIN 0x00000100 /* waiting for data input */ +#define STATE_DATAIN_MASK 0x00000100 /* data input states mask */ + +#define STATE_DATAOUT 0x00001000 /* waiting for page data output */ +#define STATE_DATAOUT_ID 0x00002000 /* waiting for ID bytes output */ +#define STATE_DATAOUT_STATUS 0x00003000 /* waiting for status output */ +#define STATE_DATAOUT_STATUS_M 0x00004000 /* waiting for multi-plane status output */ +#define STATE_DATAOUT_MASK 0x00007000 /* data output states mask */ + +/* Previous operation is done, ready to accept new requests */ +#define STATE_READY 0x00000000 + +/* This state is used to mark that the next state isn't known yet */ +#define STATE_UNKNOWN 0x10000000 + +/* Simulator's actions bit masks */ +#define ACTION_CPY 0x00100000 /* copy page/OOB to the internal buffer */ +#define ACTION_PRGPAGE 0x00200000 /* programm the internal buffer to flash */ +#define ACTION_SECERASE 0x00300000 /* erase sector */ +#define ACTION_ZEROOFF 0x00400000 /* don't add any offset to address */ +#define ACTION_HALFOFF 0x00500000 /* add to address half of page */ +#define ACTION_OOBOFF 0x00600000 /* add to address OOB offset */ +#define ACTION_MASK 0x00700000 /* action mask */ + +#define NS_OPER_NUM 12 /* Number of operations supported by the simulator */ +#define NS_OPER_STATES 6 /* Maximum number of states in operation */ + +#define OPT_ANY 0xFFFFFFFF /* any chip supports this operation */ +#define OPT_PAGE256 0x00000001 /* 256-byte page chips */ +#define OPT_PAGE512 0x00000002 /* 512-byte page chips */ +#define OPT_PAGE2048 0x00000008 /* 2048-byte page chips */ +#define OPT_SMARTMEDIA 0x00000010 /* SmartMedia technology chips */ +#define OPT_AUTOINCR 0x00000020 /* page number auto inctimentation is possible */ +#define OPT_PAGE512_8BIT 0x00000040 /* 512-byte page chips with 8-bit bus width */ +#define OPT_LARGEPAGE (OPT_PAGE2048) /* 2048-byte page chips */ +#define OPT_SMALLPAGE (OPT_PAGE256 | OPT_PAGE512) /* 256 and 512-byte page chips */ + +/* Remove action bits ftom state */ +#define NS_STATE(x) ((x) & ~ACTION_MASK) + +/* + * Maximum previous states which need to be saved. Currently saving is + * only needed for page programm operation with preceeded read command + * (which is only valid for 512-byte pages). + */ +#define NS_MAX_PREVSTATES 1 + +/* + * The structure which describes all the internal simulator data. + */ +struct nandsim { + struct mtd_partition part; + + uint busw; /* flash chip bus width (8 or 16) */ + u_char ids[4]; /* chip's ID bytes */ + uint32_t options; /* chip's characteristic bits */ + uint32_t state; /* current chip state */ + uint32_t nxstate; /* next expected state */ + + uint32_t *op; /* current operation, NULL operations isn't known yet */ + uint32_t pstates[NS_MAX_PREVSTATES]; /* previous states */ + uint16_t npstates; /* number of previous states saved */ + uint16_t stateidx; /* current state index */ + + /* The simulated NAND flash image */ + union flash_media { + u_char *byte; + uint16_t *word; + } mem; + + /* Internal buffer of page + OOB size bytes */ + union internal_buffer { + u_char *byte; /* for byte access */ + uint16_t *word; /* for 16-bit word access */ + } buf; + + /* NAND flash "geometry" */ + struct nandsin_geometry { + uint32_t totsz; /* total flash size, bytes */ + uint32_t secsz; /* flash sector (erase block) size, bytes */ + uint pgsz; /* NAND flash page size, bytes */ + uint oobsz; /* page OOB area size, bytes */ + uint32_t totszoob; /* total flash size including OOB, bytes */ + uint pgszoob; /* page size including OOB , bytes*/ + uint secszoob; /* sector size including OOB, bytes */ + uint pgnum; /* total number of pages */ + uint pgsec; /* number of pages per sector */ + uint secshift; /* bits number in sector size */ + uint pgshift; /* bits number in page size */ + uint oobshift; /* bits number in OOB size */ + uint pgaddrbytes; /* bytes per page address */ + uint secaddrbytes; /* bytes per sector address */ + uint idbytes; /* the number ID bytes that this chip outputs */ + } geom; + + /* NAND flash internal registers */ + struct nandsim_regs { + unsigned command; /* the command register */ + u_char status; /* the status register */ + uint row; /* the page number */ + uint column; /* the offset within page */ + uint count; /* internal counter */ + uint num; /* number of bytes which must be processed */ + uint off; /* fixed page offset */ + } regs; + + /* NAND flash lines state */ + struct ns_lines_status { + int ce; /* chip Enable */ + int cle; /* command Latch Enable */ + int ale; /* address Latch Enable */ + int wp; /* write Protect */ + } lines; +}; + +/* + * Operations array. To perform any operation the simulator must pass + * through the correspondent states chain. + */ +static struct nandsim_operations { + uint32_t reqopts; /* options which are required to perform the operation */ + uint32_t states[NS_OPER_STATES]; /* operation's states */ +} ops[NS_OPER_NUM] = { + /* Read page + OOB from the beginning */ + {OPT_SMALLPAGE, {STATE_CMD_READ0 | ACTION_ZEROOFF, STATE_ADDR_PAGE | ACTION_CPY, + STATE_DATAOUT, STATE_READY}}, + /* Read page + OOB from the second half */ + {OPT_PAGE512_8BIT, {STATE_CMD_READ1 | ACTION_HALFOFF, STATE_ADDR_PAGE | ACTION_CPY, + STATE_DATAOUT, STATE_READY}}, + /* Read OOB */ + {OPT_SMALLPAGE, {STATE_CMD_READOOB | ACTION_OOBOFF, STATE_ADDR_PAGE | ACTION_CPY, + STATE_DATAOUT, STATE_READY}}, + /* Programm page starting from the beginning */ + {OPT_ANY, {STATE_CMD_SEQIN, STATE_ADDR_PAGE, STATE_DATAIN, + STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, + /* Programm page starting from the beginning */ + {OPT_SMALLPAGE, {STATE_CMD_READ0, STATE_CMD_SEQIN | ACTION_ZEROOFF, STATE_ADDR_PAGE, + STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, + /* Programm page starting from the second half */ + {OPT_PAGE512, {STATE_CMD_READ1, STATE_CMD_SEQIN | ACTION_HALFOFF, STATE_ADDR_PAGE, + STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, + /* Programm OOB */ + {OPT_SMALLPAGE, {STATE_CMD_READOOB, STATE_CMD_SEQIN | ACTION_OOBOFF, STATE_ADDR_PAGE, + STATE_DATAIN, STATE_CMD_PAGEPROG | ACTION_PRGPAGE, STATE_READY}}, + /* Erase sector */ + {OPT_ANY, {STATE_CMD_ERASE1, STATE_ADDR_SEC, STATE_CMD_ERASE2 | ACTION_SECERASE, STATE_READY}}, + /* Read status */ + {OPT_ANY, {STATE_CMD_STATUS, STATE_DATAOUT_STATUS, STATE_READY}}, + /* Read multi-plane status */ + {OPT_SMARTMEDIA, {STATE_CMD_STATUS_M, STATE_DATAOUT_STATUS_M, STATE_READY}}, + /* Read ID */ + {OPT_ANY, {STATE_CMD_READID, STATE_ADDR_ZERO, STATE_DATAOUT_ID, STATE_READY}}, + /* Large page devices read page */ + {OPT_LARGEPAGE, {STATE_CMD_READ0, STATE_ADDR_PAGE, STATE_CMD_READSTART | ACTION_CPY, + STATE_DATAOUT, STATE_READY}} +}; + +/* MTD structure for NAND controller */ +static struct mtd_info *nsmtd; + +static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE]; + +/* + * Initialize the nandsim structure. + * + * RETURNS: 0 if success, -ERRNO if failure. + */ +static int +init_nandsim(struct mtd_info *mtd) +{ + struct nand_chip *chip = (struct nand_chip *)mtd->priv; + struct nandsim *ns = (struct nandsim *)(chip->priv); + int i; + + if (NS_IS_INITIALIZED(ns)) { + NS_ERR("init_nandsim: nandsim is already initialized\n"); + return -EIO; + } + + /* Force mtd to not do delays */ + chip->chip_delay = 0; + + /* Initialize the NAND flash parameters */ + ns->busw = chip->options & NAND_BUSWIDTH_16 ? 16 : 8; + ns->geom.totsz = mtd->size; + ns->geom.pgsz = mtd->oobblock; + ns->geom.oobsz = mtd->oobsize; + ns->geom.secsz = mtd->erasesize; + ns->geom.pgszoob = ns->geom.pgsz + ns->geom.oobsz; + ns->geom.pgnum = ns->geom.totsz / ns->geom.pgsz; + ns->geom.totszoob = ns->geom.totsz + ns->geom.pgnum * ns->geom.oobsz; + ns->geom.secshift = ffs(ns->geom.secsz) - 1; + ns->geom.pgshift = chip->page_shift; + ns->geom.oobshift = ffs(ns->geom.oobsz) - 1; + ns->geom.pgsec = ns->geom.secsz / ns->geom.pgsz; + ns->geom.secszoob = ns->geom.secsz + ns->geom.oobsz * ns->geom.pgsec; + ns->options = 0; + + if (ns->geom.pgsz == 256) { + ns->options |= OPT_PAGE256; + } + else if (ns->geom.pgsz == 512) { + ns->options |= (OPT_PAGE512 | OPT_AUTOINCR); + if (ns->busw == 8) + ns->options |= OPT_PAGE512_8BIT; + } else if (ns->geom.pgsz == 2048) { + ns->options |= OPT_PAGE2048; + } else { + NS_ERR("init_nandsim: unknown page size %u\n", ns->geom.pgsz); + return -EIO; + } + + if (ns->options & OPT_SMALLPAGE) { + if (ns->geom.totsz < (64 << 20)) { + ns->geom.pgaddrbytes = 3; + ns->geom.secaddrbytes = 2; + } else { + ns->geom.pgaddrbytes = 4; + ns->geom.secaddrbytes = 3; + } + } else { + if (ns->geom.totsz <= (128 << 20)) { + ns->geom.pgaddrbytes = 5; + ns->geom.secaddrbytes = 2; + } else { + ns->geom.pgaddrbytes = 5; + ns->geom.secaddrbytes = 3; + } + } + + /* Detect how many ID bytes the NAND chip outputs */ + for (i = 0; nand_flash_ids[i].name != NULL; i++) { + if (second_id_byte != nand_flash_ids[i].id) + continue; + if (!(nand_flash_ids[i].options & NAND_NO_AUTOINCR)) + ns->options |= OPT_AUTOINCR; + } + + if (ns->busw == 16) + NS_WARN("16-bit flashes support wasn't tested\n"); + + printk("flash size: %u MiB\n", ns->geom.totsz >> 20); + printk("page size: %u bytes\n", ns->geom.pgsz); + printk("OOB area size: %u bytes\n", ns->geom.oobsz); + printk("sector size: %u KiB\n", ns->geom.secsz >> 10); + printk("pages number: %u\n", ns->geom.pgnum); + printk("pages per sector: %u\n", ns->geom.pgsec); + printk("bus width: %u\n", ns->busw); + printk("bits in sector size: %u\n", ns->geom.secshift); + printk("bits in page size: %u\n", ns->geom.pgshift); + printk("bits in OOB size: %u\n", ns->geom.oobshift); + printk("flash size with OOB: %u KiB\n", ns->geom.totszoob >> 10); + printk("page address bytes: %u\n", ns->geom.pgaddrbytes); + printk("sector address bytes: %u\n", ns->geom.secaddrbytes); + printk("options: %#x\n", ns->options); + + /* Map / allocate and initialize the flash image */ +#ifdef CONFIG_NS_ABS_POS + ns->mem.byte = ioremap(CONFIG_NS_ABS_POS, ns->geom.totszoob); + if (!ns->mem.byte) { + NS_ERR("init_nandsim: failed to map the NAND flash image at address %p\n", + (void *)CONFIG_NS_ABS_POS); + return -ENOMEM; + } +#else + ns->mem.byte = vmalloc(ns->geom.totszoob); + if (!ns->mem.byte) { + NS_ERR("init_nandsim: unable to allocate %u bytes for flash image\n", + ns->geom.totszoob); + return -ENOMEM; + } + memset(ns->mem.byte, 0xFF, ns->geom.totszoob); +#endif + + /* Allocate / initialize the internal buffer */ + ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL); + if (!ns->buf.byte) { + NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n", + ns->geom.pgszoob); + goto error; + } + memset(ns->buf.byte, 0xFF, ns->geom.pgszoob); + + /* Fill the partition_info structure */ + ns->part.name = "NAND simulator partition"; + ns->part.offset = 0; + ns->part.size = ns->geom.totsz; + + return 0; + +error: +#ifdef CONFIG_NS_ABS_POS + iounmap(ns->mem.byte); +#else + vfree(ns->mem.byte); +#endif + + return -ENOMEM; +} + +/* + * Free the nandsim structure. + */ +static void +free_nandsim(struct nandsim *ns) +{ + kfree(ns->buf.byte); + +#ifdef CONFIG_NS_ABS_POS + iounmap(ns->mem.byte); +#else + vfree(ns->mem.byte); +#endif + + return; +} + +/* + * Returns the string representation of 'state' state. + */ +static char * +get_state_name(uint32_t state) +{ + switch (NS_STATE(state)) { + case STATE_CMD_READ0: + return "STATE_CMD_READ0"; + case STATE_CMD_READ1: + return "STATE_CMD_READ1"; + case STATE_CMD_PAGEPROG: + return "STATE_CMD_PAGEPROG"; + case STATE_CMD_READOOB: + return "STATE_CMD_READOOB"; + case STATE_CMD_READSTART: + return "STATE_CMD_READSTART"; + case STATE_CMD_ERASE1: + return "STATE_CMD_ERASE1"; + case STATE_CMD_STATUS: + return "STATE_CMD_STATUS"; + case STATE_CMD_STATUS_M: + return "STATE_CMD_STATUS_M"; + case STATE_CMD_SEQIN: + return "STATE_CMD_SEQIN"; + case STATE_CMD_READID: + return "STATE_CMD_READID"; + case STATE_CMD_ERASE2: + return "STATE_CMD_ERASE2"; + case STATE_CMD_RESET: + return "STATE_CMD_RESET"; + case STATE_ADDR_PAGE: + return "STATE_ADDR_PAGE"; + case STATE_ADDR_SEC: + return "STATE_ADDR_SEC"; + case STATE_ADDR_ZERO: + return "STATE_ADDR_ZERO"; + case STATE_DATAIN: + return "STATE_DATAIN"; + case STATE_DATAOUT: + return "STATE_DATAOUT"; + case STATE_DATAOUT_ID: + return "STATE_DATAOUT_ID"; + case STATE_DATAOUT_STATUS: + return "STATE_DATAOUT_STATUS"; + case STATE_DATAOUT_STATUS_M: + return "STATE_DATAOUT_STATUS_M"; + case STATE_READY: + return "STATE_READY"; + case STATE_UNKNOWN: + return "STATE_UNKNOWN"; + } + + NS_ERR("get_state_name: unknown state, BUG\n"); + return NULL; +} + +/* + * Check if command is valid. + * + * RETURNS: 1 if wrong command, 0 if right. + */ +static int +check_command(int cmd) +{ + switch (cmd) { + + case NAND_CMD_READ0: + case NAND_CMD_READSTART: + case NAND_CMD_PAGEPROG: + case NAND_CMD_READOOB: + case NAND_CMD_ERASE1: + case NAND_CMD_STATUS: + case NAND_CMD_SEQIN: + case NAND_CMD_READID: + case NAND_CMD_ERASE2: + case NAND_CMD_RESET: + case NAND_CMD_READ1: + return 0; + + case NAND_CMD_STATUS_MULTI: + default: + return 1; + } +} + +/* + * Returns state after command is accepted by command number. + */ +static uint32_t +get_state_by_command(unsigned command) +{ + switch (command) { + case NAND_CMD_READ0: + return STATE_CMD_READ0; + case NAND_CMD_READ1: + return STATE_CMD_READ1; + case NAND_CMD_PAGEPROG: + return STATE_CMD_PAGEPROG; + case NAND_CMD_READSTART: + return STATE_CMD_READSTART; + case NAND_CMD_READOOB: + return STATE_CMD_READOOB; + case NAND_CMD_ERASE1: + return STATE_CMD_ERASE1; + case NAND_CMD_STATUS: + return STATE_CMD_STATUS; + case NAND_CMD_STATUS_MULTI: + return STATE_CMD_STATUS_M; + case NAND_CMD_SEQIN: + return STATE_CMD_SEQIN; + case NAND_CMD_READID: + return STATE_CMD_READID; + case NAND_CMD_ERASE2: + return STATE_CMD_ERASE2; + case NAND_CMD_RESET: + return STATE_CMD_RESET; + } + + NS_ERR("get_state_by_command: unknown command, BUG\n"); + return 0; +} + +/* + * Move an address byte to the correspondent internal register. + */ +static inline void +accept_addr_byte(struct nandsim *ns, u_char bt) +{ + uint byte = (uint)bt; + + if (ns->regs.count < (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) + ns->regs.column |= (byte << 8 * ns->regs.count); + else { + ns->regs.row |= (byte << 8 * (ns->regs.count - + ns->geom.pgaddrbytes + + ns->geom.secaddrbytes)); + } + + return; +} + +/* + * Switch to STATE_READY state. + */ +static inline void +switch_to_ready_state(struct nandsim *ns, u_char status) +{ + NS_DBG("switch_to_ready_state: switch to %s state\n", get_state_name(STATE_READY)); + + ns->state = STATE_READY; + ns->nxstate = STATE_UNKNOWN; + ns->op = NULL; + ns->npstates = 0; + ns->stateidx = 0; + ns->regs.num = 0; + ns->regs.count = 0; + ns->regs.off = 0; + ns->regs.row = 0; + ns->regs.column = 0; + ns->regs.status = status; +} + +/* + * If the operation isn't known yet, try to find it in the global array + * of supported operations. + * + * Operation can be unknown because of the following. + * 1. New command was accepted and this is the firs call to find the + * correspondent states chain. In this case ns->npstates = 0; + * 2. There is several operations which begin with the same command(s) + * (for example program from the second half and read from the + * second half operations both begin with the READ1 command). In this + * case the ns->pstates[] array contains previous states. + * + * Thus, the function tries to find operation containing the following + * states (if the 'flag' parameter is 0): + * ns->pstates[0], ... ns->pstates[ns->npstates], ns->state + * + * If (one and only one) matching operation is found, it is accepted ( + * ns->ops, ns->state, ns->nxstate are initialized, ns->npstate is + * zeroed). + * + * If there are several maches, the current state is pushed to the + * ns->pstates. + * + * The operation can be unknown only while commands are input to the chip. + * As soon as address command is accepted, the operation must be known. + * In such situation the function is called with 'flag' != 0, and the + * operation is searched using the following pattern: + * ns->pstates[0], ... ns->pstates[ns->npstates], <address input> + * + * It is supposed that this pattern must either match one operation on + * none. There can't be ambiguity in that case. + * + * If no matches found, the functions does the following: + * 1. if there are saved states present, try to ignore them and search + * again only using the last command. If nothing was found, switch + * to the STATE_READY state. + * 2. if there are no saved states, switch to the STATE_READY state. + * + * RETURNS: -2 - no matched operations found. + * -1 - several matches. + * 0 - operation is found. + */ +static int +find_operation(struct nandsim *ns, uint32_t flag) +{ + int opsfound = 0; + int i, j, idx = 0; + + for (i = 0; i < NS_OPER_NUM; i++) { + + int found = 1; + + if (!(ns->options & ops[i].reqopts)) + /* Ignore operations we can't perform */ + continue; + + if (flag) { + if (!(ops[i].states[ns->npstates] & STATE_ADDR_MASK)) + continue; + } else { + if (NS_STATE(ns->state) != NS_STATE(ops[i].states[ns->npstates])) + continue; + } + + for (j = 0; j < ns->npstates; j++) + if (NS_STATE(ops[i].states[j]) != NS_STATE(ns->pstates[j]) + && (ns->options & ops[idx].reqopts)) { + found = 0; + break; + } + + if (found) { + idx = i; + opsfound += 1; + } + } + + if (opsfound == 1) { + /* Exact match */ + ns->op = &ops[idx].states[0]; + if (flag) { + /* + * In this case the find_operation function was + * called when address has just began input. But it isn't + * yet fully input and the current state must + * not be one of STATE_ADDR_*, but the STATE_ADDR_* + * state must be the next state (ns->nxstate). + */ + ns->stateidx = ns->npstates - 1; + } else { + ns->stateidx = ns->npstates; + } + ns->npstates = 0; + ns->state = ns->op[ns->stateidx]; + ns->nxstate = ns->op[ns->stateidx + 1]; + NS_DBG("find_operation: operation found, index: %d, state: %s, nxstate %s\n", + idx, get_state_name(ns->state), get_state_name(ns->nxstate)); + return 0; + } + + if (opsfound == 0) { + /* Nothing was found. Try to ignore previous commands (if any) and search again */ + if (ns->npstates != 0) { + NS_DBG("find_operation: no operation found, try again with state %s\n", + get_state_name(ns->state)); + ns->npstates = 0; + return find_operation(ns, 0); + + } + NS_DBG("find_operation: no operations found\n"); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return -2; + } + + if (flag) { + /* This shouldn't happen */ + NS_DBG("find_operation: BUG, operation must be known if address is input\n"); + return -2; + } + + NS_DBG("find_operation: there is still ambiguity\n"); + + ns->pstates[ns->npstates++] = ns->state; + + return -1; +} + +/* + * If state has any action bit, perform this action. + * + * RETURNS: 0 if success, -1 if error. + */ +static int +do_state_action(struct nandsim *ns, uint32_t action) +{ + int i, num; + int busdiv = ns->busw == 8 ? 1 : 2; + + action &= ACTION_MASK; + + /* Check that page address input is correct */ + if (action != ACTION_SECERASE && ns->regs.row >= ns->geom.pgnum) { + NS_WARN("do_state_action: wrong page number (%#x)\n", ns->regs.row); + return -1; + } + + switch (action) { + + case ACTION_CPY: + /* + * Copy page data to the internal buffer. + */ + + /* Column shouldn't be very large */ + if (ns->regs.column >= (ns->geom.pgszoob - ns->regs.off)) { + NS_ERR("do_state_action: column number is too large\n"); + break; + } + num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; + memcpy(ns->buf.byte, ns->mem.byte + NS_RAW_OFFSET(ns) + ns->regs.off, num); + + NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n", + num, NS_RAW_OFFSET(ns) + ns->regs.off); + + if (ns->regs.off == 0) + NS_LOG("read page %d\n", ns->regs.row); + else if (ns->regs.off < ns->geom.pgsz) + NS_LOG("read page %d (second half)\n", ns->regs.row); + else + NS_LOG("read OOB of page %d\n", ns->regs.row); + + NS_UDELAY(access_delay); + NS_UDELAY(input_cycle * ns->geom.pgsz / 1000 / busdiv); + + break; + + case ACTION_SECERASE: + /* + * Erase sector. + */ + + if (ns->lines.wp) { + NS_ERR("do_state_action: device is write-protected, ignore sector erase\n"); + return -1; + } + + if (ns->regs.row >= ns->geom.pgnum - ns->geom.pgsec + || (ns->regs.row & ~(ns->geom.secsz - 1))) { + NS_ERR("do_state_action: wrong sector address (%#x)\n", ns->regs.row); + return -1; + } + + ns->regs.row = (ns->regs.row << + 8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column; + ns->regs.column = 0; + + NS_DBG("do_state_action: erase sector at address %#x, off = %d\n", + ns->regs.row, NS_RAW_OFFSET(ns)); + NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift)); + + memset(ns->mem.byte + NS_RAW_OFFSET(ns), 0xFF, ns->geom.secszoob); + + NS_MDELAY(erase_delay); + + break; + + case ACTION_PRGPAGE: + /* + * Programm page - move internal buffer data to the page. + */ + + if (ns->lines.wp) { + NS_WARN("do_state_action: device is write-protected, programm\n"); + return -1; + } + + num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; + if (num != ns->regs.count) { + NS_ERR("do_state_action: too few bytes were input (%d instead of %d)\n", + ns->regs.count, num); + return -1; + } + + for (i = 0; i < num; i++) + ns->mem.byte[NS_RAW_OFFSET(ns) + ns->regs.off + i] &= ns->buf.byte[i]; + + NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n", + num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off); + NS_LOG("programm page %d\n", ns->regs.row); + + NS_UDELAY(programm_delay); + NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv); + + break; + + case ACTION_ZEROOFF: + NS_DBG("do_state_action: set internal offset to 0\n"); + ns->regs.off = 0; + break; + + case ACTION_HALFOFF: + if (!(ns->options & OPT_PAGE512_8BIT)) { + NS_ERR("do_state_action: BUG! can't skip half of page for non-512" + "byte page size 8x chips\n"); + return -1; + } + NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz/2); + ns->regs.off = ns->geom.pgsz/2; + break; + + case ACTION_OOBOFF: + NS_DBG("do_state_action: set internal offset to %d\n", ns->geom.pgsz); + ns->regs.off = ns->geom.pgsz; + break; + + default: + NS_DBG("do_state_action: BUG! unknown action\n"); + } + + return 0; +} + +/* + * Switch simulator's state. + */ +static void +switch_state(struct nandsim *ns) +{ + if (ns->op) { + /* + * The current operation have already been identified. + * Just follow the states chain. + */ + + ns->stateidx += 1; + ns->state = ns->nxstate; + ns->nxstate = ns->op[ns->stateidx + 1]; + + NS_DBG("switch_state: operation is known, switch to the next state, " + "state: %s, nxstate: %s\n", + get_state_name(ns->state), get_state_name(ns->nxstate)); + + /* See, whether we need to do some action */ + if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + } else { + /* + * We don't yet know which operation we perform. + * Try to identify it. + */ + + /* + * The only event causing the switch_state function to + * be called with yet unknown operation is new command. + */ + ns->state = get_state_by_command(ns->regs.command); + + NS_DBG("switch_state: operation is unknown, try to find it\n"); + + if (find_operation(ns, 0) != 0) + return; + + if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + } + + /* For 16x devices column means the page offset in words */ + if ((ns->nxstate & STATE_ADDR_MASK) && ns->busw == 16) { + NS_DBG("switch_state: double the column number for 16x device\n"); + ns->regs.column <<= 1; + } + + if (NS_STATE(ns->nxstate) == STATE_READY) { + /* + * The current state is the last. Return to STATE_READY + */ + + u_char status = NS_STATUS_OK(ns); + + /* In case of data states, see if all bytes were input/output */ + if ((ns->state & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) + && ns->regs.count != ns->regs.num) { + NS_WARN("switch_state: not all bytes were processed, %d left\n", + ns->regs.num - ns->regs.count); + status = NS_STATUS_FAILED(ns); + } + + NS_DBG("switch_state: operation complete, switch to STATE_READY state\n"); + + switch_to_ready_state(ns, status); + + return; + } else if (ns->nxstate & (STATE_DATAIN_MASK | STATE_DATAOUT_MASK)) { + /* + * If the next state is data input/output, switch to it now + */ + + ns->state = ns->nxstate; + ns->nxstate = ns->op[++ns->stateidx + 1]; + ns->regs.num = ns->regs.count = 0; + + NS_DBG("switch_state: the next state is data I/O, switch, " + "state: %s, nxstate: %s\n", + get_state_name(ns->state), get_state_name(ns->nxstate)); + + /* + * Set the internal register to the count of bytes which + * are expected to be input or output + */ + switch (NS_STATE(ns->state)) { + case STATE_DATAIN: + case STATE_DATAOUT: + ns->regs.num = ns->geom.pgszoob - ns->regs.off - ns->regs.column; + break; + + case STATE_DATAOUT_ID: + ns->regs.num = ns->geom.idbytes; + break; + + case STATE_DATAOUT_STATUS: + case STATE_DATAOUT_STATUS_M: + ns->regs.count = ns->regs.num = 0; + break; + + default: + NS_ERR("switch_state: BUG! unknown data state\n"); + } + + } else if (ns->nxstate & STATE_ADDR_MASK) { + /* + * If the next state is address input, set the internal + * register to the number of expected address bytes + */ + + ns->regs.count = 0; + + switch (NS_STATE(ns->nxstate)) { + case STATE_ADDR_PAGE: + ns->regs.num = ns->geom.pgaddrbytes; + + break; + case STATE_ADDR_SEC: + ns->regs.num = ns->geom.secaddrbytes; + break; + + case STATE_ADDR_ZERO: + ns->regs.num = 1; + break; + + default: + NS_ERR("switch_state: BUG! unknown address state\n"); + } + } else { + /* + * Just reset internal counters. + */ + + ns->regs.num = 0; + ns->regs.count = 0; + } +} + +static void +ns_hwcontrol(struct mtd_info *mtd, int cmd) +{ + struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; + + switch (cmd) { + + /* set CLE line high */ + case NAND_CTL_SETCLE: + NS_DBG("ns_hwcontrol: start command latch cycles\n"); + ns->lines.cle = 1; + break; + + /* set CLE line low */ + case NAND_CTL_CLRCLE: + NS_DBG("ns_hwcontrol: stop command latch cycles\n"); + ns->lines.cle = 0; + break; + + /* set ALE line high */ + case NAND_CTL_SETALE: + NS_DBG("ns_hwcontrol: start address latch cycles\n"); + ns->lines.ale = 1; + break; + + /* set ALE line low */ + case NAND_CTL_CLRALE: + NS_DBG("ns_hwcontrol: stop address latch cycles\n"); + ns->lines.ale = 0; + break; + + /* set WP line high */ + case NAND_CTL_SETWP: + NS_DBG("ns_hwcontrol: enable write protection\n"); + ns->lines.wp = 1; + break; + + /* set WP line low */ + case NAND_CTL_CLRWP: + NS_DBG("ns_hwcontrol: disable write protection\n"); + ns->lines.wp = 0; + break; + + /* set CE line low */ + case NAND_CTL_SETNCE: + NS_DBG("ns_hwcontrol: enable chip\n"); + ns->lines.ce = 1; + break; + + /* set CE line high */ + case NAND_CTL_CLRNCE: + NS_DBG("ns_hwcontrol: disable chip\n"); + ns->lines.ce = 0; + break; + + default: + NS_ERR("hwcontrol: unknown command\n"); + } + + return; +} + +static u_char +ns_nand_read_byte(struct mtd_info *mtd) +{ + struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; + u_char outb = 0x00; + + /* Sanity and correctness checks */ + if (!ns->lines.ce) { + NS_ERR("read_byte: chip is disabled, return %#x\n", (uint)outb); + return outb; + } + if (ns->lines.ale || ns->lines.cle) { + NS_ERR("read_byte: ALE or CLE pin is high, return %#x\n", (uint)outb); + return outb; + } + if (!(ns->state & STATE_DATAOUT_MASK)) { + NS_WARN("read_byte: unexpected data output cycle, state is %s " + "return %#x\n", get_state_name(ns->state), (uint)outb); + return outb; + } + + /* Status register may be read as many times as it is wanted */ + if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS) { + NS_DBG("read_byte: return %#x status\n", ns->regs.status); + return ns->regs.status; + } + + /* Check if there is any data in the internal buffer which may be read */ + if (ns->regs.count == ns->regs.num) { + NS_WARN("read_byte: no more data to output, return %#x\n", (uint)outb); + return outb; + } + + switch (NS_STATE(ns->state)) { + case STATE_DATAOUT: + if (ns->busw == 8) { + outb = ns->buf.byte[ns->regs.count]; + ns->regs.count += 1; + } else { + outb = (u_char)cpu_to_le16(ns->buf.word[ns->regs.count >> 1]); + ns->regs.count += 2; + } + break; + case STATE_DATAOUT_ID: + NS_DBG("read_byte: read ID byte %d, total = %d\n", ns->regs.count, ns->regs.num); + outb = ns->ids[ns->regs.count]; + ns->regs.count += 1; + break; + default: + BUG(); + } + + if (ns->regs.count == ns->regs.num) { + NS_DBG("read_byte: all bytes were read\n"); + + /* + * The OPT_AUTOINCR allows to read next conseqitive pages without + * new read operation cycle. + */ + if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) { + ns->regs.count = 0; + if (ns->regs.row + 1 < ns->geom.pgnum) + ns->regs.row += 1; + NS_DBG("read_byte: switch to the next page (%#x)\n", ns->regs.row); + do_state_action(ns, ACTION_CPY); + } + else if (NS_STATE(ns->nxstate) == STATE_READY) + switch_state(ns); + + } + + return outb; +} + +static void +ns_nand_write_byte(struct mtd_info *mtd, u_char byte) +{ + struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; + + /* Sanity and correctness checks */ + if (!ns->lines.ce) { + NS_ERR("write_byte: chip is disabled, ignore write\n"); + return; + } + if (ns->lines.ale && ns->lines.cle) { + NS_ERR("write_byte: ALE and CLE pins are high simultaneously, ignore write\n"); + return; + } + + if (ns->lines.cle == 1) { + /* + * The byte written is a command. + */ + + if (byte == NAND_CMD_RESET) { + NS_LOG("reset chip\n"); + switch_to_ready_state(ns, NS_STATUS_OK(ns)); + return; + } + + /* + * Chip might still be in STATE_DATAOUT + * (if OPT_AUTOINCR feature is supported), STATE_DATAOUT_STATUS or + * STATE_DATAOUT_STATUS_M state. If so, switch state. + */ + if (NS_STATE(ns->state) == STATE_DATAOUT_STATUS + || NS_STATE(ns->state) == STATE_DATAOUT_STATUS_M + || ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT)) + switch_state(ns); + + /* Check if chip is expecting command */ + if (NS_STATE(ns->nxstate) != STATE_UNKNOWN && !(ns->nxstate & STATE_CMD_MASK)) { + /* + * We are in situation when something else (not command) + * was expected but command was input. In this case ignore + * previous command(s)/state(s) and accept the last one. + */ + NS_WARN("write_byte: command (%#x) wasn't expected, expected state is %s, " + "ignore previous states\n", (uint)byte, get_state_name(ns->nxstate)); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + } + + /* Check that the command byte is correct */ + if (check_command(byte)) { + NS_ERR("write_byte: unknown command %#x\n", (uint)byte); + return; + } + + NS_DBG("command byte corresponding to %s state accepted\n", + get_state_name(get_state_by_command(byte))); + ns->regs.command = byte; + switch_state(ns); + + } else if (ns->lines.ale == 1) { + /* + * The byte written is an address. + */ + + if (NS_STATE(ns->nxstate) == STATE_UNKNOWN) { + + NS_DBG("write_byte: operation isn't known yet, identify it\n"); + + if (find_operation(ns, 1) < 0) + return; + + if ((ns->state & ACTION_MASK) && do_state_action(ns, ns->state) < 0) { + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + ns->regs.count = 0; + switch (NS_STATE(ns->nxstate)) { + case STATE_ADDR_PAGE: + ns->regs.num = ns->geom.pgaddrbytes; + break; + case STATE_ADDR_SEC: + ns->regs.num = ns->geom.secaddrbytes; + break; + case STATE_ADDR_ZERO: + ns->regs.num = 1; + break; + default: + BUG(); + } + } + + /* Check that chip is expecting address */ + if (!(ns->nxstate & STATE_ADDR_MASK)) { + NS_ERR("write_byte: address (%#x) isn't expected, expected state is %s, " + "switch to STATE_READY\n", (uint)byte, get_state_name(ns->nxstate)); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + /* Check if this is expected byte */ + if (ns->regs.count == ns->regs.num) { + NS_ERR("write_byte: no more address bytes expected\n"); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + accept_addr_byte(ns, byte); + + ns->regs.count += 1; + + NS_DBG("write_byte: address byte %#x was accepted (%d bytes input, %d expected)\n", + (uint)byte, ns->regs.count, ns->regs.num); + + if (ns->regs.count == ns->regs.num) { + NS_DBG("address (%#x, %#x) is accepted\n", ns->regs.row, ns->regs.column); + switch_state(ns); + } + + } else { + /* + * The byte written is an input data. + */ + + /* Check that chip is expecting data input */ + if (!(ns->state & STATE_DATAIN_MASK)) { + NS_ERR("write_byte: data input (%#x) isn't expected, state is %s, " + "switch to %s\n", (uint)byte, + get_state_name(ns->state), get_state_name(STATE_READY)); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + /* Check if this is expected byte */ + if (ns->regs.count == ns->regs.num) { + NS_WARN("write_byte: %u input bytes has already been accepted, ignore write\n", + ns->regs.num); + return; + } + + if (ns->busw == 8) { + ns->buf.byte[ns->regs.count] = byte; + ns->regs.count += 1; + } else { + ns->buf.word[ns->regs.count >> 1] = cpu_to_le16((uint16_t)byte); + ns->regs.count += 2; + } + } + + return; +} + +static int +ns_device_ready(struct mtd_info *mtd) +{ + NS_DBG("device_ready\n"); + return 1; +} + +static uint16_t +ns_nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *chip = (struct nand_chip *)mtd->priv; + + NS_DBG("read_word\n"); + + return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8); +} + +static void +ns_nand_write_word(struct mtd_info *mtd, uint16_t word) +{ + struct nand_chip *chip = (struct nand_chip *)mtd->priv; + + NS_DBG("write_word\n"); + + chip->write_byte(mtd, word & 0xFF); + chip->write_byte(mtd, word >> 8); +} + +static void +ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; + + /* Check that chip is expecting data input */ + if (!(ns->state & STATE_DATAIN_MASK)) { + NS_ERR("write_buf: data input isn't expected, state is %s, " + "switch to STATE_READY\n", get_state_name(ns->state)); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + /* Check if these are expected bytes */ + if (ns->regs.count + len > ns->regs.num) { + NS_ERR("write_buf: too many input bytes\n"); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + memcpy(ns->buf.byte + ns->regs.count, buf, len); + ns->regs.count += len; + + if (ns->regs.count == ns->regs.num) { + NS_DBG("write_buf: %d bytes were written\n", ns->regs.count); + } +} + +static void +ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv; + + /* Sanity and correctness checks */ + if (!ns->lines.ce) { + NS_ERR("read_buf: chip is disabled\n"); + return; + } + if (ns->lines.ale || ns->lines.cle) { + NS_ERR("read_buf: ALE or CLE pin is high\n"); + return; + } + if (!(ns->state & STATE_DATAOUT_MASK)) { + NS_WARN("read_buf: unexpected data output cycle, current state is %s\n", + get_state_name(ns->state)); + return; + } + + if (NS_STATE(ns->state) != STATE_DATAOUT) { + int i; + + for (i = 0; i < len; i++) + buf[i] = ((struct nand_chip *)mtd->priv)->read_byte(mtd); + + return; + } + + /* Check if these are expected bytes */ + if (ns->regs.count + len > ns->regs.num) { + NS_ERR("read_buf: too many bytes to read\n"); + switch_to_ready_state(ns, NS_STATUS_FAILED(ns)); + return; + } + + memcpy(buf, ns->buf.byte + ns->regs.count, len); + ns->regs.count += len; + + if (ns->regs.count == ns->regs.num) { + if ((ns->options & OPT_AUTOINCR) && NS_STATE(ns->state) == STATE_DATAOUT) { + ns->regs.count = 0; + if (ns->regs.row + 1 < ns->geom.pgnum) + ns->regs.row += 1; + NS_DBG("read_buf: switch to the next page (%#x)\n", ns->regs.row); + do_state_action(ns, ACTION_CPY); + } + else if (NS_STATE(ns->nxstate) == STATE_READY) + switch_state(ns); + } + + return; +} + +static int +ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + ns_nand_read_buf(mtd, (u_char *)&ns_verify_buf[0], len); + + if (!memcmp(buf, &ns_verify_buf[0], len)) { + NS_DBG("verify_buf: the buffer is OK\n"); + return 0; + } else { + NS_DBG("verify_buf: the buffer is wrong\n"); + return -EFAULT; + } +} + +/* + * 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) +{ + struct nand_chip *chip; + struct nandsim *nand; + int retval = -ENOMEM; + + if (bus_width != 8 && bus_width != 16) { + NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width); + return -EINVAL; + } + + /* Allocate and initialize mtd_info, nand_chip and nandsim structures */ + nsmtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip) + + sizeof(struct nandsim), GFP_KERNEL); + if (!nsmtd) { + NS_ERR("unable to allocate core structures.\n"); + return -ENOMEM; + } + memset(nsmtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip) + + sizeof(struct nandsim)); + chip = (struct nand_chip *)(nsmtd + 1); + nsmtd->priv = (void *)chip; + nand = (struct nandsim *)(chip + 1); + chip->priv = (void *)nand; + + /* + * Register simulator's callbacks. + */ + 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; + chip->verify_buf = ns_nand_verify_buf; + chip->write_word = ns_nand_write_word; + chip->read_word = ns_nand_read_word; + chip->eccmode = NAND_ECC_SOFT; + + /* + * Perform minimum nandsim structure initialization to handle + * the initial ID read command correctly + */ + if (third_id_byte != 0xFF || fourth_id_byte != 0xFF) + nand->geom.idbytes = 4; + else + nand->geom.idbytes = 2; + nand->regs.status = NS_STATUS_OK(nand); + nand->nxstate = STATE_UNKNOWN; + nand->options |= OPT_PAGE256; /* temporary value */ + nand->ids[0] = first_id_byte; + nand->ids[1] = second_id_byte; + nand->ids[2] = third_id_byte; + nand->ids[3] = fourth_id_byte; + if (bus_width == 16) { + nand->busw = 16; + chip->options |= NAND_BUSWIDTH_16; + } + + if ((retval = nand_scan(nsmtd, 1)) != 0) { + NS_ERR("can't register NAND Simulator\n"); + if (retval > 0) + retval = -ENXIO; + goto error; + } + + /* Register NAND as one big partition */ + add_mtd_partitions(nsmtd, &nand->part, 1); + + return 0; + +error: + kfree(nsmtd); + + return retval; +} + +module_init(ns_init_module); + +/* + * Module clean-up function + */ +static void __exit ns_cleanup_module(void) +{ + struct nandsim *ns = (struct nandsim *)(((struct nand_chip *)nsmtd->priv)->priv); + + free_nandsim(ns); /* Free nandsim private resources */ + nand_release(nsmtd); /* Unregisterd drived */ + kfree(nsmtd); /* Free other structures */ +} + +module_exit(ns_cleanup_module); + +MODULE_LICENSE ("GPL"); +MODULE_AUTHOR ("Artem B. Bityuckiy"); +MODULE_DESCRIPTION ("The NAND flash simulator"); + diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index d11fe47c9e40..d05e9b97947d 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -11,7 +11,7 @@ * 28-Sep-2004 BJD Fixed ECC placement for Hardware mode * 12-Oct-2004 BJD Fixed errors in use of platform data * - * $Id: s3c2410.c,v 1.5 2004/10/12 10:10:15 bjd Exp $ + * $Id: s3c2410.c,v 1.7 2005/01/05 18:05:14 dwmw2 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 @@ -117,12 +117,12 @@ static struct s3c2410_nand_info *s3c2410_nand_mtd_toinfo(struct mtd_info *mtd) static struct s3c2410_nand_info *to_nand_info(struct device *dev) { - return (struct s3c2410_nand_info *)dev_get_drvdata(dev); + return dev_get_drvdata(dev); } static struct s3c2410_platform_nand *to_nand_plat(struct device *dev) { - return (struct s3c2410_platform_nand *)dev->platform_data; + return dev->platform_data; } /* timing calculations */ @@ -167,7 +167,7 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, if (plat != NULL) { tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 8); twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8); - twrph1 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8); + twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8); } else { /* default timings */ tacls = 8; @@ -205,7 +205,7 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) struct nand_chip *this = mtd->priv; unsigned long cur; - nmtd = (struct s3c2410_nand_mtd *)this->priv; + nmtd = this->priv; info = nmtd->info; cur = readl(info->regs + S3C2410_NFCONF); @@ -424,14 +424,14 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) { - struct nand_chip *this = (struct nand_chip *)mtd->priv; + struct nand_chip *this = mtd->priv; readsb(this->IO_ADDR_R, buf, len); } static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) { - struct nand_chip *this = (struct nand_chip *)mtd->priv; + struct nand_chip *this = mtd->priv; writesb(this->IO_ADDR_W, buf, len); } diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c new file mode 100755 index 000000000000..29572793334c --- /dev/null +++ b/drivers/mtd/nand/sharpsl.c @@ -0,0 +1,260 @@ +/* + * drivers/mtd/nand/sharpsl.c + * + * Copyright (C) 2004 Richard Purdie + * + * $Id: sharpsl.c,v 1.3 2005/01/03 14:53:50 rpurdie Exp $ + * + * Based on Sharp's NAND driver sharp_sl.c + * + * 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/genhd.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/mtd/partitions.h> +#include <linux/interrupt.h> +#include <asm/io.h> +#include <asm/hardware.h> +#include <asm/mach-types.h> + +static void __iomem *sharpsl_io_base; +static int sharpsl_phys_base = 0x0C000000; + +/* register offset */ +#define ECCLPLB sharpsl_io_base+0x00 /* line parity 7 - 0 bit */ +#define ECCLPUB sharpsl_io_base+0x04 /* line parity 15 - 8 bit */ +#define ECCCP sharpsl_io_base+0x08 /* column parity 5 - 0 bit */ +#define ECCCNTR sharpsl_io_base+0x0C /* ECC byte counter */ +#define ECCCLRR sharpsl_io_base+0x10 /* cleare ECC */ +#define FLASHIO sharpsl_io_base+0x14 /* Flash I/O */ +#define FLASHCTL sharpsl_io_base+0x18 /* Flash Control */ + +/* Flash control bit */ +#define FLRYBY (1 << 5) +#define FLCE1 (1 << 4) +#define FLWP (1 << 3) +#define FLALE (1 << 2) +#define FLCLE (1 << 1) +#define FLCE0 (1 << 0) + + +/* + * MTD structure for SharpSL + */ +static struct mtd_info *sharpsl_mtd = NULL; + +/* + * Define partitions for flash device + */ +#define DEFAULT_NUM_PARTITIONS 3 + +static int nr_partitions; +static struct mtd_partition sharpsl_nand_default_partition_info[] = { + { + .name = "System Area", + .offset = 0, + .size = 7 * 1024 * 1024, + }, + { + .name = "Root Filesystem", + .offset = 7 * 1024 * 1024, + .size = 30 * 1024 * 1024, + }, + { + .name = "Home Filesystem", + .offset = MTDPART_OFS_APPEND , + .size = MTDPART_SIZ_FULL , + }, +}; + +/* + * hardware specific access to control-lines + */ +static void +sharpsl_nand_hwcontrol(struct mtd_info* mtd, int cmd) +{ + switch (cmd) { + case NAND_CTL_SETCLE: + writeb(readb(FLASHCTL) | FLCLE, FLASHCTL); + break; + case NAND_CTL_CLRCLE: + writeb(readb(FLASHCTL) & ~FLCLE, FLASHCTL); + break; + + case NAND_CTL_SETALE: + writeb(readb(FLASHCTL) | FLALE, FLASHCTL); + break; + case NAND_CTL_CLRALE: + writeb(readb(FLASHCTL) & ~FLALE, FLASHCTL); + break; + + case NAND_CTL_SETNCE: + writeb(readb(FLASHCTL) & ~(FLCE0|FLCE1), FLASHCTL); + break; + case NAND_CTL_CLRNCE: + writeb(readb(FLASHCTL) | (FLCE0|FLCE1), FLASHCTL); + break; + } +} + +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr sharpsl_bbt = { + .options = 0, + .offs = 4, + .len = 2, + .pattern = scan_ff_pattern +}; + +static int +sharpsl_nand_dev_ready(struct mtd_info* mtd) +{ + return !((readb(FLASHCTL) & FLRYBY) == 0); +} + +static void +sharpsl_nand_enable_hwecc(struct mtd_info* mtd, int mode) +{ + writeb(0 ,ECCCLRR); +} + +static int +sharpsl_nand_calculate_ecc(struct mtd_info* mtd, const u_char* dat, + u_char* ecc_code) +{ + ecc_code[0] = ~readb(ECCLPUB); + ecc_code[1] = ~readb(ECCLPLB); + ecc_code[2] = (~readb(ECCCP) << 2) | 0x03; + return readb(ECCCNTR) != 0; +} + + +#ifdef CONFIG_MTD_PARTITIONS +const char *part_probes[] = { "cmdlinepart", NULL }; +#endif + + +/* + * Main initialization routine + */ +int __init +sharpsl_nand_init(void) +{ + struct nand_chip *this; + struct mtd_partition* sharpsl_partition_info; + int err = 0; + + /* Allocate memory for MTD device structure and private data */ + sharpsl_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), + GFP_KERNEL); + if (!sharpsl_mtd) { + printk ("Unable to allocate SharpSL NAND MTD device structure.\n"); + return -ENOMEM; + } + + /* map physical adress */ + sharpsl_io_base = ioremap(sharpsl_phys_base, 0x1000); + if(!sharpsl_io_base){ + printk("ioremap to access Sharp SL NAND chip failed\n"); + kfree(sharpsl_mtd); + return -EIO; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&sharpsl_mtd[1]); + + /* Initialize structures */ + memset((char *) sharpsl_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + sharpsl_mtd->priv = this; + + /* + * PXA initialize + */ + writeb(readb(FLASHCTL) | FLWP, FLASHCTL); + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = FLASHIO; + this->IO_ADDR_W = FLASHIO; + /* Set address of hardware control function */ + this->hwcontrol = sharpsl_nand_hwcontrol; + this->dev_ready = sharpsl_nand_dev_ready; + /* 15 us command delay time */ + this->chip_delay = 15; + /* set eccmode using hardware ECC */ + this->eccmode = NAND_ECC_HW3_256; + this->enable_hwecc = sharpsl_nand_enable_hwecc; + this->calculate_ecc = sharpsl_nand_calculate_ecc; + this->correct_data = nand_correct_data; + this->badblock_pattern = &sharpsl_bbt; + + /* Scan to find existence of the device */ + err=nand_scan(sharpsl_mtd,1); + if (err) { + iounmap(sharpsl_io_base); + kfree(sharpsl_mtd); + return err; + } + + /* Register the partitions */ + sharpsl_mtd->name = "sharpsl-nand"; + nr_partitions = parse_mtd_partitions(sharpsl_mtd, part_probes, + &sharpsl_partition_info, 0); + + if (nr_partitions <= 0) { + 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; + } else if (machine_is_corgi() || machine_is_shepherd()) { + sharpsl_partition_info[1].size=25 * 1024 * 1024; + } else if (machine_is_husky()) { + sharpsl_partition_info[1].size=53 * 1024 * 1024; + } + } + + if (machine_is_husky()) { + /* Need to use small eraseblock size for backward compatibility */ + sharpsl_mtd->flags |= MTD_NO_VIRTBLOCKS; + } + + add_mtd_partitions(sharpsl_mtd, sharpsl_partition_info, nr_partitions); + + /* Return happy */ + return 0; +} +module_init(sharpsl_nand_init); + +/* + * Clean up routine + */ +#ifdef MODULE +static void __exit sharpsl_nand_cleanup(void) +{ + struct nand_chip *this = (struct nand_chip *) &sharpsl_mtd[1]; + + /* Release resources, unregister device */ + nand_release(sharpsl_mtd); + + iounmap(sharpsl_io_base); + + /* Free the MTD device structure */ + kfree(sharpsl_mtd); +} +module_exit(sharpsl_nand_cleanup); +#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>"); +MODULE_DESCRIPTION("Device specific logic for NAND flash on Sharp SL-C7xx Series"); diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index 19d6192f6f67..84afd9029f53 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -4,7 +4,7 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: nftlmount.c,v 1.39 2004/11/05 22:51:41 kalev Exp $ + * $Id: nftlmount.c,v 1.40 2004/11/22 14:38:29 kalev 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 @@ -31,7 +31,7 @@ #define SECTORSIZE 512 -char nftlmountrev[]="$Revision: 1.39 $"; +char nftlmountrev[]="$Revision: 1.40 $"; /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the * various device information of the NFTL partition and Bad Unit Table. Update @@ -302,8 +302,6 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) struct nftl_uci1 uci; struct erase_info *instr = &nftl->instr; - instr->mtd = nftl->mbd.mtd; - /* Read the Unit Control Information #1 for Wear-Leveling */ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, &retlen, (char *)&uci) < 0) @@ -320,6 +318,7 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) memset(instr, 0, sizeof(struct erase_info)); /* XXX: use async erase interface, XXX: test return code */ + instr->mtd = nftl->mbd.mtd; instr->addr = block * nftl->EraseSize; instr->len = nftl->EraseSize; MTD_ERASE(nftl->mbd.mtd, instr); diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index 0212a7df84c4..13f9e992bef8 100644 --- a/drivers/mtd/redboot.c +++ b/drivers/mtd/redboot.c @@ -1,5 +1,5 @@ /* - * $Id: redboot.c,v 1.15 2004/08/10 07:55:16 dwmw2 Exp $ + * $Id: redboot.c,v 1.17 2004/11/22 11:33:56 ijc Exp $ * * Parse RedBoot-style Flash Image System (FIS) tables and * produce a Linux partition array to match. @@ -30,6 +30,9 @@ struct fis_list { struct fis_list *next; }; +static int directory = CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK; +module_param(directory, int, 0); + static inline int redboot_checksum(struct fis_image_desc *img) { /* RedBoot doesn't actually write the desc_cksum field yet AFAICT */ @@ -50,6 +53,8 @@ static int parse_redboot_partitions(struct mtd_info *master, char *nullname; int namelen = 0; int nulllen = 0; + int numslots; + unsigned long offset; #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED static char nullstring[] = "unallocated"; #endif @@ -59,8 +64,15 @@ static int parse_redboot_partitions(struct mtd_info *master, if (!buf) return -ENOMEM; - /* Read the start of the last erase block */ - ret = master->read(master, master->size - master->erasesize, + if ( directory < 0 ) + offset = master->size + directory*master->erasesize; + else + offset = directory*master->erasesize; + + printk(KERN_NOTICE "Searching for RedBoot partition table in %s at offset 0x%lx\n", + master->name, offset); + + ret = master->read(master, offset, master->erasesize, &retlen, (void *)buf); if (ret) @@ -71,12 +83,16 @@ static int parse_redboot_partitions(struct mtd_info *master, goto out; } - /* RedBoot image could appear in any of the first three slots */ - for (i = 0; i < 3; i++) { - if (!memcmp(buf[i].name, "RedBoot", 8)) + numslots = (master->erasesize / sizeof(struct fis_image_desc)); + for (i = 0; i < numslots; i++) { + if (buf[i].name[0] == 0xff) { + i = numslots; + break; + } + if (!memcmp(buf[i].name, "FIS directory", 14)) break; } - if (i == 3) { + if (i == numslots) { /* Didn't find it */ printk(KERN_NOTICE "No RedBoot partition table detected in %s\n", master->name); @@ -84,7 +100,7 @@ static int parse_redboot_partitions(struct mtd_info *master, goto out; } - for (i = 0; i < master->erasesize / sizeof(struct fis_image_desc); i++) { + for (i = 0; i < numslots; i++) { struct fis_list *new_fl, **prev; if (buf[i].name[0] == 0xff) diff --git a/fs/Kconfig b/fs/Kconfig index 3e47986557f6..f36e155f1091 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -1191,6 +1191,15 @@ config JFFS2_FS_NAND Say 'N' unless you have NAND flash. +config JFFS2_FS_NOR_ECC + bool "JFFS2 support for ECC'd NOR flash (EXPERIMENTAL)" + depends on JFFS2_FS && EXPERIMENTAL + default n + help + This enables the experimental support for NOR flash with transparent + ECC for JFFS2. This type of flash chip is not common, however it is + available from ST Microelectronics. + config JFFS2_COMPRESSION_OPTIONS bool "Advanced compression options for JFFS2" depends on JFFS2_FS diff --git a/fs/jffs2/Makefile b/fs/jffs2/Makefile index 27eaa6a2b54f..e3c38ccf9c7d 100644 --- a/fs/jffs2/Makefile +++ b/fs/jffs2/Makefile @@ -1,7 +1,7 @@ # # Makefile for the Linux Journalling Flash File System v2 (JFFS2) # -# $Id: Makefile.common,v 1.6 2004/07/16 15:17:57 dwmw2 Exp $ +# $Id: Makefile.common,v 1.7 2004/11/03 12:57:38 jwboyer Exp $ # obj-$(CONFIG_JFFS2_FS) += jffs2.o @@ -12,6 +12,7 @@ jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o jffs2-y += super.o jffs2-$(CONFIG_JFFS2_FS_NAND) += wbuf.o +jffs2-$(CONFIG_JFFS2_FS_NOR_ECC) += wbuf.o jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o diff --git a/fs/jffs2/README.Locking b/fs/jffs2/README.Locking index 72dbceaf0427..49771cf8513a 100644 --- a/fs/jffs2/README.Locking +++ b/fs/jffs2/README.Locking @@ -1,4 +1,4 @@ - $Id: README.Locking,v 1.4 2002/03/08 16:20:06 dwmw2 Exp $ + $Id: README.Locking,v 1.9 2004/11/20 10:35:40 dwmw2 Exp $ JFFS2 LOCKING DOCUMENTATION --------------------------- @@ -80,10 +80,10 @@ per-eraseblock lists of physical jffs2_raw_node_ref structures, and (NB) the per-inode list of physical nodes. The latter is a special case - see below. -As the MTD API permits erase-completion callback functions to be -called from bottom-half (timer) context, and these functions access -the data structures protected by this lock, it must be locked with -spin_lock_bh(). +As the MTD API no longer permits erase-completion callback functions +to be called from bottom-half (timer) context (on the basis that nobody +ever actually implemented such a thing), it's now sufficient to use +a simple spin_lock() rather than spin_lock_bh(). Note that the per-inode list of physical nodes (f->nodes) is a special case. Any changes to _valid_ nodes (i.e. ->flash_offset & 1 == 0) in @@ -99,8 +99,27 @@ pointer when the garbage collection thread exits. The code to kill the GC thread locks it, sends the signal, then unlocks it - while the GC thread itself locks it, zeroes c->gc_task, then unlocks on the exit path. - node_free_sem - ------------- + + inocache_lock spinlock + ---------------------- + +This spinlock protects the hashed list (c->inocache_list) of the +in-core jffs2_inode_cache objects (each inode in JFFS2 has the +correspondent jffs2_inode_cache object). So, the inocache_lock +has to be locked while walking the c->inocache_list hash buckets. + +Note, the f->sem guarantees that the correspondent jffs2_inode_cache +will not be removed. So, it is allowed to access it without locking +the inocache_lock spinlock. + +Ordering constraints: + + If both erase_completion_lock and inocache_lock are needed, the + c->erase_completion has to be acquired first. + + + erase_free_sem + -------------- This semaphore is only used by the erase code which frees obsolete node references and the jffs2_garbage_collect_deletion_dirent() @@ -114,3 +133,16 @@ the jffs2_raw_node_ref structures in question while the garbage collection code is looking at them. Suggestions for alternative solutions to this problem would be welcomed. + + + wbuf_sem + -------- + +This read/write semaphore protects against concurrent access to the +write-behind buffer ('wbuf') used for flash chips where we must write +in blocks. It protects both the contents of the wbuf and the metadata +which indicates which flash region (if any) is currently covered by +the buffer. + +Ordering constraints: + Lock wbuf_sem last, after the alloc_sem or and f->sem. diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c index ac59ee35da65..f342ec9c12d3 100644 --- a/fs/jffs2/background.c +++ b/fs/jffs2/background.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: background.c,v 1.49 2004/07/13 08:56:40 dwmw2 Exp $ + * $Id: background.c,v 1.50 2004/11/16 20:36:10 dwmw2 Exp $ * */ diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index 9b6e1d8e486a..a01dd5fdbb95 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -3,17 +3,19 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: build.c,v 1.55 2003/10/28 17:02:44 dwmw2 Exp $ + * $Id: build.c,v 1.69 2004/12/16 20:22:18 dmarlin Exp $ * */ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/mtd/mtd.h> #include "nodelist.h" static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **); @@ -62,6 +64,7 @@ static inline void jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2 if (!child_ic) { printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n", fd->name, fd->ino, ic->ino); + jffs2_mark_node_obsolete(c, fd->raw); continue; } @@ -88,6 +91,7 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c) int ret; int i; struct jffs2_inode_cache *ic; + struct jffs2_full_dirent *fd; struct jffs2_full_dirent *dead_fds = NULL; /* First, scan the medium and build all the inode caches with @@ -95,13 +99,11 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c) c->flags |= JFFS2_SB_FLAG_MOUNTING; ret = jffs2_scan_medium(c); - c->flags &= ~JFFS2_SB_FLAG_MOUNTING; - if (ret) - return ret; + goto exit; D1(printk(KERN_DEBUG "Scanned flash completely\n")); - D1(jffs2_dump_block_lists(c)); + D2(jffs2_dump_block_lists(c)); /* Now scan the directory tree, increasing nlink according to every dirent found. */ for_each_inode(i, c, ic) { @@ -114,6 +116,8 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c) cond_resched(); } } + c->flags &= ~JFFS2_SB_FLAG_MOUNTING; + D1(printk(KERN_DEBUG "Pass 1 complete\n")); /* Next, scan for inodes with nlink == 0 and remove them. If @@ -135,9 +139,7 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c) D1(printk(KERN_DEBUG "Pass 2a starting\n")); while (dead_fds) { - struct jffs2_inode_cache *ic; - struct jffs2_full_dirent *fd = dead_fds; - + fd = dead_fds; dead_fds = fd->next; ic = jffs2_get_ino_cache(c, fd->ino); @@ -152,7 +154,6 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c) /* Finally, we can scan again and free the dirent structs */ for_each_inode(i, c, ic) { - struct jffs2_full_dirent *fd; D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes)); while(ic->scan_dents) { @@ -164,11 +165,24 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c) cond_resched(); } D1(printk(KERN_DEBUG "Pass 3 complete\n")); - D1(jffs2_dump_block_lists(c)); + D2(jffs2_dump_block_lists(c)); /* Rotate the lists by some number to ensure wear levelling */ jffs2_rotate_lists(c); + ret = 0; + +exit: + if (ret) { + for_each_inode(i, c, ic) { + while(ic->scan_dents) { + fd = ic->scan_dents; + ic->scan_dents = fd->next; + jffs2_free_full_dirent(fd); + } + } + } + return ret; } @@ -179,9 +193,12 @@ static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jf D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino)); - for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) { + raw = ic->nodes; + while (raw != (void *)ic) { + struct jffs2_raw_node_ref *next = raw->next_in_ino; D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", ref_offset(raw))); jffs2_mark_node_obsolete(c, raw); + raw = next; } if (ic->scan_dents) { @@ -297,7 +314,10 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c) c->free_size = c->flash_size; c->nr_blocks = c->flash_size / c->sector_size; - c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL); + if (c->mtd->flags & MTD_NO_VIRTBLOCKS) + c->blocks = vmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks); + else + c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL); if (!c->blocks) return -ENOMEM; for (i=0; i<c->nr_blocks; i++) { @@ -310,6 +330,7 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c) c->blocks[i].used_size = 0; c->blocks[i].first_node = NULL; c->blocks[i].last_node = NULL; + c->blocks[i].bad_count = 0; } init_MUTEX(&c->alloc_sem); @@ -336,7 +357,11 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c) D1(printk(KERN_DEBUG "build_fs failed\n")); jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); - kfree(c->blocks); + if (c->mtd->flags & MTD_NO_VIRTBLOCKS) { + vfree(c->blocks); + } else { + kfree(c->blocks); + } return -EIO; } diff --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c index f3b0bc97ee1c..9f9932c22adb 100644 --- a/fs/jffs2/compr_zlib.c +++ b/fs/jffs2/compr_zlib.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_zlib.c,v 1.28 2004/06/23 16:34:40 havasi Exp $ + * $Id: compr_zlib.c,v 1.29 2004/11/16 20:36:11 dwmw2 Exp $ * */ diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 0ec0be3f0bba..757306fa3ff4 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: dir.c,v 1.83 2004/10/19 07:48:44 havasi Exp $ + * $Id: dir.c,v 1.84 2004/11/16 20:36:11 dwmw2 Exp $ * */ diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index 724e7ee0e82e..8a00b27622dc 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: erase.c,v 1.61 2004/10/20 23:59:49 dwmw2 Exp $ + * $Id: erase.c,v 1.66 2004/11/16 20:36:11 dwmw2 Exp $ * */ @@ -43,6 +43,7 @@ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) jffs2_erase_succeeded(c, jeb); return; } + bad_offset = jeb->offset; #else /* Linux */ struct erase_info *instr; @@ -386,6 +387,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb jeb->dirty_size = 0; jeb->wasted_size = 0; } else { + struct kvec vecs[1]; struct jffs2_unknown_node marker = { .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK), .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER), @@ -394,8 +396,10 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4)); - /* We only write the header; the rest was noise or padding anyway */ - ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker); + vecs[0].iov_base = (unsigned char *) ▮ + vecs[0].iov_len = sizeof(marker); + ret = jffs2_flash_direct_writev(c, vecs, 1, jeb->offset, &retlen); + if (ret) { printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n", jeb->offset, ret); diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index 4b141011f803..1fc3cf673279 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: file.c,v 1.98 2004/03/19 16:41:09 dwmw2 Exp $ + * $Id: file.c,v 1.99 2004/11/16 20:36:11 dwmw2 Exp $ * */ diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index 66563ca71d47..3a72e6f4d50d 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: fs.c,v 1.46 2004/07/13 08:56:54 dwmw2 Exp $ + * $Id: fs.c,v 1.51 2004/11/28 12:19:37 dedekind Exp $ * */ @@ -20,6 +20,7 @@ #include <linux/mtd/mtd.h> #include <linux/pagemap.h> #include <linux/slab.h> +#include <linux/vmalloc.h> #include <linux/vfs.h> #include <linux/crc32.h> #include "nodelist.h" @@ -202,7 +203,7 @@ int jffs2_statfs(struct super_block *sb, struct kstatfs *buf) buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT; - D1(jffs2_dump_block_lists(c)); + D2(jffs2_dump_block_lists(c)); spin_unlock(&c->erase_completion_lock); @@ -463,11 +464,13 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) */ c->sector_size = c->mtd->erasesize; blocks = c->flash_size / c->sector_size; - while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) { - blocks >>= 1; - c->sector_size <<= 1; - } - + if (!(c->mtd->flags & MTD_NO_VIRTBLOCKS)) { + while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) { + blocks >>= 1; + c->sector_size <<= 1; + } + } + /* * Size alignment check */ @@ -533,7 +536,10 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) out_nodes: jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); - kfree(c->blocks); + if (c->mtd->flags & MTD_NO_VIRTBLOCKS) + vfree(c->blocks); + else + kfree(c->blocks); out_inohash: kfree(c->inocache_list); out_wbuf: @@ -649,6 +655,11 @@ int jffs2_flash_setup(struct jffs2_sb_info *c) { } /* add setups for other bizarre flashes here... */ + if (jffs2_nor_ecc(c)) { + ret = jffs2_nor_ecc_flash_setup(c); + if (ret) + return ret; + } return ret; } @@ -659,4 +670,7 @@ void jffs2_flash_cleanup(struct jffs2_sb_info *c) { } /* add cleanups for other bizarre flashes here... */ + if (jffs2_nor_ecc(c)) { + jffs2_nor_ecc_flash_cleanup(c); + } } diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 18c68802d790..87ec74ff5930 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: gc.c,v 1.140 2004/11/13 10:59:22 dedekind Exp $ + * $Id: gc.c,v 1.144 2004/12/21 11:18:50 dwmw2 Exp $ * */ @@ -103,7 +103,7 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) ret->wasted_size = 0; } - D1(jffs2_dump_block_lists(c)); + D2(jffs2_dump_block_lists(c)); return ret; } @@ -134,7 +134,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) if (c->checked_ino > c->highest_ino) { printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n", c->unchecked_size); - D1(jffs2_dump_block_lists(c)); + D2(jffs2_dump_block_lists(c)); spin_unlock(&c->erase_completion_lock); BUG(); } @@ -602,7 +602,7 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset); jffs2_free_raw_node_ref(nraw); } - if (!retried && (nraw == jffs2_alloc_raw_node_ref())) { + if (!retried && (nraw = jffs2_alloc_raw_node_ref())) { /* Try to reallocate space and retry */ uint32_t dummy; struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size]; diff --git a/fs/jffs2/ioctl.c b/fs/jffs2/ioctl.c index 1f8fb486cf9d..238c7992064c 100644 --- a/fs/jffs2/ioctl.c +++ b/fs/jffs2/ioctl.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: ioctl.c,v 1.8 2003/10/28 16:16:28 dwmw2 Exp $ + * $Id: ioctl.c,v 1.9 2004/11/16 20:36:11 dwmw2 Exp $ * */ diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c index b0bbe8a01cc2..5abb431c2a00 100644 --- a/fs/jffs2/malloc.c +++ b/fs/jffs2/malloc.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: malloc.c,v 1.27 2003/10/28 17:14:58 dwmw2 Exp $ + * $Id: malloc.c,v 1.28 2004/11/16 20:36:11 dwmw2 Exp $ * */ diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c index 99b05580c81b..cd6a8bd13e0b 100644 --- a/fs/jffs2/nodelist.c +++ b/fs/jffs2/nodelist.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.c,v 1.87 2004/11/14 17:07:07 dedekind Exp $ + * $Id: nodelist.c,v 1.90 2004/12/08 17:59:20 dwmw2 Exp $ * */ @@ -92,6 +92,17 @@ static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd) } } +/* Returns first valid node after 'ref'. May return 'ref' */ +static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_ref *ref) +{ + while (ref && ref->next_in_ino) { + if (!ref_obsolete(ref)) + return ref; + D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref))); + ref = ref->next_in_ino; + } + return NULL; +} /* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated with this ino, returning the former in order of version */ @@ -101,7 +112,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t *highest_version, uint32_t *latest_mctime, uint32_t *mctime_ver) { - struct jffs2_raw_node_ref *ref = f->inocache->nodes; + struct jffs2_raw_node_ref *ref, *valid_ref; struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL; struct jffs2_full_dirent *fd, *ret_fd = NULL; union jffs2_node_union node; @@ -111,22 +122,23 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f, *mctime_ver = 0; D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%u\n", f->inocache->ino)); - if (!f->inocache->nodes) { - printk(KERN_WARNING "Eep. no nodes for ino #%u\n", f->inocache->ino); - } spin_lock(&c->erase_completion_lock); - for (ref = f->inocache->nodes; ref && ref->next_in_ino; ref = ref->next_in_ino) { - /* Work out whether it's a data node or a dirent node */ - if (ref_obsolete(ref)) { - /* FIXME: On NAND flash we may need to read these */ - D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref))); - continue; - } + valid_ref = jffs2_first_valid_node(f->inocache->nodes); + + if (!valid_ref) + printk(KERN_WARNING "Eep. No valid nodes for ino #%u\n", f->inocache->ino); + + while (valid_ref) { /* We can hold a pointer to a non-obsolete node without the spinlock, but _obsolete_ nodes may disappear at any time, if the block - they're in gets erased */ + they're in gets erased. So if we mark 'ref' obsolete while we're + not holding the lock, it can go away immediately. For that reason, + we find the next valid node first, before processing 'ref'. + */ + ref = valid_ref; + valid_ref = jffs2_first_valid_node(ref->next_in_ino); spin_unlock(&c->erase_completion_lock); cond_resched(); @@ -182,7 +194,6 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f, err = -ENOMEM; goto free_out; } - memset(fd,0,sizeof(struct jffs2_full_dirent) + node.d.nsize+1); fd->raw = ref; fd->version = je32_to_cpu(node.d.version); fd->ino = je32_to_cpu(node.d.ino); @@ -220,6 +231,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f, } fd->nhash = full_name_hash(fd->name, node.d.nsize); fd->next = NULL; + fd->name[node.d.nsize] = '\0'; /* Wheee. We now have a complete jffs2_full_dirent structure, with the name in it and everything. Link it into the list */ diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index 4e6661caa895..bb49491b88df 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.h,v 1.121 2004/11/14 17:07:07 dedekind Exp $ + * $Id: nodelist.h,v 1.126 2004/11/19 15:06:29 dedekind Exp $ * */ @@ -107,16 +107,6 @@ struct jffs2_raw_node_ref #define ref_obsolete(ref) (((ref)->flash_offset & 3) == REF_OBSOLETE) #define mark_ref_normal(ref) do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0) -/* - Used for keeping track of deletion nodes &c, which can only be marked - as obsolete when the node which they mark as deleted has actually been - removed from the flash. -*/ -struct jffs2_raw_node_ref_list { - struct jffs2_raw_node_ref *rew; - struct jffs2_raw_node_ref_list *next; -}; - /* For each inode in the filesystem, we need to keep a record of nlink, because it would be a PITA to scan the whole directory tree at read_inode() time to calculate it, and to keep sufficient information @@ -148,13 +138,6 @@ struct jffs2_inode_cache { #define INOCACHE_HASHSIZE 128 -struct jffs2_scan_info { - struct jffs2_full_dirent *dents; - struct jffs2_tmp_dnode_info *tmpnodes; - /* Latest i_size info */ - uint32_t version; - uint32_t isize; -}; /* Larger representation of a raw node, kept in-core only when the struct inode for this particular ino is instantiated. @@ -163,12 +146,11 @@ struct jffs2_scan_info { struct jffs2_full_dnode { struct jffs2_raw_node_ref *raw; - uint32_t ofs; /* Don't really need this, but optimisation */ + uint32_t ofs; /* The offset to which the data of this node belongs */ uint32_t size; uint32_t frags; /* Number of fragments which currently refer to this node. When this reaches zero, - the node is obsolete. - */ + the node is obsolete. */ }; /* @@ -193,6 +175,7 @@ struct jffs2_full_dirent unsigned char type; unsigned char name[0]; }; + /* Fragments - used to build a map of which raw node to obtain data from for each part of the ino @@ -202,7 +185,7 @@ struct jffs2_node_frag struct rb_node rb; struct jffs2_full_dnode *node; /* NULL for holes */ uint32_t size; - uint32_t ofs; /* Don't really need this, but optimisation */ + uint32_t ofs; /* The offset to which this fragment belongs */ }; struct jffs2_eraseblock @@ -221,14 +204,6 @@ struct jffs2_eraseblock struct jffs2_raw_node_ref *last_node; struct jffs2_raw_node_ref *gc_node; /* Next node to be garbage collected */ - - /* For deletia. When a dirent node in this eraseblock is - deleted by a node elsewhere, that other node can only - be marked as obsolete when this block is actually erased. - So we keep a list of the nodes to mark as obsolete when - the erase is completed. - */ - // MAYBE struct jffs2_raw_node_ref_list *deletia; }; #define ACCT_SANITY_CHECK(c, jeb) do { \ @@ -396,7 +371,7 @@ static inline struct jffs2_node_frag *frag_first(struct rb_root *root) #define frag_erase(frag, list) rb_erase(&frag->rb, list); /* nodelist.c */ -D1(void jffs2_print_frag_list(struct jffs2_inode_info *f)); +D2(void jffs2_print_frag_list(struct jffs2_inode_info *f)); void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list); int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp, diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index 322395b81ffe..2651135bdf42 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodemgmt.c,v 1.109 2004/10/07 15:08:47 havasi Exp $ + * $Id: nodemgmt.c,v 1.115 2004/11/22 11:07:21 dwmw2 Exp $ * */ @@ -399,6 +399,17 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref } jeb = &c->blocks[blocknr]; + if (jffs2_can_mark_obsolete(c) && !jffs2_is_readonly(c) && + !(c->flags & JFFS2_SB_FLAG_MOUNTING)) { + /* Hm. This may confuse static lock analysis. If any of the above + three conditions is false, we're going to return from this + function without actually obliterating any nodes or freeing + any jffs2_raw_node_refs. So we don't need to stop erases from + happening, or protect against people holding an obsolete + jffs2_raw_node_ref without the erase_completion_lock. */ + down(&c->erase_free_sem); + } + spin_lock(&c->erase_completion_lock); if (ref_flags(ref) == REF_UNCHECKED) { @@ -463,6 +474,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref marked obsolete on the flash at the time they _became_ obsolete, there was probably a reason for that. */ spin_unlock(&c->erase_completion_lock); + /* We didn't lock the erase_free_sem */ return; } @@ -515,61 +527,87 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref spin_unlock(&c->erase_completion_lock); - if (!jffs2_can_mark_obsolete(c)) - return; - if (jffs2_is_readonly(c)) + if (!jffs2_can_mark_obsolete(c) || jffs2_is_readonly(c)) { + /* We didn't lock the erase_free_sem */ return; + } + + /* The erase_free_sem is locked, and has been since before we marked the node obsolete + and potentially put its eraseblock onto the erase_pending_list. Thus, we know that + the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet + by jffs2_free_all_node_refs() in erase.c. Which is nice. */ D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref))); ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n); if (ret) { printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret); - return; + goto out_erase_sem; } if (retlen != sizeof(n)) { printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); - return; + goto out_erase_sem; } if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) { printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref)); - return; + goto out_erase_sem; } if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) { D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype))); - return; + goto out_erase_sem; } /* XXX FIXME: This is ugly now */ n.nodetype = cpu_to_je16(je16_to_cpu(n.nodetype) & ~JFFS2_NODE_ACCURATE); ret = jffs2_flash_write(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n); if (ret) { printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret); - return; + goto out_erase_sem; } if (retlen != sizeof(n)) { printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); - return; + goto out_erase_sem; } /* Nodes which have been marked obsolete no longer need to be - associated with any inode. Remove them from the per-inode list */ + associated with any inode. Remove them from the per-inode list. + + Note we can't do this for NAND at the moment because we need + obsolete dirent nodes to stay on the lists, because of the + horridness in jffs2_garbage_collect_deletion_dirent(). Also + because we delete the inocache, and on NAND we need that to + stay around until all the nodes are actually erased, in order + to stop us from giving the same inode number to another newly + created inode. */ if (ref->next_in_ino) { struct jffs2_inode_cache *ic; struct jffs2_raw_node_ref **p; + spin_lock(&c->erase_completion_lock); + ic = jffs2_raw_ref_to_ic(ref); for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino)) ; *p = ref->next_in_ino; ref->next_in_ino = NULL; + + if (ic->nodes == (void *)ic) { + D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino)); + jffs2_del_ino_cache(c, ic); + jffs2_free_inode_cache(ic); + } + + spin_unlock(&c->erase_completion_lock); } /* Merge with the next node in the physical list, if there is one - and if it's also obsolete. */ - if (ref->next_phys && ref_obsolete(ref->next_phys) ) { + and if it's also obsolete and if it doesn't belong to any inode */ + if (ref->next_phys && ref_obsolete(ref->next_phys) && + !ref->next_phys->next_in_ino) { struct jffs2_raw_node_ref *n = ref->next_phys; + spin_lock(&c->erase_completion_lock); + ref->__totlen += n->__totlen; ref->next_phys = n->next_phys; if (jeb->last_node == n) jeb->last_node = ref; @@ -577,7 +615,8 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref /* gc will be happy continuing gc on this node */ jeb->gc_node=ref; } - BUG_ON(n->next_in_ino); + spin_unlock(&c->erase_completion_lock); + jffs2_free_raw_node_ref(n); } @@ -585,11 +624,13 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref and that one is obsolete */ if (ref != jeb->first_node ) { struct jffs2_raw_node_ref *p = jeb->first_node; - + + spin_lock(&c->erase_completion_lock); + while (p->next_phys != ref) p = p->next_phys; - if (ref_obsolete(p) ) { + if (ref_obsolete(p) && !ref->next_in_ino) { p->__totlen += ref->__totlen; if (jeb->last_node == ref) { jeb->last_node = p; @@ -601,10 +642,13 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref p->next_phys = ref->next_phys; jffs2_free_raw_node_ref(ref); } + spin_unlock(&c->erase_completion_lock); } + out_erase_sem: + up(&c->erase_free_sem); } -#if CONFIG_JFFS2_FS_DEBUG > 0 +#if CONFIG_JFFS2_FS_DEBUG >= 2 void jffs2_dump_block_lists(struct jffs2_sb_info *c) { diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index 9a7e01ca265a..55c0982a909b 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -3,11 +3,11 @@ * * Copyright (C) 2002-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: os-linux.h,v 1.47 2004/07/14 13:20:23 dwmw2 Exp $ + * $Id: os-linux.h,v 1.51 2004/11/16 20:36:11 dwmw2 Exp $ * */ @@ -99,7 +99,7 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) #define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY) -#ifndef CONFIG_JFFS2_FS_NAND +#if (!defined CONFIG_JFFS2_FS_NAND && !defined CONFIG_JFFS2_FS_NOR_ECC) #define jffs2_can_mark_obsolete(c) (1) #define jffs2_cleanmarker_oob(c) (0) #define jffs2_write_nand_cleanmarker(c,jeb) (-EIO) @@ -115,10 +115,13 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) #define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e) #define jffs2_wbuf_timeout NULL #define jffs2_wbuf_process NULL +#define jffs2_nor_ecc(c) (0) +#define jffs2_nor_ecc_flash_setup(c) (0) +#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0) -#else /* NAND support present */ +#else /* NAND and/or ECC'd NOR support present */ -#define jffs2_can_mark_obsolete(c) (c->mtd->type == MTD_NORFLASH || c->mtd->type == MTD_RAM) +#define jffs2_can_mark_obsolete(c) ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & MTD_ECC)) || c->mtd->type == MTD_RAM) #define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH) #define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf)) @@ -135,8 +138,19 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_erasebloc int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset); void jffs2_wbuf_timeout(unsigned long data); void jffs2_wbuf_process(void *data); +int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino); +int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c); int jffs2_nand_flash_setup(struct jffs2_sb_info *c); void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c); +#ifdef CONFIG_JFFS2_FS_NOR_ECC +#define jffs2_nor_ecc(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_ECC)) +int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c); +void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c); +#else +#define jffs2_nor_ecc(c) (0) +#define jffs2_nor_ecc_flash_setup(c) (0) +#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0) +#endif /* NOR ECC */ #endif /* NAND */ /* erase.c */ diff --git a/fs/jffs2/pushpull.h b/fs/jffs2/pushpull.h index 953b65320558..c0c2a9158dff 100644 --- a/fs/jffs2/pushpull.h +++ b/fs/jffs2/pushpull.h @@ -3,11 +3,11 @@ * * Copyright (C) 2001, 2002 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: pushpull.h,v 1.9 2003/10/04 08:33:06 dwmw2 Exp $ + * $Id: pushpull.h,v 1.10 2004/11/16 20:36:11 dwmw2 Exp $ * */ diff --git a/fs/jffs2/read.c b/fs/jffs2/read.c index 37375950f692..eb493dc06db7 100644 --- a/fs/jffs2/read.c +++ b/fs/jffs2/read.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: read.c,v 1.36 2004/05/25 11:12:32 havasi Exp $ + * $Id: read.c,v 1.38 2004/11/16 20:36:12 dwmw2 Exp $ * */ @@ -174,7 +174,7 @@ int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, if (frag) { D1(printk(KERN_NOTICE "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", f->inocache->ino, frag->ofs, offset)); holesize = min(holesize, frag->ofs - offset); - D1(jffs2_print_frag_list(f)); + D2(jffs2_print_frag_list(f)); } D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize)); memset(buf, 0, holesize); diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index b5f19c753d48..aca4a0b17bcd 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: readinode.c,v 1.114 2004/11/14 17:07:07 dedekind Exp $ + * $Id: readinode.c,v 1.117 2004/11/20 18:06:54 dwmw2 Exp $ * */ @@ -22,7 +22,7 @@ static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag); -#if CONFIG_JFFS2_FS_DEBUG >= 1 +#if CONFIG_JFFS2_FS_DEBUG >= 2 static void jffs2_print_fragtree(struct rb_root *list, int permitbug) { struct jffs2_node_frag *this = frag_first(list); @@ -56,7 +56,9 @@ void jffs2_print_frag_list(struct jffs2_inode_info *f) printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw)); } } +#endif +#if CONFIG_JFFS2_FS_DEBUG >= 1 static int jffs2_sanitycheck_fragtree(struct jffs2_inode_info *f) { struct jffs2_node_frag *frag; @@ -225,7 +227,7 @@ static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *l If so, both 'this' and the new node get marked REF_NORMAL so the GC can take a look. */ - if ((lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) { + if (lastend && (lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) { if (this->node) mark_ref_normal(this->node->raw); mark_ref_normal(newfrag->node->raw); diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 1ddd6bf1a3c9..ded53584a897 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: scan.c,v 1.112 2004/09/12 09:56:13 gleixner Exp $ + * $Id: scan.c,v 1.115 2004/11/17 12:59:08 dedekind Exp $ * */ #include <linux/kernel.h> @@ -68,7 +68,7 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo static inline int min_free(struct jffs2_sb_info *c) { uint32_t min = 2 * sizeof(struct jffs2_raw_inode); -#ifdef CONFIG_JFFS2_FS_NAND +#if defined CONFIG_JFFS2_FS_NAND || defined CONFIG_JFFS2_FS_NOR_ECC if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize) return c->wbuf_pagesize; #endif @@ -160,11 +160,8 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) case BLK_STATE_PARTDIRTY: /* Some data, but not full. Dirty list. */ - /* Except that we want to remember the block with most free space, - and stick it in the 'nextblock' position to start writing to it. - Later when we do snapshots, this must be the most recent block, - not the one with most free space. - */ + /* We want to remember the block with most free space + and stick it in the 'nextblock' position to start writing to it. */ if (jeb->free_size > min_free(c) && (!c->nextblock || c->nextblock->free_size < jeb->free_size)) { /* Better candidate for the next writes to go to */ @@ -223,7 +220,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) c->dirty_size -= c->nextblock->dirty_size; c->nextblock->dirty_size = 0; } -#ifdef CONFIG_JFFS2_FS_NAND +#if defined CONFIG_JFFS2_FS_NAND || defined CONFIG_JFFS2_FS_NOR_ECC if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) { /* If we're going to start writing into a block which already contains data, and the end of the data isn't page-aligned, diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index 678f7c9d6b62..6b2a441d2766 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: super.c,v 1.102 2004/11/12 02:42:17 tpoynor Exp $ + * $Id: super.c,v 1.104 2004/11/23 15:37:31 gleixner Exp $ * */ @@ -277,7 +277,10 @@ static void jffs2_put_super (struct super_block *sb) up(&c->alloc_sem); jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); - kfree(c->blocks); + if (c->mtd->flags & MTD_NO_VIRTBLOCKS) + vfree(c->blocks); + else + kfree(c->blocks); jffs2_flash_cleanup(c); kfree(c->inocache_list); if (c->mtd->sync) diff --git a/fs/jffs2/symlink.c b/fs/jffs2/symlink.c index 958cafb9068e..7b1820d13712 100644 --- a/fs/jffs2/symlink.c +++ b/fs/jffs2/symlink.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001, 2002 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: symlink.c,v 1.13 2004/07/13 08:59:04 dwmw2 Exp $ + * $Id: symlink.c,v 1.14 2004/11/16 20:36:12 dwmw2 Exp $ * */ diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 254f441647af..5aa0a718d134 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -4,12 +4,12 @@ * Copyright (C) 2001-2003 Red Hat, Inc. * Copyright (C) 2004 Thomas Gleixner <tglx@linutronix.de> * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * Modified debugged and enhanced by Thomas Gleixner <tglx@linutronix.de> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: wbuf.c,v 1.72 2004/09/11 19:22:43 gleixner Exp $ + * $Id: wbuf.c,v 1.82 2004/11/20 22:08:31 dwmw2 Exp $ * */ @@ -130,22 +130,8 @@ static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c) } } -/* Recover from failure to write wbuf. Recover the nodes up to the - * wbuf, not the one which we were starting to try to write. */ - -static void jffs2_wbuf_recover(struct jffs2_sb_info *c) +static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { - struct jffs2_eraseblock *jeb, *new_jeb; - struct jffs2_raw_node_ref **first_raw, **raw; - size_t retlen; - int ret; - unsigned char *buf; - uint32_t start, end, ofs, len; - - spin_lock(&c->erase_completion_lock); - - jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; - D1(printk("About to refile bad block at %08x\n", jeb->offset)); D2(jffs2_dump_block_lists(c)); @@ -175,6 +161,25 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) ACCT_SANITY_CHECK(c,jeb); D1(ACCT_PARANOIA_CHECK(jeb)); +} + +/* Recover from failure to write wbuf. Recover the nodes up to the + * wbuf, not the one which we were starting to try to write. */ + +static void jffs2_wbuf_recover(struct jffs2_sb_info *c) +{ + struct jffs2_eraseblock *jeb, *new_jeb; + struct jffs2_raw_node_ref **first_raw, **raw; + size_t retlen; + int ret; + unsigned char *buf; + uint32_t start, end, ofs, len; + + spin_lock(&c->erase_completion_lock); + + jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; + + jffs2_block_refile(c, jeb); /* Find the first node to be recovered, by skipping over every node which ends before the wbuf starts, or which is obsolete. */ @@ -224,7 +229,11 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) } /* Do the read... */ - ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo); + if (jffs2_cleanmarker_oob(c)) + ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo); + else + ret = c->mtd->read(c->mtd, start, c->wbuf_ofs - start, &retlen, buf); + if (ret == -EBADMSG && retlen == c->wbuf_ofs - start) { /* ECC recovered */ ret = 0; @@ -281,8 +290,11 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) ret = -EIO; } else #endif + if (jffs2_cleanmarker_oob(c)) ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen, buf, NULL, c->oobinfo); + else + ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, buf); if (ret || retlen != towrite) { /* Argh. We tried. Really we did. */ @@ -392,6 +404,10 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c) 1: Pad, do not adjust nextblock free_size 2: Pad, adjust nextblock free_size */ +#define NOPAD 0 +#define PAD_NOACCOUNT 1 +#define PAD_ACCOUNTING 2 + static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) { int ret; @@ -419,6 +435,10 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) */ if (pad) { c->wbuf_len = PAD(c->wbuf_len); + + /* Pad with JFFS2_DIRTY_BITMASK initially. this helps out ECC'd NOR + with 8 byte page size */ + memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len); if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) { struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len); @@ -426,9 +446,6 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING); padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len); padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4)); - } else { - /* Pad with JFFS2_DIRTY_BITMASK */ - memset(c->wbuf + c->wbuf_len, 0, c->wbuf_pagesize - c->wbuf_len); } } /* else jffs2_flash_writev has actually filled in the rest of the @@ -444,8 +461,11 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) ret = -EIO; } else #endif - ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo); - + + if (jffs2_cleanmarker_oob(c)) + ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo); + else + ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf); if (ret || retlen != c->wbuf_pagesize) { if (ret) @@ -458,7 +478,7 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) jffs2_wbuf_recover(c); - return ret; + return ret; } spin_lock(&c->erase_completion_lock); @@ -525,7 +545,9 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) if (c->unchecked_size) { /* GC won't make any progress for a while */ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n")); - ret = __jffs2_flush_wbuf(c, 2); + down_write(&c->wbuf_sem); + ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); + up_write(&c->wbuf_sem); } else while (old_wbuf_len && old_wbuf_ofs == c->wbuf_ofs) { @@ -537,7 +559,9 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) if (ret) { /* GC failed. Flush it with padding instead */ down(&c->alloc_sem); - ret = __jffs2_flush_wbuf(c, 2); + down_write(&c->wbuf_sem); + ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING); + up_write(&c->wbuf_sem); break; } down(&c->alloc_sem); @@ -552,9 +576,14 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) /* Pad write-buffer to end and write it, wasting space. */ int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c) { - return __jffs2_flush_wbuf(c, 1); -} + int ret; + + down_write(&c->wbuf_sem); + ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); + up_write(&c->wbuf_sem); + return ret; +} #define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) ) #define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) ) @@ -575,6 +604,8 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig if (!c->wbuf) return jffs2_flash_direct_writev(c, invecs, count, to, retlen); + down_write(&c->wbuf_sem); + /* If wbuf_ofs is not initialized, set it to target address */ if (c->wbuf_ofs == 0xFFFFFFFF) { c->wbuf_ofs = PAGE_DIV(to); @@ -582,6 +613,17 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig memset(c->wbuf,0xff,c->wbuf_pagesize); } + /* Fixup the wbuf if we are moving to a new eraseblock. The checks below + fail for ECC'd NOR because cleanmarker == 16, so a block starts at + xxx0010. */ + if (jffs2_nor_ecc(c)) { + if (((c->wbuf_ofs % c->sector_size) == 0) && !c->wbuf_len) { + c->wbuf_ofs = PAGE_DIV(to); + c->wbuf_len = PAGE_MOD(to); + memset(c->wbuf,0xff,c->wbuf_pagesize); + } + } + /* Sanity checks on target address. It's permitted to write at PAD(c->wbuf_len+c->wbuf_ofs), and it's permitted to write at the beginning of a new @@ -592,12 +634,12 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig /* It's a write to a new block */ if (c->wbuf_len) { D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs)); - ret = jffs2_flush_wbuf_pad(c); + ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT); if (ret) { /* the underlying layer has to check wbuf_len to do the cleanup */ D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); *retlen = 0; - return ret; + goto exit; } } /* set pointer to new block */ @@ -623,7 +665,6 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig invec = 0; outvec = 0; - /* Fill writebuffer first, if already in use */ if (c->wbuf_len) { uint32_t invec_ofs = 0; @@ -658,14 +699,14 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig } /* write buffer is full, flush buffer */ - ret = __jffs2_flush_wbuf(c, 0); + ret = __jffs2_flush_wbuf(c, NOPAD); if (ret) { /* the underlying layer has to check wbuf_len to do the cleanup */ D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); /* Retlen zero to make sure our caller doesn't mark the space dirty. We've already done everything that's necessary */ *retlen = 0; - return ret; + goto exit; } outvec_to += donelen; c->wbuf_ofs = outvec_to; @@ -709,19 +750,22 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig if (splitvec != -1) { uint32_t remainder; - int ret; remainder = outvecs[splitvec].iov_len - split_ofs; outvecs[splitvec].iov_len = split_ofs; /* We did cross a page boundary, so we write some now */ - ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo); + if (jffs2_cleanmarker_oob(c)) + ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo); + else + ret = jffs2_flash_direct_writev(c, outvecs, splitvec+1, outvec_to, &wbuf_retlen); + if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) { /* At this point we have no problem, c->wbuf is empty. */ *retlen = donelen; - return ret; + goto exit; } donelen += wbuf_retlen; @@ -760,7 +804,11 @@ alldone: if (c->wbuf_len && ino) jffs2_wbuf_dirties_inode(c, ino); - return 0; + ret = 0; + +exit: + up_write(&c->wbuf_sem); + return ret; } /* @@ -789,7 +837,12 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re /* Read flash */ if (!jffs2_can_mark_obsolete(c)) { - ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo); + down_read(&c->wbuf_sem); + + if (jffs2_cleanmarker_oob(c)) + ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo); + else + ret = c->mtd->read(c->mtd, ofs, len, retlen, buf); if ( (ret == -EBADMSG) && (*retlen == len) ) { printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n", @@ -811,23 +864,23 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re /* if no writebuffer available or write buffer empty, return */ if (!c->wbuf_pagesize || !c->wbuf_len) - return ret; + goto exit; /* if we read in a different block, return */ if ( (ofs & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) - return ret; + goto exit; if (ofs >= c->wbuf_ofs) { owbf = (ofs - c->wbuf_ofs); /* offset in write buffer */ if (owbf > c->wbuf_len) /* is read beyond write buffer ? */ - return ret; + goto exit; lwbf = c->wbuf_len - owbf; /* number of bytes to copy */ if (lwbf > len) lwbf = len; } else { orbf = (c->wbuf_ofs - ofs); /* offset in read buffer */ if (orbf > len) /* is write beyond write buffer ? */ - return ret; + goto exit; lwbf = len - orbf; /* number of bytes to copy */ if (lwbf > c->wbuf_len) lwbf = c->wbuf_len; @@ -835,6 +888,8 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re if (lwbf > 0) memcpy(buf+orbf,c->wbuf+owbf,lwbf); +exit: + up_read(&c->wbuf_sem); return ret; } @@ -1079,9 +1134,9 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c) int res; /* Initialise write buffer */ + init_rwsem(&c->wbuf_sem); c->wbuf_pagesize = c->mtd->oobblock; c->wbuf_ofs = 0xFFFFFFFF; - c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); if (!c->wbuf) @@ -1105,3 +1160,25 @@ void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) { kfree(c->wbuf); } + +#ifdef CONFIG_JFFS2_FS_NOR_ECC +int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c) { + /* Cleanmarker is actually larger on the flashes */ + c->cleanmarker_size = 16; + + /* Initialize write buffer */ + init_rwsem(&c->wbuf_sem); + c->wbuf_pagesize = c->mtd->eccsize; + c->wbuf_ofs = 0xFFFFFFFF; + + c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); + if (!c->wbuf) + return -ENOMEM; + + return 0; +} + +void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c) { + kfree(c->wbuf); +} +#endif diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index 799dc554ff0d..80a5db542629 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: write.c,v 1.86 2004/11/13 10:44:26 dedekind Exp $ + * $Id: write.c,v 1.87 2004/11/16 20:36:12 dwmw2 Exp $ * */ diff --git a/fs/jffs2/writev.c b/fs/jffs2/writev.c index 7e46e952d932..f079f8388566 100644 --- a/fs/jffs2/writev.c +++ b/fs/jffs2/writev.c @@ -3,11 +3,11 @@ * * Copyright (C) 2001, 2002 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: writev.c,v 1.5 2004/07/13 08:58:25 dwmw2 Exp $ + * $Id: writev.c,v 1.6 2004/11/16 20:36:12 dwmw2 Exp $ * */ diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h index 162f06ef56fe..419fc953ac16 100644 --- a/include/linux/jffs2.h +++ b/include/linux/jffs2.h @@ -3,12 +3,12 @@ * * Copyright (C) 2001-2003 Red Hat, Inc. * - * Created by David Woodhouse <dwmw2@redhat.com> + * Created by David Woodhouse <dwmw2@infradead.org> * * For licensing information, see the file 'LICENCE' in the * jffs2 directory. * - * $Id: jffs2.h,v 1.33 2004/05/25 11:31:55 havasi Exp $ + * $Id: jffs2.h,v 1.34 2004/11/16 20:36:14 dwmw2 Exp $ * */ diff --git a/include/linux/jffs2_fs_sb.h b/include/linux/jffs2_fs_sb.h index ee80d2520afb..4afc8d8c2e9e 100644 --- a/include/linux/jffs2_fs_sb.h +++ b/include/linux/jffs2_fs_sb.h @@ -1,4 +1,4 @@ -/* $Id: jffs2_fs_sb.h,v 1.45 2003/10/08 11:46:27 dwmw2 Exp $ */ +/* $Id: jffs2_fs_sb.h,v 1.48 2004/11/20 10:41:12 dwmw2 Exp $ */ #ifndef _JFFS2_FS_SB #define _JFFS2_FS_SB @@ -11,6 +11,7 @@ #include <linux/timer.h> #include <linux/wait.h> #include <linux/list.h> +#include <linux/rwsem.h> #define JFFS2_SB_FLAG_RO 1 #define JFFS2_SB_FLAG_MOUNTING 2 @@ -35,9 +36,7 @@ struct jffs2_sb_info { struct semaphore alloc_sem; /* Used to protect all the following fields, and also to protect against - out-of-order writing of nodes. - And GC. - */ + out-of-order writing of nodes. And GC. */ uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER (i.e. zero for OOB CLEANMARKER */ @@ -95,7 +94,7 @@ struct jffs2_sb_info { to an obsoleted node. I don't like this. Alternatives welcomed. */ struct semaphore erase_free_sem; -#ifdef CONFIG_JFFS2_FS_NAND +#if defined CONFIG_JFFS2_FS_NAND || defined CONFIG_JFFS2_FS_NOR_ECC /* Write-behind buffer for NAND flash */ unsigned char *wbuf; uint32_t wbuf_ofs; @@ -103,6 +102,8 @@ struct jffs2_sb_info { uint32_t wbuf_pagesize; struct jffs2_inodirty *wbuf_inodes; + struct rw_semaphore wbuf_sem; /* Protects the write buffer */ + /* Information about out-of-band area usage... */ struct nand_oobinfo *oobinfo; uint32_t badblock_pos; diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h index ba4b0d649661..2ed8c585021e 100644 --- a/include/linux/mtd/cfi.h +++ b/include/linux/mtd/cfi.h @@ -1,7 +1,7 @@ /* Common Flash Interface structures * See http://support.intel.com/design/flash/technote/index.htm - * $Id: cfi.h,v 1.49 2004/11/15 20:56:32 nico Exp $ + * $Id: cfi.h,v 1.50 2004/11/20 12:46:51 dwmw2 Exp $ */ #ifndef __MTD_CFI_H__ @@ -349,14 +349,12 @@ static inline uint8_t cfi_read_query(struct map_info *map, uint32_t addr) static inline void cfi_udelay(int us) { - unsigned long t = us * HZ / 1000000; - if (t) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(t); - return; + if (us >= 1000) { + msleep((us+999)/1000); + } else { + udelay(us); + cond_resched(); } - udelay(us); - cond_resched(); } static inline void cfi_spin_lock(spinlock_t *mutex) diff --git a/include/linux/mtd/doc2000.h b/include/linux/mtd/doc2000.h index 42646922b032..953e64fb8ac5 100644 --- a/include/linux/mtd/doc2000.h +++ b/include/linux/mtd/doc2000.h @@ -6,7 +6,7 @@ * Copyright (C) 2002-2003 Greg Ungerer <gerg@snapgear.com> * Copyright (C) 2002-2003 SnapGear Inc * - * $Id: doc2000.h,v 1.23 2004/09/16 23:26:08 gleixner Exp $ + * $Id: doc2000.h,v 1.24 2005/01/05 12:40:38 dwmw2 Exp $ * * Released under GPL */ diff --git a/include/linux/mtd/flashchip.h b/include/linux/mtd/flashchip.h index c3ac4df7273f..c66ba812bf90 100644 --- a/include/linux/mtd/flashchip.h +++ b/include/linux/mtd/flashchip.h @@ -6,7 +6,7 @@ * * (C) 2000 Red Hat. GPLd. * - * $Id: flashchip.h,v 1.14 2004/06/15 16:44:59 nico Exp $ + * $Id: flashchip.h,v 1.15 2004/11/05 22:41:06 nico Exp $ * */ @@ -37,6 +37,8 @@ typedef enum { FL_LOCKING, FL_UNLOCKING, FL_POINT, + FL_XIP_WHILE_ERASING, + FL_XIP_WHILE_WRITING, FL_UNKNOWN } flstate_t; diff --git a/include/linux/mtd/gen_probe.h b/include/linux/mtd/gen_probe.h index f6208098660c..3d7bdec14f97 100644 --- a/include/linux/mtd/gen_probe.h +++ b/include/linux/mtd/gen_probe.h @@ -1,7 +1,7 @@ /* * (C) 2001, 2001 Red Hat, Inc. * GPL'd - * $Id: gen_probe.h,v 1.2 2003/11/08 00:51:21 dsaxena Exp $ + * $Id: gen_probe.h,v 1.3 2004/10/20 22:10:33 dwmw2 Exp $ */ #ifndef __LINUX_MTD_GEN_PROBE_H__ diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index a960be2cc33c..f0268b99c900 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -1,6 +1,6 @@ /* Overhauled routines for dealing with different mmap regions of flash */ -/* $Id: map.h,v 1.45 2004/09/21 14:31:17 bjd Exp $ */ +/* $Id: map.h,v 1.46 2005/01/05 17:09:44 dwmw2 Exp $ */ #ifndef __LINUX_MTD_MAP_H__ #define __LINUX_MTD_MAP_H__ @@ -322,7 +322,7 @@ static inline map_word map_word_load_partial(struct map_info *map, map_word orig bitpos = (map_bankwidth(map)-1-i)*8; #endif orig.x[0] &= ~(0xff << bitpos); - orig.x[0] |= buf[i] << bitpos; + orig.x[0] |= buf[i-start] << bitpos; } } return orig; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 9453cb58c683..9a19c65abd74 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -5,7 +5,7 @@ * Steven J. Hill <sjhill@realitydiluted.com> * Thomas Gleixner <tglx@linutronix.de> * - * $Id: nand.h,v 1.66 2004/10/02 10:07:08 gleixner Exp $ + * $Id: nand.h,v 1.68 2004/11/12 10:40:37 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 @@ -138,6 +138,8 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_ #define NAND_ECC_HW6_512 4 /* Hardware ECC 8 byte ECC per 512 Byte data */ #define NAND_ECC_HW8_512 6 +/* Hardware ECC 12 byte ECC per 2048 Byte data */ +#define NAND_ECC_HW12_2048 7 /* * Constants for Hardware ECC @@ -253,6 +255,7 @@ struct nand_hw_control { * @scan_bbt: [REPLACEABLE] function to scan bad block table * @eccmode: [BOARDSPECIFIC] mode of ecc, see defines * @eccsize: [INTERN] databytes used per ecc-calculation + * @eccbytes: [INTERN] number of ecc bytes per ecc-calculation step * @eccsteps: [INTERN] number of ecc calculation steps per page * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR) * @chip_lock: [INTERN] spinlock used to protect access to this structure and the chip @@ -277,6 +280,7 @@ struct nand_hw_control { * @bbt: [INTERN] bad block table pointer * @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup * @bbt_md: [REPLACEABLE] bad block table mirror descriptor + * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan * @controller: [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices * @priv: [OPTIONAL] pointer to private chip date */ @@ -307,6 +311,7 @@ struct nand_chip { int (*scan_bbt)(struct mtd_info *mtd); int eccmode; int eccsize; + int eccbytes; int eccsteps; int chip_delay; spinlock_t chip_lock; @@ -330,6 +335,7 @@ struct nand_chip { uint8_t *bbt; struct nand_bbt_descr *bbt_td; struct nand_bbt_descr *bbt_md; + struct nand_bbt_descr *badblock_pattern; struct nand_hw_control *controller; void *priv; }; diff --git a/include/linux/mtd/xip.h b/include/linux/mtd/xip.h new file mode 100644 index 000000000000..fc071125cbcc --- /dev/null +++ b/include/linux/mtd/xip.h @@ -0,0 +1,107 @@ +/* + * MTD primitives for XIP support + * + * Author: Nicolas Pitre + * Created: Nov 2, 2004 + * Copyright: (C) 2004 MontaVista Software, Inc. + * + * This XIP support for MTD has been loosely inspired + * by an earlier patch authored by David Woodhouse. + * + * 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. + * + * $Id: xip.h,v 1.2 2004/12/01 15:49:10 nico Exp $ + */ + +#ifndef __LINUX_MTD_XIP_H__ +#define __LINUX_MTD_XIP_H__ + +#include <linux/config.h> + +#ifdef CONFIG_MTD_XIP + +/* + * Function that are modifying the flash state away from array mode must + * obviously not be running from flash. The __xipram is therefore marking + * those functions so they get relocated to ram. + */ +#define __xipram __attribute__ ((__section__ (".data"))) + +/* + * We really don't want gcc to guess anything. + * We absolutely _need_ proper inlining. + */ +#include <linux/compiler.h> + +/* + * Each architecture has to provide the following macros. They must access + * the hardware directly and not rely on any other (XIP) functions since they + * won't be available when used (flash not in array mode). + * + * xip_irqpending() + * + * return non zero when any hardware interrupt is pending. + * + * xip_currtime() + * + * return a platform specific time reference to be used with + * xip_elapsed_since(). + * + * xip_elapsed_since(x) + * + * return in usecs the elapsed timebetween now and the reference x as + * returned by xip_currtime(). + * + * note 1: convertion to usec can be approximated, as long as the + * returned value is <= the real elapsed time. + * note 2: this should be able to cope with a few seconds without + * overflowing. + */ + +#if defined(CONFIG_ARCH_SA1100) || defined(CONFIG_ARCH_PXA) + +#include <asm/hardware.h> +#ifdef CONFIG_ARCH_PXA +#include <asm/arch/pxa-regs.h> +#endif + +#define xip_irqpending() (ICIP & ICMR) + +/* we sample OSCR and convert desired delta to usec (1/4 ~= 1000000/3686400) */ +#define xip_currtime() (OSCR) +#define xip_elapsed_since(x) (signed)((OSCR - (x)) / 4) + +#else + +#warning "missing IRQ and timer primitives for XIP MTD support" +#warning "some of the XIP MTD support code will be disabled" +#warning "your system will therefore be unresponsive when writing or erasing flash" + +#define xip_irqpending() (0) +#define xip_currtime() (0) +#define xip_elapsed_since(x) (0) + +#endif + +/* + * xip_cpu_idle() is used when waiting for a delay equal or larger than + * the system timer tick period. This should put the CPU into idle mode + * to save power and to be woken up only when some interrupts are pending. + * As above, this should not rely upon standard kernel code. + */ + +#if defined(CONFIG_CPU_XSCALE) +#define xip_cpu_idle() asm volatile ("mcr p14, 0, %0, c7, c0, 0" :: "r" (1)) +#else +#define xip_cpu_idle() do { } while (0) +#endif + +#else + +#define __xipram + +#endif /* CONFIG_MTD_XIP */ + +#endif /* __LINUX_MTD_XIP_H__ */ diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index db93b7b108bb..a76ab898f445 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -1,5 +1,5 @@ /* - * $Id: mtd-abi.h,v 1.6 2004/08/09 13:38:30 dwmw2 Exp $ + * $Id: mtd-abi.h,v 1.7 2004/11/23 15:37:32 gleixner Exp $ * * Portions of MTD ABI definition which are shared by kernel and user space */ @@ -40,6 +40,7 @@ struct mtd_oob_buf { #define MTD_XIP 32 // eXecute-In-Place possible #define MTD_OOB 64 // Out-of-band data (NAND flash) #define MTD_ECC 128 // Device capable of automatic ECC +#define MTD_NO_VIRTBLOCKS 256 // Virtual blocks not allowed // Some common devices / combinations of capabilities #define MTD_CAP_ROM 0 diff --git a/kernel/workqueue.c b/kernel/workqueue.c index ee77ccd01d04..9e08e0c343f8 100644 --- a/kernel/workqueue.c +++ b/kernel/workqueue.c @@ -8,7 +8,7 @@ * * Derived from the taskqueue/keventd code by: * - * David Woodhouse <dwmw2@redhat.com> + * David Woodhouse <dwmw2@infradead.org> * Andrew Morton <andrewm@uow.edu.au> * Kai Petzke <wpp@marie.physik.tu-berlin.de> * Theodore Ts'o <tytso@mit.edu> diff --git a/lib/reed_solomon/reed_solomon.c b/lib/reed_solomon/reed_solomon.c index 87d425929b11..6604e3b1940c 100644 --- a/lib/reed_solomon/reed_solomon.c +++ b/lib/reed_solomon/reed_solomon.c @@ -9,7 +9,7 @@ * Reed Solomon code lifted from reed solomon library written by Phil Karn * Copyright 2002 Phil Karn, KA9Q * - * $Id: rslib.c,v 1.4 2004/10/05 22:07:53 gleixner Exp $ + * $Id: rslib.c,v 1.5 2004/10/22 15:41:47 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 @@ -22,19 +22,19 @@ * Each user must call init_rs to get a pointer to a rs_control * structure for the given rs parameters. This structure is either * generated or a already available matching control structure is used. - * If a structure is generated then the polynominal arrays for + * If a structure is generated then the polynomial arrays for * fast encoding / decoding are built. This can take some time so - * make sure not to call this function from a timecritical path. - * Usually a module / driver should initialize the neccecary + * make sure not to call this function from a time critical path. + * Usually a module / driver should initialize the necessary * rs_control structure on module / driver init and release it * on exit. - * The encoding puts the calculated syndrome into a given syndrom + * The encoding puts the calculated syndrome into a given syndrome * buffer. * The decoding is a two step process. The first step calculates - * the syndrome over the received (data + syndrom) and calls the + * the syndrome over the received (data + syndrome) and calls the * second stage, which does the decoding / error correction itself. - * Many hw encoders provide a syndrom calculation over the received - * data + syndrom and can call the second stage directly. + * Many hw encoders provide a syndrome calculation over the received + * data + syndrome and can call the second stage directly. * */ |
