summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Woodhouse <dwmw2@shinybook.infradead.org>2005-01-05 21:11:47 +0100
committerDavid Woodhouse <dwmw2@shinybook.infradead.org>2005-01-05 21:11:47 +0100
commitee7ce018c513404c68148688b4c692ede25719cf (patch)
tree70552f5afa45ce908eecf2fb6e0663e42341d659
parent1db608d19a9479d0cae16a2cab6c77e7f1457453 (diff)
parent956846c2ac0246da330c16720c9325442205f029 (diff)
Merge shinybook.infradead.org:/home/dwmw2/bk/linus-2.6
into shinybook.infradead.org:/home/dwmw2/bk/mtd-2.6
-rw-r--r--CREDITS1
-rw-r--r--MAINTAINERS2
-rw-r--r--drivers/char/applicom.c2
-rw-r--r--drivers/mtd/Kconfig23
-rw-r--r--drivers/mtd/chips/Kconfig20
-rw-r--r--drivers/mtd/chips/amd_flash.c4
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0001.c321
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0002.c32
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0020.c8
-rw-r--r--drivers/mtd/chips/cfi_probe.c108
-rw-r--r--drivers/mtd/chips/cfi_util.c40
-rw-r--r--drivers/mtd/chips/jedec.c16
-rw-r--r--drivers/mtd/chips/jedec_probe.c78
-rw-r--r--drivers/mtd/chips/map_ram.c8
-rw-r--r--drivers/mtd/chips/map_rom.c4
-rw-r--r--drivers/mtd/cmdlinepart.c6
-rw-r--r--drivers/mtd/devices/Kconfig26
-rw-r--r--drivers/mtd/devices/Makefile3
-rw-r--r--drivers/mtd/devices/block2mtd.c505
-rw-r--r--drivers/mtd/devices/doc1000.c594
-rw-r--r--drivers/mtd/devices/doc2000.c22
-rw-r--r--drivers/mtd/devices/doc2001.c20
-rw-r--r--drivers/mtd/devices/doc2001plus.c24
-rw-r--r--drivers/mtd/devices/docprobe.c2
-rw-r--r--drivers/mtd/devices/ms02-nv.c8
-rw-r--r--drivers/mtd/devices/mtdram.c6
-rw-r--r--drivers/mtd/devices/phram.c148
-rw-r--r--drivers/mtd/devices/pmc551.c12
-rw-r--r--drivers/mtd/devices/slram.c10
-rw-r--r--drivers/mtd/inftlmount.c7
-rw-r--r--drivers/mtd/maps/Kconfig22
-rw-r--r--drivers/mtd/maps/Makefile6
-rw-r--r--drivers/mtd/maps/amd76xrom.c2
-rw-r--r--drivers/mtd/maps/chestnut.c6
-rw-r--r--drivers/mtd/maps/dilnetpc.c2
-rw-r--r--drivers/mtd/maps/ebony.c6
-rw-r--r--drivers/mtd/maps/elan-104nc.c2
-rw-r--r--drivers/mtd/maps/ichxrom.c2
-rw-r--r--drivers/mtd/maps/l440gx.c2
-rw-r--r--drivers/mtd/maps/netsc520.c2
-rw-r--r--drivers/mtd/maps/nettel.c20
-rw-r--r--drivers/mtd/maps/ocelot.c4
-rw-r--r--drivers/mtd/maps/pci.c2
-rw-r--r--drivers/mtd/maps/physmap.c2
-rw-r--r--drivers/mtd/maps/sbc_gxx.c2
-rw-r--r--drivers/mtd/maps/sc520cdp.c2
-rw-r--r--drivers/mtd/maps/scb2_flash.c2
-rw-r--r--drivers/mtd/maps/scx200_docflash.c2
-rw-r--r--drivers/mtd/maps/sharpsl-flash.c101
-rw-r--r--drivers/mtd/maps/ts5500_flash.c2
-rw-r--r--drivers/mtd/maps/uclinux.c6
-rw-r--r--drivers/mtd/maps/walnut.c122
-rw-r--r--drivers/mtd/mtdblock.c4
-rw-r--r--drivers/mtd/mtdchar.c12
-rw-r--r--drivers/mtd/nand/Kconfig18
-rw-r--r--drivers/mtd/nand/Makefile4
-rw-r--r--drivers/mtd/nand/diskonchip.c81
-rw-r--r--drivers/mtd/nand/nand_base.c101
-rw-r--r--drivers/mtd/nand/nand_bbt.c22
-rw-r--r--drivers/mtd/nand/nandsim.c1613
-rw-r--r--drivers/mtd/nand/s3c2410.c14
-rwxr-xr-xdrivers/mtd/nand/sharpsl.c260
-rw-r--r--drivers/mtd/nftlmount.c7
-rw-r--r--drivers/mtd/redboot.c32
-rw-r--r--fs/Kconfig9
-rw-r--r--fs/jffs2/Makefile3
-rw-r--r--fs/jffs2/README.Locking46
-rw-r--r--fs/jffs2/background.c4
-rw-r--r--fs/jffs2/build.c53
-rw-r--r--fs/jffs2/compr_zlib.c4
-rw-r--r--fs/jffs2/dir.c4
-rw-r--r--fs/jffs2/erase.c12
-rw-r--r--fs/jffs2/file.c4
-rw-r--r--fs/jffs2/fs.c32
-rw-r--r--fs/jffs2/gc.c10
-rw-r--r--fs/jffs2/ioctl.c4
-rw-r--r--fs/jffs2/malloc.c4
-rw-r--r--fs/jffs2/nodelist.c42
-rw-r--r--fs/jffs2/nodelist.h39
-rw-r--r--fs/jffs2/nodemgmt.c80
-rw-r--r--fs/jffs2/os-linux.h24
-rw-r--r--fs/jffs2/pushpull.h4
-rw-r--r--fs/jffs2/read.c6
-rw-r--r--fs/jffs2/readinode.c10
-rw-r--r--fs/jffs2/scan.c15
-rw-r--r--fs/jffs2/super.c9
-rw-r--r--fs/jffs2/symlink.c4
-rw-r--r--fs/jffs2/wbuf.c163
-rw-r--r--fs/jffs2/write.c4
-rw-r--r--fs/jffs2/writev.c4
-rw-r--r--include/linux/jffs2.h4
-rw-r--r--include/linux/jffs2_fs_sb.h11
-rw-r--r--include/linux/mtd/cfi.h14
-rw-r--r--include/linux/mtd/doc2000.h2
-rw-r--r--include/linux/mtd/flashchip.h4
-rw-r--r--include/linux/mtd/gen_probe.h2
-rw-r--r--include/linux/mtd/map.h4
-rw-r--r--include/linux/mtd/nand.h8
-rw-r--r--include/linux/mtd/xip.h107
-rw-r--r--include/mtd/mtd-abi.h3
-rw-r--r--kernel/workqueue.c2
-rw-r--r--lib/reed_solomon/reed_solomon.c16
102 files changed, 3988 insertions, 1329 deletions
diff --git a/CREDITS b/CREDITS
index e7635747ec62..7a8edb5f7dd0 100644
--- a/CREDITS
+++ b/CREDITS
@@ -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 *) &marker;
+ 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.
*
*/