summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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.c8
-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.c35
-rw-r--r--drivers/mtd/chips/jedec_probe.c78
-rw-r--r--drivers/mtd/cmdlinepart.c6
-rw-r--r--drivers/mtd/devices/Kconfig17
-rw-r--r--drivers/mtd/devices/phram.c16
-rw-r--r--drivers/mtd/inftlmount.c7
-rw-r--r--drivers/mtd/maps/Kconfig8
-rw-r--r--drivers/mtd/maps/Makefile3
-rw-r--r--drivers/mtd/maps/amd76xrom.c2
-rw-r--r--drivers/mtd/maps/dilnetpc.c2
-rw-r--r--drivers/mtd/maps/ebony.c2
-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.c2
-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/mtdblock.c4
-rw-r--r--drivers/mtd/nand/Kconfig18
-rw-r--r--drivers/mtd/nand/Makefile4
-rw-r--r--drivers/mtd/nand/nand_base.c99
-rw-r--r--drivers/mtd/nand/nand_bbt.c22
-rw-r--r--drivers/mtd/nand/nandsim.c1619
-rw-r--r--drivers/mtd/nand/s3c2410.c4
-rwxr-xr-xdrivers/mtd/nand/sharpsl.c273
-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.c47
-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.c8
-rw-r--r--fs/jffs2/ioctl.c4
-rw-r--r--fs/jffs2/malloc.c4
-rw-r--r--fs/jffs2/nodelist.c6
-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/flashchip.h4
-rw-r--r--include/linux/mtd/nand.h8
-rw-r--r--include/linux/mtd/xip.h99
-rw-r--r--include/mtd/mtd-abi.h3
-rw-r--r--kernel/workqueue.c2
-rw-r--r--lib/reed_solomon/reed_solomon.c16
79 files changed, 3140 insertions, 450 deletions
diff --git a/CREDITS b/CREDITS
index ef7dd2206d4e..fb060229fa14 100644
--- a/CREDITS
+++ b/CREDITS
@@ -3569,7 +3569,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 30993e54f19c..5309f80c3f03 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1439,7 +1439,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..b279c0b396b9 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.11 2004/11/29 22:40:44 dwmw2 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..54b5f69ab72b 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.112 2004/11/20 12:49:04 dwmw2 Exp $
*
*/
@@ -1173,8 +1173,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 +1258,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..0de8995d851b 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.7 2004/11/05 22:41:05 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);
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/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..e0ee91b0531e 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.14 2004/11/29 22:40:45 dwmw2 Exp $
menu "Self-contained MTD device drivers"
depends on MTD!=n
@@ -130,6 +130,8 @@ 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 +153,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 +175,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 +192,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/phram.c b/drivers/mtd/devices/phram.c
index 8aab73e6f26a..caf011a4b550 100644
--- a/drivers/mtd/devices/phram.c
+++ b/drivers/mtd/devices/phram.c
@@ -1,6 +1,6 @@
/**
*
- * $Id: phram.c,v 1.3 2004/11/16 18:29:01 dwmw2 Exp $
+ * $Id: phram.c,v 1.6 2004/11/25 16:51:09 joern Exp $
*
* Copyright (c) Jochen Schaeuble <psionic@psionic.de>
* 07/2003 rewritten by Joern Engel <joern@wh.fh-wedel.de>
@@ -15,9 +15,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,896Mi,110Mi phram=test,1006Mi,1Mi
+ *
*/
#include <asm/io.h>
@@ -184,7 +187,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 +240,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);
@@ -283,7 +288,7 @@ static int __init slram_setup(const char *val, struct kernel_param *kp)
if (!val || !val[0])
parse_err("no arguments to \"slram=\"\n");
- if (strnlen(val, sizeof(str)) >= sizeof(str))
+ if (strnlen(val, sizeof(buf)) >= sizeof(buf))
parse_err("parameter too long\n");
strcpy(str, val);
@@ -342,7 +347,6 @@ MODULE_PARM_DESC(slram, "List of memory regions to map. \"map=<name>,<start><len
static int __init init_phram(void)
{
- printk(KERN_ERR "phram loaded\n");
return 0;
}
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 7833587cd4a4..6af092c5d4aa 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.38 2004/11/24 19:42:51 rpurdie Exp $
menu "Mapping drivers for chip access"
depends on MTD!=n
@@ -645,5 +645,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 74fa440e072a..232bb4035b41 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.20 2004/11/24 19:42:51 rpurdie Exp $
ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y)
obj-$(CONFIG_MTD) += map_funcs.o
@@ -69,3 +69,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/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..7f8175a6c275 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.14 2004/11/28 09:40:39 dwmw2 Exp $
*
* Mapping for Ebony user flash
*
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..df45600a7284 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.9 2004/11/28 09:40:40 dwmw2 Exp $
*/
/****************************************************************************/
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..284f6d5b095c 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.19 2004/11/28 09:40:40 dwmw2 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/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/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 89ab7da160f1..ff42cc9422cf 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.25 2004/11/29 22:40:45 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
+
+ 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/nand_base.c b/drivers/mtd/nand/nand_base.c
index 181b95275352..1b1fcb623ded 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.123 2004/11/02 22:36:59 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
@@ -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..6cdff41b05ee
--- /dev/null
+++ b/drivers/mtd/nand/nandsim.c
@@ -0,0 +1,1619 @@
+/*
+ * 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.3 2004/11/26 13:00:24 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 0x36
+#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");
+
+/*
+ * There is no macro for 0x30 command which is used in "large page"
+ * devices in standard mtd header, define it here.
+ */
+#define NAND_CMD_READ2LP 0x30
+
+/* 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_INFO NS_OUTPUT_PREFIX " log: " args); } while(0)
+#define NS_DBG(args...) \
+ do { if (dbg) printk(KERN_INFO NS_OUTPUT_PREFIX " debug: " args); } while(0)
+#define NS_WARN(args...) \
+ do { printk(KERN_INFO NS_OUTPUT_PREFIX " warnig: " args); } while(0)
+#define NS_ERR(args...) \
+ do { printk(KERN_INFO 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_READ2LP 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_READ2LP | 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_READ2LP:
+ return "STATE_CMD_READ2LP";
+ 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_READ2LP:
+ 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_READ2LP:
+ return STATE_CMD_READ2LP;
+ 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..22b4c4004308 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.6 2004/11/24 12:25:48 bjd Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -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;
diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c
new file mode 100755
index 000000000000..7b4ed45f5dda
--- /dev/null
+++ b/drivers/mtd/nand/sharpsl.c
@@ -0,0 +1,273 @@
+/*
+ * drivers/mtd/nand/sharpsl.c
+ *
+ * Copyright (C) 2004 Richard Purdie
+ *
+ * $Id: sharpsl.c,v 1.2 2004/11/24 20:38:07 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>
+
+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
+
+#if defined CONFIG_MACH_POODLE
+#define SHARPSL_ROOTFS_SIZE 22
+#define SHARPSL_FLASH_SIZE 64
+#elif defined CONFIG_MACH_CORGI
+#define SHARPSL_ROOTFS_SIZE 25
+#define SHARPSL_FLASH_SIZE 32
+#elif defined CONFIG_MACH_SHEPHERD
+#define SHARPSL_ROOTFS_SIZE 25
+#define SHARPSL_FLASH_SIZE 64
+#elif defined CONFIG_MACH_HUSKY
+#define SHARPSL_ROOTFS_SIZE 53
+#define SHARPSL_FLASH_SIZE 128
+#elif defined CONFIG_MACH_TOSA
+#define SHARPSL_ROOTFS_SIZE 28
+#define SHARPSL_FLASH_SIZE 64
+#else
+#define SHARPSL_ROOTFS_SIZE 30
+#define SHARPSL_FLASH_SIZE 64
+#endif
+
+static int nr_partitions;
+static struct mtd_partition sharpsl_nand_default_partition_info[] = {
+ {
+ .name = "NAND flash partition 0",
+ .offset = 0,
+ .size = 7 * 1024 * 1024,
+ },
+
+ {
+ .name = "NAND flash partition 1",
+ .offset = 7 * 1024 * 1024,
+ .size = SHARPSL_ROOTFS_SIZE * 1024 * 1024,
+ },
+ {
+ .name = "NAND flash partition 2",
+ .offset = (SHARPSL_ROOTFS_SIZE+7) * 1024 * 1024,
+ .size = (SHARPSL_FLASH_SIZE - SHARPSL_ROOTFS_SIZE - 7) * 1024 * 1024,
+ },
+};
+
+/*
+ * 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;
+ }
+
+#ifdef CONFIG_MACH_HUSKY
+ /* Need to use small eraseblock size for backward compatibility */
+ sharpsl_mtd->flags |= MTD_NO_VIRTBLOCKS;
+#endif
+
+ 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 3a0bb962c67e..4a548ac9d8e3 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -1179,6 +1179,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..1718bce8daeb 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.68 2004/11/27 13:38:10 gleixner 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);
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 76d7279c8f66..a13a0411732a 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..6eb51ee9549a 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.143 2004/11/16 20:36:11 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();
}
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..e16067dcd55f 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.89 2004/11/28 12:20:35 dedekind Exp $
*
*/
@@ -182,7 +182,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 +219,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/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/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..521aa8e826ee
--- /dev/null
+++ b/include/linux/mtd/xip.h
@@ -0,0 +1,99 @@
+/*
+ * 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.1 2004/11/05 22:41:06 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
+#error "missing IRQ and timer primitives for XIP MTD support"
+#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.
*
*/