summaryrefslogtreecommitdiffstats
path: root/drivers/mtd
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2001-01-10 05:27:25 +0000
committerRalf Baechle <ralf@linux-mips.org>2001-01-10 05:27:25 +0000
commitc9c06167e7933d93a6e396174c68abf242294abb (patch)
treed9a8bb30663e9a3405a1ef37ffb62bc14b9f019f /drivers/mtd
parentf79e8cc3c34e4192a3e5ef4cc9c6542fdef703c0 (diff)
Merge with Linux 2.4.0-test12.
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/Config.in45
-rw-r--r--drivers/mtd/Makefile8
-rw-r--r--drivers/mtd/cfi_cmdset_0002.c2
-rw-r--r--drivers/mtd/doc1000.c71
-rw-r--r--drivers/mtd/doc2000.c1101
-rw-r--r--drivers/mtd/doc2001.c356
-rw-r--r--drivers/mtd/docecc.c522
-rw-r--r--drivers/mtd/docprobe.c97
-rw-r--r--drivers/mtd/ftl.c24
-rw-r--r--drivers/mtd/map_ram.c13
-rw-r--r--drivers/mtd/map_rom.c46
-rw-r--r--drivers/mtd/mtdblock.c677
-rw-r--r--drivers/mtd/mtdchar.c195
-rw-r--r--drivers/mtd/mtdcore.c158
-rw-r--r--drivers/mtd/mtdpart.c228
-rw-r--r--drivers/mtd/nftl.c1263
-rw-r--r--drivers/mtd/nftlmount.c678
-rw-r--r--drivers/mtd/nora.c50
-rw-r--r--drivers/mtd/octagon-5066.c57
-rw-r--r--drivers/mtd/physmap.c38
-rw-r--r--drivers/mtd/pmc551.c344
-rw-r--r--drivers/mtd/pnc2000.c140
-rw-r--r--drivers/mtd/rpxlite.c105
-rw-r--r--drivers/mtd/vmax301.c61
24 files changed, 4128 insertions, 2151 deletions
diff --git a/drivers/mtd/Config.in b/drivers/mtd/Config.in
index 9023f0a81..87bf2e9b6 100644
--- a/drivers/mtd/Config.in
+++ b/drivers/mtd/Config.in
@@ -1,15 +1,21 @@
-# $Id: Config.in,v 1.20 2000/07/13 12:40:46 scote1 Exp $
+# $Id: No. :) $
mainmenu_option next_comment
comment 'Memory Technology Devices (MTD)'
tristate 'Memory Technology Device (MTD) support' CONFIG_MTD
-if [ "$CONFIG_MTD" != "n" ]; then
- dep_tristate ' M-Systems Disk-On-Chip 1000 support' CONFIG_MTD_DOC1000 $CONFIG_MTD
- dep_tristate ' M-Systems Disk-On-Chip 2000' CONFIG_MTD_DOC2000 $CONFIG_MTD
- dep_tristate ' M-Systems Disk-On-Chip Millennium' CONFIG_MTD_DOC2001 $CONFIG_MTD
+if [ "$CONFIG_MTD" = "y" -o "$CONFIG_MTD" = "m" ]; then
+ bool 'Debugging' CONFIG_MTD_DEBUG
+ if [ "$CONFIG_MTD_DEBUG" = "y" ]; then
+ int ' Debugging verbosity (0 = quiet, 3 = noisy)' CONFIG_MTD_DEBUG_VERBOSE 0
+ fi
+
+comment 'Disk-On-Chip Device Drivers'
+ dep_tristate ' M-Systems Disk-On-Chip 1000' CONFIG_MTD_DOC1000 $CONFIG_MTD
+ dep_tristate ' M-Systems Disk-On-Chip 2000 and Millennium' CONFIG_MTD_DOC2000 $CONFIG_MTD
+ dep_tristate ' M-Systems Disk-On-Chip Millennium-only alternative driver' CONFIG_MTD_DOC2001 $CONFIG_MTD
if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" ]; then
define_tristate CONFIG_MTD_DOCPROBE y
else
@@ -19,10 +25,18 @@ if [ "$CONFIG_MTD" != "n" ]; then
define_tristate CONFIG_MTD_DOCPROBE n
fi
fi
+ if [ "$CONFIG_MTD_DOCPROBE" = "y" -o "$CONFIG_MTD_DOCPROBE" = "m" ]; then
+ hex ' Physical address of DiskOnChip' CONFIG_MTD_DOCPROBE_ADDRESS 0x0000
+ bool ' Probe high addresses' CONFIG_MTD_DOCPROBE_HIGH
+ bool ' Probe for 0x55 0xAA BIOS Extension Signature' CONFIG_MTD_DOCPROBE_55AA
+ fi
+
+comment 'RAM/ROM Device Drivers'
dep_tristate ' Use extra onboard system memory as MTD device' CONFIG_MTD_SLRAM $CONFIG_MTD
dep_tristate ' Ramix PMC551 PCI Mezzanine ram card support' CONFIG_MTD_PMC551 $CONFIG_MTD $CONFIG_PCI
if [ "$CONFIG_MTD_PMC551" != "n" ]; then
bool ' PMC551 256M DRAM Bugfix' CONFIG_MTD_PMC551_BUGFIX
+ bool ' PMC551 Debugging' CONFIG_MTD_PMC551_DEBUG
fi
dep_tristate ' Debugging RAM test driver' CONFIG_MTD_MTDRAM $CONFIG_MTD
if [ "$CONFIG_MTD_MTDRAM" != "n" ]; then
@@ -30,20 +44,21 @@ if [ "$CONFIG_MTD" != "n" ]; then
int 'Size of the erase sectors in kB' CONFIG_MTDRAM_ERASE_SIZE 128
fi
-comment 'MTD drivers for mapped chips'
+comment 'Linearly Mapped Flash Device Drivers'
dep_tristate ' Common Flash Interface (CFI) support' CONFIG_MTD_CFI $CONFIG_MTD
dep_tristate ' CFI support for Intel/Sharp Extended Command Set chips' CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_CFI
dep_tristate ' CFI support for AMD/Fujitsu Standard Command Set chips' CONFIG_MTD_CFI_AMDSTD $CONFIG_MTD_CFI
+ dep_tristate ' Support for RAM chips in bus mapping' CONFIG_MTD_RAM $CONFIG_MTD
+ dep_tristate ' Support for ROM chips in bus mapping' CONFIG_MTD_ROM $CONFIG_MTD
# These will later become config-options
define_bool CONFIG_MTD_JEDEC n
-define_bool CONFIG_MTD_RAM n
-define_bool CONFIG_MTD_ROM n
- dep_tristate ' Flash chip mapping in physical memory' CONFIG_MTD_PHYSMAP $CONFIG_MTD_CFI
- if [ "$CONFIG_MTD_PHYSMAP" != "n" ]; then
- hex 'Physical start location of flash chip mapping' CONFIG_MTD_PHYSMAP_START 0x8000000
- hex 'Physical length of flash chip mapping' CONFIG_MTD_PHYSMAP_LEN 0x4000000
+ dep_tristate ' Flash chip mapping in physical memory' CONFIG_MTD_PHYSMAP $CONFIG_MTD_CFI
+ if [ "$CONFIG_MTD_PHYSMAP" = "y" -o "$CONFIG_MTD_PHYSMAP" = "m" ]; then
+ hex ' Physical start location of flash chip mapping' CONFIG_MTD_PHYSMAP_START 0x8000000
+ hex ' Physical length of flash chip mapping' CONFIG_MTD_PHYSMAP_LEN 0x4000000
+ int ' Bus width in octets' CONFIG_MTD_PHYSMAP_BUSWIDTH 2
fi
comment 'Drivers for chip mappings'
@@ -56,11 +71,11 @@ comment 'Drivers for chip mappings'
comment 'User modules and translation layers for MTD devices'
dep_tristate ' Direct chardevice access to MTD devices' CONFIG_MTD_CHAR $CONFIG_MTD
- dep_tristate ' Pseudo-blockdevice access to MTD devices' CONFIG_MTD_BLOCK $CONFIG_MTD
+ dep_tristate ' Caching blockdevice access to MTD devices' CONFIG_MTD_BLOCK $CONFIG_MTD
dep_tristate ' FTL (Flash Translation Layer) support' CONFIG_FTL $CONFIG_MTD
dep_tristate ' NFTL (NAND Flash Translation Layer) support' CONFIG_NFTL $CONFIG_MTD
- if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_NFTL" != "n" ]; then
- bool ' Write support for NFTL (EXPERIMENTAL)' CONFIG_NFTL_RW
+ if [ "$CONFIG_NFTL" = "y" -o "$CONFIG_NFTL" = "m" ]; then
+ bool ' Write support for NFTL (BETA)' CONFIG_NFTL_RW
fi
fi
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index 3f28868e3..7fdbf65a4 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -44,7 +44,7 @@ SUB_DIRS :=
ALL_SUB_DIRS :=
MOD_SUB_DIRS :=
-export-objs := mtdcore.o
+export-objs := mtdcore.o mtdpart.o
list-multi :=
# MTD devices
@@ -52,7 +52,7 @@ obj-$(CONFIG_MTD) += mtdcore.o
obj-$(CONFIG_MTD_DOC1000) += doc1000.o
obj-$(CONFIG_MTD_DOC2000) += doc2000.o
obj-$(CONFIG_MTD_DOC2001) += doc2001.o
-obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o
+obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o docecc.o
obj-$(CONFIG_MTD_SLRAM) += slram.o
obj-$(CONFIG_MTD_PMC551) += pmc551.o
obj-$(CONFIG_MTD_MTDRAM) += mtdram.o
@@ -70,7 +70,7 @@ obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
obj-$(CONFIG_MTD_MIXMEM) += mixmem.o
obj-$(CONFIG_MTD_NORA) += nora.o
obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o
-obj-$(CONFIG_MTD_PNC2000) += pnc2000.o
+obj-$(CONFIG_MTD_PNC2000) += pnc2000.o mtdpart.o
obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o
obj-$(CONFIG_MTD_VMAX) += vmax301.o
@@ -78,7 +78,7 @@ obj-$(CONFIG_MTD_VMAX) += vmax301.o
obj-$(CONFIG_MTD_CHAR) += mtdchar.o
obj-$(CONFIG_MTD_BLOCK) += mtdblock.o
obj-$(CONFIG_FTL) += ftl.o
-obj-$(CONFIG_NFTL) += nftl.o
+obj-$(CONFIG_NFTL) += nftl.o nftlmount.o
# Extract lists of the multi-part drivers.
# The 'int-*' lists are the intermediate files used to build the multi's.
diff --git a/drivers/mtd/cfi_cmdset_0002.c b/drivers/mtd/cfi_cmdset_0002.c
index d6cce6474..6cb0de4e9 100644
--- a/drivers/mtd/cfi_cmdset_0002.c
+++ b/drivers/mtd/cfi_cmdset_0002.c
@@ -437,7 +437,7 @@ static int cfi_amdext_erase_2_by_16 (struct mtd_info *mtd, struct erase_info *in
adr = instr->addr - (chipnum << cfi->chipshift) * (cfi->interleave);
len = instr->len;
-printk("erase : 0x%lx 0x%lx 0x%x 0x%x\n", adr, len, chipnum, mtd->size);
+ printk("erase : 0x%lx 0x%lx 0x%x 0x%lx\n", adr, len, chipnum, mtd->size);
while(len) {
//printk("erase : 0x%x 0x%x 0x%x 0x%x\n", chipnum, adr, len, cfi->chipshift);
diff --git a/drivers/mtd/doc1000.c b/drivers/mtd/doc1000.c
index ce1b6fdd8..584745359 100644
--- a/drivers/mtd/doc1000.c
+++ b/drivers/mtd/doc1000.c
@@ -1,16 +1,7 @@
/*======================================================================
- $Id: doc1000.c,v 1.8 2000/07/03 10:01:38 dwmw2 Exp $
+ $Id: doc1000.c,v 1.11 2000/11/24 13:43:16 dwmw2 Exp $
- A general driver for accessing PCMCIA card memory via Bulk
- Memory Services.
-
- This driver provides the equivalent of /dev/mem for a PCMCIA
- card's attribute and common memory. It includes character
- and block devices.
-
- Written by David Hinds, dhinds@allegro.stanford.edu
-
======================================================================*/
@@ -295,10 +286,9 @@ int flashcard_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen
static inline int byte_write (volatile u_char *addr, u_char byte)
{
register u_char status;
- register u_short i;
-
- for (i = 0; i < max_tries; i++)
- {
+ register u_short i = 0;
+
+ do {
status = readb(addr);
if (status & CSR_WR_READY)
{
@@ -306,7 +296,9 @@ static inline int byte_write (volatile u_char *addr, u_char byte)
writeb(byte, addr);
return 0;
}
- }
+ i++;
+ } while(i < max_tries);
+
printk(KERN_NOTICE "flashcard: byte_write timed out, status 0x%x\n",status);
return -EIO;
@@ -314,11 +306,10 @@ static inline int byte_write (volatile u_char *addr, u_char byte)
static inline int word_write (volatile u_char *addr, __u16 word)
{
- register u_short status = 0;
- register u_short i;
+ register u_short status;
+ register u_short i = 0;
- for (i = 0; i < max_tries; i++)
- {
+ do {
status = readw(addr);
if ((status & CSR_WR_READY) == CSR_WR_READY)
{
@@ -326,7 +317,8 @@ static inline int word_write (volatile u_char *addr, __u16 word)
writew(word, addr);
return 0;
}
- }
+ i++;
+ } while(i < max_tries);
printk(KERN_NOTICE "flashcard: word_write timed out at %p, status 0x%x\n", addr, status);
return -EIO;
@@ -362,24 +354,22 @@ static inline int check_erase(volatile u_char *addr)
static inline int suspend_erase(volatile u_char *addr)
{
- __u16 status = 0;
- u_long i;
+ __u16 status;
+ u_long i = 0;
writew(IF_ERASE_SUSPEND, addr);
writew(IF_READ_CSR, addr);
- for (i = 0; i < max_tries; i++)
- {
+ do {
status = readw(addr);
- if ((status & CSR_WR_READY) == CSR_WR_READY) break;
- }
- if (i == max_tries)
- {
- printk(KERN_NOTICE "flashcard: suspend_erase timed out, status 0x%x\n", status);
- return -EIO;
- }
-
- return 0;
+ if ((status & CSR_WR_READY) == CSR_WR_READY)
+ return 0;
+ i++;
+ } while(i < max_tries);
+
+ printk(KERN_NOTICE "flashcard: suspend_erase timed out, status 0x%x\n", status);
+ return -EIO;
+
}
static inline void resume_erase(volatile u_char *addr)
@@ -413,12 +403,11 @@ static inline void reset_block(volatile u_char *addr)
static inline int check_write(volatile u_char *addr)
{
- u_short status = 0, i;
+ u_short status, i = 0;
writew(IF_READ_CSR, addr);
- for (i=0; i < max_tries; i++)
- {
+ do {
status = readw(addr);
if (status & (CSR_WR_ERR | CSR_VPP_LOW))
{
@@ -428,7 +417,9 @@ static inline int check_write(volatile u_char *addr)
}
if ((status & CSR_WR_READY) == CSR_WR_READY)
return 0;
- }
+ i++;
+ } while (i < max_tries);
+
printk(KERN_NOTICE "flashcard: write timed out at %p, status 0x%x\n", addr, status);
return -EIO;
}
@@ -519,7 +510,7 @@ static void flashcard_periodic(unsigned long data)
}
-#if defined (MODULE) && LINUX_VERSION_CODE < 0x20300
+#if defined (MODULE) && LINUX_VERSION_CODE < 0x20211
#define init_doc1000 init_module
#define cleanup_doc1000 cleanup_module
#endif
@@ -600,3 +591,7 @@ static void __init cleanup_doc1000(void)
kfree(mymtd);
}
+#if LINUX_VERSION_CODE >= 0x20211
+module_init(init_doc1000);
+module_exit(cleanup_doc1000);
+#endif
diff --git a/drivers/mtd/doc2000.c b/drivers/mtd/doc2000.c
index cef67512b..22ef68e62 100644
--- a/drivers/mtd/doc2000.c
+++ b/drivers/mtd/doc2000.c
@@ -1,8 +1,11 @@
-/* Linux driver for Disk-On-Chip 2000 */
-/* (c) 1999 Machine Vision Holdings, Inc. */
-/* Author: David Woodhouse <dwmw2@mvhi.com> */
-/* $Id: doc2000.c,v 1.24 2000/07/13 10:03:31 dwmw2 Exp $ */
+/*
+ * Linux driver for Disk-On-Chip 2000 and Millennium
+ * (c) 1999 Machine Vision Holdings, Inc.
+ * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
+ *
+ * $Id: doc2000.c,v 1.39 2000/12/01 17:34:29 dwmw2 Exp $
+ */
#include <linux/kernel.h>
#include <linux/module.h>
@@ -19,286 +22,383 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ids.h>
#include <linux/mtd/doc2000.h>
-//#define PRERELEASE
+#define DOC_SUPPORT_2000
+#define DOC_SUPPORT_MILLENNIUM
-static int doc_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
-static int doc_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
-static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eecbuf);
-static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf);
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, u_char *buf);
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, const u_char *buf);
-static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
+#ifdef DOC_SUPPORT_2000
+#define DoC_is_2000(doc) (doc->ChipID == DOC_ChipID_Doc2k)
+#else
+#define DoC_is_2000(doc) (0)
+#endif
+#ifdef DOC_SUPPORT_MILLENNIUM
+#define DoC_is_Millennium(doc) (doc->ChipID == DOC_ChipID_DocMil)
+#else
+#define DoC_is_Millennium(doc) (0)
+#endif
+
+/* #define ECC_DEBUG */
+
+/* I have no idea why some DoC chips can not use memcpy_from|to_io().
+ * This may be due to the different revisions of the ASIC controller built-in or
+ * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment
+ * this:
+ #undef USE_MEMCPY
+*/
+
+static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf);
+static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf);
+static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf, u_char *eccbuf);
+static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf, u_char *eccbuf);
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t *retlen, u_char *buf);
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t *retlen, const u_char *buf);
+static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
static struct mtd_info *doc2klist = NULL;
-/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
+/* Perform the required delay cycles by reading from the appropriate register */
+static void DoC_Delay(struct DiskOnChip *doc, unsigned short cycles)
+{
+ volatile char dummy;
+ int i;
+
+ for (i = 0; i < cycles; i++) {
+ if (DoC_is_Millennium(doc))
+ dummy = ReadDOC(doc->virtadr, NOP);
+ else
+ dummy = ReadDOC(doc->virtadr, DOCStatus);
+ }
+
+}
-static int _DoC_WaitReady (unsigned long docptr)
+/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
+static int _DoC_WaitReady(struct DiskOnChip *doc)
{
- //long c=-1;
- short c=-1;
+ unsigned long docptr = doc->virtadr;
+ unsigned short c = 0xffff;
- DEBUG(2,"_DoC_WaitReady called for out-of-line wait\n");
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "_DoC_WaitReady called for out-of-line wait\n");
/* Out-of-line routine to wait for chip response */
while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B) && --c)
;
if (c == 0)
- DEBUG(2, "_DoC_WaitReady timed out.\n");
-
- return (c==0);
+ DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n");
+
+ return (c == 0);
}
-static inline int DoC_WaitReady(unsigned long docptr)
+static inline int DoC_WaitReady(struct DiskOnChip *doc)
{
+ unsigned long docptr = doc->virtadr;
/* This is inline, to optimise the common case, where it's ready instantly */
- volatile char dummy;
int ret = 0;
- /* Out-of-line routine to wait for chip response */
- /* TPW: Add 4 reads - see Software Requirement 2.3.2 */
- dummy = ReadDOC(docptr, CDSNControl);
- dummy = ReadDOC(docptr, CDSNControl);
- dummy = ReadDOC(docptr, CDSNControl);
- dummy = ReadDOC(docptr, CDSNControl);
-
+ /* 4 read form NOP register should be issued in prior to the read from CDSNControl
+ see Software Requirement 11.4 item 2. */
+ DoC_Delay(doc, 4);
+
if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B))
- ret = _DoC_WaitReady(docptr); /* Call the out-of-line routine to wait */
-
- /* TPW: Add 2 reads - see Software Requirement 2.3.2 */
- dummy = ReadDOC(docptr, CDSNControl);
- dummy = ReadDOC(docptr, CDSNControl);
+ /* Call the out-of-line routine to wait */
+ ret = _DoC_WaitReady(doc);
+
+ /* issue 2 read from NOP register after reading from CDSNControl register
+ see Software Requirement 11.4 item 2. */
+ DoC_Delay(doc, 2);
return ret;
}
+/* DoC_Command: Send a flash command to the flash chip through the CDSN Slow IO register to
+ bypass the internal pipeline. Each of 4 delay cycles (read from the NOP register) is
+ required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */
-/* DoC_Command: Send a flash command to the flash chip */
-
-static inline int DoC_Command(unsigned long docptr, unsigned char command, unsigned char xtraflags)
+static inline int DoC_Command(struct DiskOnChip *doc, unsigned char command,
+ unsigned char xtraflags)
{
+ unsigned long docptr = doc->virtadr;
+
+ if (DoC_is_2000(doc))
+ xtraflags |= CDSN_CTRL_FLASH_IO;
+
/* Assert the CLE (Command Latch Enable) line to the flash chip */
- WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE,
- docptr, CDSNControl);
+ WriteDOC(xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, docptr, CDSNControl);
+ DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
+
+ if (DoC_is_Millennium(doc))
+ WriteDOC(command, docptr, CDSNSlowIO);
/* Send the command */
- WriteDOC(command, docptr, 2k_CDSN_IO);
-
+ WriteDOC_(command, docptr, doc->ioreg);
+
/* Lower the CLE line */
- WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags | CDSN_CTRL_CE, docptr, CDSNControl);
+ WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl);
+ DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
- /* Wait for the chip to respond */
- return DoC_WaitReady(docptr);
+ /* Wait for the chip to respond - Software requirement 11.4.1 (extended for any command) */
+ return DoC_WaitReady(doc);
}
-/* DoC_Address: Set the current address for the flash chip */
+/* DoC_Address: Set the current address for the flash chip through the CDSN Slow IO register to
+ bypass the internal pipeline. Each of 4 delay cycles (read from the NOP register) is
+ required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */
-static inline int DoC_Address (unsigned long docptr, int numbytes, unsigned long ofs,
- unsigned char xtraflags1, unsigned char xtraflags2)
+static int DoC_Address(struct DiskOnChip *doc, int numbytes, unsigned long ofs,
+ unsigned char xtraflags1, unsigned char xtraflags2)
{
- /* Assert the ALE (Address Latch Enable line to the flash chip */
- WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE,
- docptr, CDSNControl);
+ unsigned long docptr;
+ int i;
+
+ docptr = doc->virtadr;
+
+ if (DoC_is_2000(doc))
+ xtraflags1 |= CDSN_CTRL_FLASH_IO;
+
+ /* Assert the ALE (Address Latch Enable) line to the flash chip */
+ WriteDOC(xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, docptr, CDSNControl);
+
+ DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
/* Send the address */
- /* Three cases:
- numbytes == 1: Send single byte, bits 0-7.
- numbytes == 2: Send bits 9-16 followed by 17-23
- numbytes == 3: Send 0-7, 9-16, then 17-23
- */
- if (numbytes != 2)
- WriteDOC(ofs & 0xff, docptr, 2k_CDSN_IO);
-
- if (numbytes != 1) {
- WriteDOC((ofs >> 9) & 0xff, docptr, 2k_CDSN_IO);
- WriteDOC((ofs >> 17) & 0xff, docptr, 2k_CDSN_IO);
+ /* Devices with 256-byte page are addressed as:
+ Column (bits 0-7), Page (bits 8-15, 16-23, 24-31)
+ * there is no device on the market with page256
+ and more than 24 bits.
+ Devices with 512-byte page are addressed as:
+ Column (bits 0-7), Page (bits 9-16, 17-24, 25-31)
+ * 25-31 is sent only if the chip support it.
+ * bit 8 changes the read command to be sent
+ (NAND_CMD_READ0 or NAND_CMD_READ1).
+ */
+
+ if (numbytes == ADDR_COLUMN || numbytes == ADDR_COLUMN_PAGE) {
+ if (DoC_is_Millennium(doc))
+ WriteDOC(ofs & 0xff, docptr, CDSNSlowIO);
+ WriteDOC_(ofs & 0xff, docptr, doc->ioreg);
}
- /* Lower the ALE line */
- WriteDOC( CDSN_CTRL_FLASH_IO | xtraflags1 | xtraflags2 | CDSN_CTRL_CE, docptr, CDSNControl);
+
+ if (doc->page256) {
+ ofs = ofs >> 8;
+ } else {
+ ofs = ofs >> 9;
+ }
+
+ if (numbytes == ADDR_PAGE || numbytes == ADDR_COLUMN_PAGE) {
+ for (i = 0; i < doc->pageadrlen; i++, ofs = ofs >> 8) {
+ if (DoC_is_Millennium(doc))
+ WriteDOC(ofs & 0xff, docptr, CDSNSlowIO);
+ WriteDOC_(ofs & 0xff, docptr, doc->ioreg);
+ }
+ }
+
+ DoC_Delay(doc, 2); /* Needed for some slow flash chips. mf. */
- /* Wait for the chip to respond */
- return DoC_WaitReady(docptr);
+ /* FIXME: The SlowIO's for millennium could be replaced by
+ a single WritePipeTerm here. mf. */
+
+ /* Lower the ALE line */
+ WriteDOC(xtraflags1 | xtraflags2 | CDSN_CTRL_CE, docptr,
+ CDSNControl);
+
+ DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
+
+ /* Wait for the chip to respond - Software requirement 11.4.1 */
+ return DoC_WaitReady(doc);
+}
+
+/* Read a buffer from DoC, taking care of Millennium odditys */
+static void DoC_ReadBuf(struct DiskOnChip *doc, u_char * buf, int len)
+{
+ int dummy;
+ int modulus = 0xffff;
+ unsigned long docptr;
+ int i;
+
+ docptr = doc->virtadr;
+
+ if (len <= 0)
+ return;
+
+ if (DoC_is_Millennium(doc)) {
+ /* Read the data via the internal pipeline through CDSN IO register,
+ see Pipelined Read Operations 11.3 */
+ dummy = ReadDOC(docptr, ReadPipeInit);
+
+ /* Millennium should use the LastDataRead register - Pipeline Reads */
+ len--;
+
+ /* This is needed for correctly ECC calculation */
+ modulus = 0xff;
+ }
+
+ for (i = 0; i < len; i++)
+ buf[i] = ReadDOC_(docptr, doc->ioreg + (i & modulus));
+
+ if (DoC_is_Millennium(doc)) {
+ buf[i] = ReadDOC(docptr, LastDataRead);
+ }
+}
+
+/* Write a buffer to DoC, taking care of Millennium odditys */
+static void DoC_WriteBuf(struct DiskOnChip *doc, const u_char * buf, int len)
+{
+ unsigned long docptr;
+ int i;
+
+ docptr = doc->virtadr;
+
+ if (len <= 0)
+ return;
+
+ for (i = 0; i < len; i++)
+ WriteDOC_(buf[i], docptr, doc->ioreg + i);
+
+ if (DoC_is_Millennium(doc)) {
+ WriteDOC(0x00, docptr, WritePipeTerm);
+ }
}
+
/* DoC_SelectChip: Select a given flash chip within the current floor */
-static inline int DoC_SelectChip(unsigned long docptr, int chip)
+static inline int DoC_SelectChip(struct DiskOnChip *doc, int chip)
{
+ unsigned long docptr = doc->virtadr;
+
+ /* Software requirement 11.4.4 before writing DeviceSelect */
+ /* Deassert the CE line to eliminate glitches on the FCE# outputs */
+ WriteDOC(CDSN_CTRL_WP, docptr, CDSNControl);
+ DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
+
/* Select the individual flash chip requested */
- WriteDOC( chip, docptr, CDSNDeviceSelect);
-
+ WriteDOC(chip, docptr, CDSNDeviceSelect);
+ DoC_Delay(doc, 4);
+
+ /* Reassert the CE line */
+ WriteDOC(CDSN_CTRL_CE | CDSN_CTRL_FLASH_IO | CDSN_CTRL_WP, docptr,
+ CDSNControl);
+ DoC_Delay(doc, 4); /* Software requirement 11.4.3 for Millennium */
+
/* Wait for it to be ready */
- return DoC_WaitReady(docptr);
+ return DoC_WaitReady(doc);
}
/* DoC_SelectFloor: Select a given floor (bank of flash chips) */
-static inline int DoC_SelectFloor(unsigned long docptr, int floor)
+static inline int DoC_SelectFloor(struct DiskOnChip *doc, int floor)
{
+ unsigned long docptr = doc->virtadr;
+
/* Select the floor (bank) of chips required */
- WriteDOC( floor, docptr, FloorSelect);
+ WriteDOC(floor, docptr, FloorSelect);
/* Wait for the chip to be ready */
- return DoC_WaitReady(docptr);
+ return DoC_WaitReady(doc);
}
-
+
/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */
static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
{
- int mfr, id, chipshift=0;
- char *mfrname=NULL, *idname=NULL;
+ int mfr, id, i;
+ volatile char dummy;
/* Page in the required floor/chip */
- DoC_SelectFloor(doc->virtadr, floor);
- DoC_SelectChip(doc->virtadr, chip);
+ DoC_SelectFloor(doc, floor);
+ DoC_SelectChip(doc, chip);
/* Reset the chip */
- if (DoC_Command(doc->virtadr, NAND_CMD_RESET, CDSN_CTRL_WP)) {
- DEBUG(2, "DoC_Command (reset) for %d,%d returned true\n", floor,chip);
+ if (DoC_Command(doc, NAND_CMD_RESET, CDSN_CTRL_WP)) {
+ DEBUG(MTD_DEBUG_LEVEL2,
+ "DoC_Command (reset) for %d,%d returned true\n",
+ floor, chip);
return 0;
}
-
- /* Read the NAND chip ID: 1. Send ReadID command */
- if(DoC_Command(doc->virtadr, NAND_CMD_READID, CDSN_CTRL_WP)) {
- DEBUG(2,"DoC_Command (ReadID) for %d,%d returned true\n", floor,chip);
+
+
+ /* Read the NAND chip ID: 1. Send ReadID command */
+ if (DoC_Command(doc, NAND_CMD_READID, CDSN_CTRL_WP)) {
+ DEBUG(MTD_DEBUG_LEVEL2,
+ "DoC_Command (ReadID) for %d,%d returned true\n",
+ floor, chip);
return 0;
}
- /* Read the NAND chip ID: 2. Send address byte zero
- */
- DoC_Address(doc->virtadr, 1, 0, CDSN_CTRL_WP, 0);
-
+ /* Read the NAND chip ID: 2. Send address byte zero */
+ DoC_Address(doc, ADDR_COLUMN, 0, CDSN_CTRL_WP, 0);
+
/* Read the manufacturer and device id codes from the device */
- mfr = ReadDOC(doc->virtadr, 2k_CDSN_IO);
- id = ReadDOC(doc->virtadr, 2k_CDSN_IO);
-
+
+ /* CDSN Slow IO register see Software Requirement 11.4 item 5. */
+ dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
+ DoC_Delay(doc, 2);
+ mfr = ReadDOC_(doc->virtadr, doc->ioreg);
+
+ /* CDSN Slow IO register see Software Requirement 11.4 item 5. */
+ dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
+ DoC_Delay(doc, 2);
+ id = ReadDOC_(doc->virtadr, doc->ioreg);
+
/* No response - return failure */
if (mfr == 0xff || mfr == 0)
return 0;
-
+
/* Check it's the same as the first chip we identified.
* M-Systems say that any given DiskOnChip device should only
* contain _one_ type of flash part, although that's not a
* hardware restriction. */
if (doc->mfr) {
if (doc->mfr == mfr && doc->id == id)
- return 1; /* This is another the same the first */
+ return 1; /* This is another the same the first */
else
- printk(KERN_WARNING "Flash chip at floor %d, chip %d is different:\n",
+ printk(KERN_WARNING
+ "Flash chip at floor %d, chip %d is different:\n",
floor, chip);
}
-
- /* Print (and store if first time) the manufacturer and ID codes. */
-
- switch(mfr) {
- case NAND_MFR_TOSHIBA: /* Toshiba */
- mfrname = "Toshiba";
-
- switch(id) {
- case 0x64:
- idname = "TC5816BDC";
- chipshift = 21;
- break;
-
- case 0x6b:
- idname = "TC5832DC";
- chipshift = 22;
- break;
-
- case 0x73:
- idname = "TH58V128DC";
- chipshift = 24;
- break;
-
- case 0x75:
- idname = "TC58256FT/DC";
- chipshift = 25;
- break;
-
- case 0xe5:
- idname = "TC58V32DC";
- chipshift = 22;
- break;
-
- case 0xe6:
- idname = "TC58V64DC";
- chipshift = 23;
- break;
-
- case 0xea:
- idname = "TC58V16BDC";
- chipshift = 21;
- break;
- }
- break; /* End of Toshiba parts */
-
- case NAND_MFR_SAMSUNG: /* Samsung */
- mfrname = "Samsung";
-
- switch(id) {
- case 0x64:
- idname = "KM29N16000";
- chipshift = 21;
-
- case 0x73:
- idname = "KM29U128T";
- chipshift = 24;
- break;
-
- case 0x75:
- idname = "KM29U256T";
- chipshift = 25;
- break;
-
- case 0xe3:
- idname = "KM29W32000";
- chipshift = 22;
- break;
-
- case 0xe6:
- idname = "KM29U64000";
- chipshift = 23;
- break;
-
- case 0xea:
- idname = "KM29W16000";
- chipshift = 21;
- break;
- }
- break; /* End of Samsung parts */
- }
-
- /* If we've identified it fully, print the full names */
- if (idname) {
-#ifdef PRERELEASE
- DEBUG(1, "Flash chip found: %2.2X %2.2X (%s %s)\n",
- mfr,id,mfrname,idname);
-#endif
- /* If this is the first chip, store the id codes */
- if (!doc->mfr) {
- doc->mfr = mfr;
- doc->id = id;
- doc->chipshift = chipshift;
- return 1;
+
+ /* Print and store the manufacturer and ID codes. */
+ for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+ if (mfr == nand_flash_ids[i].manufacture_id &&
+ id == nand_flash_ids[i].model_id) {
+ printk(KERN_INFO
+ "Flash chip found: Manufacturer ID: %2.2X, "
+ "Chip ID: %2.2X (%s)\n", mfr, id,
+ nand_flash_ids[i].name);
+ if (!doc->mfr) {
+ doc->mfr = mfr;
+ doc->id = id;
+ doc->chipshift =
+ nand_flash_ids[i].chipshift;
+ doc->page256 = nand_flash_ids[i].page256;
+ doc->pageadrlen =
+ nand_flash_ids[i].pageadrlen;
+ doc->erasesize =
+ nand_flash_ids[i].erasesize;
+ return 1;
+ }
+ return 0;
}
- return 0;
}
+
/* We haven't fully identified the chip. Print as much as we know. */
- if (mfrname)
- printk(KERN_WARNING "Unknown %s flash chip found: %2.2X %2.2X\n", mfrname,
- id, mfr);
- else
- printk(KERN_WARNING "Unknown flash chip found: %2.2X %2.2X\n", id, mfr);
-
- printk(KERN_WARNING "Please report to David.Woodhouse@mvhi.com\n");
+ printk(KERN_WARNING "Unknown flash chip found: %2.2X %2.2X\n",
+ id, mfr);
+
+ printk(KERN_WARNING "Please report to dwmw2@infradead.org\n");
return 0;
-}
+}
/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */
@@ -306,18 +406,22 @@ static void DoC_ScanChips(struct DiskOnChip *this)
{
int floor, chip;
int numchips[MAX_FLOORS];
+ int maxchips = MAX_CHIPS;
int ret = 1;
-
+
this->numchips = 0;
this->mfr = 0;
this->id = 0;
-
+
+ if (DoC_is_Millennium(this))
+ maxchips = MAX_CHIPS_MIL;
+
/* For each floor, find the number of valid chips it contains */
- for (floor = 0 ; floor < MAX_FLOORS ; floor++) {
+ for (floor = 0; floor < MAX_FLOORS; floor++) {
ret = 1;
- numchips[floor]=0;
- for (chip = 0 ; chip < MAX_CHIPS && ret != 0; chip++ ) {
-
+ numchips[floor] = 0;
+ for (chip = 0; chip < maxchips && ret != 0; chip++) {
+
ret = DoC_IdentChip(this, floor, chip);
if (ret) {
numchips[floor]++;
@@ -325,26 +429,26 @@ static void DoC_ScanChips(struct DiskOnChip *this)
}
}
}
-
+
/* If there are none at all that we recognise, bail */
if (!this->numchips) {
printk("No flash chips recognised.\n");
return;
}
-
+
/* Allocate an array to hold the information for each chip */
this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL);
- if (!this->chips){
+ if (!this->chips) {
printk("No memory for allocating chip info structures\n");
return;
}
-
+
ret = 0;
-
+
/* Fill out the chip array with {floor, chipno} for each
* detected chip in the device. */
for (floor = 0; floor < MAX_FLOORS; floor++) {
- for (chip = 0 ; chip < numchips[floor] ; chip++) {
+ for (chip = 0; chip < numchips[floor]; chip++) {
this->chips[ret].floor = floor;
this->chips[ret].chip = chip;
this->chips[ret].curadr = 0;
@@ -356,8 +460,9 @@ static void DoC_ScanChips(struct DiskOnChip *this)
/* Calculate and print the total size of the device */
this->totlen = this->numchips * (1 << this->chipshift);
- printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld Mb\n", this->numchips ,
- this->totlen >> 20);
+ printk(KERN_INFO
+ "%d flash chips found. Total DiskOnChip size: %ld Mb\n",
+ this->numchips, this->totlen >> 20);
}
@@ -371,15 +476,15 @@ static int DoC2k_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2)
* purpose. If it's value is the same on both chips, they might
* be the same chip, and we write to one and check for a change in
* the other. It's unclear if this register is usuable in the
- * DoC 2000 (it's in the Millenium docs), but it seems to work. */
+ * DoC 2000 (it's in the Millennium docs), but it seems to work. */
tmp1 = ReadDOC(doc1->virtadr, AliasResolution);
tmp2 = ReadDOC(doc2->virtadr, AliasResolution);
if (tmp1 != tmp2)
return 0;
-
- WriteDOC((tmp1+1) % 0xff, doc1->virtadr, AliasResolution);
+
+ WriteDOC((tmp1 + 1) % 0xff, doc1->virtadr, AliasResolution);
tmp2 = ReadDOC(doc2->virtadr, AliasResolution);
- if (tmp2 == (tmp1+1) % 0xff)
+ if (tmp2 == (tmp1 + 1) % 0xff)
retval = 1;
else
retval = 0;
@@ -387,11 +492,10 @@ static int DoC2k_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2)
/* Restore register contents. May not be necessary, but do it just to
* be safe. */
WriteDOC(tmp1, doc1->virtadr, AliasResolution);
-
+
return retval;
}
-
static const char im_name[] = "DoC2k_init";
/* This routine is made available to other mtd code via
@@ -403,36 +507,48 @@ static const char im_name[] = "DoC2k_init";
*/
static void DoC2k_init(struct mtd_info *mtd)
{
- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
struct DiskOnChip *old = NULL;
/* We must avoid being called twice for the same device. */
if (doc2klist)
- old = (struct DiskOnChip *)doc2klist->priv;
+ old = (struct DiskOnChip *) doc2klist->priv;
while (old) {
if (DoC2k_is_alias(old, this)) {
- printk(KERN_NOTICE "Ignoring DiskOnChip 2000 at 0x%lX - already configured\n",
+ printk(KERN_NOTICE
+ "Ignoring DiskOnChip 2000 at 0x%lX - already configured\n",
this->physadr);
- iounmap((void *)this->virtadr);
+ iounmap((void *) this->virtadr);
kfree(mtd);
return;
}
if (old->nextdoc)
- old = (struct DiskOnChip *)old->nextdoc->priv;
+ old = (struct DiskOnChip *) old->nextdoc->priv;
else
old = NULL;
}
-
-
- mtd->name = "DiskOnChip 2000";
- printk(KERN_NOTICE "DiskOnChip 2000 found at address 0x%lX\n",this->physadr);
+
+
+ switch (this->ChipID) {
+ case DOC_ChipID_Doc2k:
+ mtd->name = "DiskOnChip 2000";
+ this->ioreg = DoC_2k_CDSN_IO;
+ break;
+ case DOC_ChipID_DocMil:
+ mtd->name = "DiskOnChip Millennium";
+ this->ioreg = DoC_Mil_CDSN_IO;
+ break;
+ }
+
+ printk(KERN_NOTICE "%s found at address 0x%lX\n", mtd->name,
+ this->physadr);
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH;
mtd->size = 0;
- mtd->erasesize = 0x2000;
+ mtd->erasesize = 0;
mtd->oobblock = 512;
mtd->oobsize = 16;
mtd->module = THIS_MODULE;
@@ -446,131 +562,182 @@ static void DoC2k_init(struct mtd_info *mtd)
mtd->read_oob = doc_read_oob;
mtd->write_oob = doc_write_oob;
mtd->sync = NULL;
-
+
this->totlen = 0;
this->numchips = 0;
-
+
this->curfloor = -1;
this->curchip = -1;
-
+
/* Ident all the chips present. */
DoC_ScanChips(this);
-
+
if (!this->totlen) {
kfree(mtd);
- iounmap((void *)this->virtadr);
+ iounmap((void *) this->virtadr);
} else {
this->nextdoc = doc2klist;
doc2klist = mtd;
- mtd->size = this->totlen;
+ mtd->size = this->totlen;
+ mtd->erasesize = this->erasesize;
add_mtd_device(mtd);
return;
}
}
-
-static int doc_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
+static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t * retlen, u_char * buf)
{
/* Just a special case of doc_read_ecc */
return doc_read_ecc(mtd, from, len, retlen, buf, NULL);
}
-static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf)
+static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t * retlen, u_char * buf, u_char * eccbuf)
{
- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
- int di=0; /* Yes, DI is a hangover from when I was disassembling the binary driver */
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
unsigned long docptr;
struct Nand *mychip;
+ unsigned char syndrome[6];
+ volatile char dummy;
+ int i, len256 = 0, ret=0;
docptr = this->virtadr;
/* Don't allow read past end of device */
if (from >= this->totlen)
return -EINVAL;
-
+
/* Don't allow a single read to cross a 512-byte block boundary */
- if (from + len > ( (from | 0x1ff) + 1))
+ if (from + len > ((from | 0x1ff) + 1))
len = ((from | 0x1ff) + 1) - from;
+ /* The ECC will not be calculated correctly if less than 512 is read */
+ if (len != 0x200 && eccbuf)
+ printk(KERN_WARNING
+ "ECC needs a full sector read (adr: %lx size %lx)\n",
+ (long) from, (long) len);
+
+ /* printk("DoC_Read (adr: %lx size %lx)\n", (long) from, (long) len); */
+
+
/* Find the chip which is to be used and select it */
mychip = &this->chips[from >> (this->chipshift)];
-
+
if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(docptr, mychip->floor);
- DoC_SelectChip(docptr, mychip->chip);
+ DoC_SelectFloor(this, mychip->floor);
+ DoC_SelectChip(this, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(this, mychip->chip);
}
- else if (this->curchip != mychip->chip) {
- DoC_SelectChip(docptr, mychip->chip);
- }
-
+
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
-
+
+ DoC_Command(this,
+ (!this->page256
+ && (from & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0,
+ CDSN_CTRL_WP);
+ DoC_Address(this, ADDR_COLUMN_PAGE, from, CDSN_CTRL_WP,
+ CDSN_CTRL_ECC_IO);
if (eccbuf) {
/* Prime the ECC engine */
- WriteDOC ( DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC ( DOC_ECC_EN, docptr, ECCConf);
+ WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC(DOC_ECC_EN, docptr, ECCConf);
+ } else {
+ /* disable the ECC engine */
+ WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
}
- DoC_Command(docptr, (from >> 8) & 1, CDSN_CTRL_WP);
- DoC_Address(docptr, 3, from, CDSN_CTRL_WP , CDSN_CTRL_ECC_IO);
+ /* treat crossing 256-byte sector for 2M x 8bits devices */
+ if (this->page256 && from + len > (from | 0xff) + 1) {
+ len256 = (from | 0xff) + 1 - from;
+ DoC_ReadBuf(this, buf, len256);
- for (di=0; di < len ; di++) {
- buf[di] = ReadDOC(docptr, 2k_CDSN_IO);
+ DoC_Command(this, NAND_CMD_READ0, CDSN_CTRL_WP);
+ DoC_Address(this, ADDR_COLUMN_PAGE, from + len256,
+ CDSN_CTRL_WP, CDSN_CTRL_ECC_IO);
}
+ DoC_ReadBuf(this, &buf[len256], len - len256);
+
/* Let the caller know we completed it */
*retlen = len;
if (eccbuf) {
/* Read the ECC data through the DiskOnChip ECC logic */
- for (di=0; di<6; di++) {
- eccbuf[di] = ReadDOC(docptr, 2k_CDSN_IO);
- }
-
+ /* Note: this will work even with 2M x 8bit devices as */
+ /* they have 8 bytes of OOB per 256 page. mf. */
+ DoC_ReadBuf(this, eccbuf, 6);
+
/* Flush the pipeline */
- (void) ReadDOC(docptr, 2k_ECCStatus);
- (void) ReadDOC(docptr, 2k_ECCStatus);
-
+ if (DoC_is_Millennium(this)) {
+ dummy = ReadDOC(docptr, ECCConf);
+ dummy = ReadDOC(docptr, ECCConf);
+ i = ReadDOC(docptr, ECCConf);
+ } else {
+ dummy = ReadDOC(docptr, 2k_ECCStatus);
+ dummy = ReadDOC(docptr, 2k_ECCStatus);
+ i = ReadDOC(docptr, 2k_ECCStatus);
+ }
+
/* Check the ECC Status */
- if (ReadDOC(docptr, 2k_ECCStatus) & 0x80) {
+ if (i & 0x80) {
+ int nb_errors;
/* There was an ECC error */
+#ifdef ECC_DEBUG
printk("DiskOnChip ECC Error: Read at %lx\n", (long)from);
+#endif
+ /* Read the ECC syndrom through the DiskOnChip ECC logic.
+ These syndrome will be all ZERO when there is no error */
+ for (i = 0; i < 6; i++) {
+ syndrome[i] =
+ ReadDOC(docptr, ECCSyndrome0 + i);
+ }
+ nb_errors = doc_decode_ecc(buf, syndrome);
- /* FIXME: Implement ECC error correction, don't just whinge */
-
- /* We return error, but have actually done the read. Not that
- this can be told to user-space, via sys_read(), but at least
- MTD-aware stuff can know about it by checking *retlen */
- return -EIO;
+#ifdef ECC_DEBUG
+ printk("Errors corrected: %x\n", nb_errors);
+#endif
+ if (nb_errors < 0) {
+ /* We return error, but have actually done the read. Not that
+ this can be told to user-space, via sys_read(), but at least
+ MTD-aware stuff can know about it by checking *retlen */
+ ret = -EIO;
+ }
}
+
#ifdef PSYCHO_DEBUG
- else
- printk("ECC OK at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
- (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], eccbuf[4],
- eccbuf[5]);
+ printk("ECC DATA at %lxB: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+ (long)from, eccbuf[0], eccbuf[1], eccbuf[2],
+ eccbuf[3], eccbuf[4], eccbuf[5]);
#endif
- /* Reset the ECC engine */
- WriteDOC(DOC_ECC_RESV, docptr , ECCConf);
-
+ /* disable the ECC engine */
+ WriteDOC(DOC_ECC_DIS, docptr , ECCConf);
}
- return 0;
+ return ret;
}
-static int doc_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
+static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t * retlen, const u_char * buf)
{
- static char as[6];
- return doc_write_ecc(mtd, to, len, retlen, buf, as);
+ char eccbuf[6];
+ return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf);
}
-static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf)
+static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t * retlen, const u_char * buf,
+ u_char * eccbuf)
{
- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
- int di=0;
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
+ int di; /* Yes, DI is a hangover from when I was disassembling the binary driver */
unsigned long docptr;
+ volatile char dummy;
+ int len256 = 0;
struct Nand *mychip;
docptr = this->virtadr;
@@ -578,82 +745,118 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t *r
/* Don't allow write past end of device */
if (to >= this->totlen)
return -EINVAL;
-#if 0
+
/* Don't allow a single write to cross a 512-byte block boundary */
- if (to + len > ( (to | 0x1ff) + 1))
+ if (to + len > ((to | 0x1ff) + 1))
len = ((to | 0x1ff) + 1) - to;
-#else
- /* Don't allow writes which aren't exactly one block */
- if (to & 0x1ff || len != 0x200)
- return -EINVAL;
-#endif
+ /* The ECC will not be calculated correctly if less than 512 is written */
+ if (len != 0x200 && eccbuf)
+ printk(KERN_WARNING
+ "ECC needs a full sector write (adr: %lx size %lx)\n",
+ (long) to, (long) len);
+
+ /* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */
/* Find the chip which is to be used and select it */
mychip = &this->chips[to >> (this->chipshift)];
-
+
if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(docptr, mychip->floor);
- DoC_SelectChip(docptr, mychip->chip);
+ DoC_SelectFloor(this, mychip->floor);
+ DoC_SelectChip(this, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(this, mychip->chip);
}
- else if (this->curchip != mychip->chip) {
- DoC_SelectChip(docptr, mychip->chip);
- }
-
+
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
-
+
/* Set device to main plane of flash */
- DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP);
- DoC_Command(docptr, NAND_CMD_READ0, CDSN_CTRL_WP);
+ DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP);
+ DoC_Command(this,
+ (!this->page256
+ && (to & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0,
+ CDSN_CTRL_WP);
+
+ DoC_Command(this, NAND_CMD_SEQIN, 0);
+ DoC_Address(this, ADDR_COLUMN_PAGE, to, 0, CDSN_CTRL_ECC_IO);
if (eccbuf) {
/* Prime the ECC engine */
- WriteDOC ( DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC ( DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
+ WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
+ } else {
+ /* disable the ECC engine */
+ WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
}
- DoC_Command(docptr, NAND_CMD_SEQIN, 0);
- DoC_Address(docptr, 3, to, 0, CDSN_CTRL_ECC_IO);
+ /* treat crossing 256-byte sector for 2M x 8bits devices */
+ if (this->page256 && to + len > (to | 0xff) + 1) {
+ len256 = (to | 0xff) + 1 - to;
+ DoC_WriteBuf(this, buf, len256);
+
+ DoC_Command(this, NAND_CMD_PAGEPROG, 0);
+
+ DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
+ /* There's an implicit DoC_WaitReady() in DoC_Command */
+
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
- for (di=0; di < len ; di++) {
- WriteDOC(buf[di], docptr, 2k_CDSN_IO);
+ if (ReadDOC_(docptr, this->ioreg) & 1) {
+ printk("Error programming flash\n");
+ /* Error in programming */
+ *retlen = 0;
+ return -EIO;
+ }
+
+ DoC_Command(this, NAND_CMD_SEQIN, 0);
+ DoC_Address(this, ADDR_COLUMN_PAGE, to + len256, 0,
+ CDSN_CTRL_ECC_IO);
}
+ DoC_WriteBuf(this, &buf[len256], len - len256);
if (eccbuf) {
- WriteDOC( CDSN_CTRL_ECC_IO | CDSN_CTRL_CE , docptr, CDSNControl );
-
-#if 1
- /* eduardp@m-sys.com says this shouldn't be necessary,
- * but it doesn't actually work without it, so I've
- * left it in for now. dwmw2.
- */
-
- WriteDOC( 0, docptr, 2k_CDSN_IO);
- WriteDOC( 0, docptr, 2k_CDSN_IO);
- WriteDOC( 0, docptr, 2k_CDSN_IO);
-#endif
+ WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_CE, docptr,
+ CDSNControl);
+
+ if (DoC_is_Millennium(this)) {
+ WriteDOC(0, docptr, NOP);
+ WriteDOC(0, docptr, NOP);
+ WriteDOC(0, docptr, NOP);
+ } else {
+ WriteDOC_(0, docptr, this->ioreg);
+ WriteDOC_(0, docptr, this->ioreg);
+ WriteDOC_(0, docptr, this->ioreg);
+ }
+
/* Read the ECC data through the DiskOnChip ECC logic */
- for (di=0; di<6; di++) {
+ for (di = 0; di < 6; di++) {
eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di);
}
+
+ /* Reset the ECC engine */
+ WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+
#ifdef PSYCHO_DEBUG
- printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
- (long) to, eccbuf[0], eccbuf[1], eccbuf[2],
- eccbuf[3], eccbuf[4], eccbuf[5] );
+ printk
+ ("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+ (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
+ eccbuf[4], eccbuf[5]);
#endif
- /* Reset the ECC engine */
- WriteDOC(DOC_ECC_RESV, docptr , ECCConf);
-
}
- DoC_Command(docptr, NAND_CMD_PAGEPROG, 0);
+ DoC_Command(this, NAND_CMD_PAGEPROG, 0);
- DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP);
+ DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
/* There's an implicit DoC_WaitReady() in DoC_Command */
- if (ReadDOC(docptr, 2k_CDSN_IO) & 1) {
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
+
+ if (ReadDOC_(docptr, this->ioreg) & 1) {
printk("Error programming flash\n");
/* Error in programming */
*retlen = 0;
@@ -662,84 +865,155 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, size_t *r
/* Let the caller know we completed it */
*retlen = len;
+
+ if (eccbuf) {
+ unsigned char x[8];
+ size_t dummy;
+
+ /* Write the ECC data to flash */
+ for (di=0; di<6; di++)
+ x[di] = eccbuf[di];
+
+ x[6]=0x55;
+ x[7]=0x55;
+
+ return doc_write_oob(mtd, to, 8, &dummy, x);
+ }
return 0;
}
-
-
-static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, u_char *buf)
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t * retlen, u_char * buf)
{
- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
- int i;
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
+ int len256 = 0;
unsigned long docptr;
struct Nand *mychip;
-
+
docptr = this->virtadr;
-
+
mychip = &this->chips[ofs >> this->chipshift];
-
+
if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(docptr, mychip->floor);
- DoC_SelectChip(docptr, mychip->chip);
- }
- else if (this->curchip != mychip->chip) {
- DoC_SelectChip(docptr, mychip->chip);
+ DoC_SelectFloor(this, mychip->floor);
+ DoC_SelectChip(this, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(this, mychip->chip);
}
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
-
-
-
- DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP);
- DoC_Address(docptr, 3, ofs, CDSN_CTRL_WP, 0);
-
- for (i=0; i<len; i++)
- buf[i] = ReadDOC(docptr, 2k_CDSN_IO);
-
+
+ /* update address for 2M x 8bit devices. OOB starts on the second */
+ /* page to maintain compatibility with doc_read_ecc. */
+ if (this->page256) {
+ if (!(ofs & 0x8))
+ ofs += 0x100;
+ else
+ ofs -= 0x8;
+ }
+
+ DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP);
+ DoC_Address(this, ADDR_COLUMN_PAGE, ofs, CDSN_CTRL_WP, 0);
+
+ /* treat crossing 8-byte OOB data for 2M x 8bit devices */
+ /* Note: datasheet says it should automaticaly wrap to the */
+ /* next OOB block, but it didn't work here. mf. */
+ if (this->page256 && ofs + len > (ofs | 0x7) + 1) {
+ len256 = (ofs | 0x7) + 1 - ofs;
+ DoC_ReadBuf(this, buf, len256);
+
+ DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP);
+ DoC_Address(this, ADDR_COLUMN_PAGE, ofs & (~0x1ff),
+ CDSN_CTRL_WP, 0);
+ }
+
+ DoC_ReadBuf(this, &buf[len256], len - len256);
+
*retlen = len;
return 0;
}
-static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *retlen, const u_char *buf)
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+ size_t * retlen, const u_char * buf)
{
- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
- int i;
- unsigned long docptr;
- struct Nand *mychip;
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
+ int len256 = 0;
+ unsigned long docptr = this->virtadr;
+ struct Nand *mychip = &this->chips[ofs >> this->chipshift];
+ int dummy;
- // printk("doc_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n",(long)ofs, len,
+ // printk("doc_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n",(long)ofs, len,
// buf[0], buf[1], buf[2], buf[3], buf[8], buf[9], buf[14],buf[15]);
- docptr = this->virtadr;
-
- mychip = &this->chips[ofs >> this->chipshift];
-
+ /* Find the chip which is to be used and select it */
if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(docptr, mychip->floor);
- DoC_SelectChip(docptr, mychip->chip);
- }
- else if (this->curchip != mychip->chip) {
- DoC_SelectChip(docptr, mychip->chip);
+ DoC_SelectFloor(this, mychip->floor);
+ DoC_SelectChip(this, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(this, mychip->chip);
}
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
-
- DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP);
- DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP);
- DoC_Command(docptr, NAND_CMD_SEQIN, 0);
- DoC_Address(docptr, 3, ofs, 0, 0);
-
- for (i=0; i<len; i++)
- WriteDOC(buf[i], docptr, 2k_CDSN_IO);
+ /* disable the ECC engine */
+ WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
+ WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
+
+ /* Reset the chip, see Software Requirement 11.4 item 1. */
+ DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP);
- DoC_Command(docptr, NAND_CMD_PAGEPROG, 0);
- DoC_Command(docptr, NAND_CMD_STATUS, 0);
+ /* issue the Read2 command to set the pointer to the Spare Data Area. */
+ DoC_Command(this, NAND_CMD_READOOB, CDSN_CTRL_WP);
+
+ /* update address for 2M x 8bit devices. OOB starts on the second */
+ /* page to maintain compatibility with doc_read_ecc. */
+ if (this->page256) {
+ if (!(ofs & 0x8))
+ ofs += 0x100;
+ else
+ ofs -= 0x8;
+ }
+
+ /* issue the Serial Data In command to initial the Page Program process */
+ DoC_Command(this, NAND_CMD_SEQIN, 0);
+ DoC_Address(this, ADDR_COLUMN_PAGE, ofs, 0, 0);
+
+ /* treat crossing 8-byte OOB data for 2M x 8bit devices */
+ /* Note: datasheet says it should automaticaly wrap to the */
+ /* next OOB block, but it didn't work here. mf. */
+ if (this->page256 && ofs + len > (ofs | 0x7) + 1) {
+ len256 = (ofs | 0x7) + 1 - ofs;
+ DoC_WriteBuf(this, buf, len256);
+
+ DoC_Command(this, NAND_CMD_PAGEPROG, 0);
+ DoC_Command(this, NAND_CMD_STATUS, 0);
+ /* DoC_WaitReady() is implicit in DoC_Command */
+
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
+
+ if (ReadDOC_(docptr, this->ioreg) & 1) {
+ printk("Error programming oob data\n");
+ /* There was an error */
+ *retlen = 0;
+ return -EIO;
+ }
+ DoC_Command(this, NAND_CMD_SEQIN, 0);
+ DoC_Address(this, ADDR_COLUMN_PAGE, ofs & (~0x1ff), 0, 0);
+ }
+
+ DoC_WriteBuf(this, &buf[len256], len - len256);
+
+ DoC_Command(this, NAND_CMD_PAGEPROG, 0);
+ DoC_Command(this, NAND_CMD_STATUS, 0);
/* DoC_WaitReady() is implicit in DoC_Command */
- if (ReadDOC(docptr, 2k_CDSN_IO) & 1) {
+ dummy = ReadDOC(docptr, CDSNSlowIO);
+ DoC_Delay(this, 2);
+
+ if (ReadDOC_(docptr, this->ioreg) & 1) {
printk("Error programming oob data\n");
/* There was an error */
*retlen = 0;
@@ -751,102 +1025,89 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, size_t *r
}
-
-int doc_erase (struct mtd_info *mtd, struct erase_info *instr)
+int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
{
- struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
+ struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
unsigned long ofs = instr->addr;
unsigned long len = instr->len;
unsigned long docptr;
struct Nand *mychip;
-
- if(len != mtd->erasesize)
- printk(KERN_WARNING "Erase not right size (%lx != %lx)n", len, mtd->erasesize);
-
+
+ if (len != mtd->erasesize)
+ printk(KERN_WARNING "Erase not right size (%lx != %lx)n",
+ len, mtd->erasesize);
docptr = this->virtadr;
-
+
mychip = &this->chips[ofs >> this->chipshift];
-
+
if (this->curfloor != mychip->floor) {
- DoC_SelectFloor(docptr, mychip->floor);
- DoC_SelectChip(docptr, mychip->chip);
- }
- else if (this->curchip != mychip->chip) {
- DoC_SelectChip(docptr, mychip->chip);
+ DoC_SelectFloor(this, mychip->floor);
+ DoC_SelectChip(this, mychip->chip);
+ } else if (this->curchip != mychip->chip) {
+ DoC_SelectChip(this, mychip->chip);
}
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
-
+
instr->state = MTD_ERASE_PENDING;
- DoC_Command(docptr, NAND_CMD_ERASE1, 0);
- DoC_Address(docptr, 2, ofs, 0, 0);
- DoC_Command(docptr, NAND_CMD_ERASE2, 0);
+ DoC_Command(this, NAND_CMD_ERASE1, 0);
+ DoC_Address(this, ADDR_PAGE, ofs, 0, 0);
+ DoC_Command(this, NAND_CMD_ERASE2, 0);
instr->state = MTD_ERASING;
- DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP);
+ DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
- if (ReadDOC(docptr, 2k_CDSN_IO) & 1) {
+ if (ReadDOC_(docptr, this->ioreg) & 1) {
printk("Error writing\n");
/* There was an error */
instr->state = MTD_ERASE_FAILED;
- }
- else
+ } else
instr->state = MTD_ERASE_DONE;
- if (instr->callback)
+ if (instr->callback)
instr->callback(instr);
-
+
return 0;
}
-
-
-
/****************************************************************************
*
* Module stuff
*
****************************************************************************/
-static int __init init_doc2000(void)
-{
- inter_module_register(im_name, THIS_MODULE, &DoC2k_init);
- return 0;
-}
-
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define cleanup_doc2000 cleanup_module
-#endif
-#define __exit
+#define init_doc2000 init_module
#endif
+int __init init_doc2000(void)
+{
+ inter_module_register(im_name, THIS_MODULE, &DoC2k_init);
+ return 0;
+}
static void __exit cleanup_doc2000(void)
{
struct mtd_info *mtd;
struct DiskOnChip *this;
- while((mtd=doc2klist)) {
- this = (struct DiskOnChip *)mtd->priv;
+ while ((mtd = doc2klist)) {
+ this = (struct DiskOnChip *) mtd->priv;
doc2klist = this->nextdoc;
-
+
del_mtd_device(mtd);
-
- iounmap((void *)this->virtadr);
+
+ iounmap((void *) this->virtadr);
kfree(this->chips);
kfree(mtd);
}
inter_module_unregister(im_name);
-
}
-module_init(init_doc2000);
-
-#if LINUX_VERSION_CODE > 0x20300
module_exit(cleanup_doc2000);
-#endif
+module_init(init_doc2000);
diff --git a/drivers/mtd/doc2001.c b/drivers/mtd/doc2001.c
index 8a9f03235..79aa3630d 100644
--- a/drivers/mtd/doc2001.c
+++ b/drivers/mtd/doc2001.c
@@ -1,7 +1,11 @@
-/* Linux driver for Disk-On-Chip Millennium */
-/* (c) 1999 Machine Vision Holdings, Inc. */
-/* Author: David Woodhouse <dwmw2@mvhi.com> */
-/* $Id: doc2001.c,v 1.7 2000/07/13 10:41:39 dwmw2 Exp $ */
+
+/*
+ * Linux driver for Disk-On-Chip Millennium
+ * (c) 1999 Machine Vision Holdings, Inc.
+ * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
+ *
+ * $Id: doc2001.c,v 1.24 2000/12/01 13:11:02 dwmw2 Exp $
+ */
#include <linux/kernel.h>
#include <linux/module.h>
@@ -18,38 +22,26 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ids.h>
#include <linux/mtd/doc2000.h>
-static struct {
- char * name;
- int manufacture_id;
- int model_id;
- int chipshift;
-} nand_flash_ids[] = {
- {"Toshiba TC5816BDC", NAND_MFR_TOSHIBA, 0x64, 21},
- {"Toshiba TC5832DC", NAND_MFR_TOSHIBA, 0x6b, 22},
- {"Toshiba TH58V128DC", NAND_MFR_TOSHIBA, 0x73, 24},
- {"Toshiba TC58256FT/DC", NAND_MFR_TOSHIBA, 0x75, 25},
- {"Toshiba TC58V32DC", NAND_MFR_TOSHIBA, 0xe5, 22},
- {"Toshiba TC58V64DC", NAND_MFR_TOSHIBA, 0xe6, 23},
- {"Toshiba TC58V16BDC", NAND_MFR_TOSHIBA, 0xea, 21},
- {"Samsung KM29N16000", NAND_MFR_SAMSUNG, 0x64, 21},
- {"Samsung KM29U128T", NAND_MFR_SAMSUNG, 0x73, 24},
- {"Samsung KM29U256T", NAND_MFR_SAMSUNG, 0x75, 25},
- {"Samsung KM29W32000", NAND_MFR_SAMSUNG, 0xe3, 22},
- {"Samsung KM29U64000", NAND_MFR_SAMSUNG, 0xe6, 23},
- {"Samsung KM29W16000", NAND_MFR_SAMSUNG, 0xea, 21},
- {NULL,}
-};
-
-static int doc_read (struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf);
-static int doc_write (struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf);
-static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
- size_t *retlen, u_char *buf, u_char *eecbuf);
-static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
- size_t *retlen, const u_char *buf, u_char *eccbuf);
+/* #define ECC_DEBUG */
+
+/* I have no idea why some DoC chips can not use memcop_form|to_io().
+ * This may be due to the different revisions of the ASIC controller built-in or
+ * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment
+ * this:
+ #undef USE_MEMCPY
+*/
+
+static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf);
+static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf);
+static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf, u_char *eccbuf);
+static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf, u_char *eccbuf);
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
size_t *retlen, u_char *buf);
static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
@@ -58,6 +50,7 @@ static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
static struct mtd_info *docmillist = NULL;
+/* Perform the required delay cycles by reading from the NOP register */
static void DoC_Delay(unsigned long docptr, unsigned short cycles)
{
volatile char dummy;
@@ -72,14 +65,20 @@ static int _DoC_WaitReady(unsigned long docptr)
{
unsigned short c = 0xffff;
+ DEBUG(MTD_DEBUG_LEVEL3,
+ "_DoC_WaitReady called for out-of-line wait\n");
+
/* Out-of-line routine to wait for chip response */
while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B) && --c)
;
+ if (c == 0)
+ DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n");
+
return (c == 0);
}
-static __inline__ int DoC_WaitReady(unsigned long docptr)
+static inline int DoC_WaitReady(unsigned long docptr)
{
/* This is inline, to optimise the common case, where it's ready instantly */
int ret = 0;
@@ -99,33 +98,35 @@ static __inline__ int DoC_WaitReady(unsigned long docptr)
return ret;
}
-/* DoC_Command: Send a flash command to the flash chip through the CDSN Slow IO register to bypass
- the internal pipeline. Each of 4 delay cycles (read from the NOP register) is required after
- writing to CDSN Control register, see Software Requirement 11.4 item 3. */
-static __inline__ void DoC_Command(unsigned long docptr, unsigned char command,
- unsigned char xtraflags)
+/* DoC_Command: Send a flash command to the flash chip through the CDSN Slow IO register to
+ bypass the internal pipeline. Each of 4 delay cycles (read from the NOP register) is
+ required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */
+
+static inline void DoC_Command(unsigned long docptr, unsigned char command,
+ unsigned char xtraflags)
{
/* Assert the CLE (Command Latch Enable) line to the flash chip */
- WriteDOC(xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, docptr, CDSNControl);
+ WriteDOC(xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE, docptr, CDSNControl);
DoC_Delay(docptr, 4);
/* Send the command */
WriteDOC(command, docptr, CDSNSlowIO);
WriteDOC(command, docptr, Mil_CDSN_IO);
-
+
/* Lower the CLE line */
WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl);
DoC_Delay(docptr, 4);
}
-/* DoC_Address: Set the current address for the flash chip through the CDSN Slow IO register to bypass
- the internal pipeline. Each of 4 delay cycles (read from the NOP register) is required after
- writing to CDSN Control register, see Software Requirement 11.4 item 3. */
-static __inline__ void DoC_Address (unsigned long docptr, int numbytes, unsigned long ofs,
+/* DoC_Address: Set the current address for the flash chip through the CDSN Slow IO register to
+ bypass the internal pipeline. Each of 4 delay cycles (read from the NOP register) is
+ required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */
+
+static inline void DoC_Address(unsigned long docptr, int numbytes, unsigned long ofs,
unsigned char xtraflags1, unsigned char xtraflags2)
{
- /* Assert the ALE (Address Latch Enable line to the flash chip */
- WriteDOC(xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, docptr, CDSNControl);
+ /* Assert the ALE (Address Latch Enable) line to the flash chip */
+ WriteDOC(xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE, docptr, CDSNControl);
DoC_Delay(docptr, 4);
/* Send the address */
@@ -217,11 +218,11 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
if (mfr == 0xff || mfr == 0)
return 0;
- /* FIXME: to deal with mulit-flash on multi-Millennium case more carefully */
+ /* FIXME: to deal with multi-flash on multi-Millennium case more carefully */
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
if (mfr == nand_flash_ids[i].manufacture_id &&
id == nand_flash_ids[i].model_id) {
- printk(KERN_INFO "Flash chip found: Manufacture ID: %2.2X, "
+ printk(KERN_INFO "Flash chip found: Manufacturer ID: %2.2X, "
"Chip ID: %2.2X (%s)\n",
mfr, id, nand_flash_ids[i].name);
doc->mfr = mfr;
@@ -235,7 +236,7 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
return 0;
else
return 1;
-}
+}
/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */
static void DoC_ScanChips(struct DiskOnChip *this)
@@ -243,11 +244,11 @@ static void DoC_ScanChips(struct DiskOnChip *this)
int floor, chip;
int numchips[MAX_FLOORS_MIL];
int ret;
-
+
this->numchips = 0;
this->mfr = 0;
this->id = 0;
-
+
/* For each floor, find the number of valid chips it contains */
for (floor = 0,ret = 1; floor < MAX_FLOORS_MIL; floor++) {
numchips[floor] = 0;
@@ -264,14 +265,14 @@ static void DoC_ScanChips(struct DiskOnChip *this)
printk("No flash chips recognised.\n");
return;
}
-
+
/* Allocate an array to hold the information for each chip */
this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL);
if (!this->chips){
printk("No memory for allocating chip info structures\n");
return;
}
-
+
/* Fill out the chip array with {floor, chipno} for each
* detected chip in the device. */
for (floor = 0, ret = 0; floor < MAX_FLOORS_MIL; floor++) {
@@ -286,7 +287,7 @@ static void DoC_ScanChips(struct DiskOnChip *this)
/* Calculate and print the total size of the device */
this->totlen = this->numchips * (1 << this->chipshift);
- printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld Mbytes\n",
+ printk(KERN_NOTICE "%d flash chips found. Total DiskOnChip size: %ld Mbytes\n",
this->numchips ,this->totlen >> 20);
}
@@ -317,7 +318,7 @@ static int DoCMil_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2)
/* Restore register contents. May not be necessary, but do it just to
* be safe. */
WriteDOC(tmp1, doc1->virtadr, AliasResolution);
-
+
return retval;
}
@@ -330,7 +331,7 @@ static const char im_name[] = "DoCMil_init";
* this module is non-zero, i.e. between inter_module_get and
* inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000.
*/
-void DoCMil_init(struct mtd_info *mtd)
+static void DoCMil_init(struct mtd_info *mtd)
{
struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
struct DiskOnChip *old = NULL;
@@ -355,12 +356,15 @@ void DoCMil_init(struct mtd_info *mtd)
mtd->name = "DiskOnChip Millennium";
printk(KERN_NOTICE "DiskOnChip Millennium found at address 0x%lX\n",
- this->physadr);
+ this->physadr);
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH;
mtd->size = 0;
+
+ /* FIXME: erase size is not always 8kB */
mtd->erasesize = 0x2000;
+
mtd->oobblock = 512;
mtd->oobsize = 16;
mtd->module = THIS_MODULE;
@@ -374,15 +378,15 @@ void DoCMil_init(struct mtd_info *mtd)
mtd->read_oob = doc_read_oob;
mtd->write_oob = doc_write_oob;
mtd->sync = NULL;
-
+
this->totlen = 0;
- this->numchips = 0;
+ this->numchips = 0;
this->curfloor = -1;
this->curchip = -1;
-
+
/* Ident all the chips present. */
DoC_ScanChips(this);
-
+
if (!this->totlen) {
kfree(mtd);
iounmap((void *)this->virtadr);
@@ -405,8 +409,9 @@ static int doc_read (struct mtd_info *mtd, loff_t from, size_t len,
static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf, u_char *eccbuf)
{
- int i;
+ int i, ret;
volatile char dummy;
+ unsigned char syndrome[6];
struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
unsigned long docptr = this->virtadr;
struct Nand *mychip = &this->chips[from >> (this->chipshift)];
@@ -429,45 +434,55 @@ static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
+ /* issue the Read0 or Read1 command depend on which half of the page
+ we are accessing. Polling the Flash Ready bit after issue 3 bytes
+ address in Sequence Read Mode, see Software Requirement 11.4 item 1.*/
+ DoC_Command(docptr, (from >> 8) & 1, CDSN_CTRL_WP);
+ DoC_Address(docptr, 3, from, CDSN_CTRL_WP, 0x00);
+ DoC_WaitReady(docptr);
+
if (eccbuf) {
/* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/
WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
WriteDOC (DOC_ECC_EN, docptr, ECCConf);
} else {
- /* disable the ECC engine, FIXME: is this correct ?? */
+ /* disable the ECC engine */
WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
- WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
+ WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
}
- /* issue the Read0 or Read1 command depend on which half of the page
- we are accessing. Polling the Flash Ready bit after issue 3 bytes
- address in Sequence Read Mode, see Software Requirement 11.4 item 1.*/
- DoC_Command(docptr, (from >> 8) & 1, CDSN_CTRL_WP);
- DoC_Address(docptr, 3, from, CDSN_CTRL_WP, 0x00);
- DoC_WaitReady(docptr);
-
/* Read the data via the internal pipeline through CDSN IO register,
see Pipelined Read Operations 11.3 */
dummy = ReadDOC(docptr, ReadPipeInit);
+#ifndef USE_MEMCPY
for (i = 0; i < len-1; i++) {
- buf[i] = ReadDOC(docptr, Mil_CDSN_IO);
+ /* N.B. you have to increase the source address in this way or the
+ ECC logic will not work properly */
+ buf[i] = ReadDOC(docptr, Mil_CDSN_IO + (i & 0xff));
}
- buf[i] = ReadDOC(docptr, LastDataRead);
+#else
+ memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len - 1);
+#endif
+ buf[len - 1] = ReadDOC(docptr, LastDataRead);
/* Let the caller know we completed it */
*retlen = len;
+ ret = 0;
if (eccbuf) {
- /* FIXME: are we reading the ECC from the ECC logic of DOC or
- the spare data space on the flash chip i.e. How do we
- control the Spare Area Enable bit of the flash ?? */
- /* Read the ECC data through the DiskOnChip ECC logic
+ /* Read the ECC data from Spare Data Area,
see Reed-Solomon EDC/ECC 11.1 */
dummy = ReadDOC(docptr, ReadPipeInit);
+#ifndef USE_MEMCPY
for (i = 0; i < 5; i++) {
- eccbuf[i] = ReadDOC(docptr, Mil_CDSN_IO);
+ /* N.B. you have to increase the source address in this way or the
+ ECC logic will not work properly */
+ eccbuf[i] = ReadDOC(docptr, Mil_CDSN_IO + i);
}
- eccbuf[i] = ReadDOC(docptr, LastDataRead);
+#else
+ memcpy_fromio(eccbuf, docptr + DoC_Mil_CDSN_IO, 5);
+#endif
+ eccbuf[5] = ReadDOC(docptr, LastDataRead);
/* Flush the pipeline */
dummy = ReadDOC(docptr, ECCConf);
@@ -475,34 +490,45 @@ static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
/* Check the ECC Status */
if (ReadDOC(docptr, ECCConf) & 0x80) {
+ int nb_errors;
/* There was an ECC error */
+#ifdef ECC_DEBUG
printk("DiskOnChip ECC Error: Read at %lx\n", (long)from);
-
- /* FIXME: Implement ECC error correction, don't just whinge */
-
- /* We return error, but have actually done the read. Not that
- this can be told to user-space, via sys_read(), but at least
- MTD-aware stuff can know about it by checking *retlen */
- return -EIO;
+#endif
+ /* Read the ECC syndrom through the DiskOnChip ECC logic.
+ These syndrome will be all ZERO when there is no error */
+ for (i = 0; i < 6; i++) {
+ syndrome[i] = ReadDOC(docptr, ECCSyndrome0 + i);
+ }
+ nb_errors = doc_decode_ecc(buf, syndrome);
+#ifdef ECC_DEBUG
+ printk("Errors corrected: %x\n", nb_errors);
+#endif
+ if (nb_errors < 0) {
+ /* We return error, but have actually done the read. Not that
+ this can be told to user-space, via sys_read(), but at least
+ MTD-aware stuff can know about it by checking *retlen */
+ ret = -EIO;
+ }
}
+
#ifdef PSYCHO_DEBUG
- else
- printk("ECC OK at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
- (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
- eccbuf[4], eccbuf[5]);
+ printk("ECC DATA at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+ (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
+ eccbuf[4], eccbuf[5]);
#endif
- /* Reset the ECC engine */
- WriteDOC(DOC_ECC_RESV, docptr , ECCConf);
+ /* disable the ECC engine */
+ WriteDOC(DOC_ECC_DIS, docptr , ECCConf);
}
- return 0;
+ return ret;
}
static int doc_write (struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
- static char as[6];
- return doc_write_ecc(mtd, to, len, retlen, buf, as);
+ char eccbuf[6];
+ return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf);
}
static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
@@ -532,42 +558,48 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
if (this->curfloor != mychip->floor) {
DoC_SelectFloor(docptr, mychip->floor);
DoC_SelectChip(docptr, mychip->chip);
- }
- else if (this->curchip != mychip->chip) {
+ } else if (this->curchip != mychip->chip) {
DoC_SelectChip(docptr, mychip->chip);
}
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
/* Reset the chip, see Software Requirement 11.4 item 1. */
- DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP);
+ DoC_Command(docptr, NAND_CMD_RESET, 0x00);
DoC_WaitReady(docptr);
/* Set device to main plane of flash */
- DoC_Command(docptr, NAND_CMD_READ0, CDSN_CTRL_WP);
+ DoC_Command(docptr, NAND_CMD_READ0, 0x00);
+
+ /* issue the Serial Data In command to initial the Page Program process */
+ DoC_Command(docptr, NAND_CMD_SEQIN, 0x00);
+ DoC_Address(docptr, 3, to, 0x00, 0x00);
+ DoC_WaitReady(docptr);
if (eccbuf) {
/* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/
WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
WriteDOC (DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
} else {
- /* disable the ECC engine, FIXME: is this correct ?? */
+ /* disable the ECC engine */
WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
}
- /* issue the Serial Data In command to initial the Page Program process */
- DoC_Command(docptr, NAND_CMD_SEQIN, 0x00);
- DoC_Address(docptr, 3, to, 0x00, 0x00);
-
/* Write the data via the internal pipeline through CDSN IO register,
see Pipelined Write Operations 11.2 */
+#ifndef USE_MEMCPY
for (i = 0; i < len; i++) {
- WriteDOC(buf[i], docptr, Mil_CDSN_IO);
+ /* N.B. you have to increase the source address in this way or the
+ ECC logic will not work properly */
+ WriteDOC(buf[i], docptr, Mil_CDSN_IO + i);
}
+#else
+ memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len);
+#endif
WriteDOC(0x00, docptr, WritePipeTerm);
if (eccbuf) {
- /* Write ECC data to flash, the ECC info is generated by the DiskOnChip DECC logic
+ /* Write ECC data to flash, the ECC info is generated by the DiskOnChip ECC logic
see Reed-Solomon EDC/ECC 11.1 */
WriteDOC(0, docptr, NOP);
WriteDOC(0, docptr, NOP);
@@ -578,10 +610,26 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
eccbuf[i] = ReadDOC(docptr, ECCSyndrome0 + i);
}
+ /* ignore the ECC engine */
+ WriteDOC(DOC_ECC_DIS, docptr , ECCConf);
+
+#ifndef USE_MEMCPY
/* Write the ECC data to flash */
for (i = 0; i < 6; i++) {
- WriteDOC(eccbuf[i], docptr, Mil_CDSN_IO);
+ /* N.B. you have to increase the source address in this way or the
+ ECC logic will not work properly */
+ WriteDOC(eccbuf[i], docptr, Mil_CDSN_IO + i);
}
+#else
+ memcpy_toio(docptr + DoC_Mil_CDSN_IO, eccbuf, 6);
+#endif
+
+ /* write the block status BLOCK_USED (0x5555) at the end of ECC data
+ FIXME: this is only a hack for programming the IPL area for LinuxBIOS
+ and should be replace with proper codes in user space utilities */
+ WriteDOC(0x55, docptr, Mil_CDSN_IO);
+ WriteDOC(0x55, docptr, Mil_CDSN_IO + 1);
+
WriteDOC(0x00, docptr, WritePipeTerm);
#ifdef PSYCHO_DEBUG
@@ -589,9 +637,6 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
(long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
eccbuf[4], eccbuf[5]);
#endif
-
- /* Reset the ECC engine */
- WriteDOC(DOC_ECC_RESV, docptr , ECCConf);
}
/* Commit the Page Program command and wait for ready
@@ -601,12 +646,13 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
/* Read the status of the flash device through CDSN Slow IO register
see Software Requirement 11.4 item 5.*/
- DoC_Command(docptr, NAND_CMD_STATUS, 0x00);
+ DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP);
dummy = ReadDOC(docptr, CDSNSlowIO);
DoC_Delay(docptr, 2);
if (ReadDOC(docptr, Mil_CDSN_IO) & 1) {
printk("Error programming flash\n");
- /* Error in programming */
+ /* Error in programming
+ FIXME: implement Bad Block Replacement (in nftl.c ??) */
*retlen = 0;
return -EIO;
}
@@ -620,31 +666,29 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
size_t *retlen, u_char *buf)
{
- volatile char dummy;
+#ifndef USE_MEMCPY
int i;
+#endif
+ volatile char dummy;
struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
unsigned long docptr = this->virtadr;
struct Nand *mychip = &this->chips[ofs >> this->chipshift];
- /* FIXME: should we restrict the access between 512 to 527 ?? */
-
/* Find the chip which is to be used and select it */
if (this->curfloor != mychip->floor) {
DoC_SelectFloor(docptr, mychip->floor);
DoC_SelectChip(docptr, mychip->chip);
- }
- else if (this->curchip != mychip->chip) {
+ } else if (this->curchip != mychip->chip) {
DoC_SelectChip(docptr, mychip->chip);
}
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
- /* FIXME: should we disable ECC engine in this way ?? */
- /* disable the ECC engine, FIXME: is this correct ?? */
+ /* disable the ECC engine */
WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
- /* issue the Read2 command to read the Spare Data Area.
+ /* issue the Read2 command to set the pointer to the Spare Data Area.
Polling the Flash Ready bit after issue 3 bytes address in
Sequence Read Mode, see Software Requirement 11.4 item 1.*/
DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP);
@@ -654,10 +698,17 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
/* Read the data out via the internal pipeline through CDSN IO register,
see Pipelined Read Operations 11.3 */
dummy = ReadDOC(docptr, ReadPipeInit);
+#ifndef USE_MEMCPY
for (i = 0; i < len-1; i++) {
- buf[i] = ReadDOC(docptr, Mil_CDSN_IO);
+ /* N.B. you have to increase the source address in this way or the
+ ECC logic will not work properly */
+ buf[i] = ReadDOC(docptr, Mil_CDSN_IO + i);
}
buf[i] = ReadDOC(docptr, LastDataRead);
+#else
+ memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len - 1);
+#endif
+ buf[len - 1] = ReadDOC(docptr, LastDataRead);
*retlen = len;
@@ -667,32 +718,32 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
size_t *retlen, const u_char *buf)
{
+#ifndef USE_MEMCPY
int i;
+#endif
volatile char dummy;
struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
unsigned long docptr = this->virtadr;
struct Nand *mychip = &this->chips[ofs >> this->chipshift];
/* Find the chip which is to be used and select it */
- if (this->curfloor != mychip->floor) {
+ if (this->curfloor != mychip->floor) {
DoC_SelectFloor(docptr, mychip->floor);
DoC_SelectChip(docptr, mychip->chip);
- }
- else if (this->curchip != mychip->chip) {
+ } else if (this->curchip != mychip->chip) {
DoC_SelectChip(docptr, mychip->chip);
}
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
- /* FIXME: should we disable ECC engine in this way ?? */
- /* disable the ECC engine, FIXME: is this correct ?? */
+ /* disable the ECC engine */
WriteDOC (DOC_ECC_RESET, docptr, ECCConf);
WriteDOC (DOC_ECC_DIS, docptr, ECCConf);
/* Reset the chip, see Software Requirement 11.4 item 1. */
DoC_Command(docptr, NAND_CMD_RESET, CDSN_CTRL_WP);
DoC_WaitReady(docptr);
- /* issue the Read2 command to read the Spare Data Area. */
+ /* issue the Read2 command to set the pointer to the Spare Data Area. */
DoC_Command(docptr, NAND_CMD_READOOB, CDSN_CTRL_WP);
/* issue the Serial Data In command to initial the Page Program process */
@@ -701,8 +752,15 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
/* Write the data via the internal pipeline through CDSN IO register,
see Pipelined Write Operations 11.2 */
- for (i = 0; i < len; i++)
- WriteDOC(buf[i], docptr, Mil_CDSN_IO);
+#ifndef USE_MEMCPY
+ for (i = 0; i < len; i++) {
+ /* N.B. you have to increase the source address in this way or the
+ ECC logic will not work properly */
+ WriteDOC(buf[i], docptr, Mil_CDSN_IO + i);
+ }
+#else
+ memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len);
+#endif
WriteDOC(0x00, docptr, WritePipeTerm);
/* Commit the Page Program command and wait for ready
@@ -717,6 +775,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
DoC_Delay(docptr, 2);
if (ReadDOC(docptr, Mil_CDSN_IO) & 1) {
printk("Error programming oob data\n");
+ /* FIXME: implement Bad Block Replacement (in nftl.c ??) */
*retlen = 0;
return -EIO;
}
@@ -743,13 +802,12 @@ int doc_erase (struct mtd_info *mtd, struct erase_info *instr)
if (this->curfloor != mychip->floor) {
DoC_SelectFloor(docptr, mychip->floor);
DoC_SelectChip(docptr, mychip->chip);
- }
- else if (this->curchip != mychip->chip) {
+ } else if (this->curchip != mychip->chip) {
DoC_SelectChip(docptr, mychip->chip);
}
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
-
+
instr->state = MTD_ERASE_PENDING;
/* issue the Erase Setup command */
@@ -764,13 +822,16 @@ int doc_erase (struct mtd_info *mtd, struct erase_info *instr)
instr->state = MTD_ERASING;
/* Read the status of the flash device through CDSN Slow IO register
- see Software Requirement 11.4 item 5.*/
+ see Software Requirement 11.4 item 5.
+ FIXME: it seems that we are not wait long enough, some blocks are not
+ erased fully */
DoC_Command(docptr, NAND_CMD_STATUS, CDSN_CTRL_WP);
dummy = ReadDOC(docptr, CDSNSlowIO);
DoC_Delay(docptr, 2);
if (ReadDOC(docptr, Mil_CDSN_IO) & 1) {
- printk("Error Erasing\n");
- /* There was an error */
+ printk("Error Erasing at 0x%lx\n", ofs);
+ /* There was an error
+ FIXME: implement Bad Block Replacement (in nftl.c ??) */
instr->state = MTD_ERASE_FAILED;
} else
instr->state = MTD_ERASE_DONE;
@@ -787,20 +848,17 @@ int doc_erase (struct mtd_info *mtd, struct erase_info *instr)
*
****************************************************************************/
-static int __init init_doc2001(void)
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define cleanup_doc2001 cleanup_module
+#define init_doc2001 init_module
+#endif
+
+int __init init_doc2001(void)
{
inter_module_register(im_name, THIS_MODULE, &DoCMil_init);
return 0;
}
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
-#define cleanup_doc2001 cleanup_module
-#endif
-#define __exit
-#endif
-
-
static void __exit cleanup_doc2001(void)
{
struct mtd_info *mtd;
@@ -817,11 +875,9 @@ static void __exit cleanup_doc2001(void)
kfree(mtd);
}
inter_module_unregister(im_name);
-
}
+module_exit(cleanup_doc2001);
module_init(init_doc2001);
-#if LINUX_VERSION_CODE > 0x20300
-module_exit(cleanup_doc2001);
-#endif
+
diff --git a/drivers/mtd/docecc.c b/drivers/mtd/docecc.c
new file mode 100644
index 000000000..cddc968c0
--- /dev/null
+++ b/drivers/mtd/docecc.c
@@ -0,0 +1,522 @@
+/*
+ * ECC algorithm for M-systems disk on chip. We use the excellent Reed
+ * Solmon code of Phil Karn (karn@ka9q.ampr.org) available under the
+ * GNU GPL License. The rest is simply to convert the disk on chip
+ * syndrom into a standard syndom.
+ *
+ * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
+ * Copyright (C) 2000 Netgem S.A.
+ *
+ * $Id: docecc.c,v 1.1 2000/11/03 12:43:43 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/types.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/doc2000.h>
+
+/* need to undef it (from asm/termbits.h) */
+#undef B0
+
+#define MM 10 /* Symbol size in bits */
+#define KK (1023-4) /* Number of data symbols per block */
+#define B0 510 /* First root of generator polynomial, alpha form */
+#define PRIM 1 /* power of alpha used to generate roots of generator poly */
+#define NN ((1 << MM) - 1)
+
+typedef unsigned short dtype;
+
+/* 1+x^3+x^10 */
+static const int Pp[MM+1] = { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1 };
+
+/* This defines the type used to store an element of the Galois Field
+ * used by the code. Make sure this is something larger than a char if
+ * if anything larger than GF(256) is used.
+ *
+ * Note: unsigned char will work up to GF(256) but int seems to run
+ * faster on the Pentium.
+ */
+typedef int gf;
+
+/* No legal value in index form represents zero, so
+ * we need a special value for this purpose
+ */
+#define A0 (NN)
+
+/* Compute x % NN, where NN is 2**MM - 1,
+ * without a slow divide
+ */
+static inline gf
+modnn(int x)
+{
+ while (x >= NN) {
+ x -= NN;
+ x = (x >> MM) + (x & NN);
+ }
+ return x;
+}
+
+#define min(a,b) ((a) < (b) ? (a) : (b))
+
+#define CLEAR(a,n) {\
+int ci;\
+for(ci=(n)-1;ci >=0;ci--)\
+(a)[ci] = 0;\
+}
+
+#define COPY(a,b,n) {\
+int ci;\
+for(ci=(n)-1;ci >=0;ci--)\
+(a)[ci] = (b)[ci];\
+}
+
+#define COPYDOWN(a,b,n) {\
+int ci;\
+for(ci=(n)-1;ci >=0;ci--)\
+(a)[ci] = (b)[ci];\
+}
+
+#define Ldec 1
+
+/* generate GF(2**m) from the irreducible polynomial p(X) in Pp[0]..Pp[m]
+ lookup tables: index->polynomial form alpha_to[] contains j=alpha**i;
+ polynomial form -> index form index_of[j=alpha**i] = i
+ alpha=2 is the primitive element of GF(2**m)
+ HARI's COMMENT: (4/13/94) alpha_to[] can be used as follows:
+ Let @ represent the primitive element commonly called "alpha" that
+ is the root of the primitive polynomial p(x). Then in GF(2^m), for any
+ 0 <= i <= 2^m-2,
+ @^i = a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1)
+ where the binary vector (a(0),a(1),a(2),...,a(m-1)) is the representation
+ of the integer "alpha_to[i]" with a(0) being the LSB and a(m-1) the MSB. Thus for
+ example the polynomial representation of @^5 would be given by the binary
+ representation of the integer "alpha_to[5]".
+ Similarily, index_of[] can be used as follows:
+ As above, let @ represent the primitive element of GF(2^m) that is
+ the root of the primitive polynomial p(x). In order to find the power
+ of @ (alpha) that has the polynomial representation
+ a(0) + a(1) @ + a(2) @^2 + ... + a(m-1) @^(m-1)
+ we consider the integer "i" whose binary representation with a(0) being LSB
+ and a(m-1) MSB is (a(0),a(1),...,a(m-1)) and locate the entry
+ "index_of[i]". Now, @^index_of[i] is that element whose polynomial
+ representation is (a(0),a(1),a(2),...,a(m-1)).
+ NOTE:
+ The element alpha_to[2^m-1] = 0 always signifying that the
+ representation of "@^infinity" = 0 is (0,0,0,...,0).
+ Similarily, the element index_of[0] = A0 always signifying
+ that the power of alpha which has the polynomial representation
+ (0,0,...,0) is "infinity".
+
+*/
+
+static void
+generate_gf(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1])
+{
+ register int i, mask;
+
+ mask = 1;
+ Alpha_to[MM] = 0;
+ for (i = 0; i < MM; i++) {
+ Alpha_to[i] = mask;
+ Index_of[Alpha_to[i]] = i;
+ /* If Pp[i] == 1 then, term @^i occurs in poly-repr of @^MM */
+ if (Pp[i] != 0)
+ Alpha_to[MM] ^= mask; /* Bit-wise EXOR operation */
+ mask <<= 1; /* single left-shift */
+ }
+ Index_of[Alpha_to[MM]] = MM;
+ /*
+ * Have obtained poly-repr of @^MM. Poly-repr of @^(i+1) is given by
+ * poly-repr of @^i shifted left one-bit and accounting for any @^MM
+ * term that may occur when poly-repr of @^i is shifted.
+ */
+ mask >>= 1;
+ for (i = MM + 1; i < NN; i++) {
+ if (Alpha_to[i - 1] >= mask)
+ Alpha_to[i] = Alpha_to[MM] ^ ((Alpha_to[i - 1] ^ mask) << 1);
+ else
+ Alpha_to[i] = Alpha_to[i - 1] << 1;
+ Index_of[Alpha_to[i]] = i;
+ }
+ Index_of[0] = A0;
+ Alpha_to[NN] = 0;
+}
+
+/*
+ * Performs ERRORS+ERASURES decoding of RS codes. bb[] is the content
+ * of the feedback shift register after having processed the data and
+ * the ECC.
+ *
+ * Return number of symbols corrected, or -1 if codeword is illegal
+ * or uncorrectable. If eras_pos is non-null, the detected error locations
+ * are written back. NOTE! This array must be at least NN-KK elements long.
+ * The corrected data are written in eras_val[]. They must be xor with the data
+ * to retrieve the correct data : data[erase_pos[i]] ^= erase_val[i] .
+ *
+ * First "no_eras" erasures are declared by the calling program. Then, the
+ * maximum # of errors correctable is t_after_eras = floor((NN-KK-no_eras)/2).
+ * If the number of channel errors is not greater than "t_after_eras" the
+ * transmitted codeword will be recovered. Details of algorithm can be found
+ * in R. Blahut's "Theory ... of Error-Correcting Codes".
+
+ * Warning: the eras_pos[] array must not contain duplicate entries; decoder failure
+ * will result. The decoder *could* check for this condition, but it would involve
+ * extra time on every decoding operation.
+ * */
+static int
+eras_dec_rs(dtype Alpha_to[NN + 1], dtype Index_of[NN + 1],
+ gf bb[NN - KK + 1], gf eras_val[NN-KK], int eras_pos[NN-KK],
+ int no_eras)
+{
+ int deg_lambda, el, deg_omega;
+ int i, j, r,k;
+ gf u,q,tmp,num1,num2,den,discr_r;
+ gf lambda[NN-KK + 1], s[NN-KK + 1]; /* Err+Eras Locator poly
+ * and syndrome poly */
+ gf b[NN-KK + 1], t[NN-KK + 1], omega[NN-KK + 1];
+ gf root[NN-KK], reg[NN-KK + 1], loc[NN-KK];
+ int syn_error, count;
+
+ syn_error = 0;
+ for(i=0;i<NN-KK;i++)
+ syn_error |= bb[i];
+
+ if (!syn_error) {
+ /* if remainder is zero, data[] is a codeword and there are no
+ * errors to correct. So return data[] unmodified
+ */
+ count = 0;
+ goto finish;
+ }
+
+ for(i=1;i<=NN-KK;i++){
+ s[i] = bb[0];
+ }
+ for(j=1;j<NN-KK;j++){
+ if(bb[j] == 0)
+ continue;
+ tmp = Index_of[bb[j]];
+
+ for(i=1;i<=NN-KK;i++)
+ s[i] ^= Alpha_to[modnn(tmp + (B0+i-1)*PRIM*j)];
+ }
+
+ /* undo the feedback register implicit multiplication and convert
+ syndromes to index form */
+
+ for(i=1;i<=NN-KK;i++) {
+ tmp = Index_of[s[i]];
+ if (tmp != A0)
+ tmp = modnn(tmp + 2 * KK * (B0+i-1)*PRIM);
+ s[i] = tmp;
+ }
+
+ CLEAR(&lambda[1],NN-KK);
+ lambda[0] = 1;
+
+ if (no_eras > 0) {
+ /* Init lambda to be the erasure locator polynomial */
+ lambda[1] = Alpha_to[modnn(PRIM * eras_pos[0])];
+ for (i = 1; i < no_eras; i++) {
+ u = modnn(PRIM*eras_pos[i]);
+ for (j = i+1; j > 0; j--) {
+ tmp = Index_of[lambda[j - 1]];
+ if(tmp != A0)
+ lambda[j] ^= Alpha_to[modnn(u + tmp)];
+ }
+ }
+#if DEBUG >= 1
+ /* Test code that verifies the erasure locator polynomial just constructed
+ Needed only for decoder debugging. */
+
+ /* find roots of the erasure location polynomial */
+ for(i=1;i<=no_eras;i++)
+ reg[i] = Index_of[lambda[i]];
+ count = 0;
+ for (i = 1,k=NN-Ldec; i <= NN; i++,k = modnn(NN+k-Ldec)) {
+ q = 1;
+ for (j = 1; j <= no_eras; j++)
+ if (reg[j] != A0) {
+ reg[j] = modnn(reg[j] + j);
+ q ^= Alpha_to[reg[j]];
+ }
+ if (q != 0)
+ continue;
+ /* store root and error location number indices */
+ root[count] = i;
+ loc[count] = k;
+ count++;
+ }
+ if (count != no_eras) {
+ printf("\n lambda(x) is WRONG\n");
+ count = -1;
+ goto finish;
+ }
+#if DEBUG >= 2
+ printf("\n Erasure positions as determined by roots of Eras Loc Poly:\n");
+ for (i = 0; i < count; i++)
+ printf("%d ", loc[i]);
+ printf("\n");
+#endif
+#endif
+ }
+ for(i=0;i<NN-KK+1;i++)
+ b[i] = Index_of[lambda[i]];
+
+ /*
+ * Begin Berlekamp-Massey algorithm to determine error+erasure
+ * locator polynomial
+ */
+ r = no_eras;
+ el = no_eras;
+ while (++r <= NN-KK) { /* r is the step number */
+ /* Compute discrepancy at the r-th step in poly-form */
+ discr_r = 0;
+ for (i = 0; i < r; i++){
+ if ((lambda[i] != 0) && (s[r - i] != A0)) {
+ discr_r ^= Alpha_to[modnn(Index_of[lambda[i]] + s[r - i])];
+ }
+ }
+ discr_r = Index_of[discr_r]; /* Index form */
+ if (discr_r == A0) {
+ /* 2 lines below: B(x) <-- x*B(x) */
+ COPYDOWN(&b[1],b,NN-KK);
+ b[0] = A0;
+ } else {
+ /* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */
+ t[0] = lambda[0];
+ for (i = 0 ; i < NN-KK; i++) {
+ if(b[i] != A0)
+ t[i+1] = lambda[i+1] ^ Alpha_to[modnn(discr_r + b[i])];
+ else
+ t[i+1] = lambda[i+1];
+ }
+ if (2 * el <= r + no_eras - 1) {
+ el = r + no_eras - el;
+ /*
+ * 2 lines below: B(x) <-- inv(discr_r) *
+ * lambda(x)
+ */
+ for (i = 0; i <= NN-KK; i++)
+ b[i] = (lambda[i] == 0) ? A0 : modnn(Index_of[lambda[i]] - discr_r + NN);
+ } else {
+ /* 2 lines below: B(x) <-- x*B(x) */
+ COPYDOWN(&b[1],b,NN-KK);
+ b[0] = A0;
+ }
+ COPY(lambda,t,NN-KK+1);
+ }
+ }
+
+ /* Convert lambda to index form and compute deg(lambda(x)) */
+ deg_lambda = 0;
+ for(i=0;i<NN-KK+1;i++){
+ lambda[i] = Index_of[lambda[i]];
+ if(lambda[i] != A0)
+ deg_lambda = i;
+ }
+ /*
+ * Find roots of the error+erasure locator polynomial by Chien
+ * Search
+ */
+ COPY(&reg[1],&lambda[1],NN-KK);
+ count = 0; /* Number of roots of lambda(x) */
+ for (i = 1,k=NN-Ldec; i <= NN; i++,k = modnn(NN+k-Ldec)) {
+ q = 1;
+ for (j = deg_lambda; j > 0; j--){
+ if (reg[j] != A0) {
+ reg[j] = modnn(reg[j] + j);
+ q ^= Alpha_to[reg[j]];
+ }
+ }
+ if (q != 0)
+ continue;
+ /* store root (index-form) and error location number */
+ root[count] = i;
+ loc[count] = k;
+ /* If we've already found max possible roots,
+ * abort the search to save time
+ */
+ if(++count == deg_lambda)
+ break;
+ }
+ if (deg_lambda != count) {
+ /*
+ * deg(lambda) unequal to number of roots => uncorrectable
+ * error detected
+ */
+ count = -1;
+ goto finish;
+ }
+ /*
+ * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
+ * x**(NN-KK)). in index form. Also find deg(omega).
+ */
+ deg_omega = 0;
+ for (i = 0; i < NN-KK;i++){
+ tmp = 0;
+ j = (deg_lambda < i) ? deg_lambda : i;
+ for(;j >= 0; j--){
+ if ((s[i + 1 - j] != A0) && (lambda[j] != A0))
+ tmp ^= Alpha_to[modnn(s[i + 1 - j] + lambda[j])];
+ }
+ if(tmp != 0)
+ deg_omega = i;
+ omega[i] = Index_of[tmp];
+ }
+ omega[NN-KK] = A0;
+
+ /*
+ * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
+ * inv(X(l))**(B0-1) and den = lambda_pr(inv(X(l))) all in poly-form
+ */
+ for (j = count-1; j >=0; j--) {
+ num1 = 0;
+ for (i = deg_omega; i >= 0; i--) {
+ if (omega[i] != A0)
+ num1 ^= Alpha_to[modnn(omega[i] + i * root[j])];
+ }
+ num2 = Alpha_to[modnn(root[j] * (B0 - 1) + NN)];
+ den = 0;
+
+ /* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
+ for (i = min(deg_lambda,NN-KK-1) & ~1; i >= 0; i -=2) {
+ if(lambda[i+1] != A0)
+ den ^= Alpha_to[modnn(lambda[i+1] + i * root[j])];
+ }
+ if (den == 0) {
+#if DEBUG >= 1
+ printf("\n ERROR: denominator = 0\n");
+#endif
+ /* Convert to dual- basis */
+ count = -1;
+ goto finish;
+ }
+ /* Apply error to data */
+ if (num1 != 0) {
+ eras_val[j] = Alpha_to[modnn(Index_of[num1] + Index_of[num2] + NN - Index_of[den])];
+ } else {
+ eras_val[j] = 0;
+ }
+ }
+ finish:
+ for(i=0;i<count;i++)
+ eras_pos[i] = loc[i];
+ return count;
+}
+
+/***************************************************************************/
+/* The DOC specific code begins here */
+
+#define SECTOR_SIZE 512
+/* The sector bytes are packed into NB_DATA MM bits words */
+#define NB_DATA (((SECTOR_SIZE + 1) * 8 + 6) / MM)
+
+/*
+ * Correct the errors in 'sector[]' by using 'ecc1[]' which is the
+ * content of the feedback shift register applyied to the sector and
+ * the ECC. Return the number of errors corrected (and correct them in
+ * sector), or -1 if error
+ */
+int doc_decode_ecc(unsigned char sector[SECTOR_SIZE], unsigned char ecc1[6])
+{
+ int parity, i, nb_errors;
+ gf bb[NN - KK + 1];
+ gf error_val[NN-KK];
+ int error_pos[NN-KK], pos, bitpos, index, val;
+ dtype *Alpha_to, *Index_of;
+
+ /* init log and exp tables here to save memory. However, it is slower */
+ Alpha_to = kmalloc((NN + 1) * sizeof(dtype), GFP_KERNEL);
+ if (!Alpha_to)
+ return -1;
+
+ Index_of = kmalloc((NN + 1) * sizeof(dtype), GFP_KERNEL);
+ if (!Index_of) {
+ kfree(Alpha_to);
+ return -1;
+ }
+
+ generate_gf(Alpha_to, Index_of);
+
+ parity = ecc1[1];
+
+ bb[0] = (ecc1[4] & 0xff) | ((ecc1[5] & 0x03) << 8);
+ bb[1] = ((ecc1[5] & 0xfc) >> 2) | ((ecc1[2] & 0x0f) << 6);
+ bb[2] = ((ecc1[2] & 0xf0) >> 4) | ((ecc1[3] & 0x3f) << 4);
+ bb[3] = ((ecc1[3] & 0xc0) >> 6) | ((ecc1[0] & 0xff) << 2);
+
+ nb_errors = eras_dec_rs(Alpha_to, Index_of, bb,
+ error_val, error_pos, 0);
+ if (nb_errors <= 0)
+ goto the_end;
+
+ /* correct the errors */
+ for(i=0;i<nb_errors;i++) {
+ pos = error_pos[i];
+ if (pos >= NB_DATA && pos < KK) {
+ nb_errors = -1;
+ goto the_end;
+ }
+ if (pos < NB_DATA) {
+ /* extract bit position (MSB first) */
+ pos = 10 * (NB_DATA - 1 - pos) - 6;
+ /* now correct the following 10 bits. At most two bytes
+ can be modified since pos is even */
+ index = (pos >> 3) ^ 1;
+ bitpos = pos & 7;
+ if ((index >= 0 && index < SECTOR_SIZE) ||
+ index == (SECTOR_SIZE + 1)) {
+ val = error_val[i] >> (2 + bitpos);
+ parity ^= val;
+ if (index < SECTOR_SIZE)
+ sector[index] ^= val;
+ }
+ index = ((pos >> 3) + 1) ^ 1;
+ bitpos = (bitpos + 10) & 7;
+ if (bitpos == 0)
+ bitpos = 8;
+ if ((index >= 0 && index < SECTOR_SIZE) ||
+ index == (SECTOR_SIZE + 1)) {
+ val = error_val[i] << (8 - bitpos);
+ parity ^= val;
+ if (index < SECTOR_SIZE)
+ sector[index] ^= val;
+ }
+ }
+ }
+
+ /* use parity to test extra errors */
+ if ((parity & 0xff) != 0)
+ nb_errors = -1;
+
+ the_end:
+ kfree(Alpha_to);
+ kfree(Index_of);
+ return nb_errors;
+}
+
diff --git a/drivers/mtd/docprobe.c b/drivers/mtd/docprobe.c
index b38b4352d..c67a3489a 100644
--- a/drivers/mtd/docprobe.c
+++ b/drivers/mtd/docprobe.c
@@ -3,7 +3,7 @@
/* Probe routines common to all DoC devices */
/* (c) 1999 Machine Vision Holdings, Inc. */
/* Author: David Woodhouse <dwmw2@mvhi.com> */
-/* $Id: docprobe.c,v 1.10 2000/07/13 14:23:20 dwmw2 Exp $ */
+/* $Id: docprobe.c,v 1.21 2000/12/03 19:32:34 dwmw2 Exp $ */
@@ -26,6 +26,21 @@
#define DOC_PASSIVE_PROBE
*/
+
+/* DOC_SINGLE_DRIVER:
+ Millennium driver has been merged into DOC2000 driver.
+
+ The newly-merged driver doesn't appear to work for writing. It's the
+ same with the DiskOnChip 2000 and the Millennium. If you have a
+ Millennium and you want write support to work, remove the definition
+ of DOC_SINGLE_DRIVER below to use the old doc2001-specific driver.
+
+ Otherwise, it's left on in the hope that it'll annoy someone with
+ a Millennium enough that they go through and work out what the
+ difference is :)
+*/
+#define DOC_SINGLE_DRIVER
+
#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/errno.h>
@@ -44,21 +59,36 @@
#include <linux/mtd/doc2000.h>
/* Where to look for the devices? */
+#ifndef CONFIG_MTD_DOCPROBE_ADDRESS
+#define CONFIG_MTD_DOCPROBE_ADDRESS 0
+#endif
+
+
+static unsigned long doc_config_location = CONFIG_MTD_DOCPROBE_ADDRESS;
+MODULE_PARM(doc_config_location, "l");
+
+static unsigned long __initdata doc_locations[] = {
#if defined (__alpha__) || defined(__i386__)
-static unsigned long __initdata doc_locations[] = {
- 0xc8000, 0xca000, 0xcc000, 0xce000,
- 0xd0000, 0xd2000, 0xd4000, 0xd6000,
- 0xd8000, 0xda000, 0xdc000, 0xde000,
- 0xe0000, 0xe2000, 0xe4000, 0xe6000,
- 0xe8000, 0xea000, 0xec000, 0xee000, 0 };
+#ifdef CONFIG_MTD_DOCPROBE_HIGH
+ 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000,
+ 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,
+ 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
+ 0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000,
+ 0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000,
+#else /* CONFIG_MTD_DOCPROBE_HIGH */
+ 0xc8000, 0xca000, 0xcc000, 0xce000,
+ 0xd0000, 0xd2000, 0xd4000, 0xd6000,
+ 0xd8000, 0xda000, 0xdc000, 0xde000,
+ 0xe0000, 0xe2000, 0xe4000, 0xe6000,
+ 0xe8000, 0xea000, 0xec000, 0xee000,
+#endif /* CONFIG_MTD_DOCPROBE_HIGH */
#elif defined(__ppc__)
-static unsigned long __initdata doc_locations[] = {
- 0xe4000000, 0};
+ 0xe4000000,
#else
#warning Unknown architecture for DiskOnChip. No default probe locations defined
#endif
-
+ 0 };
/* doccheck: Probe a given memory window to see if there's a DiskOnChip present */
@@ -71,10 +101,13 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr
#endif
/* Routine copied from the Linux DOC driver */
-
- /* Check for 0x55 0xAA signature at beginning of window */
+
+#ifdef CONFIG_MTD_DOCPROBE_55AA
+ /* Check for 0x55 0xAA signature at beginning of window,
+ this is no longer true once we remove the IPL (for Millennium */
if (ReadDOC(window, Sig1) != 0x55 || ReadDOC(window, Sig2) != 0xaa)
return 0;
+#endif /* CONFIG_MTD_DOCPROBE_55AA */
#ifndef DOC_PASSIVE_PROBE
/* It's not possible to cleanly detect the DiskOnChip - the
@@ -118,9 +151,10 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr
break;
default:
+#ifndef CONFIG_MTD_DOCPROBE_55AA
printk(KERN_WARNING "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n",
ChipID, physadr);
-
+#endif
#ifndef DOC_PASSIVE_PROBE
/* Put back the contents of the DOCControl register, in case it's not
* actually a DiskOnChip.
@@ -177,28 +211,35 @@ static void DoC_Probe(unsigned long physadr)
this->physadr = physadr;
this->ChipID = ChipID;
sprintf(namebuf, "with ChipID %2.2X", ChipID);
-
+
switch(ChipID) {
case DOC_ChipID_Doc2k:
name="2000";
im_funcname = "DoC2k_init";
im_modname = "doc2000";
break;
-
+
case DOC_ChipID_DocMil:
name="Millennium";
+#ifdef DOC_SINGLE_DRIVER
+ im_funcname = "DoC2k_init";
+ im_modname = "doc2000";
+#else
im_funcname = "DoCMil_init";
im_modname = "doc2001";
+#endif /* DOC_SINGLE_DRIVER */
break;
}
+
if (im_funcname)
initroutine = inter_module_get_request(im_funcname, im_modname);
+
if (initroutine) {
(*initroutine)(mtd);
inter_module_put(im_funcname);
return;
}
- printk("Cannot find driver for DiskOnChip %s at 0x%X\n", name, physadr);
+ printk("Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr);
}
iounmap((void *)docptr);
}
@@ -210,12 +251,9 @@ static void DoC_Probe(unsigned long physadr)
*
****************************************************************************/
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_doc init_module
#endif
-#define __exit
-#endif
int __init init_doc(void)
{
@@ -223,19 +261,22 @@ int __init init_doc(void)
printk(KERN_NOTICE "M-Systems DiskOnChip driver. (C) 1999 Machine Vision Holdings, Inc.\n");
#ifdef PRERELEASE
- printk(KERN_INFO "$Id: docprobe.c,v 1.10 2000/07/13 14:23:20 dwmw2 Exp $\n");
+ printk(KERN_INFO "$Id: docprobe.c,v 1.21 2000/12/03 19:32:34 dwmw2 Exp $\n");
#endif
-
- for (i=0; doc_locations[i]; i++) {
- DoC_Probe(doc_locations[i]);
+ if (doc_config_location) {
+ printk("Using configured probe address 0x%lx\n", doc_config_location);
+ DoC_Probe(doc_config_location);
+ } else {
+ for (i=0; doc_locations[i]; i++) {
+ DoC_Probe(doc_locations[i]);
+ }
}
-
+ /* So it looks like we've been used and we get unloaded */
+ MOD_INC_USE_COUNT;
+ MOD_DEC_USE_COUNT;
return 0;
}
-
-#if LINUX_VERSION_CODE > 0x20300
module_init(init_doc);
-#endif
diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c
index 1cf4a5f16..21ed0cecd 100644
--- a/drivers/mtd/ftl.c
+++ b/drivers/mtd/ftl.c
@@ -1,6 +1,10 @@
/* This version ported to the Linux-MTD system by dwmw2@infradead.org
- * $Id: ftl.c,v 1.20 2000/06/23 15:17:53 dwmw2 Exp $
- * Based on:
+ *
+ * - Based on Id: ftl.c,v 1.21 2000/08/01 13:07:49 dwmw2 Exp
+ * - With the Franz Galiana's set_bam_entry fix from v1.23
+ * - Perhaps it's about time I made a branch for the 2.4 series.
+
+ * Originally based on:
*/
/*======================================================================
@@ -263,12 +267,13 @@ static struct block_device_operations ftl_blk_fops = {
static int scan_header(partition_t *part)
{
erase_unit_header_t header;
- loff_t offset;
+ loff_t offset, max_offset;
int ret;
part->header.FormattedSize = 0;
+ max_offset = (0x100000<part->mtd->size)?0x100000:part->mtd->size;
/* Search first megabyte for a valid FTL header */
for (offset = 0;
- offset < 0x100000;
+ offset < max_offset;
offset += part->mtd->erasesize?part->mtd->erasesize:0x2000) {
ret = part->mtd->read(part->mtd, offset, sizeof(header), &ret,
@@ -280,7 +285,7 @@ static int scan_header(partition_t *part)
if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break;
}
- if (offset == 0x100000) {
+ if (offset == max_offset) {
printk(KERN_NOTICE "ftl_cs: FTL header not found.\n");
return -ENOENT;
}
@@ -998,7 +1003,7 @@ static int ftl_read(partition_t *part, caddr_t buffer,
static int set_bam_entry(partition_t *part, u_int32_t log_addr,
u_int32_t virt_addr)
{
- u_int32_t bsize, blk;
+ u_int32_t bsize, blk, le_virt_addr;
#ifdef PSYCHO_DEBUG
u_int32_t old_addr;
#endif
@@ -1035,6 +1040,7 @@ static int set_bam_entry(partition_t *part, u_int32_t log_addr,
return -EIO;
}
#endif
+ le_virt_addr = cpu_to_le32(virt_addr);
if (part->bam_index == eun) {
#ifdef PSYCHO_DEBUG
if (le32_to_cpu(part->bam_cache[blk]) != old_addr) {
@@ -1049,10 +1055,10 @@ static int set_bam_entry(partition_t *part, u_int32_t log_addr,
return -EIO;
}
#endif
- part->bam_cache[blk] = cpu_to_le32(virt_addr);
+ part->bam_cache[blk] = le_virt_addr;
}
ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t),
- &retlen, (u_char *)&part->bam_cache[blk]);
+ &retlen, (u_char *)&le_virt_addr);
if (ret) {
printk(KERN_NOTICE "ftl_cs: set_bam_entry() failed!\n");
@@ -1410,8 +1416,6 @@ mod_init_t init_ftl(void)
memset(myparts, 0, sizeof(myparts));
- DEBUG(0, "$Id: ftl.c,v 1.20 2000/06/23 15:17:53 dwmw2 Exp $\n");
-
if (register_blkdev(FTL_MAJOR, "ftl", &ftl_blk_fops)) {
printk(KERN_NOTICE "ftl_cs: unable to grab major "
"device number!\n");
diff --git a/drivers/mtd/map_ram.c b/drivers/mtd/map_ram.c
index c62515406..00c009d7f 100644
--- a/drivers/mtd/map_ram.c
+++ b/drivers/mtd/map_ram.c
@@ -1,7 +1,7 @@
/*
* Common code to handle map devices which are simple RAM
* (C) 2000 Red Hat. GPL'd.
- * $Id: map_ram.c,v 1.2 2000/07/03 10:01:38 dwmw2 Exp $
+ * $Id: map_ram.c,v 1.7 2000/12/10 01:39:13 dwmw2 Exp $
*/
#include <linux/module.h>
@@ -29,6 +29,7 @@ static const char im_name[] = "map_ram_probe";
* this module is non-zero, i.e. between inter_module_get and
* inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000.
*/
+
static struct mtd_info *map_ram_probe(struct map_info *map)
{
struct mtd_info *mtd;
@@ -59,6 +60,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map)
memset(mtd, 0, sizeof(*mtd));
+ map->im_name = im_name;
map->fldrv_destroy = mapram_nop;
mtd->priv = map;
mtd->name = map->name;
@@ -69,9 +71,9 @@ static struct mtd_info *map_ram_probe(struct map_info *map)
mtd->read = mapram_read;
mtd->write = mapram_write;
mtd->sync = mapram_nop;
- mtd->im_name = im_name;
mtd->flags = MTD_CAP_RAM | MTD_VOLATILE;
-
+ mtd->erasesize = PAGE_SIZE;
+
return mtd;
}
@@ -115,6 +117,11 @@ static void mapram_nop(struct mtd_info *mtd)
/* Nothing to see here */
}
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define map_ram_init init_module
+#define map_ram_exit cleanup_module
+#endif
+
static int __init map_ram_init(void)
{
inter_module_register(im_name, THIS_MODULE, &map_ram_probe);
diff --git a/drivers/mtd/map_rom.c b/drivers/mtd/map_rom.c
index d353938e9..c976c7ecf 100644
--- a/drivers/mtd/map_rom.c
+++ b/drivers/mtd/map_rom.c
@@ -1,7 +1,7 @@
/*
* Common code to handle map devices which are simple ROM
* (C) 2000 Red Hat. GPL'd.
- * $Id: map_rom.c,v 1.2 2000/07/03 10:01:38 dwmw2 Exp $
+ * $Id: map_rom.c,v 1.10 2000/12/10 01:39:13 dwmw2 Exp $
*/
#include <linux/module.h>
@@ -14,12 +14,19 @@
#include <linux/mtd/map.h>
-
static int maprom_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
static void maprom_nop (struct mtd_info *);
-struct mtd_info *map_rom_probe(struct map_info *);
-EXPORT_SYMBOL(map_rom_probe);
+static const char im_name[] = "map_rom_probe";
+
+/* This routine is made available to other mtd code via
+ * inter_module_register. It must only be accessed through
+ * inter_module_get which will bump the use count of this module. The
+ * addresses passed back in mtd are valid as long as the use count of
+ * this module is non-zero, i.e. between inter_module_get and
+ * inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000.
+ */
struct mtd_info *map_rom_probe(struct map_info *map)
{
@@ -31,16 +38,18 @@ struct mtd_info *map_rom_probe(struct map_info *map)
memset(mtd, 0, sizeof(*mtd));
+ map->im_name = im_name;
map->fldrv_destroy = maprom_nop;
mtd->priv = map;
mtd->name = map->name;
mtd->type = MTD_ROM;
mtd->size = map->size;
mtd->read = maprom_read;
+ mtd->write = maprom_write;
mtd->sync = maprom_nop;
mtd->flags = MTD_CAP_ROM;
-
- MOD_INC_USE_COUNT;
+ mtd->erasesize = 131072;
+
return mtd;
}
@@ -58,3 +67,28 @@ static void maprom_nop(struct mtd_info *mtd)
{
/* Nothing to see here */
}
+
+static int maprom_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
+{
+ printk(KERN_NOTICE "maprom_write called\n");
+ return -EIO;
+}
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define map_rom_init init_module
+#define map_rom_exit cleanup_module
+#endif
+
+static int __init map_rom_init(void)
+{
+ inter_module_register(im_name, THIS_MODULE, &map_rom_probe);
+ return 0;
+}
+
+static void __exit map_rom_exit(void)
+{
+ inter_module_unregister(im_name);
+}
+
+module_init(map_rom_init);
+module_exit(map_rom_exit);
diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c
index b728ddcd2..0a33ed1cf 100644
--- a/drivers/mtd/mtdblock.c
+++ b/drivers/mtd/mtdblock.c
@@ -1,16 +1,15 @@
/*
* Direct MTD block device access
*
- * $Id: mtdblock.c,v 1.17 2000/07/13 14:25:54 dwmw2 Exp $
+ * $Id: mtdblock.c,v 1.38 2000/11/27 08:50:22 dwmw2 Exp $
+ *
+ * 02-nov-2000 Nicolas Pitre Added read-modify-write with cache
*/
-#ifdef MTDBLOCK_DEBUG
-#define DEBUGLVL debug
-#endif
-
#include <linux/types.h>
#include <linux/module.h>
-
+#include <linux/kernel.h>
+#include <linux/malloc.h>
#include <linux/mtd/mtd.h>
#define MAJOR_NR MTD_BLOCK_MAJOR
@@ -21,62 +20,332 @@
#define DEVICE_OFF(device)
#define DEVICE_NO_RANDOM
#include <linux/blk.h>
-
+/* for old kernels... */
+#ifndef QUEUE_EMPTY
+#define QUEUE_EMPTY (!CURRENT)
+#endif
#if LINUX_VERSION_CODE < 0x20300
-#define RQFUNC_ARG void
-#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0)
+#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].plug_tq.sync)
#else
-#define RQFUNC_ARG request_queue_t *q
+#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].request_queue.plugged)
#endif
-#ifdef MTDBLOCK_DEBUG
-static int debug = MTDBLOCK_DEBUG;
-MODULE_PARM(debug, "i");
+#ifdef CONFIG_DEVFS_FS
+#include <linux/devfs_fs_kernel.h>
+static void mtd_notify_add(struct mtd_info* mtd);
+static void mtd_notify_remove(struct mtd_info* mtd);
+static struct mtd_notifier notifier = {
+ mtd_notify_add,
+ mtd_notify_remove,
+ NULL
+};
+static devfs_handle_t devfs_dir_handle = NULL;
+static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES];
#endif
-#if 1
-static void mtdblock_end_request(struct request *req, int res)
+static struct mtdblk_dev {
+ struct mtd_info *mtd; /* Locked */
+ int count;
+ struct semaphore cache_sem;
+ unsigned char *cache_data;
+ unsigned long cache_offset;
+ unsigned int cache_size;
+ enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;
+} *mtdblks[MAX_MTD_DEVICES];
+
+static spinlock_t mtdblks_lock;
+
+static int mtd_sizes[MAX_MTD_DEVICES];
+static int mtd_blksizes[MAX_MTD_DEVICES];
+
+
+/*
+ * Cache stuff...
+ *
+ * Since typical flash erasable sectors are much larger than what Linux's
+ * buffer cache can handle, we must implement read-modify-write on flash
+ * sectors for each block write requests. To avoid over-erasing flash sectors
+ * and to speed things up, we locally cache a whole flash sector while it is
+ * being written to until a different sector is required.
+ */
+
+static void erase_callback(struct erase_info *done)
{
- if (end_that_request_first( req, res, "mtdblock" ))
- return;
- end_that_request_last( req );
+ wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
+ wake_up(wait_q);
}
-#endif
-static int mtd_sizes[MAX_MTD_DEVICES];
+static int erase_write (struct mtd_info *mtd, unsigned long pos,
+ int len, const char *buf)
+{
+ struct erase_info erase;
+ DECLARE_WAITQUEUE(wait, current);
+ wait_queue_head_t wait_q;
+ size_t retlen;
+ int ret;
+
+ /*
+ * First, let's erase the flash block.
+ */
+
+ init_waitqueue_head(&wait_q);
+ erase.mtd = mtd;
+ erase.callback = erase_callback;
+ erase.addr = pos;
+ erase.len = len;
+ erase.priv = (u_long)&wait_q;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&wait_q, &wait);
+
+ ret = MTD_ERASE(mtd, &erase);
+ if (ret) {
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&wait_q, &wait);
+ printk (KERN_WARNING "mtdblock: erase of region [0x%lx, 0x%x] "
+ "on \"%s\" failed\n",
+ pos, len, mtd->name);
+ return ret;
+ }
+ schedule(); /* Wait for erase to finish. */
+ remove_wait_queue(&wait_q, &wait);
-/* Keeping a separate list rather than just getting stuff directly out of
- the MTD core's mtd_table is perhaps not very nice, but I happen
- to dislike the idea of directly accessing mtd_table even more.
- dwmw2 31/3/0
-*/
+ /*
+ * Next, writhe data to flash.
+ */
-static int mtdblock_open(struct inode *inode, struct file *file)
+ ret = MTD_WRITE (mtd, pos, len, &retlen, buf);
+ if (ret)
+ return ret;
+ if (retlen != len)
+ return -EIO;
+ return 0;
+}
+
+
+static int write_cached_data (struct mtdblk_dev *mtdblk)
+{
+ struct mtd_info *mtd = mtdblk->mtd;
+ int ret;
+
+ if (mtdblk->cache_state != STATE_DIRTY)
+ return 0;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: writing cached data for \"%s\" "
+ "at 0x%lx, size 0x%x\n", mtd->name,
+ mtdblk->cache_offset, mtdblk->cache_size);
+
+ ret = erase_write (mtd, mtdblk->cache_offset,
+ mtdblk->cache_size, mtdblk->cache_data);
+ if (ret)
+ return ret;
+
+ /*
+ * Here we could argably set the cache state to STATE_CLEAN.
+ * However this could lead to inconsistency since we will not
+ * be notified if this content is altered on the flash by other
+ * means. Let's declare it empty and leave buffering tasks to
+ * the buffer cache instead.
+ */
+ mtdblk->cache_state = STATE_EMPTY;
+ return 0;
+}
+
+
+static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos,
+ int len, const char *buf)
{
- struct mtd_info *mtd = NULL;
+ struct mtd_info *mtd = mtdblk->mtd;
+ unsigned int sect_size = mtd->erasesize;
+ size_t retlen;
+ int ret;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: write on \"%s\" at 0x%lx, size 0x%x\n",
+ mtd->name, pos, len);
+
+ while (len > 0) {
+ unsigned long sect_start = (pos/sect_size)*sect_size;
+ unsigned int offset = pos - sect_start;
+ unsigned int size = sect_size - offset;
+ if( size > len )
+ size = len;
+
+ if (size == sect_size) {
+ /*
+ * We are covering a whole sector. Thus there is no
+ * need to bother with the cache while it may still be
+ * useful for other partial writes.
+ */
+ ret = erase_write (mtd, pos, size, buf);
+ if (ret)
+ return ret;
+ } else {
+ /* Partial sector: need to use the cache */
+
+ if (mtdblk->cache_state == STATE_DIRTY &&
+ mtdblk->cache_offset != sect_start) {
+ ret = write_cached_data(mtdblk);
+ if (ret)
+ return ret;
+ }
+
+ if (mtdblk->cache_state == STATE_EMPTY ||
+ mtdblk->cache_offset != sect_start) {
+ /* fill the cache with the current sector */
+ mtdblk->cache_state = STATE_EMPTY;
+ ret = MTD_READ(mtd, sect_start, sect_size, &retlen, mtdblk->cache_data);
+ if (ret)
+ return ret;
+ if (retlen != sect_size)
+ return -EIO;
+
+ mtdblk->cache_offset = sect_start;
+ mtdblk->cache_size = sect_size;
+ mtdblk->cache_state = STATE_CLEAN;
+ }
+
+ /* write data to our local cache */
+ memcpy (mtdblk->cache_data + offset, buf, size);
+ mtdblk->cache_state = STATE_DIRTY;
+ }
+
+ buf += size;
+ pos += size;
+ len -= size;
+ }
+
+ return 0;
+}
+
+
+static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos,
+ int len, char *buf)
+{
+ struct mtd_info *mtd = mtdblk->mtd;
+ unsigned int sect_size = mtd->erasesize;
+ size_t retlen;
+ int ret;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: read on \"%s\" at 0x%lx, size 0x%x\n",
+ mtd->name, pos, len);
+
+ while (len > 0) {
+ unsigned long sect_start = (pos/sect_size)*sect_size;
+ unsigned int offset = pos - sect_start;
+ unsigned int size = sect_size - offset;
+ if (size > len)
+ size = len;
+
+ /*
+ * Check if the requested data is already cached
+ * Read the requested amount of data from our internal cache if it
+ * contains what we want, otherwise we read the data directly
+ * from flash.
+ */
+ if (mtdblk->cache_state != STATE_EMPTY &&
+ mtdblk->cache_offset == sect_start) {
+ memcpy (buf, mtdblk->cache_data + offset, size);
+ } else {
+ ret = MTD_READ (mtd, pos, size, &retlen, buf);
+ if (ret)
+ return ret;
+ if (retlen != size)
+ return -EIO;
+ }
+
+ buf += size;
+ pos += size;
+ len -= size;
+ }
+
+ return 0;
+}
+
+
+static int mtdblock_open(struct inode *inode, struct file *file)
+{
+ struct mtdblk_dev *mtdblk;
int dev;
- DEBUG(1,"mtdblock_open\n");
+ DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n");
- if (inode == 0)
+ if (!inode)
return -EINVAL;
dev = MINOR(inode->i_rdev);
+ if (dev >= MAX_MTD_DEVICES)
+ return -EINVAL;
MOD_INC_USE_COUNT;
- mtd = get_mtd_device(NULL, dev);
+ spin_lock(&mtdblks_lock);
+
+ /* If it's already open, no need to piss about. */
+ if (mtdblks[dev]) {
+ mtdblks[dev]->count++;
+ spin_unlock(&mtdblks_lock);
+ return 0;
+ }
+
+ /* OK, it's not open. Try to find it */
+
+ /* First we have to drop the lock, because we have to
+ to things which might sleep.
+ */
+ spin_unlock(&mtdblks_lock);
+
+ mtdblk = kmalloc(sizeof(struct mtdblk_dev), GFP_KERNEL);
+ if (!mtdblk) {
+ MOD_DEC_USE_COUNT;
+ return -ENOMEM;
+ }
+ memset(mtdblk, 0, sizeof(*mtdblk));
+ mtdblk->count = 1;
+ mtdblk->mtd = get_mtd_device(NULL, dev);
- if (!mtd) {
+ if (!mtdblk->mtd) {
+ kfree(mtdblk);
MOD_DEC_USE_COUNT;
return -ENODEV;
}
- mtd_sizes[dev] = mtd->size>>9;
+ init_MUTEX (&mtdblk->cache_sem);
+ mtdblk->cache_state = STATE_EMPTY;
+ mtdblk->cache_size = mtdblk->mtd->erasesize;
+ mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize);
+ if (!mtdblk->cache_data) {
+ put_mtd_device(mtdblk->mtd);
+ kfree(mtdblk);
+ MOD_DEC_USE_COUNT;
+ return -ENOMEM;
+ }
+
+ /* OK, we've created a new one. Add it to the list. */
+
+ spin_lock(&mtdblks_lock);
- DEBUG(1, "ok\n");
+ if (mtdblks[dev]) {
+ /* Another CPU made one at the same time as us. */
+ mtdblks[dev]->count++;
+ spin_unlock(&mtdblks_lock);
+ put_mtd_device(mtdblk->mtd);
+ vfree(mtdblk->cache_data);
+ kfree(mtdblk);
+ return 0;
+ }
+
+ mtdblks[dev] = mtdblk;
+ mtd_sizes[dev] = mtdblk->mtd->size/1024;
+ mtd_blksizes[dev] = mtdblk->mtd->erasesize;
+ if (mtd_blksizes[dev] > PAGE_SIZE)
+ mtd_blksizes[dev] = PAGE_SIZE;
+ set_device_ro (inode->i_rdev, !(mtdblk->mtd->flags & MTD_WRITEABLE));
+
+ spin_unlock(&mtdblks_lock);
+
+ DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
return 0;
}
@@ -84,11 +353,11 @@ static int mtdblock_open(struct inode *inode, struct file *file)
static release_t mtdblock_release(struct inode *inode, struct file *file)
{
int dev;
- struct mtd_info *mtd;
+ struct mtdblk_dev *mtdblk;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
struct super_block * sb = get_super(inode->i_rdev);
#endif
- DEBUG(1, "mtdblock_release\n");
+ DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n");
if (inode == NULL)
release_return(-ENODEV);
@@ -100,150 +369,189 @@ static release_t mtdblock_release(struct inode *inode, struct file *file)
invalidate_buffers(inode->i_rdev);
dev = MINOR(inode->i_rdev);
- mtd = __get_mtd_device(NULL, dev);
-
- if (!mtd) {
- printk(KERN_WARNING "MTD device is absent on mtd_release!\n");
- MOD_DEC_USE_COUNT;
- release_return(-ENODEV);
-
+ mtdblk = mtdblks[dev];
+
+ down(&mtdblk->cache_sem);
+ write_cached_data(mtdblk);
+ up(&mtdblk->cache_sem);
+
+ spin_lock(&mtdblks_lock);
+ if (!--mtdblk->count) {
+ /* It was the last usage. Free the device */
+ mtdblks[dev] = NULL;
+ spin_unlock(&mtdblks_lock);
+ if (mtdblk->mtd->sync)
+ mtdblk->mtd->sync(mtdblk->mtd);
+ put_mtd_device(mtdblk->mtd);
+ vfree(mtdblk->cache_data);
+ kfree(mtdblk);
+ } else {
+ spin_unlock(&mtdblks_lock);
}
-
- if (mtd->sync)
- mtd->sync(mtd);
-
- put_mtd_device(mtd);
- DEBUG(1, "ok\n");
+ DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
MOD_DEC_USE_COUNT;
release_return(0);
}
-static void mtdblock_request(RQFUNC_ARG)
+/*
+ * This is a special request_fn because it is executed in a process context
+ * to be able to sleep independently of the caller. The io_request_lock
+ * is held upon entry and exit.
+ * The head of our request queue is considered active so there is no need
+ * to dequeue requests before we are done.
+ */
+static void handle_mtdblock_request(void)
{
- struct request *current_request;
- unsigned int res = 0;
- struct mtd_info *mtd;
-
- while (1)
- {
- /* Grab the Request and unlink it from the request list, INIT_REQUEST
- will execute a return if we are done. */
- INIT_REQUEST;
- current_request = CURRENT;
-
- if (MINOR(current_request->rq_dev) >= MAX_MTD_DEVICES)
- {
- printk("mtd: Unsupported device!\n");
- end_request(0);
- continue;
- }
-
- // Grab our MTD structure
-
- mtd = __get_mtd_device(NULL, MINOR(current_request->rq_dev));
- if (!mtd) {
- printk("MTD device %d doesn't appear to exist any more\n", CURRENT_DEV);
- end_request(0);
- }
-
- if (current_request->sector << 9 > mtd->size ||
- (current_request->sector + current_request->nr_sectors) << 9 > mtd->size)
- {
- printk("mtd: Attempt to read past end of device!\n");
- printk("size: %lx, sector: %lx, nr_sectors %lx\n", mtd->size, current_request->sector, current_request->nr_sectors);
- end_request(0);
- continue;
- }
-
- /* Remove the request we are handling from the request list so nobody messes
- with it */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
- blkdev_dequeue_request(current_request);
-
- /* Now drop the lock that the ll_rw_blk functions grabbed for us
- and process the request. This is necessary due to the extreme time
- we spend processing it. */
- spin_unlock_irq(&io_request_lock);
-#endif
+ struct request *req;
+ struct mtdblk_dev *mtdblk;
+ unsigned int res;
+
+ for (;;) {
+ INIT_REQUEST;
+ req = CURRENT;
+ spin_unlock_irq(&io_request_lock);
+ mtdblk = mtdblks[MINOR(req->rq_dev)];
+ res = 0;
+
+ if (MINOR(req->rq_dev) >= MAX_MTD_DEVICES)
+ panic(__FUNCTION__": minor out of bound");
+
+ if ((req->sector + req->current_nr_sectors) > (mtdblk->mtd->size >> 9))
+ goto end_req;
+
+ // Handle the request
+ switch (req->cmd)
+ {
+ int err;
+
+ case READ:
+ down(&mtdblk->cache_sem);
+ err = do_cached_read (mtdblk, req->sector << 9,
+ req->current_nr_sectors << 9,
+ req->buffer);
+ up(&mtdblk->cache_sem);
+ if (!err)
+ res = 1;
+ break;
+
+ case WRITE:
+ // Read only device
+ if ( !(mtdblk->mtd->flags & MTD_WRITEABLE) )
+ break;
+
+ // Do the write
+ down(&mtdblk->cache_sem);
+ err = do_cached_write (mtdblk, req->sector << 9,
+ req->current_nr_sectors << 9,
+ req->buffer);
+ up(&mtdblk->cache_sem);
+ if (!err)
+ res = 1;
+ break;
+ }
+
+end_req:
+ spin_lock_irq(&io_request_lock);
+ end_request(res);
+ }
+}
- // Handle the request
- switch (current_request->cmd)
- {
- size_t retlen;
-
- case READ:
- if (mtd->read(mtd,current_request->sector<<9,
- current_request->nr_sectors << 9,
- &retlen, current_request->buffer) == 0)
- res = 1;
- else
- res = 0;
- break;
-
- case WRITE:
-//printk("mtdblock_request WRITE sector=%d(%d)\n",current_request->sector,
-// current_request->nr_sectors);
-
- // Read only device
- if ((mtd->flags & MTD_CAP_RAM) == 0)
- {
- res = 0;
- break;
- }
-
- // Do the write
- if (mtd->write(mtd,current_request->sector<<9,
- current_request->nr_sectors << 9,
- &retlen, current_request->buffer) == 0)
- res = 1;
- else
- res = 0;
- break;
-
- // Shouldn't happen
- default:
- printk("mtd: unknown request\n");
- break;
- }
-
- // Grab the lock and re-thread the item onto the linked list
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
- spin_lock_irq(&io_request_lock);
- mtdblock_end_request(current_request, res);
+static volatile int leaving = 0;
+#if LINUX_VERSION_CODE > 0x020300
+static DECLARE_MUTEX_LOCKED(thread_sem);
+static DECLARE_WAIT_QUEUE_HEAD(thr_wq);
#else
- end_request(res);
+static struct semaphore thread_sem = MUTEX_LOCKED;
+DECLARE_WAIT_QUEUE_HEAD(thr_wq);
#endif
- }
+
+int mtdblock_thread(void *dummy)
+{
+ struct task_struct *tsk = current;
+ DECLARE_WAITQUEUE(wait, tsk);
+
+ tsk->session = 1;
+ tsk->pgrp = 1;
+ /* we might get involved when memory gets low, so use PF_MEMALLOC */
+ tsk->flags |= PF_MEMALLOC;
+ strcpy(tsk->comm, "mtdblockd");
+ tsk->tty = NULL;
+ spin_lock_irq(&tsk->sigmask_lock);
+ sigfillset(&tsk->blocked);
+ recalc_sigpending(tsk);
+ spin_unlock_irq(&tsk->sigmask_lock);
+ exit_mm(tsk);
+ exit_files(tsk);
+ exit_sighand(tsk);
+ exit_fs(tsk);
+
+ while (!leaving) {
+ add_wait_queue(&thr_wq, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_lock_irq(&io_request_lock);
+ if (QUEUE_EMPTY || QUEUE_PLUGGED) {
+ spin_unlock_irq(&io_request_lock);
+ schedule();
+ remove_wait_queue(&thr_wq, &wait);
+ } else {
+ remove_wait_queue(&thr_wq, &wait);
+ set_current_state(TASK_RUNNING);
+ handle_mtdblock_request();
+ spin_unlock_irq(&io_request_lock);
+ }
+ }
+
+ up(&thread_sem);
+ return 0;
}
+#if LINUX_VERSION_CODE < 0x20300
+#define RQFUNC_ARG void
+#else
+#define RQFUNC_ARG request_queue_t *q
+#endif
+
+static void mtdblock_request(RQFUNC_ARG)
+{
+ /* Don't do anything, except wake the thread if necessary */
+ wake_up(&thr_wq);
+}
static int mtdblock_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
- struct mtd_info *mtd;
+ struct mtdblk_dev *mtdblk;
- mtd = __get_mtd_device(NULL, MINOR(inode->i_rdev));
+ mtdblk = mtdblks[MINOR(inode->i_rdev)];
- if (!mtd) return -EINVAL;
+#ifdef PARANOIA
+ if (!mtdblk)
+ BUG();
+#endif
switch (cmd) {
case BLKGETSIZE: /* Return device size */
- if (!arg) return -EFAULT;
- return put_user((mtd->size >> 9),
- (long *) arg);
+ if (!arg)
+ return -EFAULT;
+ return put_user((mtdblk->mtd->size >> 9),
+ (long *) arg)?-EFAULT:0;
case BLKFLSBUF:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
- if(!capable(CAP_SYS_ADMIN)) return -EACCES;
+ if(!capable(CAP_SYS_ADMIN))
+ return -EACCES;
#endif
fsync_dev(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
- if (mtd->sync)
- mtd->sync(mtd);
+ down(&mtdblk->cache_sem);
+ write_cached_data(mtdblk);
+ up(&mtdblk->cache_sem);
+ if (mtdblk->mtd->sync)
+ mtdblk->mtd->sync(mtdblk->mtd);
return 0;
default:
@@ -251,7 +559,6 @@ static int mtdblock_ioctl(struct inode * inode, struct file * file,
}
}
- /*}}}*/
#if LINUX_VERSION_CODE < 0x20326
static struct file_operations mtd_fops =
{
@@ -270,32 +577,69 @@ static struct block_device_operations mtd_fops =
};
#endif
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+#ifdef CONFIG_DEVFS_FS
+/* Notification that a new device has been added. Create the devfs entry for
+ * it. */
+
+static void mtd_notify_add(struct mtd_info* mtd)
+{
+ char name[8];
+
+ if (!mtd)
+ return;
+
+ sprintf(name, "%d", mtd->index);
+ devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
+ DEVFS_FL_DEFAULT, MTD_BLOCK_MAJOR, mtd->index,
+ S_IFBLK | S_IRUGO | S_IWUGO,
+ &mtd_fops, NULL);
+}
+
+static void mtd_notify_remove(struct mtd_info* mtd)
+{
+ if (!mtd)
+ return;
+
+ devfs_unregister(devfs_rw_handle[mtd->index]);
+}
+#endif
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_mtdblock init_module
#define cleanup_mtdblock cleanup_module
#endif
-#define __exit
-#endif
-
int __init init_mtdblock(void)
{
int i;
+ spin_lock_init(&mtdblks_lock);
+#ifdef CONFIG_DEVFS_FS
+ if (devfs_register_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME, &mtd_fops))
+ {
+ printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
+ MTD_BLOCK_MAJOR);
+ return -EAGAIN;
+ }
+
+ devfs_dir_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL);
+ register_mtd_user(&notifier);
+#else
if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) {
printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
MTD_BLOCK_MAJOR);
return -EAGAIN;
}
+#endif
/* We fill it in at open() time. */
for (i=0; i< MAX_MTD_DEVICES; i++) {
mtd_sizes[i] = 0;
+ mtd_blksizes[i] = BLOCK_SIZE;
}
-
+ init_waitqueue_head(&thr_wq);
/* Allow the block size to default to BLOCK_SIZE. */
- blksize_size[MAJOR_NR] = NULL;
+ blksize_size[MAJOR_NR] = mtd_blksizes;
blk_size[MAJOR_NR] = mtd_sizes;
#if LINUX_VERSION_CODE < 0x20320
@@ -303,15 +647,30 @@ int __init init_mtdblock(void)
#else
blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request);
#endif
+ kernel_thread (mtdblock_thread, NULL, CLONE_FS|CLONE_FILES|CLONE_SIGHAND);
return 0;
}
static void __exit cleanup_mtdblock(void)
{
+ leaving = 1;
+ wake_up(&thr_wq);
+ down(&thread_sem);
+#ifdef CONFIG_DEVFS_FS
+ unregister_mtd_user(&notifier);
+ devfs_unregister(devfs_dir_handle);
+ devfs_unregister_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME);
+#else
unregister_blkdev(MAJOR_NR,DEVICE_NAME);
+#endif
+#if LINUX_VERSION_CODE < 0x20320
+ blk_dev[MAJOR_NR].request_fn = NULL;
+#else
+ blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+#endif
+ blksize_size[MAJOR_NR] = NULL;
+ blk_size[MAJOR_NR] = NULL;
}
-#if LINUX_VERSION_CODE > 0x20300
module_init(init_mtdblock);
module_exit(cleanup_mtdblock);
-#endif
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 0bb15a8fc..bccb687c0 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -1,5 +1,6 @@
/*
- * $Id: mtdchar.c,v 1.7 2000/06/30 15:54:19 dwmw2 Exp $
+ * Almost: $Id: mtdchar.c,v 1.21 2000/12/09 21:15:12 dwmw2 Exp $
+ * (With some of the compatibility for previous kernels taken out)
*
* Character-device access to raw MTD devices.
*
@@ -13,6 +14,20 @@
#include <linux/mtd/mtd.h>
#include <linux/malloc.h>
+#ifdef CONFIG_DEVFS_FS
+#include <linux/devfs_fs_kernel.h>
+static void mtd_notify_add(struct mtd_info* mtd);
+static void mtd_notify_remove(struct mtd_info* mtd);
+static struct mtd_notifier notifier = {
+ mtd_notify_add,
+ mtd_notify_remove,
+ NULL
+};
+static devfs_handle_t devfs_dir_handle = NULL;
+static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES];
+static devfs_handle_t devfs_ro_handle[MAX_MTD_DEVICES];
+#endif
+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
#else
@@ -22,11 +37,11 @@ static int mtd_lseek (struct inode *inode, struct file *file, off_t offset, int
struct mtd_info *mtd=(struct mtd_info *)file->private_data;
switch (orig) {
- case 0:
+ case 0:
/* SEEK_SET */
file->f_pos = offset;
break;
- case 1:
+ case 1:
/* SEEK_CUR */
file->f_pos += offset;
break;
@@ -34,11 +49,11 @@ static int mtd_lseek (struct inode *inode, struct file *file, off_t offset, int
/* SEEK_END */
file->f_pos =mtd->size + offset;
break;
- default:
+ default:
return -EINVAL;
}
- if (file->f_pos < 0)
+ if (file->f_pos < 0)
file->f_pos = 0;
else if (file->f_pos >= mtd->size)
file->f_pos = mtd->size - 1;
@@ -54,7 +69,7 @@ static int mtd_open(struct inode *inode, struct file *file)
int devnum = minor >> 1;
struct mtd_info *mtd;
- DEBUG(0, "MTD_open\n");
+ DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n");
if (devnum >= MAX_MTD_DEVICES)
return -ENODEV;
@@ -86,7 +101,7 @@ static release_t mtd_close(struct inode *inode,
{
struct mtd_info *mtd;
- DEBUG(0, "MTD_close\n");
+ DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
mtd = (struct mtd_info *)file->private_data;
@@ -115,7 +130,7 @@ static int mtd_read(struct inode *inode,struct file *file, char *buf, int count)
int ret=0;
char *kbuf;
- DEBUG(0,"MTD_read\n");
+ DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n");
if (FILE_POS + count > mtd->size)
count = mtd->size - FILE_POS;
@@ -124,7 +139,7 @@ static int mtd_read(struct inode *inode,struct file *file, char *buf, int count)
return 0;
/* FIXME: Use kiovec in 2.3 or 2.2+rawio, or at
- * least split the IO into smaller chunks.
+ * least split the IO into smaller chunks.
*/
kbuf = vmalloc(count);
@@ -157,7 +172,7 @@ static read_write_t mtd_write(struct inode *inode,struct file *file, const char
size_t retlen;
int ret=0;
- DEBUG(0,"MTD_write\n");
+ DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n");
if (FILE_POS == mtd->size)
return -ENOSPC;
@@ -208,7 +223,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
int ret = 0;
u_long size;
- DEBUG(0, "MTD_ioctl\n");
+ DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n");
size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
if (cmd & IOC_IN) {
@@ -222,8 +237,9 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
switch (cmd) {
case MEMGETINFO:
- copy_to_user((struct mtd_info *)arg, mtd,
- sizeof(struct mtd_info_user));
+ if (copy_to_user((struct mtd_info *)arg, mtd,
+ sizeof(struct mtd_info_user)))
+ return -EFAULT;
break;
case MEMERASE:
@@ -238,28 +254,23 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
init_waitqueue_head(&waitq);
memset (erase,0,sizeof(struct erase_info));
- copy_from_user(&erase->addr, (u_long *)arg,
- 2 * sizeof(u_long));
+ if (copy_from_user(&erase->addr, (u_long *)arg,
+ 2 * sizeof(u_long))) {
+ kfree(erase);
+ return -EFAULT;
+ }
erase->mtd = mtd;
erase->callback = mtd_erase_callback;
erase->priv = (unsigned long)&waitq;
- /* FIXME: Allow INTERRUPTIBLE. Which means
- not having the wait_queue head on the stack
-
- Does it? Why? Who wrote this? Was it my alter
- ago - the intelligent one? Or was it the stupid
- one, and now I'm being clever I don't know what
- it was on about?
-
- dwmw2.
-
- It was the intelligent one. If the wq_head is
- on the stack, and we leave because we got
- interrupted, then the wq_head is no longer
- there when the callback routine tries to
- wake us up --> BOOM!.
-
+ /*
+ FIXME: Allow INTERRUPTIBLE. Which means
+ not having the wait_queue head on the stack.
+
+ If the wq_head is on the stack, and we
+ leave because we got interrupted, then the
+ wq_head is no longer there when the
+ callback routine tries to wake us up.
*/
current->state = TASK_UNINTERRUPTIBLE;
add_wait_queue(&waitq, &wait);
@@ -281,7 +292,8 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
void *databuf;
ssize_t retlen;
- copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf));
+ if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
+ return -EFAULT;
if (buf.length > 0x4096)
return -EINVAL;
@@ -298,11 +310,13 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
if (!databuf)
return -ENOMEM;
- copy_from_user(databuf, buf.ptr, buf.length);
+ if (copy_from_user(databuf, buf.ptr, buf.length))
+ return -EFAULT;
ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf);
- copy_to_user((void *)arg + sizeof(loff_t), &retlen, sizeof(ssize_t));
+ if (copy_to_user((void *)arg + sizeof(loff_t), &retlen, sizeof(ssize_t)))
+ ret = -EFAULT;
kfree(databuf);
break;
@@ -315,7 +329,8 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
void *databuf;
ssize_t retlen;
- copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf));
+ if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
+ return -EFAULT;
if (buf.length > 0x4096)
return -EINVAL;
@@ -334,19 +349,42 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf);
- copy_to_user((void *)arg + sizeof(loff_t), &retlen, sizeof(ssize_t));
-
- if (retlen)
- copy_to_user(buf.ptr, databuf, retlen);
-
+ if (copy_to_user((void *)arg + sizeof(loff_t), &retlen, sizeof(ssize_t)))
+ ret = -EFAULT;
+ else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
+ ret = -EFAULT;
+
kfree(databuf);
break;
}
-
-
-
-
+ case MEMLOCK:
+ {
+ unsigned long adrs[2];
+
+ if (copy_from_user(adrs ,(void *)arg, 2* sizeof(unsigned long)))
+ return -EFAULT;
+
+ if (!mtd->lock)
+ ret = -EOPNOTSUPP;
+ else
+ ret = mtd->lock(mtd, adrs[0], adrs[1]);
+ }
+
+ case MEMUNLOCK:
+ {
+ unsigned long adrs[2];
+
+ if (copy_from_user(adrs, (void *)arg, 2* sizeof(unsigned long)))
+ return -EFAULT;
+
+ if (!mtd->unlock)
+ ret = -EOPNOTSUPP;
+ else
+ ret = mtd->unlock(mtd, adrs[0], adrs[1]);
+ }
+
+
default:
printk("Invalid ioctl %x (MEMGETINFO = %x)\n",cmd, MEMGETINFO);
ret = -EINVAL;
@@ -366,31 +404,84 @@ static struct file_operations mtd_fops = {
};
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+#ifdef CONFIG_DEVFS_FS
+/* Notification that a new device has been added. Create the devfs entry for
+ * it. */
+
+static void mtd_notify_add(struct mtd_info* mtd)
+{
+ char name[8];
+
+ if (!mtd)
+ return;
+
+ sprintf(name, "%d", mtd->index);
+ devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
+ DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2,
+ S_IFCHR | S_IRUGO | S_IWUGO,
+ &mtd_fops, NULL);
+
+ sprintf(name, "%dro", mtd->index);
+ devfs_ro_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
+ DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2+1,
+ S_IFCHR | S_IRUGO | S_IWUGO,
+ &mtd_fops, NULL);
+}
+
+static void mtd_notify_remove(struct mtd_info* mtd)
+{
+ if (!mtd)
+ return;
+
+ devfs_unregister(devfs_rw_handle[mtd->index]);
+ devfs_unregister(devfs_ro_handle[mtd->index]);
+}
+#endif
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_mtdchar init_module
#define cleanup_mtdchar cleanup_module
#endif
-#endif
mod_init_t init_mtdchar(void)
{
-
- if (register_chrdev(MTD_CHAR_MAJOR,"mtd",&mtd_fops)) {
+#ifdef CONFIG_DEVFS_FS
+ int i;
+ char name[8];
+ struct mtd_info* mtd;
+
+ if (devfs_register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
+ {
+ printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
+ MTD_CHAR_MAJOR);
+ return -EAGAIN;
+ }
+
+ devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL);
+
+ register_mtd_user(&notifier);
+#else
+ if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
+ {
printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
MTD_CHAR_MAJOR);
return -EAGAIN;
}
+#endif
return 0;
}
mod_exit_t cleanup_mtdchar(void)
{
- unregister_chrdev(MTD_CHAR_MAJOR,"mtd");
+#ifdef CONFIG_DEVFS_FS
+ unregister_mtd_user(&notifier);
+ devfs_unregister(devfs_dir_handle);
+ devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
+#else
+ unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
+#endif
}
-#if LINUX_VERSION_CODE > 0x20300
module_init(init_mtdchar);
module_exit(cleanup_mtdchar);
-#endif
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index b3f0157eb..8c30838ba 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -1,15 +1,11 @@
/*
- * $Id: mtdcore.c,v 1.13 2000/07/13 14:27:37 dwmw2 Exp $
+ * $Id: mtdcore.c,v 1.27 2000/12/10 01:10:09 dwmw2 Exp $
*
- * Core registration and callback routines for MTD
+ * Core registration and callback routines for MTD
* drivers and users.
*
*/
-#ifdef MTD_DEBUG
-#define DEBUGLVL debug
-#endif
-
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
@@ -29,67 +25,12 @@
#include <linux/mtd/mtd.h>
-#ifdef MTD_DEBUG
-static int debug = MTD_DEBUG;
-MODULE_PARM(debug, "i");
-#endif
-
-/* Init code required for 2.2 kernels */
-
-#if LINUX_VERSION_CODE < 0x20300
-
-#ifdef CONFIG_MTD_DOC1000
-extern int init_doc1000(void);
-#endif
-#ifdef CONFIG_MTD_DOCPROBE
-extern int init_doc(void);
-#endif
-#ifdef CONFIG_MTD_PHYSMAP
-extern int init_physmap(void);
-#endif
-#ifdef CONFIG_MTD_RPXLITE
-extern int init_rpxlite(void);
-#endif
-#ifdef CONFIG_MTD_OCTAGON
-extern int init_octagon5066(void);
-#endif
-#ifdef CONFIG_MTD_PNC2000
-extern int init_pnc2000(void);
-#endif
-#ifdef CONFIG_MTD_VMAX
-extern int init_vmax301(void);
-#endif
-#ifdef CONFIG_MTD_MIXMEM
-extern int init_mixmem(void);
-#endif
-#ifdef CONFIG_MTD_PMC551
-extern int init_pmc551(void);
-#endif
-#ifdef CONFIG_MTD_NORA
-extern int init_nora(void);
-#endif
-#ifdef CONFIG_FTL
-extern int init_ftl(void);
-#endif
-#ifdef CONFIG_NFTL
-extern int init_nftl(void);
-#endif
-#ifdef CONFIG_MTD_BLOCK
-extern int init_mtdblock(void);
-#endif
-#ifdef CONFIG_MTD_CHAR
-extern int init_mtdchar(void);
-#endif
-
-#endif /* LINUX_VERSION_CODE < 0x20300 */
-
-
static DECLARE_MUTEX(mtd_table_mutex);
static struct mtd_info *mtd_table[MAX_MTD_DEVICES];
static struct mtd_notifier *mtd_notifiers = NULL;
/**
- * add_mtd_device - register an MTD device
+ * add_mtd_device - register an MTD device
* @mtd: pointer to new MTD device info structure
*
* Add a device to the list of MTD devices present in the system, and
@@ -110,6 +51,7 @@ int add_mtd_device(struct mtd_info *mtd)
struct mtd_notifier *not=mtd_notifiers;
mtd_table[i] = mtd;
+ mtd->index = i;
DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name);
while (not)
{
@@ -126,7 +68,7 @@ int add_mtd_device(struct mtd_info *mtd)
}
/**
- * del_mtd_device - unregister an MTD device
+ * del_mtd_device - unregister an MTD device
* @mtd: pointer to MTD device info structure
*
* Remove a device from the list of MTD devices present in the system,
@@ -194,7 +136,7 @@ void register_mtd_user (struct mtd_notifier *new)
* @new: pointer to notifier info structure
*
* Removes a callback function pair from the list of 'users' to be
- * notified upon addition or removal of MTD devices. Causes the
+ * notified upon addition or removal of MTD devices. Causes the
* 'remove' callback to be immediately invoked for each MTD device
* currently present in the system.
*/
@@ -232,7 +174,7 @@ int unregister_mtd_user (struct mtd_notifier *old)
* @mtd: last known address of the required MTD device
* @num: internal device number of the required MTD device
*
- * Given a number and NULL address, return the num'th entry in the device
+ * Given a number and NULL address, return the num'th entry in the device
* table, if any. Given an address and num == -1, search the device table
* for a device with that address and return if it's still present. Given
* both, return the num'th driver only if its address matches. Return NULL
@@ -313,10 +255,10 @@ static inline int mtd_proc_info (char *buf, int i)
{
struct mtd_info *this = mtd_table[i];
- if (!this)
+ if (!this)
return 0;
- return sprintf(buf, "mtd%d: %8.8lx \"%s\"\n", i, this->size,
+ return sprintf(buf, "mtd%d: %8.8lx \"%s\"\n", i, this->size,
this->name);
}
@@ -330,7 +272,7 @@ static int mtd_read_proc ( char *page, char **start, off_t off,int count
{
int len = 0, l, i;
off_t begin = 0;
-
+
down(&mtd_table_mutex);
for (i=0; i< MAX_MTD_DEVICES; i++) {
@@ -374,79 +316,13 @@ struct proc_dir_entry mtd_proc_entry = {
/*====================================================================*/
/* Init code */
-#if LINUX_VERSION_CODE < 0x20300
-
-static inline void init_others(void)
-{
- /* Shedloads of calls to init functions of all the
- * other drivers and users of MTD, which we can
- * ditch in 2.3 because of the sexy new way of
- * finding init routines.
- */
-#ifdef CONFIG_MTD_DOC1000
- init_doc1000();
-#endif
-#ifdef CONFIG_MTD_DOCPROBE
- init_doc(); /* This covers both the DiskOnChip 2000
- * and the DiskOnChip Millennium.
- * Theoretically all other DiskOnChip
- * devices too. */
-#endif
-#ifdef CONFIG_MTD_PHYSMAP
- init_physmap();
-#endif
-#ifdef CONFIG_MTD_RPXLITE
- init_rpxlite();
-#endif
-#ifdef CONFIG_MTD_OCTAGON
- init_octagon5066();
-#endif
-#ifdef CONFIG_MTD_PNC2000
- init_pnc2000();
-#endif
-#ifdef CONFIG_MTD_VMAX
- init_vmax301();
-#endif
-#ifdef CONFIGF_MTD_MIXMEM
- init_mixmem();
-#endif
-#ifdef CONFIG_MTD_PMC551
- init_pmc551();
-#endif
-#ifdef CONFIG_MTD_NORA
- init_nora();
-#endif
-#ifdef CONFIG_MTD_MTDRAM
- init_mtdram();
-#endif
-#ifdef CONFIG_FTL
- init_ftl();
-#endif
-#ifdef CONFIG_NFTL
- init_nftl();
-#endif
-#ifdef CONFIG_MTD_BLOCK
- init_mtdblock();
-#endif
-#ifdef CONFIG_MTD_CHAR
- init_mtdchar();
-#endif
-}
-
-#ifdef MODULE
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_mtd init_module
#define cleanup_mtd cleanup_module
#endif
-#endif /* LINUX_VERSION_CODE < 0x20300 */
-
mod_init_t init_mtd(void)
{
- int i;
- DEBUG(1, "INIT_MTD:\n");
- for (i=0; i<MAX_MTD_DEVICES; i++)
- mtd_table[i]=NULL;
-
#ifdef CONFIG_PROC_FS
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
if ((proc_mtd = create_proc_entry( "mtd", 0, 0 )))
@@ -454,12 +330,12 @@ mod_init_t init_mtd(void)
#else
proc_register_dynamic(&proc_root,&mtd_proc_entry);
#endif
-
#endif
-#if LINUX_VERSION_CODE < 0x20300
- init_others();
+#if LINUX_VERSION_CODE < 0x20212
+ init_mtd_devices();
#endif
+
#ifdef CONFIG_PM
mtd_pm_dev = pm_register(PM_UNKNOWN_DEV, 0, mtd_pm_callback);
#endif
@@ -468,13 +344,13 @@ mod_init_t init_mtd(void)
mod_exit_t cleanup_mtd(void)
{
- unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
#ifdef CONFIG_PM
if (mtd_pm_dev) {
pm_unregister(mtd_pm_dev);
mtd_pm_dev = NULL;
}
#endif
+
#ifdef CONFIG_PROC_FS
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
if (proc_mtd)
@@ -484,10 +360,8 @@ mod_exit_t cleanup_mtd(void)
#endif
#endif
}
-
-#if LINUX_VERSION_CODE > 0x20300
+
module_init(init_mtd);
module_exit(cleanup_mtd);
-#endif
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c
new file mode 100644
index 000000000..0135be39e
--- /dev/null
+++ b/drivers/mtd/mtdpart.c
@@ -0,0 +1,228 @@
+/*
+ * Simple MTD partitioning layer
+ *
+ * (C) 2000 Nicolas Pitre <nico@cam.org>
+ *
+ * This code is GPL
+ *
+ * $Id: mtdpart.c,v 1.7 2000/12/09 23:29:47 dwmw2 Exp $
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/list.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+
+/* Our partition linked list */
+static LIST_HEAD(mtd_partitions);
+
+/* Our partition node structure */
+struct mtd_part {
+ struct mtd_info mtd;
+ struct mtd_info *master;
+ loff_t offset;
+ int index;
+ struct list_head list;
+};
+
+/*
+ * Given a pointer to the MTD object in the mtd_part structure, we can retrieve
+ * the pointer to that structure with this macro.
+ */
+#define PART(x) ((struct mtd_part *)(x))
+
+
+/*
+ * MTD methods which simply translate the effective address and pass through
+ * to the _real_ device.
+ */
+
+static int part_read (struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct mtd_part *part = PART(mtd);
+ if (from >= mtd->size)
+ len = 0;
+ else if (from + len > mtd->size)
+ len = mtd->size - from;
+ return part->master->read (part->master, from + part->offset,
+ len, retlen, buf);
+}
+
+static int part_write (struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct mtd_part *part = PART(mtd);
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ if (to >= mtd->size)
+ len = 0;
+ else if (to + len > mtd->size)
+ len = mtd->size - to;
+ return part->master->write (part->master, to + part->offset,
+ len, retlen, buf);
+}
+
+static int part_writev (struct mtd_info *mtd, const struct iovec *vecs,
+ unsigned long count, loff_t to, size_t *retlen)
+{
+ struct mtd_part *part = PART(mtd);
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ return part->master->writev (part->master, vecs, count,
+ to + part->offset, retlen);
+}
+
+static int part_readv (struct mtd_info *mtd, struct iovec *vecs,
+ unsigned long count, loff_t from, size_t *retlen)
+{
+ struct mtd_part *part = PART(mtd);
+ return part->master->readv (part->master, vecs, count,
+ from + part->offset, retlen);
+}
+
+static int part_erase (struct mtd_info *mtd, struct erase_info *instr)
+{
+ struct mtd_part *part = PART(mtd);
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ if (instr->addr >= mtd->size)
+ return -EINVAL;
+ instr->addr += part->offset;
+ return part->master->erase(part->master, instr);
+}
+
+static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+ struct mtd_part *part = PART(mtd);
+ return part->master->lock(part->master, ofs + part->offset, len);
+}
+
+static int part_unlock (struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+ struct mtd_part *part = PART(mtd);
+ return part->master->unlock(part->master, ofs + part->offset, len);
+}
+
+
+/*
+ * This function unregisters and destroy all slave MTD objects which are
+ * attached to the given master MTD object.
+ */
+
+int del_mtd_partitions(struct mtd_info *master)
+{
+ struct list_head *node;
+ struct mtd_part *slave;
+
+ for (node = mtd_partitions.next;
+ node != &mtd_partitions;
+ node = node->next) {
+ slave = list_entry(node, struct mtd_part, list);
+ if (slave->master == master) {
+ struct list_head *prev = node->prev;
+ __list_del(prev, node->next);
+ del_mtd_device(&slave->mtd);
+ kfree(slave);
+ node = prev;
+ MOD_DEC_USE_COUNT;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * This function, given a master MTD object and a partition table, creates
+ * and registers slave MTD objects which are bound to the master according to
+ * the partition definitions.
+ * (Q: should we register the master MTD object as well?)
+ */
+
+int add_mtd_partitions(struct mtd_info *master,
+ struct mtd_partition *parts,
+ int nbparts)
+{
+ struct mtd_part *slave;
+ u_long cur_offset = 0;
+ int i;
+
+ for (i = 0; i < nbparts; i++) {
+ /* allocate the partition structure */
+ slave = kmalloc (sizeof(*slave), GFP_KERNEL);
+ if (!slave) {
+ printk ("memory allocation error while creating partitions for \"%s\"\n",
+ master->name);
+ del_mtd_partitions(master);
+ return -ENOMEM;
+ }
+ list_add(&slave->list, &mtd_partitions);
+
+ /* set up the MTD object for this partition */
+ slave->mtd = *master;
+ slave->mtd.name = parts[i].name;
+ slave->mtd.size = parts[i].size;
+ slave->mtd.flags &= ~parts[i].mask_flags;
+ slave->mtd.read = part_read;
+ slave->mtd.write = part_write;
+ if (slave->mtd.writev)
+ slave->mtd.writev = part_writev;
+ if (slave->mtd.readv)
+ slave->mtd.readv = part_readv;
+ if (slave->mtd.lock)
+ slave->mtd.lock = part_lock;
+ if (slave->mtd.unlock)
+ slave->mtd.unlock = part_unlock;
+ slave->mtd.erase = part_erase;
+ slave->master = master;
+ slave->offset = parts[i].offset;
+ slave->index = i;
+
+ if (slave->offset == 0)
+ slave->offset = cur_offset;
+ if (slave->mtd.size == 0)
+ slave->mtd.size = master->size - slave->offset;
+ cur_offset = slave->offset + slave->mtd.size;
+
+ /* let's do some sanity checks */
+ if ((slave->mtd.flags & MTD_WRITEABLE) &&
+ (parts[i].offset % master->erasesize)) {
+ slave->mtd.flags &= ~MTD_WRITEABLE;
+ printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
+ parts[i].name);
+ }
+ if ((slave->mtd.flags & MTD_WRITEABLE) &&
+ (parts[i].size % master->erasesize)) {
+ slave->mtd.flags &= ~MTD_WRITEABLE;
+ printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
+ parts[i].name);
+ }
+ if (parts[i].offset >= master->size) {
+ /* let's register it anyway to preserve ordering */
+ slave->offset = 0;
+ slave->mtd.size = 0;
+ printk ("mtd: partition \"%s\" is out of reach -- disabled\n",
+ parts[i].name);
+ }
+ if (parts[i].offset + parts[i].size > master->size) {
+ slave->mtd.size = master->size - parts[i].offset;
+ printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#lx\n",
+ parts[i].name, master->name, slave->mtd.size);
+ }
+
+ /* register our partition */
+ add_mtd_device(&slave->mtd);
+ MOD_INC_USE_COUNT;
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL(add_mtd_partitions);
+EXPORT_SYMBOL(del_mtd_partitions);
diff --git a/drivers/mtd/nftl.c b/drivers/mtd/nftl.c
index e7040863f..d7e2aeaad 100644
--- a/drivers/mtd/nftl.c
+++ b/drivers/mtd/nftl.c
@@ -1,32 +1,35 @@
-
/* Linux driver for NAND Flash Translation Layer */
/* (c) 1999 Machine Vision Holdings, Inc. */
/* Author: David Woodhouse <dwmw2@infradead.org> */
-/* $Id: nftl.c,v 1.36 2000/07/13 14:14:20 dwmw2 Exp $ */
+/* $Id: nftl.c,v 1.57 2000/12/01 17:51:54 dwmw2 Exp $ */
/*
- The contents of this file are distributed under the GNU Public
- Licence version 2 ("GPL"). The legal note below refers only to the
- _use_ of the code in some jurisdictions, and does not in any way
- affect the copying, distribution and modification of this code,
- which is permitted under the terms of the GPL.
+ The contents of this file are distributed under the GNU General
+ Public License version 2 ("GPL"). The author places no additional
+ restrictions of any kind on it. However, local legislation in some
+ countries may restrict the use of the algorithms implemented by this
+ code in certain circumstances.
- Section 0 of the GPL says:
+ The legal note below refers only to the _use_ of the code in the
+ affected jurisdictions, and does not in any way affect the copying,
+ distribution and modification of this code, which are permitted, and
+ indeed required, under the terms of the GPL.
+ Section 0 of the GPL says:
"Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope."
You may copy, distribute and modify this code to your hearts'
content - it's just that in some jurisdictions, you may only _use_
- it under the terms of the licence below. This puts it in a similar
- situation to the ISDN code, which you may need telco approval to
- use, and indeed any code which has uses that may be restricted in
- law. For example, certain malicious uses of the networking stack
- may be illegal, but that doesn't prevent the networking code from
- being under GPL.
+ it under the terms of the patent grant below. This puts it in a
+ similar situation to the ISDN code, which you may need telco
+ approval to use, and indeed any code which has uses that may be
+ restricted in law. For example, certain malicious uses of the
+ networking stack may be illegal, but that doesn't prevent the
+ networking code from being under GPL.
In fact the ISDN case is worse than this, because modification of
- the code automatically invalidates its approval. Modificiation,
+ the code automatically invalidates its approval. Modification,
unlike usage, _is_ one of the rights which is protected by the
GPL. Happily, the law in those places where approval is required
doesn't actually prevent you from modifying the code - it's just
@@ -34,7 +37,7 @@
because usage isn't addressed by the GPL, that's just fine.
dwmw2@infradead.org
- 6/7/0
+ 30/10/0
LEGAL NOTE: The NFTL format is patented by M-Systems. They have
granted a licence for its use with their DiskOnChip products:
@@ -51,10 +54,6 @@
#define PRERELEASE
-#ifdef NFTL_DEBUG
-#define DEBUGLVL debug
-#endif
-
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -67,42 +66,28 @@
#include <linux/malloc.h>
#include <linux/sched.h>
#include <linux/init.h>
+#include <linux/blkpg.h>
+#ifdef CONFIG_KMOD
+#include <linux/kmod.h>
+#endif
#include <linux/mtd/mtd.h>
#include <linux/mtd/nftl.h>
#include <linux/mtd/compatmac.h>
-#undef WE_KNOW_WTF_THIS_DOES_NOT_WORK
+/* maximum number of loops while examining next block, to have a
+ chance to detect consistency problems (they should never happen
+ because of the checks done in the mounting */
+
+#define MAX_LOOPS 10000
/* NFTL block device stuff */
#define MAJOR_NR NFTL_MAJOR
#define DEVICE_REQUEST nftl_request
#define DEVICE_OFF(device)
-#ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK
-#define LOCAL_END_REQUEST
-#endif
-#include <linux/blk.h>
-#include <linux/hdreg.h>
-
-
-#ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK
-
-static void nftl_end_request(struct request *req, int res)
-{
- req->sector += req->current_nr_sectors;
- req->nr_sectors -= req->current_nr_sectors;
-
- if (end_that_request_first( req, res, "nftl" ))
- return;
- end_that_request_last( req );
-}
-#endif
-
-#ifdef NFTL_DEBUG
-static int debug = NFTL_DEBUG;
-MODULE_PARM(debug, "i");
-#endif
+#include <linux/blk.h>
+#include <linux/hdreg.h>
/* Linux-specific block device functions */
@@ -110,11 +95,10 @@ MODULE_PARM(debug, "i");
* encountered, except ...
*/
-static int nftl_sizes[256]={0,};
+static int nftl_sizes[256] = {0,};
static int nftl_blocksizes[256] = {0,};
/* .. for the Linux partition table handling. */
-
struct hd_struct part_table[256] = {{0,0},};
#if LINUX_VERSION_CODE < 0x20328
@@ -123,8 +107,8 @@ static void dummy_init (struct gendisk *crap)
#endif
static struct gendisk nftl_gendisk = {
- NFTL_MAJOR, /* Major number */
- "nftl", /* Major name */
+ MAJOR_NR, /* Major number */
+ "nftl", /* Major name */
4, /* Bits to shift to get real from partition */
15, /* Number of partitions per real */
#if LINUX_VERSION_CODE < 0x20328
@@ -138,281 +122,129 @@ static struct gendisk nftl_gendisk = {
NULL /* next */
};
-
struct NFTLrecord *NFTLs[MAX_NFTLS] = {NULL};
-static void NFTL_setup(struct mtd_info *mtd, unsigned long ofs,
- struct NFTLMediaHeader *hdr)
+static void NFTL_setup(struct mtd_info *mtd)
{
int i;
- struct NFTLrecord *thisNFTL;
+ struct NFTLrecord *nftl;
unsigned long temp;
int firstfree = -1;
- DEBUG(1,"NFTL_setup\n");
+ DEBUG(MTD_DEBUG_LEVEL1,"NFTL_setup\n");
- for (i=0; i < MAX_NFTLS; i++) {
- if (!NFTLs[i] && firstfree==-1)
+ for (i = 0; i < MAX_NFTLS; i++) {
+ if (!NFTLs[i] && firstfree == -1)
firstfree = i;
- else if (NFTLs[i] && NFTLs[i]->mtd == mtd &&
- NFTLs[i]->MediaHdr.FirstPhysicalEUN == hdr->FirstPhysicalEUN) {
+ else if (NFTLs[i] && NFTLs[i]->mtd == mtd) {
/* This is a Spare Media Header for an NFTL we've already found */
- DEBUG(1, "Spare Media Header for NFTL %d found at %lx\n",i, ofs);
- NFTLs[i]->SpareMediaUnit = ofs / mtd->erasesize;
+ DEBUG(MTD_DEBUG_LEVEL1, "MTD already mounted as NFTL\n");
return;
}
}
-
-
- /* OK, it's a new one. Set up all the data structures. */
-#ifdef PSYCHO_DEBUG
- printk("Found new NFTL nftl%c at offset %lx\n",firstfree + 'a', ofs);
-#endif
- if (hdr->UnitSizeFactor != 0xff) {
- printk("Sorry, we don't support UnitSizeFactor of != 1 yet\n");
+ if (firstfree == -1) {
+ printk(KERN_WARNING "No more NFTL slot available\n");
return;
- }
-
- thisNFTL = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
- if (!thisNFTL) {
+ }
+
+ nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
+ if (!nftl) {
printk(KERN_WARNING "Out of memory for NFTL data structures\n");
return;
}
- init_MUTEX(&thisNFTL->mutex);
- thisNFTL->EraseSize = mtd->erasesize;
- memcpy(&thisNFTL->MediaHdr, hdr, sizeof(*hdr));
- thisNFTL->mtd = mtd;
- thisNFTL->MediaUnit = ofs / mtd->erasesize;
- thisNFTL->SpareMediaUnit = 0xffff;
- thisNFTL->numvunits = le32_to_cpu(thisNFTL->MediaHdr.FormattedSize) / 8192;
- thisNFTL->nr_sects = thisNFTL->numvunits * (thisNFTL->EraseSize / 512);
- thisNFTL->usecount = 0;
- thisNFTL->cylinders = 1024;
- thisNFTL->heads = 16;
+ init_MUTEX(&nftl->mutex);
- temp = thisNFTL->cylinders * thisNFTL->heads;
- thisNFTL->sectors = thisNFTL->nr_sects / temp;
+ /* get physical parameters */
+ nftl->EraseSize = mtd->erasesize;
+ nftl->nb_blocks = mtd->size / mtd->erasesize;
+ nftl->mtd = mtd;
- if (thisNFTL->nr_sects % temp) {
-
- thisNFTL->sectors++;
- temp = thisNFTL->cylinders * thisNFTL->sectors;
- thisNFTL->heads = thisNFTL->nr_sects / temp;
-
- if (thisNFTL->nr_sects & temp) {
- thisNFTL->heads++;
- temp = thisNFTL->heads * thisNFTL->sectors;
-
- thisNFTL->cylinders = thisNFTL->nr_sects / temp;
- }
- }
- if (thisNFTL->nr_sects != thisNFTL->heads * thisNFTL->cylinders *
- thisNFTL->sectors) {
- printk(KERN_WARNING "Cannot calculate an NFTL geometry to match size of 0x%lx.\n", thisNFTL->nr_sects);
- printk(KERN_WARNING "Using C:%d H:%d S:%d (== %lx sects)\n",
- thisNFTL->cylinders, thisNFTL->heads ,
- thisNFTL->sectors,
- (long)thisNFTL->cylinders * (long)thisNFTL->heads *
- (long)thisNFTL->sectors );
-
- /* Oh no we don't
- * thisNFTL->nr_sects = thisNFTL->heads * thisNFTL->cylinders * thisNFTL->sectors;
- */
- }
-
-
- thisNFTL->EUNtable = kmalloc( 2 * thisNFTL->numvunits,
- GFP_KERNEL);
- if (!thisNFTL->EUNtable) {
- printk("ENOMEM\n");
- kfree(thisNFTL);
+ if (NFTL_mount(nftl) < 0) {
+ printk(KERN_WARNING "Could not mount NFTL device\n");
+ kfree(nftl);
return;
- }
- memset(thisNFTL->EUNtable, 0xff, 2 * thisNFTL->numvunits);
-
- thisNFTL->VirtualUnitTable = kmalloc( 2 * le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) , GFP_KERNEL);
- if (!thisNFTL->VirtualUnitTable) {
- printk("ENOMEM\n");
- kfree(thisNFTL->EUNtable);
- kfree(thisNFTL);
- return;
- }
- memset(thisNFTL->VirtualUnitTable, 0xff, 2 * le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits));
-
- thisNFTL->ReplUnitTable = kmalloc( 2 * le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) , GFP_KERNEL);
- if (!thisNFTL->ReplUnitTable) {
- printk("ENOMEM\n");
- kfree(thisNFTL->VirtualUnitTable);
- kfree(thisNFTL->EUNtable);
- kfree(thisNFTL);
- return;
- }
- memset(thisNFTL->ReplUnitTable, 0xff, 2 *le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) );
-
- /* Ought to check the media header for bad blocks */
- thisNFTL->lastEUN = le16_to_cpu(thisNFTL->MediaHdr.NumEraseUnits) +
- le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN) - 1;
- thisNFTL->numfreeEUNs = 0;
+ }
- /* Scan each physical Erase Unit for validity and to find the
- Virtual Erase Unit Chain to which it belongs */
-
- for (i=le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN);
- i <= thisNFTL->lastEUN; i++) {
-
- union nftl_uci uci;
- unsigned long ofs;
- size_t retlen;
- ofs = i * thisNFTL->EraseSize;
-
- MTD_READOOB(mtd, (i * thisNFTL->EraseSize) + 512 + 8, 8, &retlen, (char *)&uci);
-
- if (uci.b.EraseMark != cpu_to_le16(0x3c69) ||
- uci.b.EraseMark1 != cpu_to_le16(0x3c69)) {
- printk("EUN %d: EraseMark not 0x3c69 (0x%4.4x 0x%4.4x instead)\n",
- i, le16_to_cpu(uci.b.EraseMark), le16_to_cpu(uci.b.EraseMark1));
- thisNFTL->VirtualUnitTable[i] = 0x7fff;
- thisNFTL->ReplUnitTable[i] = 0xffff;
- continue;
- }
-
- MTD_READOOB(mtd, (i * thisNFTL->EraseSize) + 8, 8, &retlen, (u_char *)&uci);
-
- if (uci.a.VirtUnitNum != uci.a.SpareVirtUnitNum)
- printk("EUN %d: VirtualUnitNumber (%x) != SpareVirtualUnitNumber (%x)\n",
- i, le16_to_cpu(uci.a.VirtUnitNum),
- le16_to_cpu(uci.a.SpareVirtUnitNum));
-
- if (uci.a.ReplUnitNum != uci.a.SpareReplUnitNum)
- printk("EUN %d: ReplacementUnitNumber (%x) != SpareReplacementUnitNumber (%x)\n",
- i, le16_to_cpu(uci.a.ReplUnitNum),
- le16_to_cpu(uci.a.SpareReplUnitNum));
-
- /* We don't actually _do_ anything about the above, just whinge */
-
- thisNFTL->VirtualUnitTable[i] = le16_to_cpu(uci.a.VirtUnitNum);
- thisNFTL->ReplUnitTable[i] = le16_to_cpu(uci.a.ReplUnitNum);
-
- /* if (!(VUN & 0x8000) && VUN < (arraybounds)).. optimises to: */
- if (le16_to_cpu(uci.a.VirtUnitNum) < thisNFTL->numvunits)
- thisNFTL->EUNtable[le16_to_cpu(uci.a.VirtUnitNum) & 0x7fff] = i;
-
- if (uci.a.VirtUnitNum == 0xffff) {
- /* Free block */
- thisNFTL->LastFreeEUN = i;
- thisNFTL->numfreeEUNs++;
- }
-
- }
- NFTLs[firstfree] = thisNFTL;
- thisNFTL->LastFreeEUN = le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN);
-
- //#define PSYCHO_DEBUG
+ /* OK, it's a new one. Set up all the data structures. */
#ifdef PSYCHO_DEBUG
- for (i=0; i < 10/* thisNFTL->numvunits*/; i++) {
- u16 curEUN = thisNFTL->EUNtable[i];
- int sillycount=100;
-
- printk("Virtual Unit #%d: ",i);
- if (!curEUN || curEUN == 0xffff) {
- printk("Not present\n");
- continue;
- }
- printk("%d", curEUN);
-
- while ((curEUN = thisNFTL->ReplUnitTable[curEUN]) != 0xffff && --sillycount) {
- printk(", %d", curEUN & 0xffff);
-
+ printk("Found new NFTL nftl%c\n", firstfree + 'a');
+#endif
+
+ /* linux stuff */
+ nftl->usecount = 0;
+ nftl->cylinders = 1024;
+ nftl->heads = 16;
+
+ temp = nftl->cylinders * nftl->heads;
+ nftl->sectors = nftl->nr_sects / temp;
+ if (nftl->nr_sects % temp) {
+ nftl->sectors++;
+ temp = nftl->cylinders * nftl->sectors;
+ nftl->heads = nftl->nr_sects / temp;
+
+ if (nftl->nr_sects % temp) {
+ nftl->heads++;
+ temp = nftl->heads * nftl->sectors;
+ nftl->cylinders = nftl->nr_sects / temp;
}
- printk("\n");
}
-#endif
- /* OK. Now we deal with the fact that we're in the real world. Sometimes
- things don't actually happen the way they're supposed to. Find, fix,
- and whinge about the most common deviations from spec that we have
- been known to encounter.
- */
- /* Except that I haven't implemented that bit yet :) */
+ if (nftl->nr_sects != nftl->heads * nftl->cylinders * nftl->sectors) {
+ printk(KERN_WARNING "Cannot calculate an NFTL geometry to "
+ "match size of 0x%lx.\n", nftl->nr_sects);
+ printk(KERN_WARNING "Using C:%d H:%d S:%d (== 0x%lx sects)\n",
+ nftl->cylinders, nftl->heads , nftl->sectors,
+ (long)nftl->cylinders * (long)nftl->heads * (long)nftl->sectors );
+ /* Oh no we don't have nftl->nr_sects = nftl->heads * nftl->cylinders * nftl->sectors; */
+ }
+ NFTLs[firstfree] = nftl;
/* Finally, set up the block device sizes */
- nftl_sizes[firstfree * 16]=thisNFTL->nr_sects;
-// nftl_blocksizes[firstfree*16] = 512;
- part_table[firstfree * 16].nr_sects = thisNFTL->nr_sects;
+ nftl_sizes[firstfree * 16] = nftl->nr_sects;
+ //nftl_blocksizes[firstfree*16] = 512;
+ part_table[firstfree * 16].nr_sects = nftl->nr_sects;
+
+ /* partition check ... */
#if LINUX_VERSION_CODE < 0x20328
resetup_one_dev(&nftl_gendisk, firstfree);
#else
- grok_partitions(&nftl_gendisk, firstfree, 1<<4, thisNFTL->nr_sects);
+ grok_partitions(&nftl_gendisk, firstfree, 1<<4, nftl->nr_sects);
#endif
-
}
-
static void NFTL_unsetup(int i)
{
- struct NFTLrecord *thisNFTL = NFTLs[i];
+ struct NFTLrecord *nftl = NFTLs[i];
- DEBUG(1, "NFTL_unsetup %d\n", i);
+ DEBUG(MTD_DEBUG_LEVEL1, "NFTL_unsetup %d\n", i);
NFTLs[i] = NULL;
- if (thisNFTL->VirtualUnitTable)
- kfree(thisNFTL->VirtualUnitTable);
- if (thisNFTL->ReplUnitTable)
- kfree(thisNFTL->ReplUnitTable);
- if (thisNFTL->EUNtable)
- kfree(thisNFTL->EUNtable);
+ if (nftl->ReplUnitTable)
+ kfree(nftl->ReplUnitTable);
+ if (nftl->EUNtable)
+ kfree(nftl->EUNtable);
- kfree(thisNFTL);
+ kfree(nftl);
}
-
-
-
/* Search the MTD device for NFTL partitions */
static void NFTL_notify_add(struct mtd_info *mtd)
{
- int i;
- unsigned long ofs;
- struct NFTLMediaHeader hdr;
-
- DEBUG(1, "NFTL_notify_add for %s\n", mtd->name);
+ DEBUG(MTD_DEBUG_LEVEL1, "NFTL_notify_add for %s\n", mtd->name);
if (mtd) {
- if (!mtd->read_oob) /* If this MTD doesn't have out-of-band data,
- then there's no point continuing */
- {
- DEBUG(1, "No OOB data, quitting\n");
+ if (!mtd->read_oob) {
+ /* If this MTD doesn't have out-of-band data,
+ then there's no point continuing */
+ DEBUG(MTD_DEBUG_LEVEL1, "No OOB data, quitting\n");
return;
}
- DEBUG(3, "mtd->read = %p,size = %d, erasesize = %d\n",
- mtd->read, mtd->size, mtd->erasesize);
- for (ofs = 0; ofs < mtd->size ; ofs += mtd->erasesize) {
- size_t retlen = 0;
- MTD_READ(mtd, ofs, sizeof(hdr), &retlen, (u_char *)&hdr);
-
- if (retlen < sizeof(hdr))
- {
- continue;
- }
-
- if (!strncmp(hdr.DataOrgID, "ANAND", 6)) {
- DEBUG(2, "Valid NFTL partition at ofs %ld\n", ofs);
- NFTL_setup(mtd, ofs, &hdr);
- }
- else {
- DEBUG(3,"No valid NFTL Partition at ofs %d\n", ofs);
- for(i = 0; i < 6; i++) {
- DEBUG(3,"%x, ", hdr.DataOrgID[i]);
- }
- DEBUG(3," = %s\n", hdr.DataOrgID);
- DEBUG(3,"%d, %d, %d, %d\n", hdr.NumEraseUnits, hdr.FirstPhysicalEUN,
- hdr.FormattedSize, hdr.UnitSizeFactor);
+ DEBUG(MTD_DEBUG_LEVEL3, "mtd->read = %p, size = %d, erasesize = %d\n",
+ mtd->read, mtd->size, mtd->erasesize);
- }
- }
- return;
+ NFTL_setup(mtd);
}
}
@@ -420,100 +252,101 @@ static void NFTL_notify_remove(struct mtd_info *mtd)
{
int i;
- for (i=0; i< MAX_NFTLS; i++) {
+ for (i = 0; i < MAX_NFTLS; i++) {
if (NFTLs[i] && NFTLs[i]->mtd == mtd)
NFTL_unsetup(i);
}
}
-
#ifdef CONFIG_NFTL_RW
/* Actual NFTL access routines */
-
-
-static u16 NFTL_findfreeblock( struct NFTLrecord *thisNFTL, int desperate )
+/* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
+ * when the give Virtual Unit Chain
+ */
+static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
{
/* For a given Virtual Unit Chain: find or create a free block and
add it to the chain */
/* We're passed the number of the last EUN in the chain, to save us from
having to look it up again */
-
- u16 pot = thisNFTL->LastFreeEUN;
+ u16 pot = nftl->LastFreeEUN;
int silly = -1;
/* Normally, we force a fold to happen before we run out of free blocks completely */
-
- if (!desperate && thisNFTL->numfreeEUNs < 2) {
- // printk("NFTL_findfreeblock: there are too few free EUNs\n");
+ if (!desperate && nftl->numfreeEUNs < 2) {
+ DEBUG(MTD_DEBUG_LEVEL1, "NFTL_findfreeblock: there are too few free EUNs\n");
return 0xffff;
}
/* Scan for a free block */
-
do {
- if (thisNFTL->VirtualUnitTable[pot] == 0xffff) {
- thisNFTL->LastFreeEUN = pot;
- thisNFTL->numfreeEUNs--;
+ if (nftl->ReplUnitTable[pot] == BLOCK_FREE) {
+ nftl->LastFreeEUN = pot;
+ nftl->numfreeEUNs--;
return pot;
}
- if (++pot > thisNFTL->lastEUN)
- pot = le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN);
+ /* This will probably point to the MediaHdr unit itself,
+ right at the beginning of the partition. But that unit
+ (and the backup unit too) should have the UCI set
+ up so that it's not selected for overwriting */
+ if (++pot > nftl->lastEUN)
+ pot = le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN);
if (!silly--) {
- printk("Tell Dave he fucked up. LastFreeEUN = %d, FirstEUN = %d\n",
- thisNFTL->LastFreeEUN, le16_to_cpu(thisNFTL->MediaHdr.FirstPhysicalEUN));
+ printk("Argh! No free blocks found! LastFreeEUN = %d, "
+ "FirstEUN = %d\n", nftl->LastFreeEUN,
+ le16_to_cpu(nftl->MediaHdr.FirstPhysicalEUN));
return 0xffff;
}
-
- } while (pot != thisNFTL->LastFreeEUN);
+ } while (pot != nftl->LastFreeEUN);
return 0xffff;
}
-
-
-
-
-static u16 NFTL_foldchain (struct NFTLrecord *thisNFTL, u16 thisVUC, unsigned pendingblock )
+static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
{
- u16 BlockMap[thisNFTL->EraseSize / 512];
- unsigned char BlockLastState[thisNFTL->EraseSize / 512];
- unsigned char BlockFreeFound[thisNFTL->EraseSize / 512];
- u16 thisEUN;
+ u16 BlockMap[MAX_SECTORS_PER_UNIT];
+ unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
+ unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
+ unsigned int thisEUN;
int block;
- int silly = -1;
- u16 targetEUN = 0xffff;
+ int silly;
+ unsigned int targetEUN;
struct nftl_oob oob;
int inplace = 1;
+ size_t retlen;
memset(BlockMap, 0xff, sizeof(BlockMap));
memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
- thisEUN = thisNFTL->EUNtable[thisVUC];
+ thisEUN = nftl->EUNtable[thisVUC];
- if (thisEUN == 0xffff) {
- printk(KERN_WARNING "Trying to fold non-existent Virtual Unit Chain %d!\n", thisVUC);
- return 0xffff;
+ if (thisEUN == BLOCK_NIL) {
+ printk(KERN_WARNING "Trying to fold non-existent "
+ "Virtual Unit Chain %d!\n", thisVUC);
+ return BLOCK_NIL;
}
/* Scan to find the Erase Unit which holds the actual data for each
512-byte block within the Chain.
*/
+ silly = MAX_LOOPS;
+ targetEUN = BLOCK_NIL;
+ while (thisEUN <= nftl->lastEUN ) {
+ unsigned int status, foldmark;
- while( thisEUN <= thisNFTL->lastEUN ) {
- size_t retlen;
-
targetEUN = thisEUN;
-
- for (block = 0 ; block < thisNFTL->EraseSize / 512; block ++) {
-
- MTD_READOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + (block * 512),16 , &retlen, (char *)&oob);
-
+ for (block = 0; block < nftl->EraseSize / 512; block ++) {
+ MTD_READOOB(nftl->mtd,
+ (thisEUN * nftl->EraseSize) + (block * 512),
+ 16 , &retlen, (char *)&oob);
if (block == 2) {
- if (oob.u.c.WriteInh != 0xffffffff) {
- printk("Write Inhibited on EUN %d\n", thisEUN);
+ foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
+ if (foldmark == FOLD_MARK_IN_PROGRESS) {
+ DEBUG(MTD_DEBUG_LEVEL1,
+ "Write Inhibited on EUN %d\n", thisEUN);
inplace = 0;
} else {
/* There's no other reason not to do inplace,
@@ -522,153 +355,139 @@ static u16 NFTL_foldchain (struct NFTLrecord *thisNFTL, u16 thisVUC, unsigned pe
inplace = 1;
}
}
+ status = oob.b.Status | oob.b.Status1;
+ BlockLastState[block] = status;
- BlockLastState[block] = (unsigned char) oob.b.Status & 0xff;
-
- switch(oob.b.Status) {
- case __constant_cpu_to_le16(BLOCK_FREE):
- BlockFreeFound[block]=1;
+ switch(status) {
+ case SECTOR_FREE:
+ BlockFreeFound[block] = 1;
break;
- case __constant_cpu_to_le16(BLOCK_USED):
+ case SECTOR_USED:
if (!BlockFreeFound[block])
BlockMap[block] = thisEUN;
else
- printk(KERN_WARNING "BLOCK_USED found after BLOCK_FREE in Virtual Unit Chain %d for block %d\n", thisVUC, block);
+ printk(KERN_WARNING
+ "SECTOR_USED found after SECTOR_FREE "
+ "in Virtual Unit Chain %d for block %d\n",
+ thisVUC, block);
break;
- case __constant_cpu_to_le16(BLOCK_IGNORE):
- case __constant_cpu_to_le16(BLOCK_DELETED):
+ case SECTOR_IGNORE:
+ case SECTOR_DELETED:
break;
default:
- printk("Unknown status for block %d in EUN %d: %x\n",block,thisEUN, oob.b.Status);
+ printk("Unknown status for block %d in EUN %d: %x\n",
+ block, thisEUN, status);
}
}
if (!silly--) {
- printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", thisVUC);
- return 0xffff;
+ printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
+ thisVUC);
+ return BLOCK_NIL;
}
- thisEUN = thisNFTL->ReplUnitTable[thisEUN] & 0x7fff;
+ thisEUN = nftl->ReplUnitTable[thisEUN];
}
if (inplace) {
/* We're being asked to be a fold-in-place. Check
- that all blocks are either present or BLOCK_FREE
+ that all blocks are either present or SECTOR_FREE
in the target block. If not, we're going to have
to fold out-of-place anyway.
*/
-
- for (block = 0; block < thisNFTL->EraseSize / 512 ; block++) {
-
- if (BlockLastState[block] != (unsigned char) (cpu_to_le16(BLOCK_FREE) & 0xff) &&
+ for (block = 0; block < nftl->EraseSize / 512 ; block++) {
+ if (BlockLastState[block] != SECTOR_FREE &&
BlockMap[block] != targetEUN) {
- DEBUG(1, "Setting inplace to 0. VUC %d, block %d was %x lastEUN, and is in EUN %d (%s) %d\n",
- thisVUC, block, BlockLastState[block], BlockMap[block] , BlockMap[block]==targetEUN?"==":"!=", targetEUN);
-
+ DEBUG(MTD_DEBUG_LEVEL1, "Setting inplace to 0. VUC %d, "
+ "block %d was %x lastEUN, "
+ "and is in EUN %d (%s) %d\n",
+ thisVUC, block, BlockLastState[block],
+ BlockMap[block],
+ BlockMap[block]== targetEUN ? "==" : "!=",
+ targetEUN);
inplace = 0;
break;
}
}
- if ( pendingblock >= (thisVUC * (thisNFTL->EraseSize / 512)) &&
- pendingblock < ((thisVUC + 1)* (thisNFTL->EraseSize / 512)) &&
- BlockLastState[ pendingblock - (thisVUC * (thisNFTL->EraseSize / 512))] !=
- (unsigned char) (cpu_to_le16(BLOCK_FREE) & 0xff)) {
- DEBUG(1, "Pending write not free in EUN %d. Folding out of place.\n", targetEUN);
+ if (pendingblock >= (thisVUC * (nftl->EraseSize / 512)) &&
+ pendingblock < ((thisVUC + 1)* (nftl->EraseSize / 512)) &&
+ BlockLastState[pendingblock - (thisVUC * (nftl->EraseSize / 512))] !=
+ SECTOR_FREE) {
+ DEBUG(MTD_DEBUG_LEVEL1, "Pending write not free in EUN %d. "
+ "Folding out of place.\n", targetEUN);
inplace = 0;
}
-
}
if (!inplace) {
- DEBUG(1, "Cannot fold Virtual Unit Chain %d in place. Trying out-of-place\n", thisVUC);
+ DEBUG(MTD_DEBUG_LEVEL1, "Cannot fold Virtual Unit Chain %d in place. "
+ "Trying out-of-place\n", thisVUC);
/* We need to find a targetEUN to fold into. */
- targetEUN = NFTL_findfreeblock(thisNFTL, 1);
- if (targetEUN == 0xffff) {
- /* Ouch. Now we're screwed. We need to do a
- fold-in-place of another chain to make room
- for this one. We need a better way of selecting
- which chain to fold, because makefreeblock will
- only ask us to fold the same one again.
- */
- printk(KERN_WARNING"NFTL_findfreeblock(desperate) returns 0xffff.\n");
- return 0xffff;
+ targetEUN = NFTL_findfreeblock(nftl, 1);
+ if (targetEUN == BLOCK_NIL) {
+ /* Ouch. Now we're screwed. We need to do a
+ fold-in-place of another chain to make room
+ for this one. We need a better way of selecting
+ which chain to fold, because makefreeblock will
+ only ask us to fold the same one again.
+ */
+ printk(KERN_WARNING
+ "NFTL_findfreeblock(desperate) returns 0xffff.\n");
+ return BLOCK_NIL;
}
-
- }
-
+ } else {
+ /* We put a fold mark in the chain we are folding only if
+ we fold in place to help the mount check code. If we do
+ not fold in place, it is possible to find the valid
+ chain by selecting the longer one */
+ oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
+ oob.u.c.unused = 0xffffffff;
+ MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
+ 8, &retlen, (char *)&oob.u);
+ }
/* OK. We now know the location of every block in the Virtual Unit Chain,
and the Erase Unit into which we are supposed to be copying.
Go for it.
*/
-
- DEBUG(1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
-
- for (block = 0; block < thisNFTL->EraseSize / 512 ; block++) {
+ DEBUG(MTD_DEBUG_LEVEL1,"Folding chain %d into unit %d\n", thisVUC, targetEUN);
+ for (block = 0; block < nftl->EraseSize / 512 ; block++) {
unsigned char movebuf[512];
- struct nftl_oob oob;
- size_t retlen;
-
- memset(&oob, 0xff, sizeof(oob));
+ int ret;
/* If it's in the target EUN already, or if it's pending write, do nothing */
- if (BlockMap[block] == targetEUN ||(pendingblock == (thisVUC * (thisNFTL->EraseSize / 512) + block))) {
- /* Except if it's the first block, in which case we have to
- set the UnitNumbers */
- if (block == 0) {
-
- thisNFTL->mtd->read_oob(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) ,
- 16, &retlen, (char *)&oob);
-
- // printk("Setting VirtUnitNum on EUN %d to %x, was %x\n", targetEUN, thisVUC,
- // le16_to_cpu(oob.u.a.VirtUnitNum));
-
- oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC & 0x7fff);
-
- thisNFTL->mtd->write_oob(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) ,
- 16, &retlen, (char *)&oob);
- }
+ if (BlockMap[block] == targetEUN ||
+ (pendingblock == (thisVUC * (nftl->EraseSize / 512) + block))) {
continue;
}
- oob.b.Status = BLOCK_USED;
-
- switch(block) {
- case 0:
- oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC & 0x7fff);
- // printk("Setting VirtUnitNum on EUN %d to %x\n", targetEUN, thisVUC);
-
- oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
- break;
-
- case 1:
- oob.u.b.WearInfo = cpu_to_le32(3); // We don't use this, but M-Systems' drivers do
- oob.u.b.EraseMark = oob.u.b.EraseMark1 = cpu_to_le16(0x3c69);
- break;
-
- case 2:
- default:
- oob.u.c.WriteInh = 0xffffffff;
- oob.u.c.unused = 0xffffffff;
- }
- if (thisNFTL->mtd->read_ecc(thisNFTL->mtd, (thisNFTL->EraseSize * BlockMap[block]) + (block * 512),
- 512, &retlen, movebuf, (char *)&oob) == -EIO) {
- if (thisNFTL->mtd->read_ecc(thisNFTL->mtd, (thisNFTL->EraseSize * BlockMap[block]) + (block * 512),
- 512, &retlen, movebuf, (char *)&oob) != -EIO)
- printk("Error went away on retry.\n");
- }
-
- thisNFTL->mtd->write_ecc(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) + (block * 512),
- 512, &retlen, movebuf, (char *)&oob);
-
-
- /* FIXME: Add some error checking.... */
- thisNFTL->mtd->write_oob(thisNFTL->mtd, (thisNFTL->EraseSize * targetEUN) + (block * 512),
- 16, &retlen, (char *)&oob);
-
+ /* copy only in non free block (free blocks can only
+ happen in case of media errors or deleted blocks) */
+ if (BlockMap[block] == BLOCK_NIL)
+ continue;
+
+ ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block])
+ + (block * 512), 512, &retlen, movebuf, (char *)&oob);
+ if (ret < 0) {
+ ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block])
+ + (block * 512), 512, &retlen,
+ movebuf, (char *)&oob);
+ if (ret != -EIO)
+ printk("Error went away on retry.\n");
+ }
+ MTD_WRITEECC(nftl->mtd, (nftl->EraseSize * targetEUN) + (block * 512),
+ 512, &retlen, movebuf, (char *)&oob);
}
+
+ /* add the header so that it is now a valid chain */
+ oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum
+ = cpu_to_le16(thisVUC);
+ oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
+
+ MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 8,
+ 8, &retlen, (char *)&oob.u);
/* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
@@ -677,78 +496,37 @@ static u16 NFTL_foldchain (struct NFTLrecord *thisNFTL, u16 thisVUC, unsigned pe
shouldn't actually lose data in this case. It's just that when we load up on a medium which
has duplicate chains, we need to free one of the chains because it's not necessary any more.
*/
-
-
- thisEUN = thisNFTL->EUNtable[thisVUC];
+ thisEUN = nftl->EUNtable[thisVUC];
+ DEBUG(MTD_DEBUG_LEVEL1,"Want to erase\n");
- DEBUG(1,"Want to erase\n");
/* For each block in the old chain (except the targetEUN of course),
free it and make it available for future use */
-
- while( thisEUN <= thisNFTL->lastEUN && thisEUN != targetEUN) {
- size_t retlen;
- struct erase_info *instr;
- u16 EUNtmp;
-
- instr = kmalloc(sizeof(struct erase_info), GFP_KERNEL);
- if (!instr) {
- printk(KERN_WARNING "Out of memory for struct erase_info\n");
-
- EUNtmp = thisEUN;
-
- thisEUN = thisNFTL->ReplUnitTable[EUNtmp] & 0x7fff;
- thisNFTL->VirtualUnitTable[EUNtmp] = 0x7fff;
- thisNFTL->ReplUnitTable[EUNtmp] = 0xffff;
- } else {
- memset(instr, 0, sizeof(struct erase_info));
- instr->addr = thisEUN * thisNFTL->EraseSize;
- instr->len = thisNFTL->EraseSize;
-
- MTD_ERASE(thisNFTL->mtd, instr);
- /* This is an async interface. Or will be. At which point
- this code will break. */
-
-#if 0
- MTD_READOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + 512, 16, &retlen, (char *)&oob);
-
- printk("After erasing, EUN %d contains: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
- thisEUN, oob.b.ECCSig[0],
- oob.b.ECCSig[1],
- oob.b.ECCSig[2],
- oob.b.ECCSig[3],
- oob.b.ECCSig[4],
- oob.b.ECCSig[5]);
-#endif
- memset(&oob, 0xff, sizeof(oob));
- oob.u.b.WearInfo = cpu_to_le32(3);
- oob.u.b.EraseMark = oob.u.b.EraseMark1 = cpu_to_le16(0x3c69);
-
- MTD_WRITEOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + 512, 16, &retlen, (char *)&oob);
-
- EUNtmp = thisEUN;
-
- thisEUN = thisNFTL->ReplUnitTable[EUNtmp] & 0x7fff;
- thisNFTL->VirtualUnitTable[EUNtmp] = 0xffff;
- thisNFTL->ReplUnitTable[EUNtmp] = 0xffff;
-
- thisNFTL->numfreeEUNs++;
-
- }
-
- // shifted upwards: thisEUN = thisNFTL->ReplUnitTable[thisEUN] & 0x7fff;
-
+ while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
+ unsigned int EUNtmp;
+
+ EUNtmp = nftl->ReplUnitTable[thisEUN];
+
+ if (NFTL_formatblock(nftl, thisEUN) < 0) {
+ /* could not erase : mark block as reserved
+ * FixMe: Update Bad Unit Table on disk
+ */
+ nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
+ } else {
+ /* correctly erased : mark it as free */
+ nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
+ nftl->numfreeEUNs++;
+ }
+ thisEUN = EUNtmp;
}
/* Make this the new start of chain for thisVUC */
- thisNFTL->VirtualUnitTable[targetEUN] = thisVUC;
- thisNFTL->ReplUnitTable[targetEUN] = 0xffff;
+ nftl->ReplUnitTable[targetEUN] = BLOCK_NIL;
+ nftl->EUNtable[thisVUC] = targetEUN;
- thisNFTL->EUNtable[thisVUC] = targetEUN;
return targetEUN;
-
}
-u16 NFTL_makefreeblock( struct NFTLrecord *thisNFTL , unsigned pendingblock)
+u16 NFTL_makefreeblock( struct NFTLrecord *nftl , unsigned pendingblock)
{
/* This is the part that needs some cleverness applied.
For now, I'm doing the minimum applicable to actually
@@ -757,23 +535,21 @@ u16 NFTL_makefreeblock( struct NFTLrecord *thisNFTL , unsigned pendingblock)
and we also need to do some assessment of the results when
the system loses power half-way through the routine.
*/
-
u16 LongestChain = 0;
u16 ChainLength = 0, thislen;
u16 chain, EUN;
-
- for (chain=0; chain < thisNFTL->MediaHdr.FormattedSize / thisNFTL->EraseSize; chain++) {
- EUN = thisNFTL->EUNtable[chain];
-
+ for (chain = 0; chain < nftl->MediaHdr.FormattedSize / nftl->EraseSize; chain++) {
+ EUN = nftl->EUNtable[chain];
thislen = 0;
- while (EUN <= thisNFTL->lastEUN) {
+ while (EUN <= nftl->lastEUN) {
thislen++;
- // printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
- EUN = thisNFTL->ReplUnitTable[EUN] & 0x7fff;
+ //printk("VUC %d reaches len %d with EUN %d\n", chain, thislen, EUN);
+ EUN = nftl->ReplUnitTable[EUN] & 0x7fff;
if (thislen > 0xff00) {
- printk("Endless loop in Virtual Chain %d: Unit %x\n", chain, EUN);
+ printk("Endless loop in Virtual Chain %d: Unit %x\n",
+ chain, EUN);
}
if (thislen > 0xff10) {
/* Actually, don't return failure. Just ignore this chain and
@@ -781,42 +557,38 @@ u16 NFTL_makefreeblock( struct NFTLrecord *thisNFTL , unsigned pendingblock)
thislen = 0;
break;
}
-
}
-
if (thislen > ChainLength) {
- // printk("New longest chain is %d with length %d\n", chain, thislen);
+ //printk("New longest chain is %d with length %d\n", chain, thislen);
ChainLength = thislen;
LongestChain = chain;
}
- }
+ }
if (ChainLength < 2) {
- printk(KERN_WARNING "No Virtual Unit Chains available for folding. Failing request\n");
+ printk(KERN_WARNING "No Virtual Unit Chains available for folding. "
+ "Failing request\n");
return 0xffff;
}
-
- return NFTL_foldchain (thisNFTL, LongestChain, pendingblock);
+
+ return NFTL_foldchain (nftl, LongestChain, pendingblock);
}
/* NFTL_findwriteunit: Return the unit number into which we can write
for this block. Make it available if it isn't already
*/
-
-static inline u16 NFTL_findwriteunit(struct NFTLrecord *thisNFTL, unsigned block)
+static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
{
u16 lastEUN;
- u16 thisVUC = block / (thisNFTL->EraseSize / 512);
- u16 writeEUN;
- unsigned long blockofs = (block * 512) & (thisNFTL->EraseSize -1);
+ u16 thisVUC = block / (nftl->EraseSize / 512);
+ unsigned int writeEUN;
+ unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
size_t retlen;
- int silly = 0x10000, silly2 = 3;
+ int silly, silly2 = 3;
struct nftl_oob oob;
- int debug=0;
do {
-
/* Scan the media to find a unit in the VUC which has
a free space for the block in question.
*/
@@ -824,28 +596,30 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *thisNFTL, unsigned block
/* This condition catches the 0x[7f]fff cases, as well as
being a sanity check for past-end-of-media access
*/
- lastEUN = 0xffff;
- writeEUN = thisNFTL->EUNtable[thisVUC];
-
- while(writeEUN <= thisNFTL->lastEUN) {
+ lastEUN = BLOCK_NIL;
+ writeEUN = nftl->EUNtable[thisVUC];
+ silly = MAX_LOOPS;
+ while (writeEUN <= nftl->lastEUN) {
struct nftl_bci bci;
size_t retlen;
-
+ unsigned int status;
+
lastEUN = writeEUN;
+
+ MTD_READOOB(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs,
+ 8, &retlen, (char *)&bci);
- MTD_READOOB(thisNFTL->mtd, (writeEUN * thisNFTL->EraseSize)
- + blockofs,8, &retlen, (char *)&bci);
-
- if (debug)
- printk("Status of block %d in EUN %d is %x\n", block , writeEUN, le16_to_cpu(bci.Status));
+ DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
+ block , writeEUN, le16_to_cpu(bci.Status));
- switch(bci.Status) {
- case __constant_cpu_to_le16(BLOCK_FREE):
+ status = bci.Status | bci.Status1;
+ switch(status) {
+ case SECTOR_FREE:
return writeEUN;
- case __constant_cpu_to_le16(BLOCK_DELETED):
- case __constant_cpu_to_le16(BLOCK_USED):
- case __constant_cpu_to_le16(BLOCK_IGNORE):
+ case SECTOR_DELETED:
+ case SECTOR_USED:
+ case SECTOR_IGNORE:
break;
default:
// Invalid block. Don't use it any more. Must implement.
@@ -853,35 +627,35 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *thisNFTL, unsigned block
}
if (!silly--) {
- printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", thisVUC);
+ printk(KERN_WARNING
+ "Infinite loop in Virtual Unit Chain 0x%x\n",
+ thisVUC);
return 0xffff;
}
/* Skip to next block in chain */
-
- writeEUN = thisNFTL->ReplUnitTable[writeEUN] & 0x7fff;
+ writeEUN = nftl->ReplUnitTable[writeEUN];
}
/* OK. We didn't find one in the existing chain, or there
is no existing chain. */
/* Try to find an already-free block */
+ writeEUN = NFTL_findfreeblock(nftl, 0);
- writeEUN = NFTL_findfreeblock(thisNFTL, 0);
-
- if (writeEUN == 0xffff) {
+ if (writeEUN == BLOCK_NIL) {
/* That didn't work - there were no free blocks just
waiting to be picked up. We're going to have to fold
a chain to make room.
*/
/* First remember the start of this chain */
- // u16 startEUN = thisNFTL->EUNtable[thisVUC];
+ //u16 startEUN = nftl->EUNtable[thisVUC];
//printk("Write to VirtualUnitChain %d, calling makefreeblock()\n", thisVUC);
- writeEUN = NFTL_makefreeblock(thisNFTL, block);
+ writeEUN = NFTL_makefreeblock(nftl, 0xffff);
- if (writeEUN == 0xffff) {
+ if (writeEUN == BLOCK_NIL) {
/* Ouch. This should never happen - we should
always be able to make some room somehow.
If we get here, we've allocated more storage
@@ -889,235 +663,223 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *thisNFTL, unsigned block
routine is missing something.
*/
printk(KERN_WARNING "Cannot make free space.\n");
- return 0xffff;
+ return BLOCK_NIL;
}
- // printk("Restarting scan\n");
- lastEUN = 0xffff;
- // debug = 1;
+ //printk("Restarting scan\n");
+ lastEUN = BLOCK_NIL;
continue;
-#if 0
- if (startEUN != thisNFTL->EUNtable[thisVUC]) {
- /* The fold operation has moved the chain
- that we're looking at. Start the scan again.
- */
- continue;
- }
-#endif
}
/* We've found a free block. Insert it into the chain. */
- if (lastEUN != 0xffff) {
- /* Addition to an existing chain. Make the previous
- last block in the chain point to this one.
- */
-
- //printk("Linking EUN %d to EUN %d in VUC %d\n",
- // lastEUN, writeEUN, thisVUC);
- /* Both in our cache... */
- thisNFTL->ReplUnitTable[lastEUN] = writeEUN;
-
-
- /* ... and on the flash itself */
- MTD_READOOB(thisNFTL->mtd, (lastEUN * thisNFTL->EraseSize), 16, &retlen,
- (char *)&oob);
+ if (lastEUN != BLOCK_NIL) {
+ thisVUC |= 0x8000; /* It's a replacement block */
+ } else {
+ /* The first block in a new chain */
+ nftl->EUNtable[thisVUC] = writeEUN;
+ }
- oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = cpu_to_le16(writeEUN);
+ /* set up the actual EUN we're writing into */
+ /* Both in our cache... */
+ nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
- MTD_WRITEOOB(thisNFTL->mtd, (lastEUN * thisNFTL->EraseSize), 16, &retlen,
- (char *)&oob);
+ /* ... and on the flash itself */
+ MTD_READOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8,
+ &retlen, (char *)&oob.u);
- thisVUC |= 0x8000; /* It's a replacement block */
- } else {
- /* The first block in a new chain */
- thisNFTL->EUNtable[thisVUC] = writeEUN;
- }
+ oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
- /* Now set up the actual EUN we're writing into */
+ MTD_WRITEOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8,
+ &retlen, (char *)&oob.u);
+ /* we link the new block to the chain only after the
+ block is ready. It avoids the case where the chain
+ could point to a free block */
+ if (lastEUN != BLOCK_NIL) {
/* Both in our cache... */
- thisNFTL->VirtualUnitTable[writeEUN] = thisVUC;
- thisNFTL->ReplUnitTable[writeEUN] = 0xffff;
-
+ nftl->ReplUnitTable[lastEUN] = writeEUN;
/* ... and on the flash itself */
- MTD_READOOB(thisNFTL->mtd, writeEUN * thisNFTL->EraseSize, 16,
- &retlen, (char *)&oob);
+ MTD_READOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8,
+ 8, &retlen, (char *)&oob.u);
- oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
+ oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
+ = cpu_to_le16(writeEUN);
- MTD_WRITEOOB(thisNFTL->mtd, writeEUN * thisNFTL->EraseSize, 16,
- &retlen, (char *)&oob);
+ MTD_WRITEOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8,
+ 8, &retlen, (char *)&oob.u);
+ }
return writeEUN;
} while (silly2--);
- printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n", thisVUC);
+ printk(KERN_WARNING "Error folding to make room for Virtual Unit Chain 0x%x\n",
+ thisVUC);
return 0xffff;
}
-static int NFTL_writeblock(struct NFTLrecord *thisNFTL, unsigned block,
- char *buffer)
+static int NFTL_writeblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
{
u16 writeEUN;
- unsigned long blockofs = (block * 512) & (thisNFTL->EraseSize -1);
+ unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
size_t retlen;
- u16 eccbuf[8];
-
- // if (thisEUN == 0xffff) thisEUN = 0;
-
- writeEUN = NFTL_findwriteunit(thisNFTL, block);
+ u8 eccbuf[6];
-// printk("writeblock(%d): Write to Unit %d\n", block, writeEUN);
+ writeEUN = NFTL_findwriteunit(nftl, block);
- if (writeEUN == 0xffff) {
- printk(KERN_WARNING "NFTL_writeblock(): Cannot find block to write to\n");
+ if (writeEUN == BLOCK_NIL) {
+ printk(KERN_WARNING
+ "NFTL_writeblock(): Cannot find block to write to\n");
/* If we _still_ haven't got a block to use, we're screwed */
return 1;
}
-// printk("Writing block %lx to EUN %x\n",block, writeEUN);
-
- thisNFTL->mtd->write_ecc(thisNFTL->mtd,
- (writeEUN * thisNFTL->EraseSize) + blockofs,
- 512, &retlen, (char *)buffer, (char *)eccbuf);
- eccbuf[3] = BLOCK_USED;
- eccbuf[4] = eccbuf[5] = eccbuf[6] = eccbuf[7] = 0xffff;
-
- thisNFTL->mtd->write_oob(thisNFTL->mtd,
- (writeEUN * thisNFTL->EraseSize) + blockofs,
- 16, &retlen, (char *)eccbuf);
+ MTD_WRITEECC(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs,
+ 512, &retlen, (char *)buffer, (char *)eccbuf);
+ /* no need to write SECTOR_USED flags since they are written in mtd_writeecc */
return 0;
}
-
#endif /* CONFIG_NFTL_RW */
-static int NFTL_readblock(struct NFTLrecord *thisNFTL,
- unsigned block, char *buffer)
+static int NFTL_readblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
{
- u16 lastgoodEUN = 0xffff;
- u16 thisEUN = thisNFTL->EUNtable[block / (thisNFTL->EraseSize / 512)];
- unsigned long blockofs = (block * 512) & (thisNFTL->EraseSize -1);
-
- int silly = -1;
+ u16 lastgoodEUN;
+ u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
+ unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
+ unsigned int status;
+ int silly = MAX_LOOPS;
+ size_t retlen;
+ struct nftl_bci bci;
+
+ lastgoodEUN = BLOCK_NIL;
+
+ if (thisEUN != BLOCK_NIL) {
+ while (thisEUN < nftl->nb_blocks) {
+ if (MTD_READOOB(nftl->mtd, (thisEUN * nftl->EraseSize) + blockofs,
+ 8, &retlen, (char *)&bci) < 0)
+ status = SECTOR_IGNORE;
+ else
+ status = bci.Status | bci.Status1;
+
+ switch (status) {
+ case SECTOR_FREE:
+ /* no modification of a sector should follow a free sector */
+ goto the_end;
+ case SECTOR_DELETED:
+ lastgoodEUN = BLOCK_NIL;
+ break;
+ case SECTOR_USED:
+ lastgoodEUN = thisEUN;
+ break;
+ case SECTOR_IGNORE:
+ break;
+ default:
+ printk("Unknown status for block %d in EUN %d: %x\n",
+ block, thisEUN, status);
+ break;
+ }
- if (thisEUN == 0xffff) thisEUN = 0;
-
- while(thisEUN && (thisEUN & 0x7fff) != 0x7fff) {
- struct nftl_bci bci;
- size_t retlen;
-
- MTD_READOOB(thisNFTL->mtd, (thisEUN * thisNFTL->EraseSize) + blockofs,8, &retlen, (char *)&bci);
-
- switch(bci.Status) {
- case __constant_cpu_to_le16(BLOCK_FREE):
- thisEUN = 0;
- break;
- case __constant_cpu_to_le16(BLOCK_USED):
- lastgoodEUN = thisEUN;
- break;
- case __constant_cpu_to_le16(BLOCK_IGNORE):
- case __constant_cpu_to_le16(BLOCK_DELETED):
- break;
- default:
- printk("Unknown status for block %d in EUN %d: %x\n",block,thisEUN, bci.Status);
+ if (!silly--) {
+ printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
+ block / (nftl->EraseSize / 512));
+ return 1;
+ }
+ thisEUN = nftl->ReplUnitTable[thisEUN];
}
+ }
- if (!silly--) {
- printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",block / (thisNFTL->EraseSize / 512));
- return 1;
- }
- if (thisEUN)
- thisEUN = thisNFTL->ReplUnitTable[thisEUN] & 0x7fff;
- }
- if (lastgoodEUN == 0xffff) {
+ the_end:
+ if (lastgoodEUN == BLOCK_NIL) {
+ /* the requested block is not on the media, return all 0x00 */
memset(buffer, 0, 512);
} else {
- loff_t ptr = (lastgoodEUN * thisNFTL->EraseSize) + blockofs;
+ loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
size_t retlen;
u_char eccbuf[6];
- thisNFTL->mtd->read_ecc(thisNFTL->mtd, ptr, 512, &retlen, buffer, eccbuf);
+ if (MTD_READECC(nftl->mtd, ptr, 512, &retlen, buffer, eccbuf))
+ return -EIO;
}
return 0;
}
-
-static int nftl_ioctl(struct inode * inode, struct file * file,
- unsigned int cmd, unsigned long arg)
+static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
{
- struct NFTLrecord *thisNFTL;
-
- thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16];
+ struct NFTLrecord *nftl;
- if (!thisNFTL) return -EINVAL;
+ nftl = NFTLs[MINOR(inode->i_rdev) / 16];
+ if (!nftl) return -EINVAL;
switch (cmd) {
case HDIO_GETGEO: {
struct hd_geometry g;
- g.heads = thisNFTL->heads;
- g.sectors = thisNFTL->sectors;
- g.cylinders = thisNFTL->cylinders;
+ g.heads = nftl->heads;
+ g.sectors = nftl->sectors;
+ g.cylinders = nftl->cylinders;
g.start = part_table[MINOR(inode->i_rdev)].start_sect;
return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0;
}
case BLKGETSIZE: /* Return device size */
- if (!arg) return -EINVAL;
+ if (!arg) return -EINVAL;
return put_user(part_table[MINOR(inode->i_rdev)].nr_sects,
(long *) arg);
case BLKFLSBUF:
- if(!capable(CAP_SYS_ADMIN)) return -EACCES;
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
fsync_dev(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
- if (thisNFTL->mtd->sync)
- thisNFTL->mtd->sync(thisNFTL->mtd);
+ if (nftl->mtd->sync)
+ nftl->mtd->sync(nftl->mtd);
return 0;
case BLKRRPART:
if (!capable(CAP_SYS_ADMIN)) return -EACCES;
- if (thisNFTL->usecount > 1) {
- // printk("Use count %d\n", thisNFTL->usecount);
- return -EBUSY;
- }
+ if (nftl->usecount > 1) return -EBUSY;
#if LINUX_VERSION_CODE < 0x20328
- resetup_one_dev(&nftl_gendisk, MINOR(inode->i_dev) / 16);
+ resetup_one_dev(&nftl_gendisk, MINOR(inode->i_rdev) / 16);
#else
- grok_partitions(&nftl_gendisk, MINOR(inode->i_dev) / 16, 1<<4, thisNFTL->nr_sects);
+ grok_partitions(&nftl_gendisk, MINOR(inode->i_rdev) / 16,
+ 1<<4, nftl->nr_sects);
#endif
return 0;
-
- // RO_IOCTLS(inode->i_rdev, arg); /* ref. linux/blk.h */
+
+#if (LINUX_VERSION_CODE < 0x20303)
+ RO_IOCTLS(inode->i_rdev, arg); /* ref. linux/blk.h */
+#else
+ case BLKROSET:
+ case BLKROGET:
+ case BLKSSZGET:
+ return blk_ioctl(inode->i_rdev, cmd, arg);
+#endif
+
default:
return -EINVAL;
}
}
-
void nftl_request(RQFUNC_ARG)
{
unsigned int dev, block, nsect;
- struct NFTLrecord *thisNFTL;
+ struct NFTLrecord *nftl;
char *buffer;
struct request *req;
int res;
while (1) {
INIT_REQUEST; /* blk.h */
-
- req = CURRENT;
-#ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK
- blkdev_dequeue_request(req);
- spin_unlock_irq(&io_request_lock);
-#else
req = CURRENT;
-#endif
- DEBUG(2,"NFTL_request\n");
- DEBUG(3,"NFTL %d request, %lx, %lx", req->cmd,
- req->sector, req->current_nr_sectors);
+ /* We can do this because the generic code knows not to
+ touch the request at the head of the queue */
+ spin_unlock_irq(&io_request_lock);
+
+ DEBUG(MTD_DEBUG_LEVEL2, "NFTL_request\n");
+ DEBUG(MTD_DEBUG_LEVEL3, "NFTL %s request, from sector 0x%04lx for 0x%04lx sectors\n",
+ (req->cmd == READ) ? "Read " : "Write",
+ req->sector, req->current_nr_sectors);
+
dev = MINOR(req->rq_dev);
block = req->sector;
nsect = req->current_nr_sectors;
@@ -1125,21 +887,23 @@ void nftl_request(RQFUNC_ARG)
res = 1; /* succeed */
if (dev >= MAX_NFTLS * 16) {
- printk("fl: bad minor number: device=%s\n",
+ /* there is no such partition */
+ printk("nftl: bad minor number: device = %s\n",
kdevname(req->rq_dev));
res = 0; /* fail */
goto repeat;
}
- thisNFTL = NFTLs[dev / 16];
- DEBUG(3,"Waiting for mutex\n");
- down(&thisNFTL->mutex);
- DEBUG(3,"Got mutex\n");
-
- if (block + nsect >= part_table[dev].nr_sects) {
- printk("nftl%c%d: bad access: block=%d, count=%d\n",
+ nftl = NFTLs[dev / 16];
+ DEBUG(MTD_DEBUG_LEVEL3, "Waiting for mutex\n");
+ down(&nftl->mutex);
+ DEBUG(MTD_DEBUG_LEVEL3, "Got mutex\n");
+
+ if (block + nsect > part_table[dev].nr_sects) {
+ /* access past the end of device */
+ printk("nftl%c%d: bad access: block = %d, count = %d\n",
(MINOR(req->rq_dev)>>6)+'a', dev & 0xf, block, nsect);
- up(&thisNFTL->mutex);
+ up(&nftl->mutex);
res = 0; /* fail */
goto repeat;
}
@@ -1147,75 +911,80 @@ void nftl_request(RQFUNC_ARG)
block += part_table[dev].start_sect;
if (req->cmd == READ) {
- DEBUG(2,"NFTL read\n");
- for ( ; nsect > 0; nsect-- , block++, buffer+= 512) {
+ DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request of 0x%x sectors @ %x "
+ "(req->nr_sectors == %lx)\n", nsect, block, req->nr_sectors);
+
+ for ( ; nsect > 0; nsect-- , block++, buffer += 512) {
/* Read a single sector to req->buffer + (512 * i) */
-
- if (NFTL_readblock(thisNFTL, block, buffer)) {
- DEBUG(2,"NFTL read request failed\n");
- up(&thisNFTL->mutex);
+ if (NFTL_readblock(nftl, block, buffer)) {
+ DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request failed\n");
+ up(&nftl->mutex);
res = 0;
goto repeat;
}
}
- DEBUG(2,"NFTL read request completed OK\n");
- up(&thisNFTL->mutex);
+
+ DEBUG(MTD_DEBUG_LEVEL2,"NFTL read request completed OK\n");
+ up(&nftl->mutex);
goto repeat;
- }
- else if (req->cmd == WRITE) {
- DEBUG(2,"NFTL write request of 0x%x sectors @ %x (req->nr_sectors == %lx\n",nsect, block, req->nr_sectors);
+ } else if (req->cmd == WRITE) {
+ DEBUG(MTD_DEBUG_LEVEL2, "NFTL write request of 0x%x sectors @ %x "
+ "(req->nr_sectors == %lx)\n", nsect, block,
+ req->nr_sectors);
#ifdef CONFIG_NFTL_RW
- for ( ; nsect > 0; nsect-- , block++, buffer+= 512) {
+ for ( ; nsect > 0; nsect-- , block++, buffer += 512) {
/* Read a single sector to req->buffer + (512 * i) */
-
- if (NFTL_writeblock(thisNFTL, block, buffer)) {
- DEBUG(1,"NFTL write request failed\n");
-
- up(&thisNFTL->mutex);
+ if (NFTL_writeblock(nftl, block, buffer)) {
+ DEBUG(MTD_DEBUG_LEVEL1,"NFTL write request failed\n");
+ up(&nftl->mutex);
res = 0;
goto repeat;
}
}
- DEBUG(2,"NFTL write request completed OK\n");
+ DEBUG(MTD_DEBUG_LEVEL2,"NFTL write request completed OK\n");
#else
- res=0; /* Writes always fail */
+ res = 0; /* Writes always fail */
#endif /* CONFIG_NFTL_RW */
- up(&thisNFTL->mutex);
+ up(&nftl->mutex);
goto repeat;
- }
- else {
- DEBUG(0,"NFTL ??? request\n");
- up(&thisNFTL->mutex);
+ } else {
+ DEBUG(MTD_DEBUG_LEVEL0, "NFTL unknown request\n");
+ up(&nftl->mutex);
res = 0;
goto repeat;
}
repeat:
- DEBUG(3,"end_request(%d)\n", res);
-#ifdef WE_KNOW_WTF_THIS_DOES_NOT_WORK
+ DEBUG(MTD_DEBUG_LEVEL3, "end_request(%d)\n", res);
spin_lock_irq(&io_request_lock);
- nftl_end_request(req, res);
-#else
end_request(res);
-#endif
}
}
static int nftl_open(struct inode *ip, struct file *fp)
{
+ int nftlnum = MINOR(ip->i_rdev) / 16;
struct NFTLrecord *thisNFTL;
- thisNFTL = NFTLs[MINOR(ip->i_rdev) / 16];
+ thisNFTL = NFTLs[nftlnum];
- DEBUG(2,"NFTL_open\n");
+ DEBUG(MTD_DEBUG_LEVEL2,"NFTL_open\n");
+#ifdef CONFIG_KMOD
+ if (!thisNFTL && nftlnum == 0) {
+ request_module("docprobe");
+ thisNFTL = NFTLs[nftlnum];
+ }
+#endif
if (!thisNFTL) {
- DEBUG(2,"ENODEV: thisNFTL = %d, minor = %d, ip = %p, fp = %p\n",
- MINOR(ip->i_rdev) / 16,ip->i_rdev,ip, fp);
+ DEBUG(MTD_DEBUG_LEVEL2,"ENODEV: thisNFTL = %d, minor = %d, ip = %p, fp = %p\n",
+ nftlnum, ip->i_rdev, ip, fp);
return -ENODEV;
}
+
#ifndef CONFIG_NFTL_RW
if (fp->f_mode & FMODE_WRITE)
- return -EROFS;
+ return -EROFS;
#endif /* !CONFIG_NFTL_RW */
+
thisNFTL->usecount++;
MOD_INC_USE_COUNT;
if (!get_mtd_device(thisNFTL->mtd, -1)) {
@@ -1233,8 +1002,8 @@ static int nftl_release(struct inode *inode, struct file *fp)
thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16];
- DEBUG(2, "NFTL_release\n");
-
+ DEBUG(MTD_DEBUG_LEVEL2, "NFTL_release\n");
+
fsync_dev(inode->i_rdev);
if (sb)
invalidate_inodes(sb);
@@ -1251,19 +1020,19 @@ static int nftl_release(struct inode *inode, struct file *fp)
}
#if LINUX_VERSION_CODE < 0x20326
static struct file_operations nftl_fops = {
- read: block_read,
- write: block_write,
- ioctl: nftl_ioctl,
- open: nftl_open,
- release: nftl_release,
- fsync: block_fsync,
+ read: block_read,
+ write: block_write,
+ ioctl: nftl_ioctl,
+ open: nftl_open,
+ release: nftl_release,
+ fsync: block_fsync,
};
#else
static struct block_device_operations nftl_fops =
{
- open: nftl_open,
- release: nftl_release,
- ioctl: nftl_ioctl
+ open: nftl_open,
+ release: nftl_release,
+ ioctl: nftl_ioctl
};
#endif
@@ -1275,39 +1044,39 @@ static struct block_device_operations nftl_fops =
*
****************************************************************************/
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_nftl init_module
#define cleanup_nftl cleanup_module
#endif
-#define __exit
-#endif
static struct mtd_notifier nftl_notifier = {NFTL_notify_add, NFTL_notify_remove, NULL};
-
/* static int __init init_nftl(void) */
int __init init_nftl(void)
{
int i;
- printk(KERN_NOTICE "M-Systems NAND Flash Translation Layer driver. (C) 1999 MVHI\n");
+ printk(KERN_NOTICE
+ "M-Systems NAND Flash Translation Layer driver. (C) 1999 MVHI\n");
#ifdef PRERELEASE
- printk(KERN_INFO"$Id: nftl.c,v 1.36 2000/07/13 14:14:20 dwmw2 Exp $\n");
+ printk(KERN_INFO"$Id: nftl.c,v 1.57 2000/12/01 17:51:54 dwmw2 Exp $\n");
#endif
- if (register_blkdev(NFTL_MAJOR, "nftl", &nftl_fops)){
- printk("unable to register NFTL block device\n");
+ if (register_blkdev(MAJOR_NR, "nftl", &nftl_fops)){
+ printk("unable to register NFTL block device on major %d\n", MAJOR_NR);
+ return -EBUSY;
} else {
#if LINUX_VERSION_CODE < 0x20320
- blk_dev[MAJOR_NR].request_fn = nftl_request;
+ blk_dev[MAJOR_NR].request_fn = nftl_request;
#else
- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request);
+ blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request);
#endif
- for (i=0; i < 256 ; i++) {
+ /* set block size to 1kB each */
+ for (i = 0; i < 256; i++) {
nftl_blocksizes[i] = 1024;
}
- blksize_size[NFTL_MAJOR] = nftl_blocksizes;
+ blksize_size[MAJOR_NR] = nftl_blocksizes;
+
nftl_gendisk.next = gendisk_head;
gendisk_head = &nftl_gendisk;
}
@@ -1319,25 +1088,25 @@ int __init init_nftl(void)
static void __exit cleanup_nftl(void)
{
- struct gendisk *gd, **gdp;
+ struct gendisk *gd, **gdp;
- unregister_mtd_user(&nftl_notifier);
-
- unregister_blkdev(NFTL_MAJOR, "nftl");
+ unregister_mtd_user(&nftl_notifier);
+ unregister_blkdev(MAJOR_NR, "nftl");
+
#if LINUX_VERSION_CODE < 0x20320
- blk_dev[MAJOR_NR].request_fn = 0;
+ blk_dev[MAJOR_NR].request_fn = 0;
#else
- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+ blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
#endif
- for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
- if (*gdp == &nftl_gendisk) {
- gd = *gdp; *gdp = gd->next;
- break;
- }
-
+
+ /* remove ourself from generic harddisk list
+ FIXME: why can't I found this partition on /proc/partition */
+ for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
+ if (*gdp == &nftl_gendisk) {
+ gd = *gdp; *gdp = gd->next;
+ break;
+ }
}
-#if LINUX_VERSION_CODE > 0x20300
module_init(init_nftl);
module_exit(cleanup_nftl);
-#endif
diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c
new file mode 100644
index 000000000..d1f05930f
--- /dev/null
+++ b/drivers/mtd/nftlmount.c
@@ -0,0 +1,678 @@
+/*
+ * NFTL mount code with extensive checks
+ *
+ * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
+ * Copyright (C) 2000 Netgem S.A.
+ *
+ * $Id: nftlmount.c,v 1.11 2000/11/17 12:24:09 ollie Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nftl.h>
+#include <linux/mtd/compatmac.h>
+
+#define SECTORSIZE 512
+
+/* 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
+ * the ReplUnitTable[] table accroding to the Bad Unit Table. ReplUnitTable[]
+ * is used for management of Erase Unit in other routines in nftl.c and nftlmount.c
+ */
+static int find_boot_record(struct NFTLrecord *nftl)
+{
+ struct nftl_uci1 h1;
+ struct nftl_oob oob;
+ unsigned int block, boot_record_count;
+ int retlen;
+ u8 buf[SECTORSIZE];
+ struct NFTLMediaHeader *mh = &nftl->MediaHdr;
+
+ nftl->MediaUnit = BLOCK_NIL;
+ nftl->SpareMediaUnit = BLOCK_NIL;
+ boot_record_count = 0;
+
+ /* search for a valid boot record */
+ for (block = 0; block < nftl->nb_blocks; block++) {
+ unsigned int erase_mark;
+
+ /* read ANAND header. To be safer with BIOS, also use erase mark as discriminant */
+ if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8,
+ 8, &retlen, (char *)&h1) < 0)
+ continue;
+
+ erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
+ if (erase_mark != ERASE_MARK)
+ continue;
+
+ if (MTD_READECC(nftl->mtd, block * nftl->EraseSize, SECTORSIZE,
+ &retlen, buf, (char *)&oob) < 0)
+ continue;
+
+ memcpy(mh, buf, sizeof(struct NFTLMediaHeader));
+ if (memcmp(mh->DataOrgID, "ANAND", 6) == 0) {
+ /* first boot record */
+ if (boot_record_count == 0) {
+ unsigned int i;
+ /* header found : read the bad block table data */
+ if (mh->UnitSizeFactor != 0xff) {
+ printk("Sorry, we don't support UnitSizeFactor "
+ "of != 1 yet\n");
+ goto ReplUnitTable;
+ }
+
+ nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN);
+ if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks)
+ goto ReplUnitTable; /* small consistency check */
+
+ nftl->numvunits = le32_to_cpu(mh->FormattedSize) / nftl->EraseSize;
+ if (nftl->numvunits > (nftl->nb_blocks - nftl->nb_boot_blocks - 2))
+ goto ReplUnitTable; /* small consistency check */
+
+ /* FixMe: with bad blocks, the total size available is not FormattedSize any
+ more !!! */
+ nftl->nr_sects = nftl->numvunits * (nftl->EraseSize / SECTORSIZE);
+ nftl->MediaUnit = block;
+
+ /* read the Bad Erase Unit Table and modify ReplUnitTable[] accordingly */
+ for (i = 0; i < nftl->nb_blocks; i++) {
+ if ((i & (SECTORSIZE - 1)) == 0) {
+ /* read one sector for every SECTORSIZE of blocks */
+ if (MTD_READECC(nftl->mtd, block * nftl->EraseSize +
+ i + SECTORSIZE, SECTORSIZE,
+ &retlen, buf, (char *)&oob) < 0)
+ goto ReplUnitTable;
+ }
+ /* mark the Bad Erase Unit as RESERVED in ReplUnitTable */
+ if (buf[i & (SECTORSIZE - 1)] != 0xff)
+ nftl->ReplUnitTable[i] = BLOCK_RESERVED;
+ }
+
+ boot_record_count++;
+ } else if (boot_record_count == 1) {
+ nftl->SpareMediaUnit = block;
+ boot_record_count++;
+ break;
+ }
+ }
+ ReplUnitTable:
+ }
+
+ if (boot_record_count == 0) {
+ /* no boot record found */
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+static int memcmpb(void *a, int c, int n)
+{
+ int i;
+ for (i = 0; i < n; i++) {
+ if (c != ((unsigned char *)a)[i])
+ return 1;
+ }
+ return 0;
+}
+
+/* check_free_sector: check if a free sector is actually FREE, i.e. All 0xff in data and oob area */
+static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int len,
+ int check_oob)
+{
+ int i, retlen;
+ u8 buf[SECTORSIZE];
+
+ for (i = 0; i < len; i += SECTORSIZE) {
+ /* we want to read the sector without ECC check here since a free
+ sector does not have ECC syndrome on it yet */
+ if (MTD_READ(nftl->mtd, address, SECTORSIZE, &retlen, buf) < 0)
+ return -1;
+ if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
+ return -1;
+
+ if (check_oob) {
+ if (MTD_READOOB(nftl->mtd, address, nftl->mtd->oobsize,
+ &retlen, buf) < 0)
+ return -1;
+ if (memcmpb(buf, 0xff, nftl->mtd->oobsize) != 0)
+ return -1;
+ }
+ address += SECTORSIZE;
+ }
+
+ return 0;
+}
+
+/* NFTL_format: format a Erase Unit by erasing ALL Erase Zones in the Erase Unit and
+ * Update NFTL metadata. Each erase operation is checked with check_free_sectors
+ *
+ * Return: 0 when succeed, -1 on error.
+ *
+ * ToDo: 1. Is it neceressary to check_free_sector after erasing ??
+ * 2. UnitSizeFactor != 0xFF
+ */
+int NFTL_formatblock(struct NFTLrecord *nftl, int block)
+{
+ int retlen;
+ unsigned int nb_erases, erase_mark;
+ struct nftl_uci1 uci;
+ struct erase_info *instr = &nftl->instr;
+
+ /* Read the Unit Control Information #1 for Wear-Leveling */
+ if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8,
+ 8, &retlen, (char *)&uci) < 0)
+ goto default_uci1;
+
+ erase_mark = le16_to_cpu ((uci.EraseMark | uci.EraseMark1));
+ if (erase_mark != ERASE_MARK) {
+ default_uci1:
+ uci.EraseMark = cpu_to_le16(ERASE_MARK);
+ uci.EraseMark1 = cpu_to_le16(ERASE_MARK);
+ uci.WearInfo = cpu_to_le32(0);
+ }
+
+ memset(instr, 0, sizeof(struct erase_info));
+
+ /* XXX: use async erase interface, XXX: test return code */
+ instr->addr = block * nftl->EraseSize;
+ instr->len = nftl->EraseSize;
+ MTD_ERASE(nftl->mtd, instr);
+
+ if (instr->state == MTD_ERASE_FAILED) {
+ /* could not format, FixMe: We should update the BadUnitTable
+ both in memory and on disk */
+ printk("Error while formatting block %d\n", block);
+ return -1;
+ } else {
+ /* increase and write Wear-Leveling info */
+ nb_erases = le32_to_cpu(uci.WearInfo);
+ nb_erases++;
+
+ /* wrap (almost impossible with current flashs) or free block */
+ if (nb_erases == 0)
+ nb_erases = 1;
+
+ /* check the "freeness" of Erase Unit before updating metadata
+ * FixMe: is this check really necessary ? since we have check the
+ * return code after the erase operation. */
+ if (check_free_sectors(nftl, instr->addr, nftl->EraseSize, 1) != 0)
+ return -1;
+
+ uci.WearInfo = le32_to_cpu(nb_erases);
+ if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
+ &retlen, (char *)&uci) < 0)
+ return -1;
+ return 0;
+ }
+}
+
+/* check_sectors_in_chain: Check that each sector of a Virtual Unit Chain is correct.
+ * Mark as 'IGNORE' each incorrect sector. This check is only done if the chain
+ * was being folded when NFTL was interrupted.
+ *
+ * The check_free_sectors in this function is neceressary. There is a possible
+ * situation that after writing the Data area, the Block Control Information is
+ * not updated according (due to power failure or something) which leaves the block
+ * in an umconsistent state. So we have to check if a block is really FREE in this
+ * case. */
+static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_block)
+{
+ unsigned int block, i, status;
+ struct nftl_bci bci;
+ int sectors_per_block, retlen;
+
+ sectors_per_block = nftl->EraseSize / SECTORSIZE;
+ block = first_block;
+ for (;;) {
+ for (i = 0; i < sectors_per_block; i++) {
+ if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i * SECTORSIZE,
+ 8, &retlen, (char *)&bci) < 0)
+ status = SECTOR_IGNORE;
+ else
+ status = bci.Status | bci.Status1;
+
+ switch(status) {
+ case SECTOR_FREE:
+ /* verify that the sector is really free. If not, mark
+ as ignore */
+ if (memcmpb(&bci, 0xff, 8) != 0 ||
+ check_free_sectors(nftl, block * nftl->EraseSize + i * SECTORSIZE,
+ SECTORSIZE, 0) != 0) {
+ printk("Incorrect free sector %d in block %d: "
+ "marking it as ignored\n",
+ i, block);
+
+ /* sector not free actually : mark it as SECTOR_IGNORE */
+ bci.Status = SECTOR_IGNORE;
+ bci.Status1 = SECTOR_IGNORE;
+ MTD_WRITEOOB(nftl->mtd,
+ block * nftl->EraseSize + i * SECTORSIZE,
+ 8, &retlen, (char *)&bci);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* proceed to next Erase Unit on the chain */
+ block = nftl->ReplUnitTable[block];
+ if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
+ printk("incorrect ReplUnitTable[] : %d\n", block);
+ if (block == BLOCK_NIL || block >= nftl->nb_blocks)
+ break;
+ }
+}
+
+/* calc_chain_lenght: Walk through a Virtual Unit Chain and estimate chain length */
+static int calc_chain_length(struct NFTLrecord *nftl, unsigned int first_block)
+{
+ unsigned int length = 0, block = first_block;
+
+ for (;;) {
+ length++;
+ /* avoid infinite loops, although this is guaranted not to
+ happen because of the previous checks */
+ if (length >= nftl->nb_blocks) {
+ printk("nftl: length too long %d !\n", length);
+ break;
+ }
+
+ block = nftl->ReplUnitTable[block];
+ if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
+ printk("incorrect ReplUnitTable[] : %d\n", block);
+ if (block == BLOCK_NIL || block >= nftl->nb_blocks)
+ break;
+ }
+ return length;
+}
+
+/* format_chain: Format an invalid Virtual Unit chain. It frees all the Erase Units in a
+ * Virtual Unit Chain, i.e. all the units are disconnected.
+ *
+ * It is not stricly correct to begin from the first block of the chain because
+ * if we stop the code, we may see again a valid chain if there was a first_block
+ * flag in a block inside it. But is it really a problem ?
+ *
+ * FixMe: Figure out what the last statesment means. What if power failure when we are
+ * in the for (;;) loop formatting blocks ??
+ */
+static void format_chain(struct NFTLrecord *nftl, unsigned int first_block)
+{
+ unsigned int block = first_block, block1;
+
+ printk("Formatting chain at block %d\n", first_block);
+
+ for (;;) {
+ block1 = nftl->ReplUnitTable[block];
+
+ printk("Formatting block %d\n", block);
+ if (NFTL_formatblock(nftl, block) < 0) {
+ /* cannot format !!!! Mark it as Bad Unit,
+ FixMe: update the BadUnitTable on disk */
+ nftl->ReplUnitTable[block] = BLOCK_RESERVED;
+ } else {
+ nftl->ReplUnitTable[block] = BLOCK_FREE;
+ }
+
+ /* goto next block on the chain */
+ block = block1;
+
+ if (!(block == BLOCK_NIL || block < nftl->nb_blocks))
+ printk("incorrect ReplUnitTable[] : %d\n", block);
+ if (block == BLOCK_NIL || block >= nftl->nb_blocks)
+ break;
+ }
+}
+
+/* check_and_mark_free_block: Verify that a block is free in the NFTL sense (valid erase mark) or
+ * totally free (only 0xff).
+ *
+ * Definition: Free Erase Unit -- A properly erased/formatted Free Erase Unit should have meet the
+ * following critia:
+ * 1. */
+static int check_and_mark_free_block(struct NFTLrecord *nftl, int block)
+{
+ struct nftl_uci1 h1;
+ unsigned int erase_mark;
+ int i, retlen;
+ unsigned char buf[SECTORSIZE];
+
+ /* check erase mark. */
+ if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
+ &retlen, (char *)&h1) < 0)
+ return -1;
+
+ erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
+ if (erase_mark != ERASE_MARK) {
+ /* if no erase mark, the block must be totally free. This is
+ possible in two cases : empty filsystem or interrupted erase (very unlikely) */
+ if (check_free_sectors (nftl, block * nftl->EraseSize, nftl->EraseSize, 1) != 0)
+ return -1;
+
+ /* free block : write erase mark */
+ h1.EraseMark = cpu_to_le16(ERASE_MARK);
+ h1.EraseMark1 = cpu_to_le16(ERASE_MARK);
+ h1.WearInfo = cpu_to_le32(0);
+ if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
+ &retlen, (char *)&h1) < 0)
+ return -1;
+ } else {
+#if 0
+ /* if erase mark present, need to skip it when doing check */
+ for (i = 0; i < nftl->EraseSize; i += SECTORSIZE) {
+ /* check free sector */
+ if (check_free_sectors (nftl, block * nftl->EraseSize + i,
+ SECTORSIZE, 0) != 0)
+ return -1;
+
+ if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i,
+ 16, &retlen, buf) < 0)
+ return -1;
+ if (i == SECTORSIZE) {
+ /* skip erase mark */
+ if (memcmpb(buf, 0xff, 8))
+ return -1;
+ } else {
+ if (memcmpb(buf, 0xff, 16))
+ return -1;
+ }
+ }
+#endif
+ }
+
+ return 0;
+}
+
+/* get_fold_mark: Read fold mark from Unit Control Information #2, we use FOLD_MARK_IN_PROGRESS
+ * to indicate that we are in the progression of a Virtual Unit Chain folding. If the UCI #2
+ * is FOLD_MARK_IN_PROGRESS when mounting the NFTL, the (previous) folding process is interrupted
+ * for some reason. A clean up/check of the VUC is neceressary in this case.
+ *
+ * WARNING: return 0 if read error
+ */
+static int get_fold_mark(struct NFTLrecord *nftl, unsigned int block)
+{
+ struct nftl_uci2 uci;
+ int retlen;
+
+ if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8,
+ 8, &retlen, (char *)&uci) < 0)
+ return 0;
+
+ return le16_to_cpu((uci.FoldMark | uci.FoldMark1));
+}
+
+int NFTL_mount(struct NFTLrecord *s)
+{
+ int i;
+ unsigned int first_logical_block, logical_block, rep_block, nb_erases, erase_mark;
+ unsigned int block, first_block, is_first_block;
+ int chain_length, do_format_chain;
+ struct nftl_uci0 h0;
+ struct nftl_uci1 h1;
+ int retlen;
+
+ /* XXX: will be suppressed */
+ s->lastEUN = s->nb_blocks - 1;
+
+ /* memory alloc */
+ s->EUNtable = kmalloc(s->nb_blocks * sizeof(u16), GFP_KERNEL);
+ s->ReplUnitTable = kmalloc(s->nb_blocks * sizeof(u16), GFP_KERNEL);
+ if (!s->EUNtable || !s->ReplUnitTable) {
+ fail:
+ if (s->EUNtable)
+ kfree(s->EUNtable);
+ if (s->ReplUnitTable)
+ kfree(s->ReplUnitTable);
+ return -1;
+ }
+
+ /* mark all blocks as potentially containing data */
+ for (i = 0; i < s->nb_blocks; i++) {
+ s->ReplUnitTable[i] = BLOCK_NOTEXPLORED;
+ }
+
+ /* search for NFTL MediaHeader and Spare NFTL Media Header */
+ if (find_boot_record(s) < 0) {
+ printk("Could not find valid boot record\n");
+ goto fail;
+ }
+
+ /* mark the bios blocks (blocks before NFTL MediaHeader) as reserved */
+ for (i = 0; i < s->nb_boot_blocks; i++)
+ s->ReplUnitTable[i] = BLOCK_RESERVED;
+
+ /* also mark the boot records (NFTL MediaHeader) blocks as reserved */
+ if (s->MediaUnit != BLOCK_NIL)
+ s->ReplUnitTable[s->MediaUnit] = BLOCK_RESERVED;
+ if (s->SpareMediaUnit != BLOCK_NIL)
+ s->ReplUnitTable[s->SpareMediaUnit] = BLOCK_RESERVED;
+
+ /* init the logical to physical table */
+ for (i = 0; i < s->nb_blocks; i++) {
+ s->EUNtable[i] = BLOCK_NIL;
+ }
+
+ /* first pass : explore each block chain */
+ first_logical_block = 0;
+ for (first_block = 0; first_block < s->nb_blocks; first_block++) {
+ /* if the block was not already explored, we can look at it */
+ if (s->ReplUnitTable[first_block] == BLOCK_NOTEXPLORED) {
+ block = first_block;
+ chain_length = 0;
+ do_format_chain = 0;
+
+ for (;;) {
+ /* read the block header. If error, we format the chain */
+ if (MTD_READOOB(s->mtd, block * s->EraseSize + 8, 8,
+ &retlen, (char *)&h0) < 0 ||
+ MTD_READOOB(s->mtd, block * s->EraseSize + SECTORSIZE + 8, 8,
+ &retlen, (char *)&h1) < 0) {
+ s->ReplUnitTable[block] = BLOCK_NIL;
+ do_format_chain = 1;
+ break;
+ }
+
+ logical_block = le16_to_cpu ((h0.VirtUnitNum | h0.SpareVirtUnitNum));
+ rep_block = le16_to_cpu ((h0.ReplUnitNum | h0.SpareReplUnitNum));
+ nb_erases = le32_to_cpu (h1.WearInfo);
+ erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
+
+ is_first_block = !(logical_block >> 15);
+ logical_block = logical_block & 0x7fff;
+
+ /* invalid/free block test */
+ if (erase_mark != ERASE_MARK || logical_block >= s->nb_blocks) {
+ if (chain_length == 0) {
+ /* if not currently in a chain, we can handle it safely */
+ if (check_and_mark_free_block(s, block) < 0) {
+ /* not really free: format it */
+ printk("Formatting block %d\n", block);
+ if (NFTL_formatblock(s, block) < 0) {
+ /* could not format: reserve the block */
+ s->ReplUnitTable[block] = BLOCK_RESERVED;
+ } else {
+ s->ReplUnitTable[block] = BLOCK_FREE;
+ }
+ } else {
+ /* free block: mark it */
+ s->ReplUnitTable[block] = BLOCK_FREE;
+ }
+ /* directly examine the next block. */
+ goto examine_ReplUnitTable;
+ } else {
+ /* the block was in a chain : this is bad. We
+ must format all the chain */
+ printk("Block %d: free but referenced in chain %d\n",
+ block, first_block);
+ s->ReplUnitTable[block] = BLOCK_NIL;
+ do_format_chain = 1;
+ break;
+ }
+ }
+
+ /* we accept only first blocks here */
+ if (chain_length == 0) {
+ /* this block is not the first block in chain :
+ ignore it, it will be included in a chain
+ later, or marked as not explored */
+ if (!is_first_block)
+ goto examine_ReplUnitTable;
+ first_logical_block = logical_block;
+ } else {
+ if (logical_block != first_logical_block) {
+ printk("Block %d: incorrect logical block: %d expected: %d\n",
+ block, logical_block, first_logical_block);
+ /* the chain is incorrect : we must format it,
+ but we need to read it completly */
+ do_format_chain = 1;
+ }
+ if (is_first_block) {
+ /* we accept that a block is marked as first
+ block while being last block in a chain
+ only if the chain is being folded */
+ if (get_fold_mark(s, block) != FOLD_MARK_IN_PROGRESS ||
+ rep_block != 0xffff) {
+ printk("Block %d: incorrectly marked as first block in chain\n",
+ block);
+ /* the chain is incorrect : we must format it,
+ but we need to read it completly */
+ do_format_chain = 1;
+ } else {
+ printk("Block %d: folding in progress - ignoring first block flag\n",
+ block);
+ }
+ }
+ }
+ chain_length++;
+ if (rep_block == 0xffff) {
+ /* no more blocks after */
+ s->ReplUnitTable[block] = BLOCK_NIL;
+ break;
+ } else if (rep_block >= s->nb_blocks) {
+ printk("Block %d: referencing invalid block %d\n",
+ block, rep_block);
+ do_format_chain = 1;
+ s->ReplUnitTable[block] = BLOCK_NIL;
+ break;
+ } else if (s->ReplUnitTable[rep_block] != BLOCK_NOTEXPLORED) {
+ /* same problem as previous 'is_first_block' test:
+ we accept that the last block of a chain has
+ the first_block flag set if folding is in
+ progress. We handle here the case where the
+ last block appeared first */
+ if (s->ReplUnitTable[rep_block] == BLOCK_NIL &&
+ s->EUNtable[first_logical_block] == rep_block &&
+ get_fold_mark(s, first_block) == FOLD_MARK_IN_PROGRESS) {
+ /* EUNtable[] will be set after */
+ printk("Block %d: folding in progress - ignoring first block flag\n",
+ rep_block);
+ s->ReplUnitTable[block] = rep_block;
+ s->EUNtable[first_logical_block] = BLOCK_NIL;
+ } else {
+ printk("Block %d: referencing block %d already in another chain\n",
+ block, rep_block);
+ /* XXX: should handle correctly fold in progress chains */
+ do_format_chain = 1;
+ s->ReplUnitTable[block] = BLOCK_NIL;
+ }
+ break;
+ } else {
+ /* this is OK */
+ s->ReplUnitTable[block] = rep_block;
+ block = rep_block;
+ }
+ }
+
+ /* the chain was completely explored. Now we can decide
+ what to do with it */
+ if (do_format_chain) {
+ /* invalid chain : format it */
+ format_chain(s, first_block);
+ } else {
+ unsigned int first_block1, chain_to_format, chain_length1;
+ int fold_mark;
+
+ /* valid chain : get foldmark */
+ fold_mark = get_fold_mark(s, first_block);
+ if (fold_mark == 0) {
+ /* cannot get foldmark : format the chain */
+ printk("Could read foldmark at block %d\n", first_block);
+ format_chain(s, first_block);
+ } else {
+ if (fold_mark == FOLD_MARK_IN_PROGRESS)
+ check_sectors_in_chain(s, first_block);
+
+ /* now handle the case where we find two chains at the
+ same virtual address : we select the longer one,
+ because the shorter one is the one which was being
+ folded if the folding was not done in place */
+ first_block1 = s->EUNtable[first_logical_block];
+ if (first_block1 != BLOCK_NIL) {
+ /* XXX: what to do if same length ? */
+ chain_length1 = calc_chain_length(s, first_block1);
+ printk("Two chains at blocks %d (len=%d) and %d (len=%d)\n",
+ first_block1, chain_length1, first_block, chain_length);
+
+ if (chain_length >= chain_length1) {
+ chain_to_format = first_block1;
+ s->EUNtable[first_logical_block] = first_block;
+ } else {
+ chain_to_format = first_block;
+ }
+ format_chain(s, chain_to_format);
+ } else {
+ s->EUNtable[first_logical_block] = first_block;
+ }
+ }
+ }
+ }
+ examine_ReplUnitTable:
+ }
+
+ /* second pass to format unreferenced blocks and init free block count */
+ s->numfreeEUNs = 0;
+ s->LastFreeEUN = BLOCK_NIL;
+
+ for (block = 0; block < s->nb_blocks; block++) {
+ if (s->ReplUnitTable[block] == BLOCK_NOTEXPLORED) {
+ printk("Unreferenced block %d, formatting it\n", block);
+ if (NFTL_formatblock(s, block) < 0)
+ s->ReplUnitTable[block] = BLOCK_RESERVED;
+ else
+ s->ReplUnitTable[block] = BLOCK_FREE;
+ }
+ if (s->ReplUnitTable[block] == BLOCK_FREE) {
+ s->numfreeEUNs++;
+ s->LastFreeEUN = block;
+ }
+ }
+
+ return 0;
+}
diff --git a/drivers/mtd/nora.c b/drivers/mtd/nora.c
index 9304d7e62..48da299fc 100644
--- a/drivers/mtd/nora.c
+++ b/drivers/mtd/nora.c
@@ -1,5 +1,5 @@
/*
- * $Id: nora.c,v 1.12 2000/07/13 10:32:33 dwmw2 Exp $
+ * $Id: nora.c,v 1.17 2000/12/03 19:32:21 dwmw2 Exp $
*
* This is so simple I love it.
*/
@@ -58,19 +58,17 @@ void nora_copy_to(struct map_info *map, unsigned long to, const void *from, ssiz
}
struct map_info nora_map = {
- "NORA",
- WINDOW_SIZE,
- 2,
- nora_read8,
- nora_read16,
- nora_read32,
- nora_copy_from,
- nora_write8,
- nora_write16,
- nora_write32,
- nora_copy_to,
- 0,
- 0
+ name: "NORA",
+ size: WINDOW_SIZE,
+ buswidth: 2,
+ read8: nora_read8,
+ read16: nora_read16,
+ read32: nora_read32,
+ copy_from: nora_copy_from,
+ write8: nora_write8,
+ write16: nora_write16,
+ write32: nora_write32,
+ copy_to: nora_copy_to
};
@@ -140,7 +138,7 @@ static struct mtd_info nora_mtds[4] = { /* boot, kernel, ramdisk, fs */
{
type: MTD_NORFLASH,
flags: MTD_CAP_NORFLASH,
- size: 0xf00000,
+ size: 0x900000,
erasesize: 0x20000,
name: "NORA root filesystem",
module: THIS_MODULE,
@@ -155,9 +153,9 @@ static struct mtd_info nora_mtds[4] = { /* boot, kernel, ramdisk, fs */
{
type: MTD_NORFLASH,
flags: MTD_CAP_NORFLASH,
- size: 0x1000000,
+ size: 0x1600000,
erasesize: 0x20000,
- name: "NORA main filesystem",
+ name: "NORA second filesystem",
module: THIS_MODULE,
erase: nora_mtd_erase,
read: nora_mtd_read,
@@ -165,16 +163,15 @@ static struct mtd_info nora_mtds[4] = { /* boot, kernel, ramdisk, fs */
suspend: nora_mtd_suspend,
resume: nora_mtd_resume,
sync: nora_mtd_sync,
- priv: (void *)0x1000000
+ priv: (void *)0xa00000
}
};
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_nora init_module
#define cleanup_nora cleanup_module
#endif
-#endif
int __init init_nora(void)
{
@@ -186,10 +183,10 @@ int __init init_nora(void)
mymtd->module = &__this_module;
#endif
- add_mtd_device(&nora_mtds[3]);
+ add_mtd_device(&nora_mtds[2]);
add_mtd_device(&nora_mtds[0]);
add_mtd_device(&nora_mtds[1]);
- add_mtd_device(&nora_mtds[2]);
+ add_mtd_device(&nora_mtds[3]);
return 0;
}
@@ -199,10 +196,13 @@ int __init init_nora(void)
static void __exit cleanup_nora(void)
{
if (mymtd) {
- del_mtd_device(&nora_mtds[2]);
+ del_mtd_device(&nora_mtds[3]);
del_mtd_device(&nora_mtds[1]);
del_mtd_device(&nora_mtds[0]);
- del_mtd_device(&nora_mtds[3]);
+ del_mtd_device(&nora_mtds[2]);
map_destroy(mymtd);
}
}
+
+module_init(init_nora);
+module_exit(cleanup_nora);
diff --git a/drivers/mtd/octagon-5066.c b/drivers/mtd/octagon-5066.c
index b184cd0e7..70e78d7e9 100644
--- a/drivers/mtd/octagon-5066.c
+++ b/drivers/mtd/octagon-5066.c
@@ -1,4 +1,4 @@
-// $Id: octagon-5066.c,v 1.10 2000/07/13 14:04:23 dwmw2 Exp $
+// $Id: octagon-5066.c,v 1.12 2000/11/27 08:50:22 dwmw2 Exp $
/* ######################################################################
Octagon 5066 MTD Driver.
@@ -151,32 +151,32 @@ static void oct5066_copy_to(struct map_info *map, unsigned long to, const void *
static struct map_info oct5066_map[2] = {
{
- "Octagon 5066 Socket",
- 512 * 1024,
- 1,
- oct5066_read8,
- oct5066_read16,
- oct5066_read32,
- oct5066_copy_from,
- oct5066_write8,
- oct5066_write16,
- oct5066_write32,
- oct5066_copy_to,
- 1<<6
+ name: "Octagon 5066 Socket",
+ size: 512 * 1024,
+ buswidth: 1,
+ read8: oct5066_read8,
+ read16: oct5066_read16,
+ read32: oct5066_read32,
+ copy_from: oct5066_copy_from,
+ write8: oct5066_write8,
+ write16: oct5066_write16,
+ write32: oct5066_write32,
+ copy_to: oct5066_copy_to,
+ map_priv_1: 1<<6
},
{
- "Octagon 5066 Internal Flash",
- 2 * 1024 * 1024,
- 1,
- oct5066_read8,
- oct5066_read16,
- oct5066_read32,
- oct5066_copy_from,
- oct5066_write8,
- oct5066_write16,
- oct5066_write32,
- oct5066_copy_to,
- 2<<6
+ name: "Octagon 5066 Internal Flash",
+ size: 2 * 1024 * 1024,
+ buswidth: 1,
+ read8: oct5066_read8,
+ read16: oct5066_read16,
+ read32: oct5066_read32,
+ copy_from: oct5066_copy_from,
+ write8: oct5066_write8,
+ write16: oct5066_write16,
+ write32: oct5066_write32,
+ copy_to: oct5066_copy_to,
+ map_priv_1: 2<<6
}
};
@@ -213,13 +213,10 @@ static int __init OctProbe()
return 0;
}
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_oct5066 init_module
#define cleanup_oct5066 cleanup_module
#endif
-#define __exit
-#endif
void cleanup_oct5066(void)
{
@@ -284,7 +281,5 @@ int __init init_oct5066(void)
return 0;
}
-#if LINUX_VERSION_CODE > 0x20300
module_init(init_oct5066);
module_exit(cleanup_oct5066);
-#endif
diff --git a/drivers/mtd/physmap.c b/drivers/mtd/physmap.c
index 658b8bf47..31ac39310 100644
--- a/drivers/mtd/physmap.c
+++ b/drivers/mtd/physmap.c
@@ -1,5 +1,5 @@
/*
- * $Id: physmap.c,v 1.2 2000/07/11 09:42:32 dwmw2 Exp $
+ * $Id: physmap.c,v 1.8 2000/11/27 08:50:22 dwmw2 Exp $
*
* Normal mappings of chips in physical memory
*/
@@ -15,6 +15,7 @@
#define WINDOW_ADDR CONFIG_MTD_PHYSMAP_START
#define WINDOW_SIZE CONFIG_MTD_PHYSMAP_LEN
+#define BUSWIDTH CONFIG_MTD_PHYSMAP_BUSWIDTH
static struct mtd_info *mymtd;
@@ -59,32 +60,28 @@ void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, s
}
struct map_info physmap_map = {
- "Physically mapped flash",
- WINDOW_SIZE,
- 2,
- physmap_read8,
- physmap_read16,
- physmap_read32,
- physmap_copy_from,
- physmap_write8,
- physmap_write16,
- physmap_write32,
- physmap_copy_to,
- 0,
- 0
+ name: "Physically mapped flash",
+ size: WINDOW_SIZE,
+ buswidth: BUSWIDTH,
+ read8: physmap_read8,
+ read16: physmap_read16,
+ read32: physmap_read32,
+ copy_from: physmap_copy_from,
+ write8: physmap_write8,
+ write16: physmap_write16,
+ write32: physmap_write32,
+ copy_to: physmap_copy_to
};
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_physmap init_module
#define cleanup_physmap cleanup_module
#endif
-#endif
int __init init_physmap(void)
{
printk(KERN_NOTICE "physmap flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
- physmap_map.map_priv_1 = (unsigned long)ioremap(WINDOW_SIZE, WINDOW_ADDR);
+ physmap_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
if (!physmap_map.map_priv_1) {
printk("Failed to ioremap\n");
@@ -99,6 +96,7 @@ int __init init_physmap(void)
return 0;
}
+ iounmap((void *)physmap_map.map_priv_1);
return -ENXIO;
}
@@ -113,3 +111,7 @@ static void __exit cleanup_physmap(void)
physmap_map.map_priv_1 = 0;
}
}
+
+module_init(init_physmap);
+module_exit(cleanup_physmap);
+
diff --git a/drivers/mtd/pmc551.c b/drivers/mtd/pmc551.c
index fdc80cbcf..ab9049356 100644
--- a/drivers/mtd/pmc551.c
+++ b/drivers/mtd/pmc551.c
@@ -1,17 +1,17 @@
/*
- * $Id: pmc551.c,v 1.8 2000/07/14 07:53:31 dwmw2 Exp $
+ * $Id: pmc551.c,v 1.11 2000/11/23 13:40:12 dwmw2 Exp $
*
* PMC551 PCI Mezzanine Ram Device
*
* Author:
- * Mark Ferrell
+ * Mark Ferrell <mferrell@mvista.com>
* Copyright 1999,2000 Nortel Networks
*
- * License:
+ * License:
* As part of this driver was derrived from the slram.c driver it falls
* under the same license, which is GNU General Public License v2
*
- * Description:
+ * Description:
* This driver is intended to support the PMC551 PCI Ram device from
* Ramix Inc. The PMC551 is a PMC Mezzanine module for cPCI embeded
* systems. The device contains a single SROM that initally programs the
@@ -24,6 +24,23 @@
* it as a block device allows us to use it as high speed swap or for a
* high speed disk device of some sort. Which becomes very usefull on
* diskless systems in the embeded market I might add.
+ *
+ * Notes:
+ * Due to what I assume is more buggy SROM, the 64M PMC551 I have
+ * available claims that all 4 of it's DRAM banks have 64M of ram
+ * configured (making a grand total of 256M onboard). This is slightly
+ * annoying since the BAR0 size reflects the aperture size, not the dram
+ * size, and the V370PDC supplies no other method for memory size
+ * discovery. This problem is mostly only relivant when compiled as a
+ * module, as the unloading of the module with an aperture size smaller
+ * then the ram will cause the driver to detect the onboard memory size
+ * to be equal to the aperture size when the module is reloaded. Soooo,
+ * to help, the module supports an msize option to allow the
+ * specification of the onboard memory, and an asize option, to allow the
+ * specification of the aperture size. The aperture must be equal to or
+ * less then the memory size, the driver will correct this if you screw
+ * it up. This problem is not relivant for compiled in drivers as
+ * compiled in drivers only init once.
*
* Credits:
* Saeed Karamooz <saeed@ramix.com> of Ramix INC. for the initial
@@ -31,7 +48,7 @@
* questions I had concerning operation of the device.
*
* Most of the MTD code for this driver was originally written for the
- * slram.o module in the MTD drivers package written by David Hinds
+ * slram.o module in the MTD drivers package written by David Hinds
* <dhinds@allegro.stanford.edu> which allows the mapping of system
* memory into an mtd device. Since the PMC551 memory module is
* accessed in the same fashion as system memory, the slram.c code
@@ -44,9 +61,9 @@
* * Modified driver to utilize a sliding apature instead of mapping all
* memory into kernel space which turned out to be very wastefull.
* * Located a bug in the SROM's initialization sequence that made the
- * memory unussable, added a fix to code to touch up the DRAM some.
+ * memory unusable, added a fix to code to touch up the DRAM some.
*
- * Bugs/FIXME's:
+ * Bugs/FIXME's:
* * MUST fix the init function to not spin on a register
* waiting for it to set .. this does not safely handle busted devices
* that never reset the register correctly which will cause the system to
@@ -73,6 +90,10 @@
#include <stdarg.h>
#include <linux/pci.h>
+#ifndef CONFIG_PCI
+#error Enable PCI in your kernel config
+#endif
+
#include <linux/mtd/mtd.h>
#include <linux/mtd/pmc551.h>
#include <linux/mtd/compatmac.h>
@@ -347,14 +368,70 @@ static u32 fixup_pmc551 (struct pci_dev *dev)
#ifdef CONFIG_MTD_PMC551_BUGFIX
u32 dram_data;
#endif
- u32 size, dcmd;
- u16 cmd, i;
+ u32 size, dcmd, cfg, dtmp;
+ u16 cmd, tmp, i;
+ u8 bcmd, counter;
/* Sanity Check */
if(!dev) {
return -ENODEV;
}
+ /*
+ * Attempt to reset the card
+ * FIXME: Stop Spinning registers
+ */
+ counter=0;
+ /* unlock registers */
+ pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, 0xA5 );
+ /* read in old data */
+ pci_read_config_byte(dev, PMC551_SYS_CTRL_REG, &bcmd );
+ /* bang the reset line up and down for a few */
+ for(i=0;i<10;i++) {
+ counter=0;
+ bcmd &= ~0x80;
+ while(counter++ < 100) {
+ pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd);
+ }
+ counter=0;
+ bcmd |= 0x80;
+ while(counter++ < 100) {
+ pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd);
+ }
+ }
+ bcmd |= (0x40|0x20);
+ pci_write_config_byte(dev, PMC551_SYS_CTRL_REG, bcmd);
+
+ /*
+ * Take care and turn off the memory on the device while we
+ * tweak the configurations
+ */
+ pci_read_config_word(dev, PCI_COMMAND, &cmd);
+ tmp = cmd & ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY);
+ pci_write_config_word(dev, PCI_COMMAND, tmp);
+
+ /*
+ * Disable existing aperture before probing memory size
+ */
+ pci_read_config_dword(dev, PMC551_PCI_MEM_MAP0, &dcmd);
+ dtmp=(dcmd|PMC551_PCI_MEM_MAP_ENABLE|PMC551_PCI_MEM_MAP_REG_EN);
+ pci_write_config_dword(dev, PMC551_PCI_MEM_MAP0, dtmp);
+ /*
+ * Grab old BAR0 config so that we can figure out memory size
+ * This is another bit of kludge going on. The reason for the
+ * redundancy is I am hoping to retain the original configuration
+ * previously assigned to the card by the BIOS or some previous
+ * fixup routine in the kernel. So we read the old config into cfg,
+ * then write all 1's to the memory space, read back the result into
+ * "size", and then write back all the old config.
+ */
+ pci_read_config_dword( dev, PCI_BASE_ADDRESS_0, &cfg );
+#ifndef CONFIG_MTD_PMC551_BUGFIX
+ pci_write_config_dword( dev, PCI_BASE_ADDRESS_0, ~0 );
+ pci_read_config_dword( dev, PCI_BASE_ADDRESS_0, &size );
+ pci_write_config_dword( dev, PCI_BASE_ADDRESS_0, cfg );
+ size=~(size&PCI_BASE_ADDRESS_MEM_MASK)+1;
+#else
/*
* Get the size of the memory by reading all the DRAM size values
* and adding them up.
@@ -363,7 +440,6 @@ static u32 fixup_pmc551 (struct pci_dev *dev)
* row mux values. We fix them here, but this will break other
* memory configurations.
*/
-#ifdef CONFIG_MTD_PMC551_BUGFIX
pci_read_config_dword(dev, PMC551_DRAM_BLK0, &dram_data);
size = PMC551_DRAM_BLK_GET_SIZE(dram_data);
dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5);
@@ -387,7 +463,6 @@ static u32 fixup_pmc551 (struct pci_dev *dev)
dram_data = PMC551_DRAM_BLK_SET_COL_MUX(dram_data, 0x5);
dram_data = PMC551_DRAM_BLK_SET_ROW_MUX(dram_data, 0x9);
pci_write_config_dword(dev, PMC551_DRAM_BLK3, dram_data);
-#endif /* CONFIG_MTD_PMC551_BUGFIX */
/*
* Oops .. something went wrong
@@ -395,34 +470,14 @@ static u32 fixup_pmc551 (struct pci_dev *dev)
if( (size &= PCI_BASE_ADDRESS_MEM_MASK) == 0) {
return -ENODEV;
}
+#endif /* CONFIG_MTD_PMC551_BUGFIX */
- /*
- * Set to be prefetchable
- */
- pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &dcmd );
- dcmd |= 0x8;
-
- /*
- * Put it back the way it was
- */
- pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, dcmd );
- pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &dcmd );
-
- /*
- * Some screen fun
- */
- printk(KERN_NOTICE "pmc551: %dM (0x%x) of %sprefetchable memory at 0x%lx\n",
- size/1024/1024, size, ((dcmd&0x8) == 0)?"non-":"",
- PCI_BASE_ADDRESS(dev)&PCI_BASE_ADDRESS_MEM_MASK );
-
- /*
- * Turn on PCI memory and I/O bus access just for kicks
- */
- pci_write_config_word( dev, PCI_COMMAND,
- PCI_COMMAND_MEMORY | PCI_COMMAND_IO );
+ if ((cfg&PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
+ return -ENODEV;
+ }
/*
- * Config DRAM
+ * Precharge Dram
*/
pci_write_config_word( dev, PMC551_SDRAM_MA, 0x0400 );
pci_write_config_word( dev, PMC551_SDRAM_CMD, 0x00bf );
@@ -431,11 +486,16 @@ static u32 fixup_pmc551 (struct pci_dev *dev)
* Wait untill command has gone through
* FIXME: register spinning issue
*/
- do { pci_read_config_word( dev, PMC551_SDRAM_CMD, &cmd );
+ do { pci_read_config_word( dev, PMC551_SDRAM_CMD, &cmd );
+ if(counter++ > 100)break;
} while ( (PCI_COMMAND_IO) & cmd );
/*
- * Must be held high for some duration of time to take effect??
+ * Turn on auto refresh
+ * The loop is taken directly from Ramix's example code. I assume that
+ * this must be held high for some duration of time, but I can find no
+ * documentation refrencing the reasons why.
+ *
*/
for ( i = 1; i<=8 ; i++) {
pci_write_config_word (dev, PMC551_SDRAM_CMD, 0x0df);
@@ -444,7 +504,9 @@ static u32 fixup_pmc551 (struct pci_dev *dev)
* Make certain command has gone through
* FIXME: register spinning issue
*/
- do { pci_read_config_word(dev, PMC551_SDRAM_CMD, &cmd);
+ counter=0;
+ do { pci_read_config_word(dev, PMC551_SDRAM_CMD, &cmd);
+ if(counter++ > 100)break;
} while ( (PCI_COMMAND_IO) & cmd );
}
@@ -455,7 +517,9 @@ static u32 fixup_pmc551 (struct pci_dev *dev)
* Wait until command completes
* FIXME: register spinning issue
*/
- do { pci_read_config_word ( dev, PMC551_SDRAM_CMD, &cmd);
+ counter=0;
+ do { pci_read_config_word ( dev, PMC551_SDRAM_CMD, &cmd);
+ if(counter++ > 100)break;
} while ( (PCI_COMMAND_IO) & cmd );
pci_read_config_dword ( dev, PMC551_DRAM_CFG, &dcmd);
@@ -481,54 +545,97 @@ static u32 fixup_pmc551 (struct pci_dev *dev)
cmd &= ~PCI_STATUS_DEVSEL_MASK;
pci_write_config_word( dev, PCI_STATUS, cmd );
}
+ /*
+ * Set to be prefetchable and put everything back based on old cfg.
+ * it's possible that the reset of the V370PDC nuked the original
+ * settup
+ */
+ cfg |= PCI_BASE_ADDRESS_MEM_PREFETCH;
+ pci_write_config_dword( dev, PCI_BASE_ADDRESS_0, cfg );
+
+ /*
+ * Turn PCI memory and I/O bus access back on
+ */
+ pci_write_config_word( dev, PCI_COMMAND,
+ PCI_COMMAND_MEMORY | PCI_COMMAND_IO );
+#ifdef CONFIG_MTD_PMC551_DEBUG
+ /*
+ * Some screen fun
+ */
+ printk(KERN_DEBUG "pmc551: %d%c (0x%x) of %sprefetchable memory at 0x%lx\n",
+ (size<1024)?size:(size<1048576)?size/1024:size/1024/1024,
+ (size<1024)?'B':(size<1048576)?'K':'M',
+ size, ((dcmd&(0x1<<3)) == 0)?"non-":"",
+ PCI_BASE_ADDRESS(dev)&PCI_BASE_ADDRESS_MEM_MASK );
/*
* Check to see the state of the memory
- * FIXME: perhaps hide some of this around an #ifdef DEBUG as
- * it doesn't effect or enhance cards functionality
*/
- pci_read_config_dword( dev, 0x74, &dcmd );
- printk(KERN_NOTICE "pmc551: DRAM_BLK3 Flags: %s,%s\n",
- ((0x2&dcmd) == 0)?"RW":"RO",
- ((0x1&dcmd) == 0)?"Off":"On" );
-
- pci_read_config_dword( dev, 0x70, &dcmd );
- printk(KERN_NOTICE "pmc551: DRAM_BLK2 Flags: %s,%s\n",
- ((0x2&dcmd) == 0)?"RW":"RO",
- ((0x1&dcmd) == 0)?"Off":"On" );
-
- pci_read_config_dword( dev, 0x6C, &dcmd );
- printk(KERN_NOTICE "pmc551: DRAM_BLK1 Flags: %s,%s\n",
- ((0x2&dcmd) == 0)?"RW":"RO",
- ((0x1&dcmd) == 0)?"Off":"On" );
-
- pci_read_config_dword( dev, 0x68, &dcmd );
- printk(KERN_NOTICE "pmc551: DRAM_BLK0 Flags: %s,%s\n",
- ((0x2&dcmd) == 0)?"RW":"RO",
- ((0x1&dcmd) == 0)?"Off":"On" );
-
- pci_read_config_word( dev, 0x4, &cmd );
- printk( KERN_NOTICE "pmc551: Memory Access %s\n",
- ((0x2&cmd) == 0)?"off":"on" );
- printk( KERN_NOTICE "pmc551: I/O Access %s\n",
- ((0x1&cmd) == 0)?"off":"on" );
-
- pci_read_config_word( dev, 0x6, &cmd );
- printk( KERN_NOTICE "pmc551: Devsel %s\n",
+ pci_read_config_dword( dev, PMC551_DRAM_BLK0, &dcmd );
+ printk(KERN_DEBUG "pmc551: DRAM_BLK0 Flags: %s,%s\n"
+ "pmc551: DRAM_BLK0 Size: %d at %d\n"
+ "pmc551: DRAM_BLK0 Row MUX: %d, Col MUX: %d\n",
+ (((0x1<<1)&dcmd) == 0)?"RW":"RO",
+ (((0x1<<0)&dcmd) == 0)?"Off":"On",
+ PMC551_DRAM_BLK_GET_SIZE(dcmd),
+ ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) );
+
+ pci_read_config_dword( dev, PMC551_DRAM_BLK1, &dcmd );
+ printk(KERN_DEBUG "pmc551: DRAM_BLK1 Flags: %s,%s\n"
+ "pmc551: DRAM_BLK1 Size: %d at %d\n"
+ "pmc551: DRAM_BLK1 Row MUX: %d, Col MUX: %d\n",
+ (((0x1<<1)&dcmd) == 0)?"RW":"RO",
+ (((0x1<<0)&dcmd) == 0)?"Off":"On",
+ PMC551_DRAM_BLK_GET_SIZE(dcmd),
+ ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) );
+
+ pci_read_config_dword( dev, PMC551_DRAM_BLK2, &dcmd );
+ printk(KERN_DEBUG "pmc551: DRAM_BLK2 Flags: %s,%s\n"
+ "pmc551: DRAM_BLK2 Size: %d at %d\n"
+ "pmc551: DRAM_BLK2 Row MUX: %d, Col MUX: %d\n",
+ (((0x1<<1)&dcmd) == 0)?"RW":"RO",
+ (((0x1<<0)&dcmd) == 0)?"Off":"On",
+ PMC551_DRAM_BLK_GET_SIZE(dcmd),
+ ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) );
+
+ pci_read_config_dword( dev, PMC551_DRAM_BLK3, &dcmd );
+ printk(KERN_DEBUG "pmc551: DRAM_BLK3 Flags: %s,%s\n"
+ "pmc551: DRAM_BLK3 Size: %d at %d\n"
+ "pmc551: DRAM_BLK3 Row MUX: %d, Col MUX: %d\n",
+ (((0x1<<1)&dcmd) == 0)?"RW":"RO",
+ (((0x1<<0)&dcmd) == 0)?"Off":"On",
+ PMC551_DRAM_BLK_GET_SIZE(dcmd),
+ ((dcmd>>20)&0x7FF), ((dcmd>>13)&0x7), ((dcmd>>9)&0xF) );
+
+ pci_read_config_word( dev, PCI_COMMAND, &cmd );
+ printk( KERN_DEBUG "pmc551: Memory Access %s\n",
+ (((0x1<<1)&cmd) == 0)?"off":"on" );
+ printk( KERN_DEBUG "pmc551: I/O Access %s\n",
+ (((0x1<<0)&cmd) == 0)?"off":"on" );
+
+ pci_read_config_word( dev, PCI_STATUS, &cmd );
+ printk( KERN_DEBUG "pmc551: Devsel %s\n",
((PCI_STATUS_DEVSEL_MASK&cmd)==0x000)?"Fast":
((PCI_STATUS_DEVSEL_MASK&cmd)==0x200)?"Medium":
((PCI_STATUS_DEVSEL_MASK&cmd)==0x400)?"Slow":"Invalid" );
- printk( KERN_NOTICE "pmc551: %sFast Back-to-Back\n",
+ printk( KERN_DEBUG "pmc551: %sFast Back-to-Back\n",
((PCI_COMMAND_FAST_BACK&cmd) == 0)?"Not ":"" );
+ pci_read_config_byte(dev, PMC551_SYS_CTRL_REG, &bcmd );
+ printk( KERN_DEBUG "pmc551: EEPROM is under %s control\n"
+ "pmc551: System Control Register is %slocked to PCI access\n"
+ "pmc551: System Control Register is %slocked to EEPROM access\n",
+ (bcmd&0x1)?"software":"hardware",
+ (bcmd&0x20)?"":"un", (bcmd&0x40)?"":"un");
+#endif
return size;
}
/*
* Kernel version specific module stuffages
*/
-#if LINUX_VERSION_CODE < 0x20300
+#if LINUX_VERSION_CODE < 0x20211
#ifdef MODULE
#define init_pmc551 init_module
#define cleanup_pmc551 cleanup_module
@@ -536,11 +643,27 @@ static u32 fixup_pmc551 (struct pci_dev *dev)
#define __exit
#endif
+#if defined(MODULE)
+MODULE_AUTHOR("Mark Ferrell <mferrell@mvista.com>");
+MODULE_DESCRIPTION(PMC551_VERSION);
+MODULE_PARM(msize, "i");
+MODULE_PARM_DESC(msize, "memory size, 6=32M, 7=64M, 8=128M, ect.. [32M-1024M]");
+MODULE_PARM(asize, "i");
+MODULE_PARM_DESC(asize, "aperture size, must be <= memsize [1M-1024M]");
+#endif
+/*
+ * Stuff these outside the ifdef so as to not bust compiled in driver support
+ */
+static int msize=0;
+#if defined(CONFIG_MTD_PMC551_APERTURE_SIZE)
+static int asize=CONFIG_MTD_PMC551_APERTURE_SIZE
+#else
+static int asize=0;
+#endif
/*
* PMC551 Card Initialization
*/
-//static int __init init_pmc551(void)
int __init init_pmc551(void)
{
struct pci_dev *PCI_Device = NULL;
@@ -549,9 +672,23 @@ int __init init_pmc551(void)
struct mtd_info *mtd;
u32 length = 0;
+ if(msize) {
+ if (msize < 6 || msize > 11 ) {
+ printk(KERN_NOTICE "pmc551: Invalid memory size\n");
+ return -ENODEV;
+ }
+ msize = (512*1024)<<msize;
+ }
- printk(KERN_NOTICE "Ramix PMC551 PCI Mezzanine Ram Driver. (C) 1999,2000 Nortel Networks.\n");
- printk(KERN_INFO "$Id: pmc551.c,v 1.8 2000/07/14 07:53:31 dwmw2 Exp $\n");
+ if(asize) {
+ if (asize < 1 || asize > 11 ) {
+ printk(KERN_NOTICE "pmc551: Invalid aperture size\n");
+ return -ENODEV;
+ }
+ asize = (512*1024)<<asize;
+ }
+
+ printk(KERN_INFO PMC551_VERSION);
if(!pci_present()) {
printk(KERN_NOTICE "pmc551: PCI not enabled.\n");
@@ -583,6 +720,10 @@ int __init init_pmc551(void)
printk(KERN_NOTICE "pmc551: Cannot init SDRAM\n");
break;
}
+ if(msize) {
+ length = msize;
+ printk(KERN_NOTICE "pmc551: Using specified memory size 0x%x\n", length);
+ }
mtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
if (!mtd) {
@@ -602,13 +743,44 @@ int __init init_pmc551(void)
mtd->priv = priv;
priv->dev = PCI_Device;
- priv->aperture_size = PMC551_APERTURE_SIZE;
+ if(asize) {
+ if(asize > length) {
+ asize=length;
+ printk(KERN_NOTICE "pmc551: reducing aperture size to fit memory [0x%x]\n",asize);
+ } else {
+ printk(KERN_NOTICE "pmc551: Using specified aperture size 0x%x\n", asize);
+ }
+ priv->aperture_size = asize;
+ } else {
+ priv->aperture_size = length;
+ }
priv->start = ioremap((PCI_BASE_ADDRESS(PCI_Device)
& PCI_BASE_ADDRESS_MEM_MASK),
priv->aperture_size);
- priv->mem_map0_base_val = (PMC551_APERTURE_VAL
- | PMC551_PCI_MEM_MAP_REG_EN
- | PMC551_PCI_MEM_MAP_ENABLE);
+
+ /*
+ * Due to the dynamic nature of the code, we need to figure
+ * this out in order to stuff the register to set the proper
+ * aperture size. If you know of an easier way to do this then
+ * PLEASE help yourself.
+ *
+ * Not with bloody floating point, you don't. Consider yourself
+ * duly LARTed. dwmw2.
+ */
+ {
+ u32 size;
+ u16 bits;
+ size = priv->aperture_size>>20;
+ for(bits=0;!(size&0x01)&&size>0;bits++,size=size>>1);
+ //size=((u32)((log10(priv->aperture_size)/.30103)-19)<<4);
+ priv->mem_map0_base_val = (PMC551_PCI_MEM_MAP_REG_EN
+ | PMC551_PCI_MEM_MAP_ENABLE
+ | size);
+#ifdef CONFIG_MTD_PMC551_DEBUG
+ printk(KERN_NOTICE "pmc551: aperture set to %d[%d]\n",
+ size, size>>4);
+#endif
+ }
priv->curr_mem_map0_val = priv->mem_map0_base_val;
pci_write_config_dword ( priv->dev,
@@ -641,7 +813,10 @@ int __init init_pmc551(void)
priv->aperture_size/1024/1024,
priv->start,
priv->start + priv->aperture_size);
- printk(KERN_NOTICE "Total memory is %dM\n", length/1024/1024);
+ printk(KERN_NOTICE "Total memory is %d%c\n",
+ (length<1024)?length:
+ (length<1048576)?length/1024:length/1024/1024,
+ (length<1024)?'B':(length<1048576)?'K':'M');
priv->nextpmc551 = pmc551list;
pmc551list = mtd;
found++;
@@ -651,8 +826,8 @@ int __init init_pmc551(void)
printk(KERN_NOTICE "pmc551: not detected,\n");
return -ENODEV;
} else {
- return 0;
printk(KERN_NOTICE "pmc551: %d pmc551 devices loaded\n", found);
+ return 0;
}
}
@@ -669,7 +844,7 @@ static void __exit cleanup_pmc551(void)
priv = (struct mypriv *)mtd->priv;
pmc551list = priv->nextpmc551;
- if(priv->start)
+ if(priv->start)
iounmap(((struct mypriv *)mtd->priv)->start);
kfree (mtd->priv);
@@ -681,10 +856,7 @@ static void __exit cleanup_pmc551(void)
printk(KERN_NOTICE "pmc551: %d pmc551 devices unloaded\n", found);
}
-#if LINUX_VERSION_CODE > 0x20300
+#if LINUX_VERSION_CODE >= 0x20211
module_init(init_pmc551);
module_exit(cleanup_pmc551);
#endif
-
-
-
diff --git a/drivers/mtd/pnc2000.c b/drivers/mtd/pnc2000.c
index 4e4b052a7..a93dd3548 100644
--- a/drivers/mtd/pnc2000.c
+++ b/drivers/mtd/pnc2000.c
@@ -5,7 +5,7 @@
*
* This code is GPL
*
- * $Id: pnc2000.c,v 1.1 2000/07/12 09:34:32 dwmw2 Exp $
+ * $Id: pnc2000.c,v 1.4 2000/11/27 08:50:22 dwmw2 Exp $
*/
#include <linux/module.h>
@@ -14,6 +14,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
#define WINDOW_ADDR 0xbf000000
@@ -64,136 +65,60 @@ void pnc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize
}
struct map_info pnc_map = {
- "PNC-2000",
- WINDOW_SIZE,
- 4,
- pnc_read8,
- pnc_read16,
- pnc_read32,
- pnc_copy_from,
- pnc_write8,
- pnc_write16,
- pnc_write32,
- pnc_copy_to,
- 0,
- 0
+ name: "PNC-2000",
+ size: WINDOW_SIZE,
+ buswidth: 4,
+ read8: pnc_read8,
+ read16: pnc_read16,
+ read32: pnc_read32,
+ copy_from: pnc_copy_from,
+ write8: pnc_write8,
+ write16: pnc_write16,
+ write32: pnc_write32,
+ copy_to: pnc_copy_to
};
/*
* MTD 'PARTITIONING' STUFF
*/
-
-/*
- * This is the _real_ MTD device for which all the others are just
- * auto-relocating aliases.
- */
-static struct mtd_info *mymtd;
-
-/*
- * MTD methods which simply translate the effective address and pass through
- * to the _real_ device.
- */
-
-static int pnc_mtd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
-{
- return mymtd->read(mymtd, from + (unsigned long)mtd->priv, len, retlen, buf);
-}
-
-static int pnc_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
-{
- return mymtd->write(mymtd, to + (unsigned long)mtd->priv, len, retlen, buf);
-}
-
-static int pnc_mtd_erase (struct mtd_info *mtd, struct erase_info *instr)
-{
- instr->addr += (unsigned long)mtd->priv;
- return mymtd->erase(mymtd, instr);
-}
-
-static void pnc_mtd_sync (struct mtd_info *mtd)
-{
- mymtd->sync(mymtd);
-}
-
-static int pnc_mtd_suspend (struct mtd_info *mtd)
-{
- return mymtd->suspend(mymtd);
-}
-
-static void pnc_mtd_resume (struct mtd_info *mtd)
-{
- mymtd->resume(mymtd);
-}
-
-
-static struct mtd_info pnc_mtds[3] = { /* boot, kernel, fs */
+static struct mtd_partition pnc_partitions[3] = {
{
- type: MTD_NORFLASH,
- flags: MTD_CAP_NORFLASH,
- size: 0x20000,
- erasesize: 0x20000,
name: "PNC-2000 boot firmware",
- module: THIS_MODULE,
- erase: pnc_mtd_erase,
- read: pnc_mtd_read,
- write: pnc_mtd_write,
- suspend: pnc_mtd_suspend,
- resume: pnc_mtd_resume,
- sync: pnc_mtd_sync,
- priv: (void *)0
+ size: 0x20000,
+ offset: 0
},
{
- type: MTD_NORFLASH,
- flags: MTD_CAP_NORFLASH,
- size: 0x1a0000,
- erasesize: 0x20000,
name: "PNC-2000 kernel",
- module: THIS_MODULE,
- erase: pnc_mtd_erase,
- read: pnc_mtd_read,
- write: pnc_mtd_write,
- suspend: pnc_mtd_suspend,
- resume: pnc_mtd_resume,
- sync: pnc_mtd_sync,
- priv: (void *)0x20000
+ size: 0x1a0000,
+ offset: 0x20000
},
{
- type: MTD_NORFLASH,
- flags: MTD_CAP_NORFLASH,
- size: 0x240000,
- erasesize: 0x20000,
name: "PNC-2000 filesystem",
- module: THIS_MODULE,
- erase: pnc_mtd_erase,
- read: pnc_mtd_read,
- write: pnc_mtd_write,
- suspend: pnc_mtd_suspend,
- resume: pnc_mtd_resume,
- sync: pnc_mtd_sync,
- priv: (void *)0x1c0000
+ size: 0x240000,
+ offset: 0x1c0000
}
};
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+/*
+ * This is the master MTD device for which all the others are just
+ * auto-relocating aliases.
+ */
+static struct mtd_info *mymtd;
+
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_pnc init_module
#define cleanup_pnc cleanup_module
#endif
-#endif
int __init init_pnc(void)
{
- printk(KERN_NOTICE "Photron PNC-2000 flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
+ printk(KERN_NOTICE "Photron PNC-2000 flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
mymtd = do_cfi_probe(&pnc_map);
if (mymtd) {
mymtd->module = THIS_MODULE;
-
- add_mtd_device(&pnc_mtds[0]); /* boot */
- add_mtd_device(&pnc_mtds[1]); /* kernel */
- add_mtd_device(&pnc_mtds[2]); /* file system */
- return 0;
+ return add_mtd_partitions(mymtd, pnc_partitions, 3);
}
return -ENXIO;
@@ -202,9 +127,10 @@ int __init init_pnc(void)
static void __exit cleanup_pnc(void)
{
if (mymtd) {
- del_mtd_device(&pnc_mtds[2]);
- del_mtd_device(&pnc_mtds[1]);
- del_mtd_device(&pnc_mtds[0]);
+ del_mtd_partitions(mymtd);
map_destroy(mymtd);
}
}
+
+module_init(init_pnc);
+module_exit(cleanup_pnc);
diff --git a/drivers/mtd/rpxlite.c b/drivers/mtd/rpxlite.c
index 783c863ac..51bcaf8a9 100644
--- a/drivers/mtd/rpxlite.c
+++ b/drivers/mtd/rpxlite.c
@@ -1,7 +1,7 @@
/*
- * $Id: rpxlite.c,v 1.2 2000/07/04 12:16:26 dwmw2 Exp $
+ * $Id: rpxlite.c,v 1.8 2000/12/09 22:00:31 dwmw2 Exp $
*
- * Handle the strange 16-in-32-bit mapping on the RPXLite board
+ * Handle mapping of the flash on the RPX Lite and CLLF boards
*/
#include <linux/module.h>
@@ -12,124 +12,74 @@
#include <linux/mtd/map.h>
-#define WINDOW_ADDR 0x8000000
-#define WINDOW_SIZE 0x2000000
-
-#define MAP_TO_ADR(x) ( ( ( x & ~1 ) << 1 ) | (x&1) )
+#define WINDOW_ADDR 0xfe000000
+#define WINDOW_SIZE 0x800000
static struct mtd_info *mymtd;
__u8 rpxlite_read8(struct map_info *map, unsigned long ofs)
{
- return readb(map->map_priv_1 + MAP_TO_ADR(ofs));
+ return readb(map->map_priv_1 * ofs);
}
__u16 rpxlite_read16(struct map_info *map, unsigned long ofs)
{
- return readw(map->map_priv_1 + MAP_TO_ADR(ofs));
+ return readw(map->map_priv_1 + ofs);
}
__u32 rpxlite_read32(struct map_info *map, unsigned long ofs)
{
- return readl(map->map_priv_1 + MAP_TO_ADR(ofs));
+ return readl(map->map_priv_1 + ofs);
}
void rpxlite_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
- if (from & 1) {
- *(__u8 *)to = readb(map->map_priv_1 + MAP_TO_ADR(from));
- from++;
- len--;
- }
- /* Can't do this if it's not aligned */
- if (!((unsigned long)to & 1)) {
- unsigned long fromadr = MAP_TO_ADR(from);
-
- while (len > 1) {
- *(__u16 *)to = readw(map->map_priv_1 + fromadr);
- to += 2;
- fromadr += 4;
- from += 2;
- len -= 2;
- }
- }
- while(len) {
- *(__u8 *)to = readb(map->map_priv_1 + MAP_TO_ADR(from));
- to++;
- from++;
- len--;
- }
+ memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
}
void rpxlite_write8(struct map_info *map, __u8 d, unsigned long adr)
{
- writeb(d, map->map_priv_1 + MAP_TO_ADR(adr));
+ writeb(d, map->map_priv_1 + adr);
}
void rpxlite_write16(struct map_info *map, __u16 d, unsigned long adr)
{
- writew(d, map->map_priv_1 + MAP_TO_ADR(adr));
+ writew(d, map->map_priv_1 + adr);
}
void rpxlite_write32(struct map_info *map, __u32 d, unsigned long adr)
{
- writel(d, map->map_priv_1 + MAP_TO_ADR(adr));
+ writel(d, map->map_priv_1 + adr);
}
void rpxlite_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
{
- if (to & 1) {
- writeb(*(__u8 *)from, map->map_priv_1 + MAP_TO_ADR(to));
- from++;
- len--;
- }
- /* Can't do this if it's not aligned */
- if (!((unsigned long)from & 1)) {
- unsigned long toadr = map->map_priv_1 + MAP_TO_ADR(to);
-
- while (len > 1) {
- writew(*(__u16 *)from, toadr);
- from += 2;
- toadr += 4;
- to += 2;
- len -= 2;
- }
- }
- while(len) {
- writeb(*(__u8 *)from, map->map_priv_1 + MAP_TO_ADR(to));
- to++;
- from++;
- len--;
- }
+ memcpy_toio((void *)(map->map_priv_1 + to), from, len);
}
struct map_info rpxlite_map = {
- "RPXLITE",
- WINDOW_SIZE,
- 2,
- rpxlite_read8,
- rpxlite_read16,
- rpxlite_read32,
- rpxlite_copy_from,
- rpxlite_write8,
- rpxlite_write16,
- rpxlite_write32,
- rpxlite_copy_to,
- 0,
- 0
+ name: "RPX",
+ size: WINDOW_SIZE,
+ buswidth: 4,
+ read8: rpxlite_read8,
+ read16: rpxlite_read16,
+ read32: rpxlite_read32,
+ copy_from: rpxlite_copy_from,
+ write8: rpxlite_write8,
+ write16: rpxlite_write16,
+ write32: rpxlite_write32,
+ copy_to: rpxlite_copy_to
};
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_rpxlite init_module
#define cleanup_rpxlite cleanup_module
#endif
-#endif
int __init init_rpxlite(void)
{
- printk(KERN_NOTICE "rpxlite flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
- rpxlite_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 2);
+ printk(KERN_NOTICE "RPX Lite or CLLF flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR);
+ rpxlite_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
if (!rpxlite_map.map_priv_1) {
printk("Failed to ioremap\n");
@@ -158,3 +108,6 @@ static void __exit cleanup_rpxlite(void)
rpxlite_map.map_priv_1 = 0;
}
}
+
+module_init(init_rpxlite);
+module_exit(cleanup_rpxlite);
diff --git a/drivers/mtd/vmax301.c b/drivers/mtd/vmax301.c
index 553beaad6..e694604e7 100644
--- a/drivers/mtd/vmax301.c
+++ b/drivers/mtd/vmax301.c
@@ -1,4 +1,4 @@
-// $Id: vmax301.c,v 1.13 2000/07/03 10:01:38 dwmw2 Exp $
+// $Id: vmax301.c,v 1.15 2000/11/27 08:50:22 dwmw2 Exp $
/* ######################################################################
Tempustech VMAX SBC301 MTD Driver.
@@ -142,46 +142,43 @@ static void vmax301_copy_to(struct map_info *map, unsigned long to, const void *
static struct map_info vmax_map[2] = {
{
- "VMAX301 Internal Flash",
- 3*2*1024*1024,
- 1,
- vmax301_read8,
- vmax301_read16,
- vmax301_read32,
- vmax301_copy_from,
- vmax301_write8,
- vmax301_write16,
- vmax301_write32,
- vmax301_copy_to,
- WINDOW_START + WINDOW_LENGTH,
- 0xFFFFFFFF
+ name: "VMAX301 Internal Flash",
+ size: 3*2*1024*1024,
+ buswidth: 1,
+ read8: vmax301_read8,
+ read16: vmax301_read16,
+ read32: vmax301_read32,
+ copy_from: vmax301_copy_from,
+ write8: vmax301_write8,
+ write16: vmax301_write16,
+ write32: vmax301_write32,
+ copy_to: vmax301_copy_to,
+ map_priv_1: WINDOW_START + WINDOW_LENGTH,
+ map_priv_2: 0xFFFFFFFF
},
{
- "VMAX301 Socket",
- 0,
- 1,
- vmax301_read8,
- vmax301_read16,
- vmax301_read32,
- vmax301_copy_from,
- vmax301_write8,
- vmax301_write16,
- vmax301_write32,
- vmax301_copy_to,
- WINDOW_START + (3*WINDOW_LENGTH),
- 0xFFFFFFFF
+ name: "VMAX301 Socket",
+ size: 0,
+ buswidth: 1,
+ read8: vmax301_read8,
+ read16: vmax301_read16,
+ read32: vmax301_read32,
+ copy_from: vmax301_copy_from,
+ write8: vmax301_write8,
+ write16: vmax301_write16,
+ write32: vmax301_write32,
+ copy_to: vmax301_copy_to,
+ map_priv_1: WINDOW_START + (3*WINDOW_LENGTH),
+ map_priv_2: 0xFFFFFFFF
}
};
static struct mtd_info *vmax_mtd[2] = {NULL, NULL};
-#if LINUX_VERSION_CODE < 0x20300
-#ifdef MODULE
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define init_vmax301 init_module
#define cleanup_vmax301 cleanup_module
#endif
-#define __exit
-#endif
static void __exit cleanup_vmax301(void)
{
@@ -237,7 +234,5 @@ int __init init_vmax301(void)
return 0;
}
-#if LINUX_VERSION_CODE > 0x20300
module_init(init_vmax301);
module_exit(cleanup_vmax301);
-#endif