Commit 7df80b4c authored by David Woodhouse's avatar David Woodhouse

MTD core include and device code cleanup

 - Move user-visible bits from headers to include/mtd/ directory.
 - Update old DiskOnChip drivers for newer hardware.
 - Switch NFTL and INFTL support to work with new DiskOnChip/NAND code.
 - New phram driver, reimplenting the ugly slram driver.
 - Bug fixes in partitioning code
parent 4af8e944
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
This is access code for flashes using ARM's flash partitioning This is access code for flashes using ARM's flash partitioning
standards. standards.
$Id: afs.c,v 1.12 2003/06/13 15:31:06 rmk Exp $ $Id: afs.c,v 1.13 2004/02/27 22:09:59 rmk Exp $
======================================================================*/ ======================================================================*/
......
/* /*
* $Id: cmdlinepart.c,v 1.9 2003/05/16 17:08:24 dwmw2 Exp $ * $Id: cmdlinepart.c,v 1.14 2004/07/12 12:34:23 dwmw2 Exp $
* *
* Read flash partition table from command line * Read flash partition table from command line
* *
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* mtdparts=<mtddef>[;<mtddef] * mtdparts=<mtddef>[;<mtddef]
* <mtddef> := <mtd-id>:<partdef>[,<partdef>] * <mtddef> := <mtd-id>:<partdef>[,<partdef>]
* <partdef> := <size>[@offset][<name>][ro] * <partdef> := <size>[@offset][<name>][ro]
* <mtd-id> := unique id used in mapping driver/device * <mtd-id> := unique name used in mapping driver/device (mtd->name)
* <size> := standard linux memsize OR "-" to denote all remaining space * <size> := standard linux memsize OR "-" to denote all remaining space
* <name> := '(' NAME ')' * <name> := '(' NAME ')'
* *
...@@ -358,14 +358,7 @@ static int __init cmdline_parser_init(void) ...@@ -358,14 +358,7 @@ static int __init cmdline_parser_init(void)
return register_mtd_parser(&cmdline_parser); return register_mtd_parser(&cmdline_parser);
} }
static void __exit cmdline_parser_exit(void)
{
deregister_mtd_parser(&cmdline_parser);
}
module_init(cmdline_parser_init); module_init(cmdline_parser_init);
module_exit(cmdline_parser_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>"); MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>");
......
# drivers/mtd/maps/Kconfig # drivers/mtd/maps/Kconfig
# $Id: Kconfig,v 1.4 2003/05/28 15:18:54 dwmw2 Exp $ # $Id: Kconfig,v 1.10 2004/07/15 00:34:49 dwmw2 Exp $
menu "Self-contained MTD device drivers" menu "Self-contained MTD device drivers"
depends on MTD!=n depends on MTD!=n
...@@ -9,9 +9,9 @@ config MTD_PMC551 ...@@ -9,9 +9,9 @@ config MTD_PMC551
depends on MTD && PCI depends on MTD && PCI
---help--- ---help---
This provides a MTD device driver for the Ramix PMC551 RAM PCI card This provides a MTD device driver for the Ramix PMC551 RAM PCI card
from Ramix Inc. <http://www.ramix.com/products/>. These devices come from Ramix Inc. <http://www.ramix.com/products/memory/pmc551.html>.
in memory configurations from 32M - 1G. If you have one, you These devices come in memory configurations from 32M - 1G. If you
probably want to enable this. have one, you probably want to enable this.
If this driver is compiled as a module you get the ability to select If this driver is compiled as a module you get the ability to select
the size of the aperture window pointing into the devices memory. the size of the aperture window pointing into the devices memory.
...@@ -40,9 +40,12 @@ config MTD_PMC551_DEBUG ...@@ -40,9 +40,12 @@ config MTD_PMC551_DEBUG
config MTD_MS02NV config MTD_MS02NV
tristate "DEC MS02-NV NVRAM module support" tristate "DEC MS02-NV NVRAM module support"
depends on CONFIG_MACH_DECSTATION depends on MTD && MACH_DECSTATION
help help
Support for NVRAM module on DECstation. This is an MTD driver for the DEC's MS02-NV (54-20948-01) battery
backed-up NVRAM module. The module was originally meant as an NFS
accelerator. Say Y here if you have a DECstation 5000/2x0 or a
DECsystem 5900 equipped with such a module.
config MTD_SLRAM config MTD_SLRAM
tristate "Uncached system RAM" tristate "Uncached system RAM"
...@@ -52,6 +55,16 @@ config MTD_SLRAM ...@@ -52,6 +55,16 @@ config MTD_SLRAM
you can still use it for storage or swap by using this driver to you can still use it for storage or swap by using this driver to
present it to the system as a Memory Technology Device. present it to the system as a Memory Technology Device.
config MTD_PHRAM
tristate "Physical system RAM"
depends on MTD
help
This is a re-implementation of the slram driver above.
Use this driver to access physical memory that the kernel proper
doesn't have access to, memory beyond the mem=xxx limit, nvram,
memory on the video card, etc...
config MTD_LART config MTD_LART
tristate "28F160xx flash driver for LART" tristate "28F160xx flash driver for LART"
depends on SA1100_LART && MTD depends on SA1100_LART && MTD
...@@ -161,11 +174,18 @@ config MTD_DOC2001PLUS ...@@ -161,11 +174,18 @@ config MTD_DOC2001PLUS
config MTD_DOCPROBE config MTD_DOCPROBE
tristate tristate
default m if MTD_DOC2001!=y && MTD_DOC2000!=y && MTD_DOC2001PLUS!=y && (MTD_DOC2001=m || MTD_DOC2000=m || MOD_DOC2001PLUS=m) default m if MTD_DOC2001!=y && MTD_DOC2000!=y && MTD_DOC2001PLUS!=y && (MTD_DOC2001=m || MTD_DOC2000=m || MTD_DOC2001PLUS=m)
default y if MTD_DOC2001=y || MTD_DOC2000=y || MTD_DOC2001PLUS=y default y if MTD_DOC2001=y || MTD_DOC2000=y || MTD_DOC2001PLUS=y
help help
This isn't a real config option, it's derived. This isn't a real config option, it's derived.
config MTD_DOCECC
tristate
default m if MTD_DOCPROBE!=y && MTD_NAND_DISKONCHIP!=y && (MTD_DOCPROBE=m || MTD_NAND_DISKONCHIP=m)
default y if MTD_DOCPROBE=y || MTD_NAND_DISKONCHIP=y
help
This isn't a real config option, it's derived.
config MTD_DOCPROBE_ADVANCED config MTD_DOCPROBE_ADVANCED
bool "Advanced detection options for DiskOnChip" bool "Advanced detection options for DiskOnChip"
depends on MTD_DOCPROBE depends on MTD_DOCPROBE
......
# #
# linux/drivers/devices/Makefile # linux/drivers/devices/Makefile
# #
# $Id: Makefile.common,v 1.3 2003/05/28 10:54:23 dwmw2 Exp $ # $Id: Makefile.common,v 1.6 2004/07/12 16:07:30 dwmw2 Exp $
# *** BIG UGLY NOTE *** # *** BIG UGLY NOTE ***
# #
...@@ -13,8 +13,10 @@ ...@@ -13,8 +13,10 @@
obj-$(CONFIG_MTD_DOC2000) += doc2000.o obj-$(CONFIG_MTD_DOC2000) += doc2000.o
obj-$(CONFIG_MTD_DOC2001) += doc2001.o obj-$(CONFIG_MTD_DOC2001) += doc2001.o
obj-$(CONFIG_MTD_DOC2001PLUS) += doc2001plus.o obj-$(CONFIG_MTD_DOC2001PLUS) += doc2001plus.o
obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o docecc.o obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o
obj-$(CONFIG_MTD_DOCECC) += docecc.o
obj-$(CONFIG_MTD_SLRAM) += slram.o obj-$(CONFIG_MTD_SLRAM) += slram.o
obj-$(CONFIG_MTD_PHRAM) += phram.o
obj-$(CONFIG_MTD_PMC551) += pmc551.o obj-$(CONFIG_MTD_PMC551) += pmc551.o
obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o
obj-$(CONFIG_MTD_MTDRAM) += mtdram.o obj-$(CONFIG_MTD_MTDRAM) += mtdram.o
......
/* /*
* $Id: blkmtd-25.c,v 1.5 2003/07/16 06:48:27 spse Exp $ * $Id: blkmtd-25.c,v 1.6 2004/07/15 15:09:15 dwmw2 Exp $
* *
* blkmtd.c - use a block device as a fake MTD * blkmtd.c - use a block device as a fake MTD
* *
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
/* Default erase size in K, always make it a multiple of PAGE_SIZE */ /* Default erase size in K, always make it a multiple of PAGE_SIZE */
#define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10) /* 128KiB */ #define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10) /* 128KiB */
#define VERSION "$Revision: 1.5 $" #define VERSION "$Revision: 1.6 $"
/* Info for the block device */ /* Info for the block device */
struct blkmtd_dev { struct blkmtd_dev {
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* (c) 1999 Machine Vision Holdings, Inc. * (c) 1999 Machine Vision Holdings, Inc.
* (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
* *
* $Id: doc2000.c,v 1.53 2003/06/11 09:45:19 dwmw2 Exp $ * $Id: doc2000.c,v 1.60 2004/04/07 08:30:04 gleixner Exp $
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -19,12 +19,14 @@ ...@@ -19,12 +19,14 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/bitops.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h> #include <linux/mtd/nand.h>
#include <linux/mtd/doc2000.h> #include <linux/mtd/doc2000.h>
#define DOC_SUPPORT_2000 #define DOC_SUPPORT_2000
#define DOC_SUPPORT_2000TSOP
#define DOC_SUPPORT_MILLENNIUM #define DOC_SUPPORT_MILLENNIUM
#ifdef DOC_SUPPORT_2000 #ifdef DOC_SUPPORT_2000
...@@ -33,7 +35,7 @@ ...@@ -33,7 +35,7 @@
#define DoC_is_2000(doc) (0) #define DoC_is_2000(doc) (0)
#endif #endif
#ifdef DOC_SUPPORT_MILLENNIUM #if defined(DOC_SUPPORT_2000TSOP) || defined(DOC_SUPPORT_MILLENNIUM)
#define DoC_is_Millennium(doc) (doc->ChipID == DOC_ChipID_DocMil) #define DoC_is_Millennium(doc) (doc->ChipID == DOC_ChipID_DocMil)
#else #else
#define DoC_is_Millennium(doc) (0) #define DoC_is_Millennium(doc) (0)
...@@ -53,11 +55,12 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -53,11 +55,12 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf); size_t *retlen, const u_char *buf);
static int doc_read_ecc(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, size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
struct nand_oobinfo *unused);
static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, 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, size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
struct nand_oobinfo *unused); static int doc_writev_ecc(struct mtd_info *mtd, const struct iovec *vecs,
unsigned long count, loff_t to, size_t *retlen,
u_char *eccbuf, struct nand_oobinfo *oobsel);
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
size_t *retlen, u_char *buf); size_t *retlen, u_char *buf);
static int doc_write_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,
...@@ -94,6 +97,10 @@ static int _DoC_WaitReady(struct DiskOnChip *doc) ...@@ -94,6 +97,10 @@ static int _DoC_WaitReady(struct DiskOnChip *doc)
/* Out-of-line routine to wait for chip response */ /* Out-of-line routine to wait for chip response */
while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
/* issue 2 read from NOP register after reading from CDSNControl register
see Software Requirement 11.4 item 2. */
DoC_Delay(doc, 2);
if (time_after(jiffies, timeo)) { if (time_after(jiffies, timeo)) {
DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n"); DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n");
return -EIO; return -EIO;
...@@ -147,6 +154,8 @@ static inline int DoC_Command(struct DiskOnChip *doc, unsigned char command, ...@@ -147,6 +154,8 @@ static inline int DoC_Command(struct DiskOnChip *doc, unsigned char command,
/* Send the command */ /* Send the command */
WriteDOC_(command, docptr, doc->ioreg); WriteDOC_(command, docptr, doc->ioreg);
if (DoC_is_Millennium(doc))
WriteDOC(command, docptr, WritePipeTerm);
/* Lower the CLE line */ /* Lower the CLE line */
WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl); WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl);
...@@ -208,6 +217,9 @@ static int DoC_Address(struct DiskOnChip *doc, int numbytes, unsigned long ofs, ...@@ -208,6 +217,9 @@ static int DoC_Address(struct DiskOnChip *doc, int numbytes, unsigned long ofs,
} }
} }
if (DoC_is_Millennium(doc))
WriteDOC(ofs & 0xff, docptr, WritePipeTerm);
DoC_Delay(doc, 2); /* Needed for some slow flash chips. mf. */ DoC_Delay(doc, 2); /* Needed for some slow flash chips. mf. */
/* FIXME: The SlowIO's for millennium could be replaced by /* FIXME: The SlowIO's for millennium could be replaced by
...@@ -346,15 +358,25 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) ...@@ -346,15 +358,25 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
/* Read the manufacturer and device id codes from the device */ /* Read the manufacturer and device id codes from the device */
/* CDSN Slow IO register see Software Requirement 11.4 item 5. */ if (DoC_is_Millennium(doc)) {
dummy = ReadDOC(doc->virtadr, CDSNSlowIO); DoC_Delay(doc, 2);
DoC_Delay(doc, 2); dummy = ReadDOC(doc->virtadr, ReadPipeInit);
mfr = ReadDOC_(doc->virtadr, doc->ioreg); mfr = ReadDOC(doc->virtadr, LastDataRead);
/* CDSN Slow IO register see Software Requirement 11.4 item 5. */ DoC_Delay(doc, 2);
dummy = ReadDOC(doc->virtadr, CDSNSlowIO); dummy = ReadDOC(doc->virtadr, ReadPipeInit);
DoC_Delay(doc, 2); id = ReadDOC(doc->virtadr, LastDataRead);
id = ReadDOC_(doc->virtadr, doc->ioreg); } else {
/* CDSN Slow IO register see Software Req 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 Req 11.4 item 5. */
dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
DoC_Delay(doc, 2);
id = ReadDOC_(doc->virtadr, doc->ioreg);
}
/* No response - return failure */ /* No response - return failure */
if (mfr == 0xff || mfr == 0) if (mfr == 0xff || mfr == 0)
...@@ -388,11 +410,10 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) ...@@ -388,11 +410,10 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
if (!doc->mfr) { if (!doc->mfr) {
doc->mfr = mfr; doc->mfr = mfr;
doc->id = id; doc->id = id;
doc->chipshift = doc->chipshift =
nand_flash_ids[i].chipshift; ffs((nand_flash_ids[i].chipsize << 20)) - 1;
doc->page256 = nand_flash_ids[i].page256; doc->page256 = (nand_flash_ids[i].pagesize == 256) ? 1 : 0;
doc->pageadrlen = doc->pageadrlen = doc->chipshift > 25 ? 3 : 2;
nand_flash_ids[i].chipshift > 25 ? 3 : 2;
doc->erasesize = doc->erasesize =
nand_flash_ids[i].erasesize; nand_flash_ids[i].erasesize;
return 1; return 1;
...@@ -412,20 +433,16 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) ...@@ -412,20 +433,16 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ /* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */
static void DoC_ScanChips(struct DiskOnChip *this) static void DoC_ScanChips(struct DiskOnChip *this, int maxchips)
{ {
int floor, chip; int floor, chip;
int numchips[MAX_FLOORS]; int numchips[MAX_FLOORS];
int maxchips = MAX_CHIPS;
int ret = 1; int ret = 1;
this->numchips = 0; this->numchips = 0;
this->mfr = 0; this->mfr = 0;
this->id = 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 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; ret = 1;
...@@ -517,6 +534,7 @@ static void DoC2k_init(struct mtd_info *mtd) ...@@ -517,6 +534,7 @@ 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; struct DiskOnChip *old = NULL;
int maxchips;
/* We must avoid being called twice for the same device. */ /* We must avoid being called twice for the same device. */
...@@ -540,14 +558,28 @@ static void DoC2k_init(struct mtd_info *mtd) ...@@ -540,14 +558,28 @@ static void DoC2k_init(struct mtd_info *mtd)
switch (this->ChipID) { switch (this->ChipID) {
case DOC_ChipID_Doc2kTSOP:
mtd->name = "DiskOnChip 2000 TSOP";
this->ioreg = DoC_Mil_CDSN_IO;
/* Pretend it's a Millennium */
this->ChipID = DOC_ChipID_DocMil;
maxchips = MAX_CHIPS;
break;
case DOC_ChipID_Doc2k: case DOC_ChipID_Doc2k:
mtd->name = "DiskOnChip 2000"; mtd->name = "DiskOnChip 2000";
this->ioreg = DoC_2k_CDSN_IO; this->ioreg = DoC_2k_CDSN_IO;
maxchips = MAX_CHIPS;
break; break;
case DOC_ChipID_DocMil: case DOC_ChipID_DocMil:
mtd->name = "DiskOnChip Millennium"; mtd->name = "DiskOnChip Millennium";
this->ioreg = DoC_Mil_CDSN_IO; this->ioreg = DoC_Mil_CDSN_IO;
maxchips = MAX_CHIPS_MIL;
break; break;
default:
printk("Unknown ChipID 0x%02x\n", this->ChipID);
kfree(mtd);
iounmap((void *) this->virtadr);
return;
} }
printk(KERN_NOTICE "%s found at address 0x%lX\n", mtd->name, printk(KERN_NOTICE "%s found at address 0x%lX\n", mtd->name,
...@@ -568,6 +600,7 @@ static void DoC2k_init(struct mtd_info *mtd) ...@@ -568,6 +600,7 @@ static void DoC2k_init(struct mtd_info *mtd)
mtd->write = doc_write; mtd->write = doc_write;
mtd->read_ecc = doc_read_ecc; mtd->read_ecc = doc_read_ecc;
mtd->write_ecc = doc_write_ecc; mtd->write_ecc = doc_write_ecc;
mtd->writev_ecc = doc_writev_ecc;
mtd->read_oob = doc_read_oob; mtd->read_oob = doc_read_oob;
mtd->write_oob = doc_write_oob; mtd->write_oob = doc_write_oob;
mtd->sync = NULL; mtd->sync = NULL;
...@@ -580,7 +613,7 @@ static void DoC2k_init(struct mtd_info *mtd) ...@@ -580,7 +613,7 @@ static void DoC2k_init(struct mtd_info *mtd)
init_MUTEX(&this->lock); init_MUTEX(&this->lock);
/* Ident all the chips present. */ /* Ident all the chips present. */
DoC_ScanChips(this); DoC_ScanChips(this, maxchips);
if (!this->totlen) { if (!this->totlen) {
kfree(mtd); kfree(mtd);
...@@ -599,12 +632,11 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -599,12 +632,11 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t * retlen, u_char * buf) size_t * retlen, u_char * buf)
{ {
/* Just a special case of doc_read_ecc */ /* Just a special case of doc_read_ecc */
return doc_read_ecc(mtd, from, len, retlen, buf, NULL, NULL); return doc_read_ecc(mtd, from, len, retlen, buf, NULL, 0);
} }
static int doc_read_ecc(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, size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel)
struct nand_oobinfo *unused)
{ {
struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
unsigned long docptr; unsigned long docptr;
...@@ -612,6 +644,7 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -612,6 +644,7 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
unsigned char syndrome[6]; unsigned char syndrome[6];
volatile char dummy; volatile char dummy;
int i, len256 = 0, ret=0; int i, len256 = 0, ret=0;
size_t left = len;
docptr = this->virtadr; docptr = this->virtadr;
...@@ -621,122 +654,131 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -621,122 +654,131 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
down(&this->lock); down(&this->lock);
/* Don't allow a single read to cross a 512-byte block boundary */ *retlen = 0;
if (from + len > ((from | 0x1ff) + 1)) while (left) {
len = ((from | 0x1ff) + 1) - from; len = left;
/* 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); */
/* Don't allow a single read to cross a 512-byte block boundary */
if (from + len > ((from | 0x1ff) + 1))
len = ((from | 0x1ff) + 1) - from;
/* Find the chip which is to be used and select it */ /* The ECC will not be calculated correctly if less than 512 is read */
mychip = &this->chips[from >> (this->chipshift)]; if (len != 0x200 && eccbuf)
printk(KERN_WARNING
"ECC needs a full sector read (adr: %lx size %lx)\n",
(long) from, (long) len);
if (this->curfloor != mychip->floor) { /* printk("DoC_Read (adr: %lx size %lx)\n", (long) from, (long) len); */
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(this, /* Find the chip which is to be used and select it */
(!this->page256 mychip = &this->chips[from >> (this->chipshift)];
&& (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);
} else {
/* disable the ECC engine */
WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
}
/* treat crossing 256-byte sector for 2M x 8bits devices */ if (this->curfloor != mychip->floor) {
if (this->page256 && from + len > (from | 0xff) + 1) { DoC_SelectFloor(this, mychip->floor);
len256 = (from | 0xff) + 1 - from; DoC_SelectChip(this, mychip->chip);
DoC_ReadBuf(this, buf, len256); } else if (this->curchip != mychip->chip) {
DoC_SelectChip(this, mychip->chip);
}
DoC_Command(this, NAND_CMD_READ0, CDSN_CTRL_WP); this->curfloor = mychip->floor;
DoC_Address(this, ADDR_COLUMN_PAGE, from + len256, this->curchip = mychip->chip;
CDSN_CTRL_WP, CDSN_CTRL_ECC_IO);
}
DoC_ReadBuf(this, &buf[len256], len - len256); 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);
/* Let the caller know we completed it */ if (eccbuf) {
*retlen = len; /* Prime the ECC engine */
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);
}
if (eccbuf) { /* treat crossing 256-byte sector for 2M x 8bits devices */
/* Read the ECC data through the DiskOnChip ECC logic */ if (this->page256 && from + len > (from | 0xff) + 1) {
/* Note: this will work even with 2M x 8bit devices as */ len256 = (from | 0xff) + 1 - from;
/* they have 8 bytes of OOB per 256 page. mf. */ DoC_ReadBuf(this, buf, len256);
DoC_ReadBuf(this, eccbuf, 6);
/* Flush the pipeline */ DoC_Command(this, NAND_CMD_READ0, CDSN_CTRL_WP);
if (DoC_is_Millennium(this)) { DoC_Address(this, ADDR_COLUMN_PAGE, from + len256,
dummy = ReadDOC(docptr, ECCConf); CDSN_CTRL_WP, CDSN_CTRL_ECC_IO);
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 */ DoC_ReadBuf(this, &buf[len256], len - len256);
if (i & 0x80) {
int nb_errors; /* Let the caller know we completed it */
/* There was an ECC error */ *retlen += len;
if (eccbuf) {
/* Read the ECC data through the DiskOnChip ECC logic */
/* 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 */
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 (i & 0x80) {
int nb_errors;
/* There was an ECC error */
#ifdef ECC_DEBUG #ifdef ECC_DEBUG
printk(KERN_ERR "DiskOnChip ECC Error: Read at %lx\n", (long)from); printk(KERN_ERR "DiskOnChip ECC Error: Read at %lx\n", (long)from);
#endif #endif
/* Read the ECC syndrom through the DiskOnChip ECC logic. /* Read the ECC syndrom through the DiskOnChip ECC logic.
These syndrome will be all ZERO when there is no error */ These syndrome will be all ZERO when there is no error */
for (i = 0; i < 6; i++) { for (i = 0; i < 6; i++) {
syndrome[i] = syndrome[i] =
ReadDOC(docptr, ECCSyndrome0 + i); ReadDOC(docptr, ECCSyndrome0 + i);
} }
nb_errors = doc_decode_ecc(buf, syndrome); nb_errors = doc_decode_ecc(buf, syndrome);
#ifdef ECC_DEBUG #ifdef ECC_DEBUG
printk(KERN_ERR "Errors corrected: %x\n", nb_errors); printk(KERN_ERR "Errors corrected: %x\n", nb_errors);
#endif #endif
if (nb_errors < 0) { if (nb_errors < 0) {
/* We return error, but have actually done the read. Not that /* We return error, but have actually done the read. Not that
this can be told to user-space, via sys_read(), but at least this can be told to user-space, via sys_read(), but at least
MTD-aware stuff can know about it by checking *retlen */ MTD-aware stuff can know about it by checking *retlen */
ret = -EIO; ret = -EIO;
} }
} }
#ifdef PSYCHO_DEBUG #ifdef PSYCHO_DEBUG
printk(KERN_DEBUG "ECC DATA at %lxB: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", printk(KERN_DEBUG "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], (long)from, eccbuf[0], eccbuf[1], eccbuf[2],
eccbuf[3], eccbuf[4], eccbuf[5]); eccbuf[3], eccbuf[4], eccbuf[5]);
#endif #endif
/* disable the ECC engine */ /* disable the ECC engine */
WriteDOC(DOC_ECC_DIS, docptr , ECCConf); WriteDOC(DOC_ECC_DIS, docptr , ECCConf);
} }
/* according to 11.4.1, we need to wait for the busy line /* according to 11.4.1, we need to wait for the busy line
* drop if we read to the end of the page. */ * drop if we read to the end of the page. */
if(0 == ((from + *retlen) & 0x1ff)) if(0 == ((from + len) & 0x1ff))
{ {
DoC_WaitReady(this); DoC_WaitReady(this);
}
from += len;
left -= len;
buf += len;
} }
up(&this->lock); up(&this->lock);
...@@ -748,13 +790,12 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -748,13 +790,12 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t * retlen, const u_char * buf) size_t * retlen, const u_char * buf)
{ {
char eccbuf[6]; char eccbuf[6];
return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, NULL); return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, 0);
} }
static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
size_t * retlen, const u_char * buf, size_t * retlen, const u_char * buf,
u_char * eccbuf, u_char * eccbuf, struct nand_oobinfo *oobsel)
struct nand_oobinfo *unused)
{ {
struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
int di; /* Yes, DI is a hangover from when I was disassembling the binary driver */ int di; /* Yes, DI is a hangover from when I was disassembling the binary driver */
...@@ -762,6 +803,8 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -762,6 +803,8 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
volatile char dummy; volatile char dummy;
int len256 = 0; int len256 = 0;
struct Nand *mychip; struct Nand *mychip;
size_t left = len;
int status;
docptr = this->virtadr; docptr = this->virtadr;
...@@ -771,65 +814,133 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -771,65 +814,133 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
down(&this->lock); down(&this->lock);
/* Don't allow a single write to cross a 512-byte block boundary */ *retlen = 0;
if (to + len > ((to | 0x1ff) + 1)) while (left) {
len = ((to | 0x1ff) + 1) - to; len = left;
/* The ECC will not be calculated correctly if less than 512 is written */ /* Don't allow a single write to cross a 512-byte block boundary */
if (len != 0x200 && eccbuf) if (to + len > ((to | 0x1ff) + 1))
printk(KERN_WARNING len = ((to | 0x1ff) + 1) - to;
"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); */ /* The ECC will not be calculated correctly if less than 512 is written */
/* DBB-
if (len != 0x200 && eccbuf)
printk(KERN_WARNING
"ECC needs a full sector write (adr: %lx size %lx)\n",
(long) to, (long) len);
-DBB */
/* Find the chip which is to be used and select it */ /* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */
mychip = &this->chips[to >> (this->chipshift)];
if (this->curfloor != mychip->floor) { /* Find the chip which is to be used and select it */
DoC_SelectFloor(this, mychip->floor); mychip = &this->chips[to >> (this->chipshift)];
DoC_SelectChip(this, mychip->chip);
} else if (this->curchip != mychip->chip) {
DoC_SelectChip(this, mychip->chip);
}
this->curfloor = mychip->floor; if (this->curfloor != mychip->floor) {
this->curchip = mychip->chip; DoC_SelectFloor(this, mychip->floor);
DoC_SelectChip(this, mychip->chip);
} else if (this->curchip != mychip->chip) {
DoC_SelectChip(this, mychip->chip);
}
/* Set device to main plane of flash */ this->curfloor = mychip->floor;
DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP); this->curchip = mychip->chip;
DoC_Command(this,
(!this->page256
&& (to & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0,
CDSN_CTRL_WP);
DoC_Command(this, NAND_CMD_SEQIN, 0); /* Set device to main plane of flash */
DoC_Address(this, ADDR_COLUMN_PAGE, to, 0, CDSN_CTRL_ECC_IO); 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);
if (eccbuf) { DoC_Command(this, NAND_CMD_SEQIN, 0);
/* Prime the ECC engine */ DoC_Address(this, ADDR_COLUMN_PAGE, to, 0, CDSN_CTRL_ECC_IO);
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);
}
/* treat crossing 256-byte sector for 2M x 8bits devices */ if (eccbuf) {
if (this->page256 && to + len > (to | 0xff) + 1) { /* Prime the ECC engine */
len256 = (to | 0xff) + 1 - to; WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
DoC_WriteBuf(this, buf, len256); 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);
}
/* 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);
if (ReadDOC_(docptr, this->ioreg) & 1) {
printk(KERN_ERR "Error programming flash\n");
/* Error in programming */
*retlen = 0;
up(&this->lock);
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 (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);
}
WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_FLASH_IO | CDSN_CTRL_CE, docptr,
CDSNControl);
/* Read the ECC data through the DiskOnChip ECC logic */
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]);
#endif
}
DoC_Command(this, NAND_CMD_PAGEPROG, 0); DoC_Command(this, NAND_CMD_PAGEPROG, 0);
DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
/* There's an implicit DoC_WaitReady() in DoC_Command */ /* There's an implicit DoC_WaitReady() in DoC_Command */
dummy = ReadDOC(docptr, CDSNSlowIO); if (DoC_is_Millennium(this)) {
DoC_Delay(this, 2); ReadDOC(docptr, ReadPipeInit);
status = ReadDOC(docptr, LastDataRead);
} else {
dummy = ReadDOC(docptr, CDSNSlowIO);
DoC_Delay(this, 2);
status = ReadDOC_(docptr, this->ioreg);
}
if (ReadDOC_(docptr, this->ioreg) & 1) { if (status & 1) {
printk(KERN_ERR "Error programming flash\n"); printk(KERN_ERR "Error programming flash\n");
/* Error in programming */ /* Error in programming */
*retlen = 0; *retlen = 0;
...@@ -837,82 +948,97 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -837,82 +948,97 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
return -EIO; return -EIO;
} }
DoC_Command(this, NAND_CMD_SEQIN, 0); /* Let the caller know we completed it */
DoC_Address(this, ADDR_COLUMN_PAGE, to + len256, 0, *retlen += len;
CDSN_CTRL_ECC_IO);
if (eccbuf) {
unsigned char x[8];
size_t dummy;
int ret;
/* Write the ECC data to flash */
for (di=0; di<6; di++)
x[di] = eccbuf[di];
x[6]=0x55;
x[7]=0x55;
ret = doc_write_oob_nolock(mtd, to, 8, &dummy, x);
if (ret) {
up(&this->lock);
return ret;
}
}
to += len;
left -= len;
buf += len;
} }
DoC_WriteBuf(this, &buf[len256], len - len256); up(&this->lock);
return 0;
}
if (eccbuf) { static int doc_writev_ecc(struct mtd_info *mtd, const struct iovec *vecs,
WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_CE, docptr, unsigned long count, loff_t to, size_t *retlen,
CDSNControl); u_char *eccbuf, struct nand_oobinfo *oobsel)
{
static char static_buf[512];
static DECLARE_MUTEX(writev_buf_sem);
if (DoC_is_Millennium(this)) { size_t totretlen = 0;
WriteDOC(0, docptr, NOP); size_t thisvecofs = 0;
WriteDOC(0, docptr, NOP); int ret= 0;
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 */ down(&writev_buf_sem);
for (di = 0; di < 6; di++) {
eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di);
}
/* Reset the ECC engine */ while(count) {
WriteDOC(DOC_ECC_DIS, docptr, ECCConf); size_t thislen, thisretlen;
unsigned char *buf;
#ifdef PSYCHO_DEBUG buf = vecs->iov_base + thisvecofs;
printk thislen = vecs->iov_len - thisvecofs;
("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
}
DoC_Command(this, NAND_CMD_PAGEPROG, 0);
DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); if (thislen >= 512) {
/* There's an implicit DoC_WaitReady() in DoC_Command */ thislen = thislen & ~(512-1);
thisvecofs += thislen;
} else {
/* Not enough to fill a page. Copy into buf */
memcpy(static_buf, buf, thislen);
buf = &static_buf[thislen];
while(count && thislen < 512) {
vecs++;
count--;
thisvecofs = min((512-thislen), vecs->iov_len);
memcpy(buf, vecs->iov_base, thisvecofs);
thislen += thisvecofs;
buf += thisvecofs;
}
buf = static_buf;
}
if (count && thisvecofs == vecs->iov_len) {
thisvecofs = 0;
vecs++;
count--;
}
ret = doc_write_ecc(mtd, to, thislen, &thisretlen, buf, eccbuf, oobsel);
dummy = ReadDOC(docptr, CDSNSlowIO); totretlen += thisretlen;
DoC_Delay(this, 2);
if (ReadDOC_(docptr, this->ioreg) & 1) { if (ret || thisretlen != thislen)
printk(KERN_ERR "Error programming flash\n"); break;
/* Error in programming */
*retlen = 0;
up(&this->lock);
return -EIO;
}
/* Let the caller know we completed it */ to += thislen;
*retlen = len; }
if (eccbuf) { up(&writev_buf_sem);
unsigned char x[8]; *retlen = totretlen;
size_t dummy; return ret;
int ret;
/* Write the ECC data to flash */
for (di=0; di<6; di++)
x[di] = eccbuf[di];
x[6]=0x55;
x[7]=0x55;
ret = doc_write_oob_nolock(mtd, to, 8, &dummy, x);
up(&this->lock);
return ret;
}
up(&this->lock);
return 0;
} }
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
size_t * retlen, u_char * buf) size_t * retlen, u_char * buf)
{ {
...@@ -982,6 +1108,7 @@ static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, ...@@ -982,6 +1108,7 @@ static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len,
unsigned long docptr = this->virtadr; unsigned long docptr = this->virtadr;
struct Nand *mychip = &this->chips[ofs >> this->chipshift]; struct Nand *mychip = &this->chips[ofs >> this->chipshift];
volatile int dummy; volatile int dummy;
int status;
// 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]); // buf[0], buf[1], buf[2], buf[3], buf[8], buf[9], buf[14],buf[15]);
...@@ -1030,10 +1157,16 @@ static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, ...@@ -1030,10 +1157,16 @@ static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len,
DoC_Command(this, NAND_CMD_STATUS, 0); DoC_Command(this, NAND_CMD_STATUS, 0);
/* DoC_WaitReady() is implicit in DoC_Command */ /* DoC_WaitReady() is implicit in DoC_Command */
dummy = ReadDOC(docptr, CDSNSlowIO); if (DoC_is_Millennium(this)) {
DoC_Delay(this, 2); ReadDOC(docptr, ReadPipeInit);
status = ReadDOC(docptr, LastDataRead);
} else {
dummy = ReadDOC(docptr, CDSNSlowIO);
DoC_Delay(this, 2);
status = ReadDOC_(docptr, this->ioreg);
}
if (ReadDOC_(docptr, this->ioreg) & 1) { if (status & 1) {
printk(KERN_ERR "Error programming oob data\n"); printk(KERN_ERR "Error programming oob data\n");
/* There was an error */ /* There was an error */
*retlen = 0; *retlen = 0;
...@@ -1049,10 +1182,16 @@ static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len, ...@@ -1049,10 +1182,16 @@ static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len,
DoC_Command(this, NAND_CMD_STATUS, 0); DoC_Command(this, NAND_CMD_STATUS, 0);
/* DoC_WaitReady() is implicit in DoC_Command */ /* DoC_WaitReady() is implicit in DoC_Command */
dummy = ReadDOC(docptr, CDSNSlowIO); if (DoC_is_Millennium(this)) {
DoC_Delay(this, 2); ReadDOC(docptr, ReadPipeInit);
status = ReadDOC(docptr, LastDataRead);
} else {
dummy = ReadDOC(docptr, CDSNSlowIO);
DoC_Delay(this, 2);
status = ReadDOC_(docptr, this->ioreg);
}
if (ReadDOC_(docptr, this->ioreg) & 1) { if (status & 1) {
printk(KERN_ERR "Error programming oob data\n"); printk(KERN_ERR "Error programming oob data\n");
/* There was an error */ /* There was an error */
*retlen = 0; *retlen = 0;
...@@ -1085,6 +1224,7 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *instr) ...@@ -1085,6 +1224,7 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
volatile int dummy; volatile int dummy;
unsigned long docptr; unsigned long docptr;
struct Nand *mychip; struct Nand *mychip;
int status;
down(&this->lock); down(&this->lock);
...@@ -1116,10 +1256,16 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *instr) ...@@ -1116,10 +1256,16 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
dummy = ReadDOC(docptr, CDSNSlowIO); if (DoC_is_Millennium(this)) {
DoC_Delay(this, 2); ReadDOC(docptr, ReadPipeInit);
status = ReadDOC(docptr, LastDataRead);
if (ReadDOC_(docptr, this->ioreg) & 1) { } else {
dummy = ReadDOC(docptr, CDSNSlowIO);
DoC_Delay(this, 2);
status = ReadDOC_(docptr, this->ioreg);
}
if (status & 1) {
printk(KERN_ERR "Error erasing at 0x%x\n", ofs); printk(KERN_ERR "Error erasing at 0x%x\n", ofs);
/* There was an error */ /* There was an error */
instr->state = MTD_ERASE_FAILED; instr->state = MTD_ERASE_FAILED;
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* (c) 1999 Machine Vision Holdings, Inc. * (c) 1999 Machine Vision Holdings, Inc.
* (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
* *
* $Id: doc2001.c,v 1.41 2003/06/11 09:45:19 dwmw2 Exp $ * $Id: doc2001.c,v 1.42 2004/04/04 12:36:45 gleixner Exp $
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/bitops.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h> #include <linux/mtd/nand.h>
...@@ -37,12 +38,9 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -37,12 +38,9 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf); size_t *retlen, const u_char *buf);
static int doc_read_ecc(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, size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel);
struct nand_oobinfo *unused);
static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, 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, size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel);
struct nand_oobinfo *unused);
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
size_t *retlen, u_char *buf); size_t *retlen, u_char *buf);
static int doc_write_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,
...@@ -229,7 +227,7 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) ...@@ -229,7 +227,7 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
mfr, id, nand_manuf_ids[j].name, nand_flash_ids[i].name); mfr, id, nand_manuf_ids[j].name, nand_flash_ids[i].name);
doc->mfr = mfr; doc->mfr = mfr;
doc->id = id; doc->id = id;
doc->chipshift = nand_flash_ids[i].chipshift; doc->chipshift = ffs((nand_flash_ids[i].chipsize << 20)) - 1;
break; break;
} }
} }
...@@ -406,12 +404,11 @@ static int doc_read (struct mtd_info *mtd, loff_t from, size_t len, ...@@ -406,12 +404,11 @@ static int doc_read (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf) size_t *retlen, u_char *buf)
{ {
/* Just a special case of doc_read_ecc */ /* Just a special case of doc_read_ecc */
return doc_read_ecc(mtd, from, len, retlen, buf, NULL, NULL); return doc_read_ecc(mtd, from, len, retlen, buf, NULL, 0);
} }
static int doc_read_ecc (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, size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel)
struct nand_oobinfo *unused)
{ {
int i, ret; int i, ret;
volatile char dummy; volatile char dummy;
...@@ -533,12 +530,11 @@ static int doc_write (struct mtd_info *mtd, loff_t to, size_t len, ...@@ -533,12 +530,11 @@ static int doc_write (struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf) size_t *retlen, const u_char *buf)
{ {
char eccbuf[6]; char eccbuf[6];
return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, NULL); return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, 0);
} }
static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, 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, size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel)
struct nand_oobinfo *unused)
{ {
int i,ret = 0; int i,ret = 0;
volatile char dummy; volatile char dummy;
......
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
* (c) 1999 Machine Vision Holdings, Inc. * (c) 1999 Machine Vision Holdings, Inc.
* (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
* *
* $Id: doc2001plus.c,v 1.5 2003/06/11 09:45:19 dwmw2 Exp $ * $Id: doc2001plus.c,v 1.8 2004/04/04 12:36:45 gleixner Exp $
*
* Released under GPL
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -21,6 +23,7 @@ ...@@ -21,6 +23,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/bitops.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h> #include <linux/mtd/nand.h>
...@@ -183,24 +186,35 @@ static int DoC_SelectFloor(unsigned long docptr, int floor) ...@@ -183,24 +186,35 @@ static int DoC_SelectFloor(unsigned long docptr, int floor)
* | Data 0 | ECC 0 |Flags0 |Flags1 | Data 1 |ECC 1 | OOB 1 + 2 | * | Data 0 | ECC 0 |Flags0 |Flags1 | Data 1 |ECC 1 | OOB 1 + 2 |
* +-----------+-------+-------+-------+--------------+---------+-----------+ * +-----------+-------+-------+-------+--------------+---------+-----------+
*/ */
/* FIXME: This lives in INFTL not here. Other users of flash devices
may not want it */
static unsigned int DoC_GetDataOffset(struct mtd_info *mtd, loff_t *from) static unsigned int DoC_GetDataOffset(struct mtd_info *mtd, loff_t *from)
{ {
unsigned int ofs = *from & 0x3ff; struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
unsigned int cmd;
if (ofs < 512) { if (this->interleave) {
cmd = NAND_CMD_READ0; unsigned int ofs = *from & 0x3ff;
ofs &= 0x1ff; unsigned int cmd;
} else if (ofs < 1014) {
cmd = NAND_CMD_READ1; if (ofs < 512) {
ofs = (ofs & 0x1ff) + 10; cmd = NAND_CMD_READ0;
ofs &= 0x1ff;
} else if (ofs < 1014) {
cmd = NAND_CMD_READ1;
ofs = (ofs & 0x1ff) + 10;
} else {
cmd = NAND_CMD_READOOB;
ofs = ofs - 1014;
}
*from = (*from & ~0x3ff) | ofs;
return cmd;
} else { } else {
cmd = NAND_CMD_READOOB; /* No interleave */
ofs = ofs - 1014; if ((*from) & 0x100)
return NAND_CMD_READ1;
return NAND_CMD_READ0;
} }
*from = (*from & ~0x3ff) | ofs;
return cmd;
} }
static unsigned int DoC_GetECCOffset(struct mtd_info *mtd, loff_t *from) static unsigned int DoC_GetECCOffset(struct mtd_info *mtd, loff_t *from)
...@@ -294,10 +308,12 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) ...@@ -294,10 +308,12 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
dummy = ReadDOC(docptr, Mplus_ReadPipeInit); dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
mfr = ReadDOC(docptr, Mil_CDSN_IO); mfr = ReadDOC(docptr, Mil_CDSN_IO);
dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ if (doc->interleave)
dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */
id = ReadDOC(docptr, Mil_CDSN_IO); id = ReadDOC(docptr, Mil_CDSN_IO);
dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ if (doc->interleave)
dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */
dummy = ReadDOC(docptr, Mplus_LastDataRead); dummy = ReadDOC(docptr, Mplus_LastDataRead);
dummy = ReadDOC(docptr, Mplus_LastDataRead); dummy = ReadDOC(docptr, Mplus_LastDataRead);
...@@ -321,10 +337,7 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) ...@@ -321,10 +337,7 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
nand_manuf_ids[j].name, nand_flash_ids[i].name); nand_manuf_ids[j].name, nand_flash_ids[i].name);
doc->mfr = mfr; doc->mfr = mfr;
doc->id = id; doc->id = id;
doc->interleave = 0; doc->chipshift = ffs((nand_flash_ids[i].chipsize << 20)) - 1;
if (doc->ChipID == DOC_ChipID_DocMilPlus32)
doc->interleave = 1;
doc->chipshift = nand_flash_ids[i].chipshift;
doc->erasesize = nand_flash_ids[i].erasesize << doc->interleave; doc->erasesize = nand_flash_ids[i].erasesize << doc->interleave;
break; break;
} }
...@@ -346,6 +359,21 @@ static void DoC_ScanChips(struct DiskOnChip *this) ...@@ -346,6 +359,21 @@ static void DoC_ScanChips(struct DiskOnChip *this)
this->mfr = 0; this->mfr = 0;
this->id = 0; this->id = 0;
/* Work out the intended interleave setting */
this->interleave = 0;
if (this->ChipID == DOC_ChipID_DocMilPlus32)
this->interleave = 1;
/* Check the ASIC agrees */
if ( (this->interleave << 2) !=
(ReadDOC(this->virtadr, Mplus_Configuration) & 4)) {
u_char conf = ReadDOC(this->virtadr, Mplus_Configuration);
printk(KERN_NOTICE "Setting DiskOnChip Millennium Plus interleave to %s\n",
this->interleave?"on (16-bit)":"off (8-bit)");
conf ^= 4;
WriteDOC(this->virtadr, conf, Mplus_Configuration);
}
/* For each floor, find the number of valid chips it contains */ /* For each floor, find the number of valid chips it contains */
for (floor = 0,ret = 1; floor < MAX_FLOORS_MPLUS; floor++) { for (floor = 0,ret = 1; floor < MAX_FLOORS_MPLUS; floor++) {
numchips[floor] = 0; numchips[floor] = 0;
...@@ -739,7 +767,7 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -739,7 +767,7 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
return -EINVAL; return -EINVAL;
/* Determine position of OOB flags, before or after data */ /* Determine position of OOB flags, before or after data */
before = to & 0x200; before = (this->interleave && (to & 0x200));
DoC_CheckASIC(docptr); DoC_CheckASIC(docptr);
...@@ -886,7 +914,10 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, ...@@ -886,7 +914,10 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
/* Figure out which region we are accessing... */ /* Figure out which region we are accessing... */
fofs = ofs; fofs = ofs;
base = ofs & 0xf; base = ofs & 0xf;
if (base < 6) { if (!this->interleave) {
DoC_Command(docptr, NAND_CMD_READOOB, 0);
size = 16 - base;
} else if (base < 6) {
DoC_Command(docptr, DoC_GetECCOffset(mtd, &fofs), 0); DoC_Command(docptr, DoC_GetECCOffset(mtd, &fofs), 0);
size = 6 - base; size = 6 - base;
} else if (base < 8) { } else if (base < 8) {
...@@ -963,7 +994,10 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, ...@@ -963,7 +994,10 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
/* Figure out which region we are accessing... */ /* Figure out which region we are accessing... */
fofs = ofs; fofs = ofs;
base = ofs & 0x0f; base = ofs & 0x0f;
if (base < 6) { if (!this->interleave) {
WriteDOC(NAND_CMD_READOOB, docptr, Mplus_FlashCmd);
size = 16 - base;
} else if (base < 6) {
WriteDOC(DoC_GetECCOffset(mtd, &fofs), docptr, Mplus_FlashCmd); WriteDOC(DoC_GetECCOffset(mtd, &fofs), docptr, Mplus_FlashCmd);
size = 6 - base; size = 6 - base;
} else if (base < 8) { } else if (base < 8) {
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
/* (C) 1999 Machine Vision Holdings, Inc. */ /* (C) 1999 Machine Vision Holdings, Inc. */
/* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> */ /* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> */
/* $Id: docprobe.c,v 1.36 2003/05/23 11:29:34 dwmw2 Exp $ */ /* $Id: docprobe.c,v 1.41 2003/12/03 10:19:57 dwmw2 Exp $ */
...@@ -135,6 +135,9 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr ...@@ -135,6 +135,9 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr
window, DOCControl); window, DOCControl);
#endif /* !DOC_PASSIVE_PROBE */ #endif /* !DOC_PASSIVE_PROBE */
/* We need to read the ChipID register four times. For some
newer DiskOnChip 2000 units, the first three reads will
return the DiskOnChip Millennium ident. Don't ask. */
ChipID = ReadDOC(window, ChipID); ChipID = ReadDOC(window, ChipID);
switch (ChipID) { switch (ChipID) {
...@@ -148,6 +151,12 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr ...@@ -148,6 +151,12 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr
break; break;
case DOC_ChipID_DocMil: case DOC_ChipID_DocMil:
/* Check for the new 2000 with Millennium ASIC */
ReadDOC(window, ChipID);
ReadDOC(window, ChipID);
if (ReadDOC(window, ChipID) != DOC_ChipID_DocMil)
ChipID = DOC_ChipID_Doc2kTSOP;
/* Check the TOGGLE bit in the ECC register */ /* Check the TOGGLE bit in the ECC register */
tmp = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; tmp = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
tmpb = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; tmpb = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
...@@ -191,7 +200,6 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr ...@@ -191,7 +200,6 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr
tmpc = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT; tmpc = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT;
if (tmp != tmpb && tmp == tmpc) if (tmp != tmpb && tmp == tmpc)
return ChipID; return ChipID;
break;
default: default:
break; break;
} }
...@@ -199,8 +207,8 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr ...@@ -199,8 +207,8 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr
default: default:
#ifndef CONFIG_MTD_DOCPROBE_55AA #ifdef CONFIG_MTD_DOCPROBE_55AA
printk(KERN_WARNING "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n", printk(KERN_DEBUG "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n",
ChipID, physadr); ChipID, physadr);
#endif #endif
#ifndef DOC_PASSIVE_PROBE #ifndef DOC_PASSIVE_PROBE
...@@ -241,6 +249,12 @@ static void __init DoC_Probe(unsigned long physadr) ...@@ -241,6 +249,12 @@ static void __init DoC_Probe(unsigned long physadr)
return; return;
if ((ChipID = doccheck(docptr, physadr))) { if ((ChipID = doccheck(docptr, physadr))) {
if (ChipID == DOC_ChipID_Doc2kTSOP) {
/* Remove this at your own peril. The hardware driver works but nothing prevents you from erasing bad blocks */
printk(KERN_NOTICE "Refusing to drive DiskOnChip 2000 TSOP until Bad Block Table is correctly supported by INFTL\n");
iounmap((void *)docptr);
return;
}
docfound = 1; docfound = 1;
mtd = kmalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL); mtd = kmalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL);
...@@ -262,6 +276,12 @@ static void __init DoC_Probe(unsigned long physadr) ...@@ -262,6 +276,12 @@ static void __init DoC_Probe(unsigned long physadr)
sprintf(namebuf, "with ChipID %2.2X", ChipID); sprintf(namebuf, "with ChipID %2.2X", ChipID);
switch(ChipID) { switch(ChipID) {
case DOC_ChipID_Doc2kTSOP:
name="2000 TSOP";
im_funcname = "DoC2k_init";
im_modname = "doc2000";
break;
case DOC_ChipID_Doc2k: case DOC_ChipID_Doc2k:
name="2000"; name="2000";
im_funcname = "DoC2k_init"; im_funcname = "DoC2k_init";
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
/* /*
* MTD driver for the 28F160F3 Flash Memory (non-CFI) on LART. * MTD driver for the 28F160F3 Flash Memory (non-CFI) on LART.
* *
* $Id: lart.c,v 1.5 2003/05/20 21:03:07 dwmw2 Exp $ * $Id: lart.c,v 1.6 2004/07/14 17:21:38 dwmw2 Exp $
* *
* Author: Abraham vd Merwe <abraham@2d3d.co.za> * Author: Abraham vd Merwe <abraham@2d3d.co.za>
* *
......
/* /*
* Copyright (c) 2001 Maciej W. Rozycki * Copyright (c) 2001 Maciej W. Rozycki
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version * as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version. * 2 of the License, or (at your option) any later version.
* *
* $Id: ms02-nv.c,v 1.4 2003/05/20 21:03:07 dwmw2 Exp $ * $Id: ms02-nv.c,v 1.6 2003/08/19 09:25:36 dwmw2 Exp $
*/ */
#include <linux/init.h> #include <linux/init.h>
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
static char version[] __initdata = static char version[] __initdata =
"ms02-nv.c: v.1.0.0 13 Aug 2001 Maciej W. Rozycki.\n"; "ms02-nv.c: v.1.0.0 13 Aug 2001 Maciej W. Rozycki.\n";
MODULE_AUTHOR("Maciej W. Rozycki <macro@ds2.pg.gda.pl>"); MODULE_AUTHOR("Maciej W. Rozycki <macro@ds2.pg.gda.pl>");
MODULE_DESCRIPTION("DEC MS02-NV NVRAM module driver"); MODULE_DESCRIPTION("DEC MS02-NV NVRAM module driver");
...@@ -38,9 +38,9 @@ MODULE_LICENSE("GPL"); ...@@ -38,9 +38,9 @@ MODULE_LICENSE("GPL");
/* /*
* Addresses we probe for an MS02-NV at. Modules may be located * Addresses we probe for an MS02-NV at. Modules may be located
* at any 8MB boundary within a 0MB up to 112MB range or at any 32MB * at any 8MiB boundary within a 0MiB up to 112MiB range or at any 32MiB
* boundary within a 0MB up to 448MB range. We don't support a module * boundary within a 0MiB up to 448MiB range. We don't support a module
* at 0MB, though. * at 0MiB, though.
*/ */
static ulong ms02nv_addrs[] __initdata = { static ulong ms02nv_addrs[] __initdata = {
0x07000000, 0x06800000, 0x06000000, 0x05800000, 0x05000000, 0x07000000, 0x06800000, 0x06000000, 0x05800000, 0x05000000,
...@@ -130,7 +130,7 @@ static int __init ms02nv_init_one(ulong addr) ...@@ -130,7 +130,7 @@ static int __init ms02nv_init_one(ulong addr)
int ret = -ENODEV; int ret = -ENODEV;
/* The module decodes 8MB of address space. */ /* The module decodes 8MiB of address space. */
mod_res = kmalloc(sizeof(*mod_res), GFP_KERNEL); mod_res = kmalloc(sizeof(*mod_res), GFP_KERNEL);
if (!mod_res) if (!mod_res)
return -ENOMEM; return -ENOMEM;
...@@ -233,7 +233,7 @@ static int __init ms02nv_init_one(ulong addr) ...@@ -233,7 +233,7 @@ static int __init ms02nv_init_one(ulong addr)
goto err_out_csr_res; goto err_out_csr_res;
} }
printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %uMB.\n", printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %uMiB.\n",
mtd->index, ms02nv_name, addr, size >> 20); mtd->index, ms02nv_name, addr, size >> 20);
mp->next = root_ms02nv_mtd; mp->next = root_ms02nv_mtd;
...@@ -293,12 +293,12 @@ static int __init ms02nv_init(void) ...@@ -293,12 +293,12 @@ static int __init ms02nv_init(void)
switch (mips_machtype) { switch (mips_machtype) {
case MACH_DS5000_200: case MACH_DS5000_200:
csr = (volatile u32 *)KN02_CSR_ADDR; csr = (volatile u32 *)KN02_CSR_BASE;
if (*csr & KN02_CSR_BNK32M) if (*csr & KN02_CSR_BNK32M)
stride = 2; stride = 2;
break; break;
case MACH_DS5000_2X0: case MACH_DS5000_2X0:
case MACH_DS5000: case MACH_DS5900:
csr = (volatile u32 *)KN03_MCR_BASE; csr = (volatile u32 *)KN03_MCR_BASE;
if (*csr & KN03_MCR_BNK32M) if (*csr & KN03_MCR_BNK32M)
stride = 2; stride = 2;
......
/* /*
* Copyright (c) 2001 Maciej W. Rozycki * Copyright (c) 2001, 2003 Maciej W. Rozycki
* *
* This program is free software; you can redistribute it and/or * DEC MS02-NV (54-20948-01) battery backed-up NVRAM module for
* modify it under the terms of the GNU General Public License * DECstation/DECsystem 5000/2x0 and DECsystem 5900 and 5900/260
* as published by the Free Software Foundation; either version * systems.
* 2 of the License, or (at your option) any later version.
* *
* $Id: ms02-nv.h,v 1.1 2002/09/13 13:46:55 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.
*
* $Id: ms02-nv.h,v 1.3 2003/08/19 09:25:36 dwmw2 Exp $
*/ */
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
/*
* Addresses are decoded as follows:
*
* 0x000000 - 0x3fffff SRAM
* 0x400000 - 0x7fffff CSR
*
* Within the SRAM area the following ranges are forced by the system
* firmware:
*
* 0x000000 - 0x0003ff diagnostic area, destroyed upon a reboot
* 0x000400 - ENDofRAM storage area, available to operating systems
*
* but we can't really use the available area right from 0x000400 as
* the first word is used by the firmware as a status flag passed
* from an operating system. If anything but the valid data magic
* ID value is found, the firmware considers the SRAM clean, i.e.
* containing no valid data, and disables the battery resulting in
* data being erased as soon as power is switched off. So the choice
* for the start address of the user-available is 0x001000 which is
* nicely page aligned. The area between 0x000404 and 0x000fff may
* be used by the driver for own needs.
*
* The diagnostic area defines two status words to be read by an
* operating system, a magic ID to distinguish a MS02-NV board from
* anything else and a status information providing results of tests
* as well as the size of SRAM available, which can be 1MiB or 2MiB
* (that's what the firmware handles; no idea if 2MiB modules ever
* existed).
*
* The firmware only handles the MS02-NV board if installed in the
* last (15th) slot, so for any other location the status information
* stored in the SRAM cannot be relied upon. But from the hardware
* point of view there is no problem using up to 14 such boards in a
* system -- only the 1st slot needs to be filled with a DRAM module.
* The MS02-NV board is ECC-protected, like other MS02 memory boards.
*
* The state of the battery as provided by the CSR is reflected on
* the two onboard LEDs. When facing the battery side of the board,
* with the LEDs at the top left and the battery at the bottom right
* (i.e. looking from the back side of the system box), their meaning
* is as follows (the system has to be powered on):
*
* left LED battery disable status: lit = enabled
* right LED battery condition status: lit = OK
*/
/* MS02-NV iomem register offsets. */ /* MS02-NV iomem register offsets. */
#define MS02NV_CSR 0x400000 /* control & status register */ #define MS02NV_CSR 0x400000 /* control & status register */
/* MS02-NV CSR status bits. */
#define MS02NV_CSR_BATT_OK 0x01 /* battery OK */
#define MS02NV_CSR_BATT_OFF 0x02 /* battery disabled */
/* MS02-NV memory offsets. */ /* MS02-NV memory offsets. */
#define MS02NV_DIAG 0x0003f8 /* diagnostic status */ #define MS02NV_DIAG 0x0003f8 /* diagnostic status */
#define MS02NV_MAGIC 0x0003fc /* MS02-NV magic ID */ #define MS02NV_MAGIC 0x0003fc /* MS02-NV magic ID */
#define MS02NV_RAM 0x000400 /* general-purpose RAM start */ #define MS02NV_VALID 0x000400 /* valid data magic ID */
#define MS02NV_RAM 0x001000 /* user-exposed RAM start */
/* MS02-NV diagnostic status constants. */ /* MS02-NV diagnostic status bits. */
#define MS02NV_DIAG_SIZE_MASK 0xf0 /* RAM size mask */ #define MS02NV_DIAG_TEST 0x01 /* SRAM test done (?) */
#define MS02NV_DIAG_SIZE_SHIFT 0x10 /* RAM size shift (left) */ #define MS02NV_DIAG_RO 0x02 /* SRAM r/o test done */
#define MS02NV_DIAG_RW 0x04 /* SRAM r/w test done */
#define MS02NV_DIAG_FAIL 0x08 /* SRAM test failed */
#define MS02NV_DIAG_SIZE_MASK 0xf0 /* SRAM size mask */
#define MS02NV_DIAG_SIZE_SHIFT 0x10 /* SRAM size shift (left) */
/* MS02-NV general constants. */ /* MS02-NV general constants. */
#define MS02NV_ID 0x03021966 /* MS02-NV magic ID value */ #define MS02NV_ID 0x03021966 /* MS02-NV magic ID value */
#define MS02NV_VALID_ID 0xbd100248 /* valid data magic ID value */
#define MS02NV_SLOT_SIZE 0x800000 /* size of the address space #define MS02NV_SLOT_SIZE 0x800000 /* size of the address space
decoded by the module */ decoded by the module */
typedef volatile u32 ms02nv_uint; typedef volatile u32 ms02nv_uint;
struct ms02nv_private { struct ms02nv_private {
......
/**
*
* $Id: phram.c,v 1.1 2003/08/21 17:52:30 joern Exp $
*
* Copyright (c) Jochen Schaeuble <psionic@psionic.de>
* 07/2003 rewritten by Joern Engel <joern@wh.fh-wedel.de>
*
* DISCLAIMER: This driver makes use of Rusty's excellent module code,
* so it will not work for 2.4 without changes and it wont work for 2.4
* as a module without major changes. Oh well!
*
* Usage:
*
* one commend line parameter per device, each in the form:
* phram=<name>,<start>,<len>
* <name> may be up to 63 characters.
* <start> and <len> can be octal, decimal or hexadecimal. If followed
* by "k", "M" or "G", the numbers will be interpreted as kilo, mega or
* gigabytes.
*
*/
#include <asm/io.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mtd/mtd.h>
#define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args)
struct phram_mtd_list {
struct list_head list;
struct mtd_info *mtdinfo;
};
static LIST_HEAD(phram_list);
int phram_erase(struct mtd_info *mtd, struct erase_info *instr)
{
u_char *start = (u_char *)mtd->priv;
if (instr->addr + instr->len > mtd->size)
return -EINVAL;
memset(start + instr->addr, 0xff, instr->len);
/* This'll catch a few races. Free the thing before returning :)
* I don't feel at all ashamed. This kind of thing is possible anyway
* with flash, but unlikely.
*/
instr->state = MTD_ERASE_DONE;
if (instr->callback)
(*(instr->callback))(instr);
else
kfree(instr);
return 0;
}
int phram_point(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char **mtdbuf)
{
u_char *start = (u_char *)mtd->priv;
if (from + len > mtd->size)
return -EINVAL;
*mtdbuf = start + from;
*retlen = len;
return 0;
}
void phram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len)
{
}
int phram_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
u_char *start = (u_char *)mtd->priv;
if (from + len > mtd->size)
return -EINVAL;
memcpy(buf, start + from, len);
*retlen = len;
return 0;
}
int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
u_char *start = (u_char *)mtd->priv;
if (to + len > mtd->size)
return -EINVAL;
memcpy(start + to, buf, len);
*retlen = len;
return 0;
}
static void unregister_devices(void)
{
struct phram_mtd_list *this;
list_for_each_entry(this, &phram_list, list) {
del_mtd_device(this->mtdinfo);
iounmap(this->mtdinfo->priv);
kfree(this->mtdinfo);
kfree(this);
}
}
static int register_device(char *name, unsigned long start, unsigned long len)
{
struct phram_mtd_list *new;
int ret = -ENOMEM;
new = kmalloc(sizeof(*new), GFP_KERNEL);
if (!new)
goto out0;
new->mtdinfo = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
if (!new->mtdinfo)
goto out1;
memset(new->mtdinfo, 0, sizeof(struct mtd_info));
ret = -EIO;
new->mtdinfo->priv = ioremap(start, len);
if (!new->mtdinfo->priv) {
ERROR("ioremap failed\n");
goto out2;
}
new->mtdinfo->name = name;
new->mtdinfo->size = len;
new->mtdinfo->flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE;
new->mtdinfo->erase = phram_erase;
new->mtdinfo->point = phram_point;
new->mtdinfo->unpoint = phram_unpoint;
new->mtdinfo->read = phram_read;
new->mtdinfo->write = phram_write;
new->mtdinfo->owner = THIS_MODULE;
new->mtdinfo->type = MTD_RAM;
new->mtdinfo->erasesize = 0x0;
ret = -EAGAIN;
if (add_mtd_device(new->mtdinfo)) {
ERROR("Failed to register new device\n");
goto out3;
}
list_add_tail(&new->list, &phram_list);
return 0;
out3:
iounmap(new->mtdinfo->priv);
out2:
kfree(new->mtdinfo);
out1:
kfree(new);
out0:
return ret;
}
static int ustrtoul(const char *cp, char **endp, unsigned int base)
{
unsigned long result = simple_strtoul(cp, endp, base);
switch (**endp) {
case 'G':
result *= 1024;
case 'M':
result *= 1024;
case 'k':
result *= 1024;
endp++;
}
return result;
}
static int parse_num32(uint32_t *num32, const char *token)
{
char *endp;
unsigned long n;
n = ustrtoul(token, &endp, 0);
if (*endp)
return -EINVAL;
*num32 = n;
return 0;
}
static int parse_name(char **pname, const char *token)
{
size_t len;
char *name;
len = strlen(token) + 1;
if (len > 64)
return -ENOSPC;
name = kmalloc(len, GFP_KERNEL);
if (!name)
return -ENOMEM;
strcpy(name, token);
*pname = name;
return 0;
}
#define parse_err(fmt, args...) do { \
ERROR(fmt , ## args); \
return 0; \
} while (0)
static int phram_setup(const char *val, struct kernel_param *kp)
{
char buf[64+12+12], *str = buf;
char *token[3];
char *name;
uint32_t start;
uint32_t len;
int i, ret;
if (strnlen(val, sizeof(str)) >= sizeof(str))
parse_err("parameter too long\n");
strcpy(str, val);
for (i=0; i<3; i++)
token[i] = strsep(&str, ",");
if (str)
parse_err("too many arguments\n");
if (!token[2])
parse_err("not enough arguments\n");
ret = parse_name(&name, token[0]);
if (ret == -ENOMEM)
parse_err("out of memory\n");
if (ret == -ENOSPC)
parse_err("name too long\n");
if (ret)
return 0;
ret = parse_num32(&start, token[1]);
if (ret)
parse_err("illegal start address\n");
ret = parse_num32(&len, token[2]);
if (ret)
parse_err("illegal device length\n");
register_device(name, start, len);
return 0;
}
module_param_call(phram, phram_setup, NULL, NULL, 000);
MODULE_PARM_DESC(phram, "Memory region to map. \"map=<name>,<start><length>\"");
/*
* Just for compatibility with slram, this is horrible and should go someday.
*/
static int __init slram_setup(const char *val, struct kernel_param *kp)
{
char buf[256], *str = buf;
if (!val || !val[0])
parse_err("no arguments to \"slram=\"\n");
if (strnlen(val, sizeof(str)) >= sizeof(str))
parse_err("parameter too long\n");
strcpy(str, val);
while (str) {
char *token[3];
char *name;
uint32_t start;
uint32_t len;
int i, ret;
for (i=0; i<3; i++) {
token[i] = strsep(&str, ",");
if (token[i])
continue;
parse_err("wrong number of arguments to \"slram=\"\n");
}
/* name */
ret = parse_name(&name, token[0]);
if (ret == -ENOMEM)
parse_err("of memory\n");
if (ret == -ENOSPC)
parse_err("too long\n");
if (ret)
return 1;
/* start */
ret = parse_num32(&start, token[1]);
if (ret)
parse_err("illegal start address\n");
/* len */
if (token[2][0] == '+')
ret = parse_num32(&len, token[2] + 1);
else
ret = parse_num32(&len, token[2]);
if (ret)
parse_err("illegal device length\n");
if (token[2][0] != '+') {
if (len < start)
parse_err("end < start\n");
len -= start;
}
register_device(name, start, len);
}
return 1;
}
module_param_call(slram, slram_setup, NULL, NULL, 000);
MODULE_PARM_DESC(slram, "List of memory regions to map. \"map=<name>,<start><length/end>\"");
int __init init_phram(void)
{
printk(KERN_ERR "phram loaded\n");
return 0;
}
static void __exit cleanup_phram(void)
{
unregister_devices();
}
module_init(init_phram);
module_exit(cleanup_phram);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jrn Engel <joern@wh.fh-wedel.de>");
MODULE_DESCRIPTION("MTD driver for physical RAM");
/* /*
* $Id: pmc551.c,v 1.24 2003/05/20 21:03:08 dwmw2 Exp $ * $Id: pmc551.c,v 1.26 2004/07/14 17:25:07 dwmw2 Exp $
* *
* PMC551 PCI Mezzanine Ram Device * PMC551 PCI Mezzanine Ram Device
* *
......
/* This version ported to the Linux-MTD system by dwmw2@infradead.org /* This version ported to the Linux-MTD system by dwmw2@infradead.org
* $Id: ftl.c,v 1.51 2003/06/23 12:00:08 dwmw2 Exp $ * $Id: ftl.c,v 1.52 2003/08/11 09:00:44 dwmw2 Exp $
* *
* Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br> * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
* - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
...@@ -1093,7 +1093,7 @@ struct mtd_blktrans_ops ftl_tr = { ...@@ -1093,7 +1093,7 @@ struct mtd_blktrans_ops ftl_tr = {
int init_ftl(void) int init_ftl(void)
{ {
DEBUG(0, "$Id: ftl.c,v 1.51 2003/06/23 12:00:08 dwmw2 Exp $\n"); DEBUG(0, "$Id: ftl.c,v 1.52 2003/08/11 09:00:44 dwmw2 Exp $\n");
return register_mtd_blktrans(&ftl_tr); return register_mtd_blktrans(&ftl_tr);
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* (c) 1999 Machine Vision Holdings, Inc. * (c) 1999 Machine Vision Holdings, Inc.
* Author: David Woodhouse <dwmw2@infradead.org> * Author: David Woodhouse <dwmw2@infradead.org>
* *
* $Id: inftlcore.c,v 1.14 2003/06/26 08:28:26 dwmw2 Exp $ * $Id: inftlcore.c,v 1.16 2004/07/12 12:34:58 dwmw2 Exp $
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -55,9 +55,19 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) ...@@ -55,9 +55,19 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
struct INFTLrecord *inftl; struct INFTLrecord *inftl;
unsigned long temp; unsigned long temp;
if (mtd->ecctype != MTD_ECC_RS_DiskOnChip) if (mtd->type != MTD_NANDFLASH)
return;
/* OK, this is moderately ugly. But probably safe. Alternatives? */
if (memcmp(mtd->name, "DiskOnChip", 10))
return; return;
if (!mtd->block_isbad) {
printk(KERN_ERR
"INFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
"Please use the new diskonchip driver under the NAND subsystem.\n");
return;
}
DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name); DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name);
inftl = kmalloc(sizeof(*inftl), GFP_KERNEL); inftl = kmalloc(sizeof(*inftl), GFP_KERNEL);
...@@ -72,6 +82,8 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) ...@@ -72,6 +82,8 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
inftl->mbd.devnum = -1; inftl->mbd.devnum = -1;
inftl->mbd.blksize = 512; inftl->mbd.blksize = 512;
inftl->mbd.tr = tr; inftl->mbd.tr = tr;
memcpy(&inftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo));
inftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY;
if (INFTL_mount(inftl) < 0) { if (INFTL_mount(inftl) < 0) {
printk(KERN_WARNING "INFTL: could not mount device\n"); printk(KERN_WARNING "INFTL: could not mount device\n");
...@@ -284,21 +296,22 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned ...@@ -284,21 +296,22 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
if (BlockMap[block] == BLOCK_NIL) if (BlockMap[block] == BLOCK_NIL)
continue; continue;
ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize * ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize *
BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE,
&retlen, movebuf, (char *)&oob, NULL); &retlen, movebuf);
if (ret < 0) { if (ret < 0) {
ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize * ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize *
BlockMap[block]) + (block * SECTORSIZE), BlockMap[block]) + (block * SECTORSIZE),
SECTORSIZE, &retlen, movebuf, (char *)&oob, SECTORSIZE, &retlen, movebuf);
NULL);
if (ret != -EIO) if (ret != -EIO)
DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went " DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "
"away on retry?\n"); "away on retry?\n");
} }
memset(&oob, 0xff, sizeof(struct inftl_oob));
oob.b.Status = oob.b.Status1 = SECTOR_USED;
MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
(block * SECTORSIZE), SECTORSIZE, &retlen, (block * SECTORSIZE), SECTORSIZE, &retlen,
movebuf, (char *)&oob, NULL); movebuf, (char *)&oob, &inftl->oobinfo);
} }
/* /*
...@@ -326,7 +339,6 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned ...@@ -326,7 +339,6 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
if (INFTL_formatblock(inftl, thisEUN) < 0) { if (INFTL_formatblock(inftl, thisEUN) < 0) {
/* /*
* Could not erase : mark block as reserved. * Could not erase : mark block as reserved.
* FixMe: Update Bad Unit Table on disk.
*/ */
inftl->PUtable[thisEUN] = BLOCK_RESERVED; inftl->PUtable[thisEUN] = BLOCK_RESERVED;
} else { } else {
...@@ -668,7 +680,6 @@ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC) ...@@ -668,7 +680,6 @@ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
if (INFTL_formatblock(inftl, thisEUN) < 0) { if (INFTL_formatblock(inftl, thisEUN) < 0) {
/* /*
* Could not erase : mark block as reserved. * Could not erase : mark block as reserved.
* FixMe: Update Bad Unit Table on medium.
*/ */
inftl->PUtable[thisEUN] = BLOCK_RESERVED; inftl->PUtable[thisEUN] = BLOCK_RESERVED;
} else { } else {
...@@ -754,7 +765,7 @@ static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, ...@@ -754,7 +765,7 @@ static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
unsigned int writeEUN; unsigned int writeEUN;
unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
size_t retlen; size_t retlen;
u8 eccbuf[6]; struct inftl_oob oob;
char *p, *pend; char *p, *pend;
DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=0x%x,block=%ld," DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=0x%x,block=%ld,"
...@@ -778,11 +789,13 @@ static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, ...@@ -778,11 +789,13 @@ static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
return 1; return 1;
} }
memset(&oob, 0xff, sizeof(struct inftl_oob));
oob.b.Status = oob.b.Status1 = SECTOR_USED;
MTD_WRITEECC(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) + MTD_WRITEECC(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
blockofs, SECTORSIZE, &retlen, (char *)buffer, blockofs, SECTORSIZE, &retlen, (char *)buffer,
(char *)eccbuf, NULL); (char *)&oob, &inftl->oobinfo);
/* /*
* No need to write SECTOR_USED flags since they are written * need to write SECTOR_USED flags since they are not written
* in mtd_writeecc * in mtd_writeecc
*/ */
} else { } else {
...@@ -846,9 +859,8 @@ static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, ...@@ -846,9 +859,8 @@ static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
} else { } else {
size_t retlen; size_t retlen;
loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
u_char eccbuf[6]; if (MTD_READ(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen,
if (MTD_READECC(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen, buffer))
buffer, eccbuf, NULL))
return -EIO; return -EIO;
} }
return 0; return 0;
...@@ -881,7 +893,7 @@ extern char inftlmountrev[]; ...@@ -881,7 +893,7 @@ extern char inftlmountrev[];
int __init init_inftl(void) int __init init_inftl(void)
{ {
printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.14 $, " printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.16 $, "
"inftlmount.c %s\n", inftlmountrev); "inftlmount.c %s\n", inftlmountrev);
return register_mtd_blktrans(&inftl_tr); return register_mtd_blktrans(&inftl_tr);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
* Copyright (C) 2000 Netgem S.A. * Copyright (C) 2000 Netgem S.A.
* *
* $Id: inftlmount.c,v 1.11 2003/06/23 07:39:21 dwmw2 Exp $ * $Id: inftlmount.c,v 1.13 2004/06/28 16:06:36 dbrown Exp $
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
#include <linux/mtd/inftl.h> #include <linux/mtd/inftl.h>
#include <linux/mtd/compatmac.h> #include <linux/mtd/compatmac.h>
char inftlmountrev[]="$Revision: 1.11 $"; char inftlmountrev[]="$Revision: 1.13 $";
/* /*
* find_boot_record: Find the INFTL Media Header and its Spare copy which * find_boot_record: Find the INFTL Media Header and its Spare copy which
...@@ -54,7 +54,7 @@ static int find_boot_record(struct INFTLrecord *inftl) ...@@ -54,7 +54,7 @@ static int find_boot_record(struct INFTLrecord *inftl)
{ {
struct inftl_unittail h1; struct inftl_unittail h1;
//struct inftl_oob oob; //struct inftl_oob oob;
unsigned int i, block, boot_record_count = 0; unsigned int i, block;
u8 buf[SECTORSIZE]; u8 buf[SECTORSIZE];
struct INFTLMediaHeader *mh = &inftl->MediaHdr; struct INFTLMediaHeader *mh = &inftl->MediaHdr;
struct INFTLPartition *ip; struct INFTLPartition *ip;
...@@ -72,7 +72,6 @@ static int find_boot_record(struct INFTLrecord *inftl) ...@@ -72,7 +72,6 @@ static int find_boot_record(struct INFTLrecord *inftl)
inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize;
inftl->MediaUnit = BLOCK_NIL; inftl->MediaUnit = BLOCK_NIL;
inftl->SpareMediaUnit = BLOCK_NIL;
/* Search for a valid boot record */ /* Search for a valid boot record */
for (block = 0; block < inftl->nb_blocks; block++) { for (block = 0; block < inftl->nb_blocks; block++) {
...@@ -82,8 +81,11 @@ static int find_boot_record(struct INFTLrecord *inftl) ...@@ -82,8 +81,11 @@ static int find_boot_record(struct INFTLrecord *inftl)
* Check for BNAND header first. Then whinge if it's found * Check for BNAND header first. Then whinge if it's found
* but later checks fail. * but later checks fail.
*/ */
if ((ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize, ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize,
SECTORSIZE, &retlen, buf))) { SECTORSIZE, &retlen, buf);
/* We ignore ret in case the ECC of the MediaHeader is invalid
(which is apparently acceptable) */
if (retlen != SECTORSIZE) {
static int warncount = 5; static int warncount = 5;
if (warncount) { if (warncount) {
...@@ -114,36 +116,28 @@ static int find_boot_record(struct INFTLrecord *inftl) ...@@ -114,36 +116,28 @@ static int find_boot_record(struct INFTLrecord *inftl)
continue; continue;
} }
if (boot_record_count) {
/*
* We've already processed one. So we just check if
* this one is the same as the first one we found.
*/
if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) {
printk(KERN_WARNING "INFTL: Media Headers at "
"0x%x and 0x%x disagree.\n",
inftl->MediaUnit * inftl->EraseSize,
block * inftl->EraseSize);
return -1;
}
if (boot_record_count == 1)
inftl->SpareMediaUnit = block;
/*
* Mark this boot record (INFTL MediaHeader) block as
* reserved.
*/
inftl->PUtable[block] = BLOCK_RESERVED;
boot_record_count++;
continue;
}
/* /*
* This is the first we've seen. * This is the first we've seen.
* Copy the media header structure into place. * Copy the media header structure into place.
*/ */
memcpy(mh, buf, sizeof(struct INFTLMediaHeader)); memcpy(mh, buf, sizeof(struct INFTLMediaHeader));
/* Read the spare media header at offset 4096 */
MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize + 4096,
SECTORSIZE, &retlen, buf);
if (retlen != SECTORSIZE) {
printk(KERN_WARNING "INFTL: Unable to read spare "
"Media Header\n");
return -1;
}
/* Check if this one is the same as the first one we found. */
if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) {
printk(KERN_WARNING "INFTL: Primary and spare Media "
"Headers disagree.\n");
return -1;
}
mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks); mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks);
mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions); mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions);
mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions); mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions);
...@@ -197,8 +191,9 @@ static int find_boot_record(struct INFTLrecord *inftl) ...@@ -197,8 +191,9 @@ static int find_boot_record(struct INFTLrecord *inftl)
"UnitSizeFactor 0x%02x is experimental\n", "UnitSizeFactor 0x%02x is experimental\n",
mh->BlockMultiplierBits); mh->BlockMultiplierBits);
inftl->EraseSize = inftl->mbd.mtd->erasesize << inftl->EraseSize = inftl->mbd.mtd->erasesize <<
(0xff - mh->BlockMultiplierBits); mh->BlockMultiplierBits;
inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize;
block >>= mh->BlockMultiplierBits;
} }
/* Scan the partitions */ /* Scan the partitions */
...@@ -317,34 +312,23 @@ static int find_boot_record(struct INFTLrecord *inftl) ...@@ -317,34 +312,23 @@ static int find_boot_record(struct INFTLrecord *inftl)
/* Mark this boot record (NFTL MediaHeader) block as reserved */ /* Mark this boot record (NFTL MediaHeader) block as reserved */
inftl->PUtable[block] = BLOCK_RESERVED; inftl->PUtable[block] = BLOCK_RESERVED;
#if 0
/* Read Bad Erase Unit Table and modify PUtable[] accordingly */ /* Read Bad Erase Unit Table and modify PUtable[] accordingly */
for (i = 0; i < inftl->nb_blocks; i++) { for (i = 0; i < inftl->nb_blocks; i++) {
if ((i & (SECTORSIZE - 1)) == 0) { int physblock;
/* read one sector for every SECTORSIZE of blocks */ /* If any of the physical eraseblocks are bad, don't
if ((ret = MTD_READECC(inftl->mbd.mtd, use the unit. */
block * inftl->EraseSize + i + SECTORSIZE, for (physblock = 0; physblock < inftl->EraseSize; physblock += inftl->mbd.mtd->erasesize) {
SECTORSIZE, &retlen, buf, if (inftl->mbd.mtd->block_isbad(inftl->mbd.mtd, i * inftl->EraseSize + physblock))
(char *)&oob, NULL)) < 0) { inftl->PUtable[i] = BLOCK_RESERVED;
printk(KERN_WARNING "INFTL: read of "
"bad sector table failed "
"(err %d)\n", ret);
kfree(inftl->VUtable);
kfree(inftl->PUtable);
return -1;
}
} }
/* Mark the Bad Erase Unit as RESERVED in PUtable */
if (buf[i & (SECTORSIZE - 1)] != 0xff)
inftl->PUtable[i] = BLOCK_RESERVED;
} }
#endif
inftl->MediaUnit = block; inftl->MediaUnit = block;
boot_record_count++; return 0;
} }
return boot_record_count ? 0 : -1; /* Not found. */
return -1;
} }
static int memcmpb(void *a, int c, int n) static int memcmpb(void *a, int c, int n)
...@@ -365,27 +349,20 @@ static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address, ...@@ -365,27 +349,20 @@ static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address,
int len, int check_oob) int len, int check_oob)
{ {
int i, retlen; int i, retlen;
u8 buf[SECTORSIZE]; u8 buf[SECTORSIZE + inftl->mbd.mtd->oobsize];
DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=0x%x," DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=0x%x,"
"address=0x%x,len=%d,check_oob=%d)\n", (int)inftl, "address=0x%x,len=%d,check_oob=%d)\n", (int)inftl,
address, len, check_oob); address, len, check_oob);
for (i = 0; i < len; i += SECTORSIZE) { for (i = 0; i < len; i += SECTORSIZE) {
/* if (MTD_READECC(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &inftl->oobinfo) < 0)
* 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(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf) < 0)
return -1; return -1;
if (memcmpb(buf, 0xff, SECTORSIZE) != 0) if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
return -1; return -1;
if (check_oob) { if (check_oob) {
if (MTD_READOOB(inftl->mbd.mtd, address, if (memcmpb(buf + SECTORSIZE, 0xff, inftl->mbd.mtd->oobsize) != 0)
inftl->mbd.mtd->oobsize, &retlen, buf) < 0)
return -1;
if (memcmpb(buf, 0xff, inftl->mbd.mtd->oobsize) != 0)
return -1; return -1;
} }
address += SECTORSIZE; address += SECTORSIZE;
...@@ -402,52 +379,62 @@ static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address, ...@@ -402,52 +379,62 @@ static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address,
* Return: 0 when succeed, -1 on error. * Return: 0 when succeed, -1 on error.
* *
* ToDo: 1. Is it neceressary to check_free_sector after erasing ?? * ToDo: 1. Is it neceressary to check_free_sector after erasing ??
* 2. UnitSizeFactor != 0xFF
*/ */
int INFTL_formatblock(struct INFTLrecord *inftl, int block) int INFTL_formatblock(struct INFTLrecord *inftl, int block)
{ {
int retlen; int retlen;
struct inftl_unittail uci; struct inftl_unittail uci;
struct erase_info *instr = &inftl->instr; struct erase_info *instr = &inftl->instr;
int physblock;
DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=0x%x," DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=0x%x,"
"block=%d)\n", (int)inftl, block); "block=%d)\n", (int)inftl, block);
memset(instr, 0, sizeof(struct erase_info)); memset(instr, 0, sizeof(struct erase_info));
/* FIXME: Shouldn't we be setting the 'discarded' flag to zero
_first_? */
/* Use async erase interface, test return code */ /* Use async erase interface, test return code */
instr->addr = block * inftl->EraseSize; instr->addr = block * inftl->EraseSize;
instr->len = inftl->EraseSize; instr->len = inftl->mbd.mtd->erasesize;
MTD_ERASE(inftl->mbd.mtd, instr); /* Erase one physical eraseblock at a time, even though the NAND api
allows us to group them. This way we if we have a failure, we can
mark only the failed block in the bbt. */
for (physblock = 0; physblock < inftl->EraseSize; physblock += instr->len, instr->addr += instr->len) {
MTD_ERASE(inftl->mbd.mtd, instr);
if (instr->state == MTD_ERASE_FAILED) {
printk(KERN_WARNING "INFTL: error while formatting block %d\n",
block);
goto fail;
}
if (instr->state == MTD_ERASE_FAILED) {
/* /*
* Could not format, FixMe: We should update the BadUnitTable * Check the "freeness" of Erase Unit before updating metadata.
* both in memory and on disk. * FixMe: is this check really necessary? Since we have check the
*/ * return code after the erase operation.
printk(KERN_WARNING "INFTL: error while formatting block %d\n", */
block); if (check_free_sectors(inftl, instr->addr, instr->len, 1) != 0)
return -1; goto fail;
} }
/*
* 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(inftl, instr->addr, inftl->EraseSize, 1) != 0)
return -1;
uci.EraseMark = cpu_to_le16(ERASE_MARK); uci.EraseMark = cpu_to_le16(ERASE_MARK);
uci.EraseMark1 = cpu_to_le16(ERASE_MARK); uci.EraseMark1 = cpu_to_le16(ERASE_MARK);
uci.Reserved[0] = 0; uci.Reserved[0] = 0;
uci.Reserved[1] = 0; uci.Reserved[1] = 0;
uci.Reserved[2] = 0; uci.Reserved[2] = 0;
uci.Reserved[3] = 0; uci.Reserved[3] = 0;
if (MTD_WRITEOOB(inftl->mbd.mtd, block * inftl->EraseSize + SECTORSIZE * 2 + instr->addr = block * inftl->EraseSize + SECTORSIZE * 2;
if (MTD_WRITEOOB(inftl->mbd.mtd, instr->addr +
8, 8, &retlen, (char *)&uci) < 0) 8, 8, &retlen, (char *)&uci) < 0)
return -1; goto fail;
return 0; return 0;
fail:
/* could not format, update the bad block table (caller is responsible
for setting the PUtable to BLOCK_RESERVED on failure) */
inftl->mbd.mtd->block_markbad(inftl->mbd.mtd, instr->addr);
return -1;
} }
/* /*
...@@ -472,7 +459,6 @@ static void format_chain(struct INFTLrecord *inftl, unsigned int first_block) ...@@ -472,7 +459,6 @@ static void format_chain(struct INFTLrecord *inftl, unsigned int first_block)
if (INFTL_formatblock(inftl, block) < 0) { if (INFTL_formatblock(inftl, block) < 0) {
/* /*
* Cannot format !!!! Mark it as Bad Unit, * Cannot format !!!! Mark it as Bad Unit,
* FixMe: update the BadUnitTable on disk.
*/ */
inftl->PUtable[block] = BLOCK_RESERVED; inftl->PUtable[block] = BLOCK_RESERVED;
} else { } else {
......
/* /*
* $Id: mtd_blkdevs.c,v 1.16 2003/06/23 13:34:43 dwmw2 Exp $ * $Id: mtd_blkdevs.c,v 1.22 2004/07/12 12:35:28 dwmw2 Exp $
* *
* (C) 2003 David Woodhouse <dwmw2@infradead.org> * (C) 2003 David Woodhouse <dwmw2@infradead.org>
* *
...@@ -295,7 +295,10 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) ...@@ -295,7 +295,10 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
snprintf(gd->devfs_name, sizeof(gd->devfs_name), snprintf(gd->devfs_name, sizeof(gd->devfs_name),
"%s/%c", tr->name, (tr->part_bits?'a':'0') + new->devnum); "%s/%c", tr->name, (tr->part_bits?'a':'0') + new->devnum);
set_capacity(gd, new->size); /* 2.5 has capacity in units of 512 bytes while still
having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */
set_capacity(gd, (new->size * new->blksize) >> 9);
gd->private_data = new; gd->private_data = new;
new->blkcore_priv = gd; new->blkcore_priv = gd;
gd->queue = tr->blkcore_priv->rq; gd->queue = tr->blkcore_priv->rq;
......
/* /*
* Direct MTD block device access * Direct MTD block device access
* *
* $Id: mtdblock.c,v 1.63 2003/06/23 12:00:08 dwmw2 Exp $ * $Id: mtdblock.c,v 1.64 2003/10/04 17:14:14 dwmw2 Exp $
* *
* (C) 2000-2003 Nicolas Pitre <nico@cam.org> * (C) 2000-2003 Nicolas Pitre <nico@cam.org>
* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
......
/* /*
* $Id: mtdchar.c,v 1.54 2003/05/21 10:50:43 dwmw2 Exp $ * $Id: mtdchar.c,v 1.62 2004/07/14 13:20:42 dwmw2 Exp $
* *
* Character-device access to raw MTD devices. * Character-device access to raw MTD devices.
* *
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/compatmac.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/fs.h> #include <linux/fs.h>
...@@ -16,14 +17,46 @@ ...@@ -16,14 +17,46 @@
#ifdef CONFIG_DEVFS_FS #ifdef CONFIG_DEVFS_FS
#include <linux/devfs_fs_kernel.h> #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 void mtd_notify_add(struct mtd_info* mtd)
{
if (!mtd)
return;
devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
S_IFCHR | S_IRUGO | S_IWUGO, "mtd/%d", mtd->index);
devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
S_IFCHR | S_IRUGO, "mtd/%dro", mtd->index);
}
static void mtd_notify_remove(struct mtd_info* mtd)
{
if (!mtd)
return;
devfs_remove("mtd/%d", mtd->index);
devfs_remove("mtd/%dro", mtd->index);
}
static struct mtd_notifier notifier = { static struct mtd_notifier notifier = {
.add = mtd_notify_add, .add = mtd_notify_add,
.remove = mtd_notify_remove, .remove = mtd_notify_remove,
}; };
static inline void mtdchar_devfs_init(void)
{
devfs_mk_dir("mtd");
register_mtd_user(&notifier);
}
static inline void mtdchar_devfs_exit(void)
{
unregister_mtd_user(&notifier);
devfs_remove("mtd");
}
#else /* !DEVFS */
#define mtdchar_devfs_init() do { } while(0)
#define mtdchar_devfs_exit() do { } while(0)
#endif #endif
static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
...@@ -298,7 +331,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, ...@@ -298,7 +331,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
memset (erase,0,sizeof(struct erase_info)); memset (erase,0,sizeof(struct erase_info));
if (copy_from_user(&erase->addr, argp, if (copy_from_user(&erase->addr, argp,
2 * sizeof(u_long))) { sizeof(struct erase_info_user))) {
kfree(erase); kfree(erase);
return -EFAULT; return -EFAULT;
} }
...@@ -366,7 +399,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, ...@@ -366,7 +399,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf); ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf);
if (copy_to_user(argp + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) if (copy_to_user(argp + sizeof(uint32_t), &retlen, sizeof(uint32_t)))
ret = -EFAULT; ret = -EFAULT;
kfree(databuf); kfree(databuf);
...@@ -400,7 +433,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, ...@@ -400,7 +433,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf); ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf);
if (copy_to_user(argp + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) if (put_user(retlen, (uint32_t __user *)argp))
ret = -EFAULT; ret = -EFAULT;
else if (retlen && copy_to_user(buf.ptr, databuf, retlen)) else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
ret = -EFAULT; ret = -EFAULT;
...@@ -411,29 +444,29 @@ static int mtd_ioctl(struct inode *inode, struct file *file, ...@@ -411,29 +444,29 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
case MEMLOCK: case MEMLOCK:
{ {
unsigned long adrs[2]; struct erase_info_user info;
if (copy_from_user(adrs, argp, 2* sizeof(unsigned long))) if (copy_from_user(&info, argp, sizeof(info)))
return -EFAULT; return -EFAULT;
if (!mtd->lock) if (!mtd->lock)
ret = -EOPNOTSUPP; ret = -EOPNOTSUPP;
else else
ret = mtd->lock(mtd, adrs[0], adrs[1]); ret = mtd->lock(mtd, info.start, info.length);
break; break;
} }
case MEMUNLOCK: case MEMUNLOCK:
{ {
unsigned long adrs[2]; struct erase_info_user info;
if (copy_from_user(adrs, argp, 2* sizeof(unsigned long))) if (copy_from_user(&info, argp, sizeof(info)))
return -EFAULT; return -EFAULT;
if (!mtd->unlock) if (!mtd->unlock)
ret = -EOPNOTSUPP; ret = -EOPNOTSUPP;
else else
ret = mtd->unlock(mtd, adrs[0], adrs[1]); ret = mtd->unlock(mtd, info.start, info.length);
break; break;
} }
...@@ -443,7 +476,40 @@ static int mtd_ioctl(struct inode *inode, struct file *file, ...@@ -443,7 +476,40 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
return -EFAULT; return -EFAULT;
break; break;
} }
case MEMGETOOBSEL:
{
if (copy_to_user(argp, &(mtd->oobinfo), sizeof(struct nand_oobinfo)))
return -EFAULT;
break;
}
case MEMGETBADBLOCK:
{
loff_t offs;
if (copy_from_user(&offs, argp, sizeof(loff_t)))
return -EFAULT;
if (!mtd->block_isbad)
ret = -EOPNOTSUPP;
else
return mtd->block_isbad(mtd, offs);
break;
}
case MEMSETBADBLOCK:
{
loff_t offs;
if (copy_from_user(&offs, argp, sizeof(loff_t)))
return -EFAULT;
if (!mtd->block_markbad)
ret = -EOPNOTSUPP;
else
return mtd->block_markbad(mtd, offs);
break;
}
default: default:
DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO); DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO);
ret = -ENOTTY; ret = -ENOTTY;
...@@ -462,30 +528,6 @@ static struct file_operations mtd_fops = { ...@@ -462,30 +528,6 @@ static struct file_operations mtd_fops = {
.release = mtd_close, .release = mtd_close,
}; };
#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)
{
if (!mtd)
return;
devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
S_IFCHR | S_IRUGO | S_IWUGO, "mtd/%d", mtd->index);
devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
S_IFCHR | S_IRUGO | S_IWUGO, "mtd/%dro", mtd->index);
}
static void mtd_notify_remove(struct mtd_info* mtd)
{
if (!mtd)
return;
devfs_remove("mtd/%d", mtd->index);
devfs_remove("mtd/%dro", mtd->index);
}
#endif
static int __init init_mtdchar(void) static int __init init_mtdchar(void)
{ {
if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) { if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) {
...@@ -494,20 +536,13 @@ static int __init init_mtdchar(void) ...@@ -494,20 +536,13 @@ static int __init init_mtdchar(void)
return -EAGAIN; return -EAGAIN;
} }
#ifdef CONFIG_DEVFS_FS mtdchar_devfs_init();
devfs_mk_dir("mtd");
register_mtd_user(&notifier);
#endif
return 0; return 0;
} }
static void __exit cleanup_mtdchar(void) static void __exit cleanup_mtdchar(void)
{ {
#ifdef CONFIG_DEVFS_FS mtdchar_devfs_exit();
unregister_mtd_user(&notifier);
devfs_remove("mtd");
#endif
unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* This code is GPL * This code is GPL
* *
* $Id: mtdconcat.c,v 1.8 2003/06/30 11:01:26 dwmw2 Exp $ * $Id: mtdconcat.c,v 1.9 2004/06/30 15:17:41 dbrown Exp $
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -391,7 +391,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) ...@@ -391,7 +391,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
struct mtd_concat *concat = CONCAT(mtd); struct mtd_concat *concat = CONCAT(mtd);
struct mtd_info *subdev; struct mtd_info *subdev;
int i, err; int i, err;
u_int32_t length; u_int32_t length, offset = 0;
struct erase_info *erase; struct erase_info *erase;
if (!(mtd->flags & MTD_WRITEABLE)) if (!(mtd->flags & MTD_WRITEABLE))
...@@ -450,6 +450,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) ...@@ -450,6 +450,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
return -EINVAL; return -EINVAL;
} }
instr->fail_addr = 0xffffffff;
/* make a local copy of instr to avoid modifying the caller's struct */ /* make a local copy of instr to avoid modifying the caller's struct */
erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL); erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL);
...@@ -465,10 +467,12 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) ...@@ -465,10 +467,12 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
*/ */
for (i = 0; i < concat->num_subdev; i++) { for (i = 0; i < concat->num_subdev; i++) {
subdev = concat->subdev[i]; subdev = concat->subdev[i];
if (subdev->size <= erase->addr) if (subdev->size <= erase->addr) {
erase->addr -= subdev->size; erase->addr -= subdev->size;
else offset += subdev->size;
} else {
break; break;
}
} }
/* must never happen since size limit has been verified above */ /* must never happen since size limit has been verified above */
...@@ -497,6 +501,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) ...@@ -497,6 +501,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
* block alignment has been checked above */ * block alignment has been checked above */
if (err == -EINVAL) if (err == -EINVAL)
BUG(); BUG();
if (erase->fail_addr != 0xffffffff)
instr->fail_addr = erase->fail_addr + offset;
break; break;
} }
/* /*
...@@ -508,12 +514,13 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) ...@@ -508,12 +514,13 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
* current subdevice, i.e. at offset zero. * current subdevice, i.e. at offset zero.
*/ */
erase->addr = 0; erase->addr = 0;
offset += subdev->size;
} }
instr->state = erase->state;
kfree(erase); kfree(erase);
if (err) if (err)
return err; return err;
instr->state = MTD_ERASE_DONE;
if (instr->callback) if (instr->callback)
instr->callback(instr); instr->callback(instr);
return 0; return 0;
......
/* /*
* $Id: mtdcore.c,v 1.39 2003/05/21 15:15:03 dwmw2 Exp $ * $Id: mtdcore.c,v 1.42 2004/07/13 10:21:13 dwmw2 Exp $
* *
* Core registration and callback routines for MTD * Core registration and callback routines for MTD
* drivers and users. * drivers and users.
...@@ -334,10 +334,7 @@ static int mtd_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data) ...@@ -334,10 +334,7 @@ static int mtd_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
/* Support for /proc/mtd */ /* Support for /proc/mtd */
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
static struct proc_dir_entry *proc_mtd; static struct proc_dir_entry *proc_mtd;
#endif
static inline int mtd_proc_info (char *buf, int i) static inline int mtd_proc_info (char *buf, int i)
{ {
...@@ -350,13 +347,8 @@ static inline int mtd_proc_info (char *buf, int i) ...@@ -350,13 +347,8 @@ static inline int mtd_proc_info (char *buf, int i)
this->erasesize, this->name); this->erasesize, this->name);
} }
static int mtd_read_proc ( char *page, char **start, off_t off,int count static int mtd_read_proc (char *page, char **start, off_t off, int count,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) int *eof, void *data_unused)
,int *eof, void *data_unused
#else
,int unused
#endif
)
{ {
int len, l, i; int len, l, i;
off_t begin = 0; off_t begin = 0;
...@@ -376,9 +368,7 @@ static int mtd_read_proc ( char *page, char **start, off_t off,int count ...@@ -376,9 +368,7 @@ static int mtd_read_proc ( char *page, char **start, off_t off,int count
} }
} }
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
*eof = 1; *eof = 1;
#endif
done: done:
up(&mtd_table_mutex); up(&mtd_table_mutex);
...@@ -388,18 +378,6 @@ static int mtd_read_proc ( char *page, char **start, off_t off,int count ...@@ -388,18 +378,6 @@ static int mtd_read_proc ( char *page, char **start, off_t off,int count
return ((count < begin+len-off) ? count : begin+len-off); return ((count < begin+len-off) ? count : begin+len-off);
} }
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0)
struct proc_dir_entry mtd_proc_entry = {
0, /* low_ino: the inode -- dynamic */
3, "mtd", /* len of name and name */
S_IFREG | S_IRUGO, /* mode */
1, 0, 0, /* nlinks, owner, group */
0, NULL, /* size - unused; operations -- use default */
&mtd_read_proc, /* function used to read data */
/* nothing more */
};
#endif
#endif /* CONFIG_PROC_FS */ #endif /* CONFIG_PROC_FS */
/*====================================================================*/ /*====================================================================*/
...@@ -408,16 +386,8 @@ struct proc_dir_entry mtd_proc_entry = { ...@@ -408,16 +386,8 @@ struct proc_dir_entry mtd_proc_entry = {
int __init init_mtd(void) int __init init_mtd(void)
{ {
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
if ((proc_mtd = create_proc_entry( "mtd", 0, NULL ))) if ((proc_mtd = create_proc_entry( "mtd", 0, NULL )))
proc_mtd->read_proc = mtd_read_proc; proc_mtd->read_proc = mtd_read_proc;
#else
proc_register_dynamic(&proc_root,&mtd_proc_entry);
#endif
#endif
#if LINUX_VERSION_CODE < 0x20212
init_mtd_devices();
#endif #endif
#ifdef CONFIG_PM #ifdef CONFIG_PM
...@@ -436,12 +406,8 @@ static void __exit cleanup_mtd(void) ...@@ -436,12 +406,8 @@ static void __exit cleanup_mtd(void)
#endif #endif
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
if (proc_mtd) if (proc_mtd)
remove_proc_entry( "mtd", NULL); remove_proc_entry( "mtd", NULL);
#else
proc_unregister(&proc_root,mtd_proc_entry.low_ino);
#endif
#endif #endif
} }
......
/* Linux driver for NAND Flash Translation Layer */ /* Linux driver for NAND Flash Translation Layer */
/* (c) 1999 Machine Vision Holdings, Inc. */ /* (c) 1999 Machine Vision Holdings, Inc. */
/* Author: David Woodhouse <dwmw2@infradead.org> */ /* Author: David Woodhouse <dwmw2@infradead.org> */
/* $Id: nftlcore.c,v 1.94 2003/06/23 12:00:08 dwmw2 Exp $ */ /* $Id: nftlcore.c,v 1.96 2004/06/28 13:52:55 dbrown Exp $ */
/* /*
The contents of this file are distributed under the GNU General The contents of this file are distributed under the GNU General
...@@ -43,9 +43,19 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) ...@@ -43,9 +43,19 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
struct NFTLrecord *nftl; struct NFTLrecord *nftl;
unsigned long temp; unsigned long temp;
if (mtd->ecctype != MTD_ECC_RS_DiskOnChip) if (mtd->type != MTD_NANDFLASH)
return;
/* OK, this is moderately ugly. But probably safe. Alternatives? */
if (memcmp(mtd->name, "DiskOnChip", 10))
return; return;
if (!mtd->block_isbad) {
printk(KERN_ERR
"NFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
"Please use the new diskonchip driver under the NAND subsystem.\n");
return;
}
DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name); DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name);
nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL); nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
...@@ -60,6 +70,8 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) ...@@ -60,6 +70,8 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
nftl->mbd.devnum = -1; nftl->mbd.devnum = -1;
nftl->mbd.blksize = 512; nftl->mbd.blksize = 512;
nftl->mbd.tr = tr; nftl->mbd.tr = tr;
memcpy(&nftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo));
nftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY;
if (NFTL_mount(nftl) < 0) { if (NFTL_mount(nftl) < 0) {
printk(KERN_WARNING "NFTL: could not mount device\n"); printk(KERN_WARNING "NFTL: could not mount device\n");
...@@ -350,17 +362,19 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p ...@@ -350,17 +362,19 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
if (BlockMap[block] == BLOCK_NIL) if (BlockMap[block] == BLOCK_NIL)
continue; continue;
ret = MTD_READECC(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); 512, &retlen, movebuf);
if (ret < 0) { if (ret < 0) {
ret = MTD_READECC(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block])
+ (block * 512), 512, &retlen, + (block * 512), 512, &retlen,
movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); movebuf);
if (ret != -EIO) if (ret != -EIO)
printk("Error went away on retry.\n"); printk("Error went away on retry.\n");
} }
memset(&oob, 0xff, sizeof(struct nftl_oob));
oob.b.Status = oob.b.Status1 = SECTOR_USED;
MTD_WRITEECC(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + (block * 512), MTD_WRITEECC(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + (block * 512),
512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); 512, &retlen, movebuf, (char *)&oob, &nftl->oobinfo);
} }
/* add the header so that it is now a valid chain */ /* add the header so that it is now a valid chain */
...@@ -390,7 +404,6 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p ...@@ -390,7 +404,6 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
if (NFTL_formatblock(nftl, thisEUN) < 0) { if (NFTL_formatblock(nftl, thisEUN) < 0) {
/* could not erase : mark block as reserved /* could not erase : mark block as reserved
* FixMe: Update Bad Unit Table on disk
*/ */
nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED; nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
} else { } else {
...@@ -617,7 +630,7 @@ static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, ...@@ -617,7 +630,7 @@ static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
u16 writeEUN; u16 writeEUN;
unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
size_t retlen; size_t retlen;
u8 eccbuf[6]; struct nftl_oob oob;
writeEUN = NFTL_findwriteunit(nftl, block); writeEUN = NFTL_findwriteunit(nftl, block);
...@@ -628,9 +641,11 @@ static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, ...@@ -628,9 +641,11 @@ static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
return 1; return 1;
} }
memset(&oob, 0xff, sizeof(struct nftl_oob));
oob.b.Status = oob.b.Status1 = SECTOR_USED;
MTD_WRITEECC(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, MTD_WRITEECC(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
512, &retlen, (char *)buffer, (char *)eccbuf, NAND_ECC_DISKONCHIP); 512, &retlen, (char *)buffer, (char *)&oob, &nftl->oobinfo);
/* no need to write SECTOR_USED flags since they are written in mtd_writeecc */ /* need to write SECTOR_USED flags since they are not written in mtd_writeecc */
return 0; return 0;
} }
...@@ -692,8 +707,7 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, ...@@ -692,8 +707,7 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
} else { } else {
loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs; loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
size_t retlen; size_t retlen;
u_char eccbuf[6]; if (MTD_READ(nftl->mbd.mtd, ptr, 512, &retlen, buffer))
if (MTD_READECC(nftl->mbd.mtd, ptr, 512, &retlen, buffer, eccbuf, NAND_ECC_DISKONCHIP))
return -EIO; return -EIO;
} }
return 0; return 0;
...@@ -735,7 +749,7 @@ extern char nftlmountrev[]; ...@@ -735,7 +749,7 @@ extern char nftlmountrev[];
int __init init_nftl(void) int __init init_nftl(void)
{ {
printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.94 $, nftlmount.c %s\n", nftlmountrev); printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.96 $, nftlmount.c %s\n", nftlmountrev);
return register_mtd_blktrans(&nftl_tr); return register_mtd_blktrans(&nftl_tr);
} }
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Author: Fabrice Bellard (fabrice.bellard@netgem.com)
* Copyright (C) 2000 Netgem S.A. * Copyright (C) 2000 Netgem S.A.
* *
* $Id: nftlmount.c,v 1.34 2003/05/21 10:54:10 dwmw2 Exp $ * $Id: nftlmount.c,v 1.36 2004/06/28 13:52:55 dbrown Exp $
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
#define SECTORSIZE 512 #define SECTORSIZE 512
char nftlmountrev[]="$Revision: 1.34 $"; char nftlmountrev[]="$Revision: 1.36 $";
/* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the /* 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 * various device information of the NFTL partition and Bad Unit Table. Update
...@@ -50,6 +50,10 @@ static int find_boot_record(struct NFTLrecord *nftl) ...@@ -50,6 +50,10 @@ static int find_boot_record(struct NFTLrecord *nftl)
/* Assume logical EraseSize == physical erasesize for starting the scan. /* Assume logical EraseSize == physical erasesize for starting the scan.
We'll sort it out later if we find a MediaHeader which says otherwise */ We'll sort it out later if we find a MediaHeader which says otherwise */
/* Actually, we won't. The new DiskOnChip driver has already scanned
the MediaHeader and adjusted the virtual erasesize it presents in
the mtd device accordingly. We could even get rid of
nftl->EraseSize if there were any point in doing so. */
nftl->EraseSize = nftl->mbd.mtd->erasesize; nftl->EraseSize = nftl->mbd.mtd->erasesize;
nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize; nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize;
...@@ -62,7 +66,10 @@ static int find_boot_record(struct NFTLrecord *nftl) ...@@ -62,7 +66,10 @@ static int find_boot_record(struct NFTLrecord *nftl)
/* Check for ANAND header first. Then can whinge if it's found but later /* Check for ANAND header first. Then can whinge if it's found but later
checks fail */ checks fail */
if ((ret = MTD_READ(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf))) { ret = MTD_READ(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf);
/* We ignore ret in case the ECC of the MediaHeader is invalid
(which is apparently acceptable) */
if (retlen != SECTORSIZE) {
static int warncount = 5; static int warncount = 5;
if (warncount) { if (warncount) {
...@@ -104,7 +111,7 @@ static int find_boot_record(struct NFTLrecord *nftl) ...@@ -104,7 +111,7 @@ static int find_boot_record(struct NFTLrecord *nftl)
/* Finally reread to check ECC */ /* Finally reread to check ECC */
if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE,
&retlen, buf, (char *)&oob, NAND_ECC_DISKONCHIP) < 0)) { &retlen, buf, (char *)&oob, NULL) < 0)) {
printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n", printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n",
block * nftl->EraseSize, nftl->mbd.mtd->index, ret); block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
continue; continue;
...@@ -149,6 +156,10 @@ static int find_boot_record(struct NFTLrecord *nftl) ...@@ -149,6 +156,10 @@ static int find_boot_record(struct NFTLrecord *nftl)
memcpy(mh, buf, sizeof(struct NFTLMediaHeader)); memcpy(mh, buf, sizeof(struct NFTLMediaHeader));
/* Do some sanity checks on it */ /* Do some sanity checks on it */
#if 0
The new DiskOnChip driver scans the MediaHeader itself, and presents a virtual
erasesize based on UnitSizeFactor. So the erasesize we read from the mtd
device is already correct.
if (mh->UnitSizeFactor == 0) { if (mh->UnitSizeFactor == 0) {
printk(KERN_NOTICE "NFTL: UnitSizeFactor 0x00 detected. This violates the spec but we think we know what it means...\n"); printk(KERN_NOTICE "NFTL: UnitSizeFactor 0x00 detected. This violates the spec but we think we know what it means...\n");
} else if (mh->UnitSizeFactor < 0xfc) { } else if (mh->UnitSizeFactor < 0xfc) {
...@@ -161,6 +172,7 @@ static int find_boot_record(struct NFTLrecord *nftl) ...@@ -161,6 +172,7 @@ static int find_boot_record(struct NFTLrecord *nftl)
nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor); nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor);
nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize; nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize;
} }
#endif
nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN); nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN);
if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) { if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) {
printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n"); printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n");
...@@ -213,11 +225,13 @@ static int find_boot_record(struct NFTLrecord *nftl) ...@@ -213,11 +225,13 @@ static int find_boot_record(struct NFTLrecord *nftl)
/* read the Bad Erase Unit Table and modify ReplUnitTable[] accordingly */ /* read the Bad Erase Unit Table and modify ReplUnitTable[] accordingly */
for (i = 0; i < nftl->nb_blocks; i++) { for (i = 0; i < nftl->nb_blocks; i++) {
#if 0
The new DiskOnChip driver already scanned the bad block table. Just query it.
if ((i & (SECTORSIZE - 1)) == 0) { if ((i & (SECTORSIZE - 1)) == 0) {
/* read one sector for every SECTORSIZE of blocks */ /* read one sector for every SECTORSIZE of blocks */
if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize + if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize +
i + SECTORSIZE, SECTORSIZE, &retlen, buf, i + SECTORSIZE, SECTORSIZE, &retlen, buf,
(char *)&oob, NAND_ECC_DISKONCHIP)) < 0) { (char *)&oob, NULL)) < 0) {
printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n", printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n",
ret); ret);
kfree(nftl->ReplUnitTable); kfree(nftl->ReplUnitTable);
...@@ -228,6 +242,9 @@ static int find_boot_record(struct NFTLrecord *nftl) ...@@ -228,6 +242,9 @@ static int find_boot_record(struct NFTLrecord *nftl)
/* mark the Bad Erase Unit as RESERVED in ReplUnitTable */ /* mark the Bad Erase Unit as RESERVED in ReplUnitTable */
if (buf[i & (SECTORSIZE - 1)] != 0xff) if (buf[i & (SECTORSIZE - 1)] != 0xff)
nftl->ReplUnitTable[i] = BLOCK_RESERVED; nftl->ReplUnitTable[i] = BLOCK_RESERVED;
#endif
if (nftl->mbd.mtd->block_isbad(nftl->mbd.mtd, i * nftl->EraseSize))
nftl->ReplUnitTable[i] = BLOCK_RESERVED;
} }
nftl->MediaUnit = block; nftl->MediaUnit = block;
...@@ -253,21 +270,16 @@ static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int ...@@ -253,21 +270,16 @@ static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int
int check_oob) int check_oob)
{ {
int i, retlen; int i, retlen;
u8 buf[SECTORSIZE]; u8 buf[SECTORSIZE + nftl->mbd.mtd->oobsize];
for (i = 0; i < len; i += SECTORSIZE) { for (i = 0; i < len; i += SECTORSIZE) {
/* we want to read the sector without ECC check here since a free if (MTD_READECC(nftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &nftl->oobinfo) < 0)
sector does not have ECC syndrome on it yet */
if (MTD_READ(nftl->mbd.mtd, address, SECTORSIZE, &retlen, buf) < 0)
return -1; return -1;
if (memcmpb(buf, 0xff, SECTORSIZE) != 0) if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
return -1; return -1;
if (check_oob) { if (check_oob) {
if (MTD_READOOB(nftl->mbd.mtd, address, nftl->mbd.mtd->oobsize, if (memcmpb(buf + SECTORSIZE, 0xff, nftl->mbd.mtd->oobsize) != 0)
&retlen, buf) < 0)
return -1;
if (memcmpb(buf, 0xff, nftl->mbd.mtd->oobsize) != 0)
return -1; return -1;
} }
address += SECTORSIZE; address += SECTORSIZE;
...@@ -282,7 +294,6 @@ static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int ...@@ -282,7 +294,6 @@ static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int
* Return: 0 when succeed, -1 on error. * Return: 0 when succeed, -1 on error.
* *
* ToDo: 1. Is it neceressary to check_free_sector after erasing ?? * ToDo: 1. Is it neceressary to check_free_sector after erasing ??
* 2. UnitSizeFactor != 0xFF
*/ */
int NFTL_formatblock(struct NFTLrecord *nftl, int block) int NFTL_formatblock(struct NFTLrecord *nftl, int block)
{ {
...@@ -312,11 +323,10 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) ...@@ -312,11 +323,10 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block)
MTD_ERASE(nftl->mbd.mtd, instr); MTD_ERASE(nftl->mbd.mtd, instr);
if (instr->state == MTD_ERASE_FAILED) { 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); printk("Error while formatting block %d\n", block);
return -1; goto fail;
} else { }
/* increase and write Wear-Leveling info */ /* increase and write Wear-Leveling info */
nb_erases = le32_to_cpu(uci.WearInfo); nb_erases = le32_to_cpu(uci.WearInfo);
nb_erases++; nb_erases++;
...@@ -329,14 +339,18 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) ...@@ -329,14 +339,18 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block)
* FixMe: is this check really necessary ? since we have check the * FixMe: is this check really necessary ? since we have check the
* return code after the erase operation. */ * return code after the erase operation. */
if (check_free_sectors(nftl, instr->addr, nftl->EraseSize, 1) != 0) if (check_free_sectors(nftl, instr->addr, nftl->EraseSize, 1) != 0)
return -1; goto fail;
uci.WearInfo = le32_to_cpu(nb_erases); uci.WearInfo = le32_to_cpu(nb_erases);
if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
&retlen, (char *)&uci) < 0) &retlen, (char *)&uci) < 0)
return -1; goto fail;
return 0; return 0;
} fail:
/* could not format, update the bad block table (caller is responsible
for setting the ReplUnitTable to BLOCK_RESERVED on failure) */
nftl->mbd.mtd->block_markbad(nftl->mbd.mtd, instr->addr);
return -1;
} }
/* check_sectors_in_chain: Check that each sector of a Virtual Unit Chain is correct. /* check_sectors_in_chain: Check that each sector of a Virtual Unit Chain is correct.
...@@ -441,8 +455,7 @@ static void format_chain(struct NFTLrecord *nftl, unsigned int first_block) ...@@ -441,8 +455,7 @@ static void format_chain(struct NFTLrecord *nftl, unsigned int first_block)
printk("Formatting block %d\n", block); printk("Formatting block %d\n", block);
if (NFTL_formatblock(nftl, block) < 0) { if (NFTL_formatblock(nftl, block) < 0) {
/* cannot format !!!! Mark it as Bad Unit, /* cannot format !!!! Mark it as Bad Unit */
FixMe: update the BadUnitTable on disk */
nftl->ReplUnitTable[block] = BLOCK_RESERVED; nftl->ReplUnitTable[block] = BLOCK_RESERVED;
} else { } else {
nftl->ReplUnitTable[block] = BLOCK_FREE; nftl->ReplUnitTable[block] = BLOCK_FREE;
......
/* /*
* $Id: redboot.c,v 1.11 2003/05/21 10:39:26 dwmw2 Exp $ * $Id: redboot.c,v 1.13 2004/04/01 10:17:40 gthomas Exp $
* *
* Parse RedBoot-style Flash Image System (FIS) tables and * Parse RedBoot-style Flash Image System (FIS) tables and
* produce a Linux partition array to match. * produce a Linux partition array to match.
...@@ -48,21 +48,24 @@ static int parse_redboot_partitions(struct mtd_info *master, ...@@ -48,21 +48,24 @@ static int parse_redboot_partitions(struct mtd_info *master,
char *names; char *names;
char *nullname; char *nullname;
int namelen = 0; int namelen = 0;
int nulllen = 0;
#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
static char nullstring[] = "unallocated"; static char nullstring[] = "unallocated";
#endif
buf = kmalloc(PAGE_SIZE, GFP_KERNEL); buf = kmalloc(master->erasesize, GFP_KERNEL);
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
/* Read the start of the last erase block */ /* Read the start of the last erase block */
ret = master->read(master, master->size - master->erasesize, ret = master->read(master, master->size - master->erasesize,
PAGE_SIZE, &retlen, (void *)buf); master->erasesize, &retlen, (void *)buf);
if (ret) if (ret)
goto out; goto out;
if (retlen != PAGE_SIZE) { if (retlen != master->erasesize) {
ret = -EIO; ret = -EIO;
goto out; goto out;
} }
...@@ -80,7 +83,7 @@ static int parse_redboot_partitions(struct mtd_info *master, ...@@ -80,7 +83,7 @@ static int parse_redboot_partitions(struct mtd_info *master,
goto out; goto out;
} }
for (i = 0; i < PAGE_SIZE / sizeof(struct fis_image_desc); i++) { for (i = 0; i < master->erasesize / sizeof(struct fis_image_desc); i++) {
struct fis_list *new_fl, **prev; struct fis_list *new_fl, **prev;
if (buf[i].name[0] == 0xff) if (buf[i].name[0] == 0xff)
...@@ -112,48 +115,69 @@ static int parse_redboot_partitions(struct mtd_info *master, ...@@ -112,48 +115,69 @@ static int parse_redboot_partitions(struct mtd_info *master,
nrparts++; nrparts++;
} }
if (fl->img->flash_base) #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
if (fl->img->flash_base) {
nrparts++; nrparts++;
nulllen = sizeof(nullstring);
}
for (tmp_fl = fl; tmp_fl->next; tmp_fl = tmp_fl->next) { for (tmp_fl = fl; tmp_fl->next; tmp_fl = tmp_fl->next) {
if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize < tmp_fl->next->img->flash_base) if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize <= tmp_fl->next->img->flash_base) {
nrparts++; nrparts++;
nulllen = sizeof(nullstring);
}
} }
parts = kmalloc(sizeof(*parts)*nrparts + sizeof(nullstring) + namelen, GFP_KERNEL); #endif
parts = kmalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL);
if (!parts) { if (!parts) {
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
} }
memset(parts, 0, sizeof(*parts)*nrparts + namelen); memset(parts, 0, sizeof(*parts)*nrparts + nulllen + namelen);
/* FIXME: Include nullname only if it's used */
nullname = (char *)&parts[nrparts]; nullname = (char *)&parts[nrparts];
sprintf(nullname, nullstring); #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
names = nullname + sizeof(nullstring); if (nulllen > 0) {
strcpy(nullname, nullstring);
}
#endif
names = nullname + nulllen;
i=0; i=0;
#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
if (fl->img->flash_base) { if (fl->img->flash_base) {
parts[0].name = nullname; parts[0].name = nullname;
parts[0].size = fl->img->flash_base; parts[0].size = fl->img->flash_base;
parts[0].offset = 0; parts[0].offset = 0;
i++;
} }
#endif
for ( ; i<nrparts; i++) { for ( ; i<nrparts; i++) {
parts[i].size = fl->img->size; parts[i].size = fl->img->size;
parts[i].offset = fl->img->flash_base; parts[i].offset = fl->img->flash_base;
parts[i].name = names; parts[i].name = names;
strcpy(names, fl->img->name); strcpy(names, fl->img->name);
#ifdef CONFIG_MTD_REDBOOT_PARTS_READONLY
if (!memcmp(names, "RedBoot", 8) ||
!memcmp(names, "RedBoot config", 15) ||
!memcmp(names, "FIS directory", 14)) {
parts[i].mask_flags = MTD_WRITEABLE;
}
#endif
names += strlen(names)+1; names += strlen(names)+1;
if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize < fl->next->img->flash_base) { #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) {
i++; i++;
parts[i].offset = parts[i-1].size + parts[i-1].offset; parts[i].offset = parts[i-1].size + parts[i-1].offset;
parts[i].size = fl->next->img->flash_base - parts[i].offset; parts[i].size = fl->next->img->flash_base - parts[i].offset;
parts[i].name = nullname; parts[i].name = nullname;
} }
#endif
tmp_fl = fl; tmp_fl = fl;
fl = fl->next; fl = fl->next;
kfree(tmp_fl); kfree(tmp_fl);
......
/*
/* Linux driver for Disk-On-Chip 2000 */ * Linux driver for Disk-On-Chip devices
/* (c) 1999 Machine Vision Holdings, Inc. */ *
/* Author: David Woodhouse <dwmw2@mvhi.com> */ * Copyright (C) 1999 Machine Vision Holdings, Inc.
/* $Id: doc2000.h,v 1.17 2003/06/12 01:20:46 gerg Exp $ */ * Copyright (C) 2001-2003 David Woodhouse <dwmw2@infradead.org>
* Copyright (C) 2002-2003 Greg Ungerer <gerg@snapgear.com>
* Copyright (C) 2002-2003 SnapGear Inc
*
* $Id: doc2000.h,v 1.22 2003/11/05 10:51:36 dwmw2 Exp $
*
* Released under GPL
*/
#ifndef __MTD_DOC2000_H__ #ifndef __MTD_DOC2000_H__
#define __MTD_DOC2000_H__ #define __MTD_DOC2000_H__
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <asm/semaphore.h>
#define DoC_Sig1 0 #define DoC_Sig1 0
#define DoC_Sig2 1 #define DoC_Sig2 1
...@@ -73,12 +81,12 @@ ...@@ -73,12 +81,12 @@
* Others use readb/writeb * Others use readb/writeb
*/ */
#if defined(__arm__) #if defined(__arm__)
#define ReadDOC_(adr, reg) ((unsigned char)(*(__u32 *)(((unsigned long)adr)+((reg)<<2)))) #define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u32 *)(((unsigned long)adr)+((reg)<<2))))
#define WriteDOC_(d, adr, reg) do{ *(__u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0) #define WriteDOC_(d, adr, reg) do{ *(volatile __u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0)
#define DOC_IOREMAP_LEN 0x8000 #define DOC_IOREMAP_LEN 0x8000
#elif defined(__ppc__) #elif defined(__ppc__)
#define ReadDOC_(adr, reg) ((unsigned char)(*(__u16 *)(((unsigned long)adr)+((reg)<<1)))) #define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u16 *)(((unsigned long)adr)+((reg)<<1))))
#define WriteDOC_(d, adr, reg) do{ *(__u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0) #define WriteDOC_(d, adr, reg) do{ *(volatile __u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0)
#define DOC_IOREMAP_LEN 0x4000 #define DOC_IOREMAP_LEN 0x4000
#else #else
#define ReadDOC_(adr, reg) readb(((unsigned long)adr) + (reg)) #define ReadDOC_(adr, reg) readb(((unsigned long)adr) + (reg))
...@@ -106,6 +114,7 @@ ...@@ -106,6 +114,7 @@
#define DOC_MODE_MDWREN 0x04 #define DOC_MODE_MDWREN 0x04
#define DOC_ChipID_Doc2k 0x20 #define DOC_ChipID_Doc2k 0x20
#define DOC_ChipID_Doc2kTSOP 0x21 /* internal number for MTD */
#define DOC_ChipID_DocMil 0x30 #define DOC_ChipID_DocMil 0x30
#define DOC_ChipID_DocMilPlus32 0x40 #define DOC_ChipID_DocMilPlus32 0x40
#define DOC_ChipID_DocMilPlus16 0x41 #define DOC_ChipID_DocMilPlus16 0x41
...@@ -147,10 +156,10 @@ struct Nand { ...@@ -147,10 +156,10 @@ struct Nand {
#define MAX_FLOORS 4 #define MAX_FLOORS 4
#define MAX_CHIPS 4 #define MAX_CHIPS 4
#define MAX_FLOORS_MIL 4 #define MAX_FLOORS_MIL 1
#define MAX_CHIPS_MIL 1 #define MAX_CHIPS_MIL 1
#define MAX_FLOORS_MPLUS 1 #define MAX_FLOORS_MPLUS 2
#define MAX_CHIPS_MPLUS 1 #define MAX_CHIPS_MPLUS 1
#define ADDR_COLUMN 1 #define ADDR_COLUMN 1
...@@ -161,7 +170,7 @@ struct DiskOnChip { ...@@ -161,7 +170,7 @@ struct DiskOnChip {
unsigned long physadr; unsigned long physadr;
unsigned long virtadr; unsigned long virtadr;
unsigned long totlen; unsigned long totlen;
char ChipID; /* Type of DiskOnChip */ unsigned char ChipID; /* Type of DiskOnChip */
int ioreg; int ioreg;
unsigned long mfr; /* Flash IDs - only one type of flash per device */ unsigned long mfr; /* Flash IDs - only one type of flash per device */
......
/* /*
* $Id: ftl.h,v 1.5 2001/06/02 20:35:51 dwmw2 Exp $ * $Id: ftl.h,v 1.6 2003/01/24 13:20:04 dwmw2 Exp $
* *
* Derived from (and probably identical to): * Derived from (and probably identical to):
* ftl.h 1.7 1999/10/25 20:23:17 * ftl.h 1.7 1999/10/25 20:23:17
......
...@@ -3,105 +3,32 @@ ...@@ -3,105 +3,32 @@
* *
* (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
* *
* $Id: inftl.h,v 1.3 2003/05/23 11:35:34 dwmw2 Exp $ * $Id: inftl.h,v 1.6 2004/06/30 14:49:00 dbrown Exp $
*/ */
#ifndef __MTD_INFTL_H__ #ifndef __MTD_INFTL_H__
#define __MTD_INFTL_H__ #define __MTD_INFTL_H__
#ifndef __KERNEL__
#error This is a kernel header. Perhaps include nftl-user.h instead?
#endif
#include <linux/mtd/blktrans.h> #include <linux/mtd/blktrans.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/nftl.h> #include <linux/mtd/nftl.h>
#define OSAK_VERSION 0x5120 #include <mtd/inftl-user.h>
#define PERCENTUSED 98
#define SECTORSIZE 512
#ifndef INFTL_MAJOR #ifndef INFTL_MAJOR
#define INFTL_MAJOR 93 /* FIXME */ #define INFTL_MAJOR 94
#endif #endif
#define INFTL_PARTN_BITS 4 #define INFTL_PARTN_BITS 4
/* Block Control Information */
struct inftl_bci {
__u8 ECCsig[6];
__u8 Status;
__u8 Status1;
} __attribute__((packed));
struct inftl_unithead1 {
__u16 virtualUnitNo;
__u16 prevUnitNo;
__u8 ANAC;
__u8 NACs;
__u8 parityPerField;
__u8 discarded;
} __attribute__((packed));
struct inftl_unithead2 {
__u8 parityPerField;
__u8 ANAC;
__u16 prevUnitNo;
__u16 virtualUnitNo;
__u8 NACs;
__u8 discarded;
} __attribute__((packed));
struct inftl_unittail {
__u8 Reserved[4];
__u16 EraseMark;
__u16 EraseMark1;
} __attribute__((packed));
union inftl_uci {
struct inftl_unithead1 a;
struct inftl_unithead2 b;
struct inftl_unittail c;
};
struct inftl_oob {
struct inftl_bci b;
union inftl_uci u;
};
/* INFTL Media Header */
struct INFTLPartition {
__u32 virtualUnits;
__u32 firstUnit;
__u32 lastUnit;
__u32 flags;
__u32 spareUnits;
__u32 Reserved0;
__u32 Reserved1;
} __attribute__((packed));
struct INFTLMediaHeader {
char bootRecordID[8];
__u32 NoOfBootImageBlocks;
__u32 NoOfBinaryPartitions;
__u32 NoOfBDTLPartitions;
__u32 BlockMultiplierBits;
__u32 FormatFlags;
__u32 OsakVersion;
__u32 PercentUsed;
struct INFTLPartition Partitions[4];
} __attribute__((packed));
/* Partition flag types */
#define INFTL_BINARY 0x20000000
#define INFTL_BDTL 0x40000000
#define INFTL_LAST 0x80000000
#ifdef __KERNEL__ #ifdef __KERNEL__
struct INFTLrecord { struct INFTLrecord {
struct mtd_blktrans_dev mbd; struct mtd_blktrans_dev mbd;
__u16 MediaUnit, SpareMediaUnit; __u16 MediaUnit;
__u32 EraseSize; __u32 EraseSize;
struct INFTLMediaHeader MediaHdr; struct INFTLMediaHeader MediaHdr;
int usecount; int usecount;
...@@ -119,6 +46,7 @@ struct INFTLrecord { ...@@ -119,6 +46,7 @@ struct INFTLrecord {
unsigned int nb_blocks; /* number of physical blocks */ unsigned int nb_blocks; /* number of physical blocks */
unsigned int nb_boot_blocks; /* number of blocks used by the bios */ unsigned int nb_boot_blocks; /* number of blocks used by the bios */
struct erase_info instr; struct erase_info instr;
struct nand_oobinfo oobinfo;
}; };
int INFTL_mount(struct INFTLrecord *s); int INFTL_mount(struct INFTLrecord *s);
......
/*
/* $Id: mtd.h,v 1.45 2003/05/20 21:56:40 dwmw2 Exp $ */ * $Id: mtd.h,v 1.54 2004/07/15 01:13:12 dwmw2 Exp $
*
* Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al.
*
* Released under GPL
*/
#ifndef __MTD_MTD_H__ #ifndef __MTD_MTD_H__
#define __MTD_MTD_H__ #define __MTD_MTD_H__
#ifdef __KERNEL__ #ifndef __KERNEL__
#error This is a kernel header. Perhaps include mtd-user.h instead?
#endif
#include <linux/config.h> #include <linux/config.h>
#include <linux/version.h> #include <linux/version.h>
...@@ -12,115 +19,26 @@ ...@@ -12,115 +19,26 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/uio.h> #include <linux/uio.h>
#endif /* __KERNEL__ */ #include <mtd/mtd-abi.h>
struct erase_info_user {
u_int32_t start;
u_int32_t length;
};
struct mtd_oob_buf {
u_int32_t start;
u_int32_t length;
unsigned char __user *ptr;
};
#define MTD_CHAR_MAJOR 90 #define MTD_CHAR_MAJOR 90
#define MTD_BLOCK_MAJOR 31 #define MTD_BLOCK_MAJOR 31
#define MAX_MTD_DEVICES 16 #define MAX_MTD_DEVICES 16
#define MTD_ABSENT 0
#define MTD_RAM 1
#define MTD_ROM 2
#define MTD_NORFLASH 3
#define MTD_NANDFLASH 4
#define MTD_PEROM 5
#define MTD_OTHER 14
#define MTD_UNKNOWN 15
#define MTD_CLEAR_BITS 1 // Bits can be cleared (flash)
#define MTD_SET_BITS 2 // Bits can be set
#define MTD_ERASEABLE 4 // Has an erase function
#define MTD_WRITEB_WRITEABLE 8 // Direct IO is possible
#define MTD_VOLATILE 16 // Set for RAMs
#define MTD_XIP 32 // eXecute-In-Place possible
#define MTD_OOB 64 // Out-of-band data (NAND flash)
#define MTD_ECC 128 // Device capable of automatic ECC
// Some common devices / combinations of capabilities
#define MTD_CAP_ROM 0
#define MTD_CAP_RAM (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE)
#define MTD_CAP_NORFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE)
#define MTD_CAP_NANDFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB)
#define MTD_WRITEABLE (MTD_CLEAR_BITS|MTD_SET_BITS)
// Types of automatic ECC/Checksum available
#define MTD_ECC_NONE 0 // No automatic ECC available
#define MTD_ECC_RS_DiskOnChip 1 // Automatic ECC on DiskOnChip
#define MTD_ECC_SW 2 // SW ECC for Toshiba & Samsung devices
struct mtd_info_user {
u_char type;
u_int32_t flags;
u_int32_t size; // Total size of the MTD
u_int32_t erasesize;
u_int32_t oobblock; // Size of OOB blocks (e.g. 512)
u_int32_t oobsize; // Amount of OOB data per block (e.g. 16)
u_int32_t ecctype;
u_int32_t eccsize;
};
struct region_info_user {
u_int32_t offset; /* At which this region starts,
* from the beginning of the MTD */
u_int32_t erasesize; /* For this region */
u_int32_t numblocks; /* Number of blocks in this region */
u_int32_t regionindex;
};
#define MEMGETINFO _IOR('M', 1, struct mtd_info_user)
#define MEMERASE _IOW('M', 2, struct erase_info_user)
#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf)
#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf)
#define MEMLOCK _IOW('M', 5, struct erase_info_user)
#define MEMUNLOCK _IOW('M', 6, struct erase_info_user)
#define MEMGETREGIONCOUNT _IOR('M', 7, int)
#define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user)
#define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo)
struct nand_oobinfo {
int useecc;
int eccpos[6];
};
#ifndef __KERNEL__
typedef struct mtd_info_user mtd_info_t;
typedef struct erase_info_user erase_info_t;
typedef struct region_info_user region_info_t;
typedef struct nand_oobinfo nand_oobinfo_t;
/* User-space ioctl definitions */
#else /* __KERNEL__ */
#define MTD_ERASE_PENDING 0x01 #define MTD_ERASE_PENDING 0x01
#define MTD_ERASING 0x02 #define MTD_ERASING 0x02
#define MTD_ERASE_SUSPEND 0x04 #define MTD_ERASE_SUSPEND 0x04
#define MTD_ERASE_DONE 0x08 #define MTD_ERASE_DONE 0x08
#define MTD_ERASE_FAILED 0x10 #define MTD_ERASE_FAILED 0x10
/* If the erase fails, fail_addr might indicate exactly which block failed. If
fail_addr = 0xffffffff, the failure was not at the device level or was not
specific to any particular block. */
struct erase_info { struct erase_info {
struct mtd_info *mtd; struct mtd_info *mtd;
u_int32_t addr; u_int32_t addr;
u_int32_t len; u_int32_t len;
u_int32_t fail_addr;
u_long time; u_long time;
u_long retries; u_long retries;
u_int dev; u_int dev;
...@@ -150,6 +68,7 @@ struct mtd_info { ...@@ -150,6 +68,7 @@ struct mtd_info {
u_int32_t oobblock; // Size of OOB blocks (e.g. 512) u_int32_t oobblock; // Size of OOB blocks (e.g. 512)
u_int32_t oobsize; // Amount of OOB data per block (e.g. 16) u_int32_t oobsize; // Amount of OOB data per block (e.g. 16)
u_int32_t oobavail; // Number of bytes in OOB area available for fs
u_int32_t ecctype; u_int32_t ecctype;
u_int32_t eccsize; u_int32_t eccsize;
...@@ -223,6 +142,10 @@ struct mtd_info { ...@@ -223,6 +142,10 @@ struct mtd_info {
int (*suspend) (struct mtd_info *mtd); int (*suspend) (struct mtd_info *mtd);
void (*resume) (struct mtd_info *mtd); void (*resume) (struct mtd_info *mtd);
/* Bad block management functions */
int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
void *priv; void *priv;
struct module *owner; struct module *owner;
...@@ -288,6 +211,4 @@ int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs, ...@@ -288,6 +211,4 @@ int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs,
#endif /* CONFIG_MTD_DEBUG */ #endif /* CONFIG_MTD_DEBUG */
#endif /* __KERNEL__ */
#endif /* __MTD_MTD_H__ */ #endif /* __MTD_MTD_H__ */
/* /*
* $Id: nftl.h,v 1.13 2003/05/23 11:25:02 dwmw2 Exp $ * $Id: nftl.h,v 1.16 2004/06/30 14:49:00 dbrown Exp $
* *
* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
*/ */
...@@ -10,71 +10,7 @@ ...@@ -10,71 +10,7 @@
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/blktrans.h> #include <linux/mtd/blktrans.h>
/* Block Control Information */ #include <mtd/nftl-user.h>
struct nftl_bci {
unsigned char ECCSig[6];
__u8 Status;
__u8 Status1;
}__attribute__((packed));
/* Unit Control Information */
struct nftl_uci0 {
__u16 VirtUnitNum;
__u16 ReplUnitNum;
__u16 SpareVirtUnitNum;
__u16 SpareReplUnitNum;
} __attribute__((packed));
struct nftl_uci1 {
__u32 WearInfo;
__u16 EraseMark;
__u16 EraseMark1;
} __attribute__((packed));
struct nftl_uci2 {
__u16 FoldMark;
__u16 FoldMark1;
__u32 unused;
} __attribute__((packed));
union nftl_uci {
struct nftl_uci0 a;
struct nftl_uci1 b;
struct nftl_uci2 c;
};
struct nftl_oob {
struct nftl_bci b;
union nftl_uci u;
};
/* NFTL Media Header */
struct NFTLMediaHeader {
char DataOrgID[6];
__u16 NumEraseUnits;
__u16 FirstPhysicalEUN;
__u32 FormattedSize;
unsigned char UnitSizeFactor;
} __attribute__((packed));
#define MAX_ERASE_ZONES (8192 - 512)
#define ERASE_MARK 0x3c69
#define SECTOR_FREE 0xff
#define SECTOR_USED 0x55
#define SECTOR_IGNORE 0x11
#define SECTOR_DELETED 0x00
#define FOLD_MARK_IN_PROGRESS 0x5555
#define ZONE_GOOD 0xff
#define ZONE_BAD_ORIGINAL 0
#define ZONE_BAD_MARKED 7
#ifdef __KERNEL__
/* these info are used in ReplUnitTable */ /* these info are used in ReplUnitTable */
#define BLOCK_NIL 0xffff /* last block of a chain */ #define BLOCK_NIL 0xffff /* last block of a chain */
...@@ -101,6 +37,7 @@ struct NFTLrecord { ...@@ -101,6 +37,7 @@ struct NFTLrecord {
unsigned int nb_blocks; /* number of physical blocks */ unsigned int nb_blocks; /* number of physical blocks */
unsigned int nb_boot_blocks; /* number of blocks used by the bios */ unsigned int nb_boot_blocks; /* number of blocks used by the bios */
struct erase_info instr; struct erase_info instr;
struct nand_oobinfo oobinfo;
}; };
int NFTL_mount(struct NFTLrecord *s); int NFTL_mount(struct NFTLrecord *s);
...@@ -114,6 +51,4 @@ int NFTL_formatblock(struct NFTLrecord *s, int block); ...@@ -114,6 +51,4 @@ int NFTL_formatblock(struct NFTLrecord *s, int block);
#define MAX_SECTORS_PER_UNIT 64 #define MAX_SECTORS_PER_UNIT 64
#define NFTL_PARTN_BITS 4 #define NFTL_PARTN_BITS 4
#endif /* __KERNEL__ */
#endif /* __MTD_NFTL_H__ */ #endif /* __MTD_NFTL_H__ */
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* *
* This code is GPL * This code is GPL
* *
* $Id: partitions.h,v 1.14 2003/05/20 21:56:29 dwmw2 Exp $ * $Id: partitions.h,v 1.15 2003/07/09 11:15:43 dwmw2 Exp $
*/ */
#ifndef MTD_PARTITIONS_H #ifndef MTD_PARTITIONS_H
...@@ -50,7 +50,7 @@ struct mtd_partition { ...@@ -50,7 +50,7 @@ struct mtd_partition {
#define MTDPART_SIZ_FULL (0) #define MTDPART_SIZ_FULL (0)
int add_mtd_partitions(struct mtd_info *, struct mtd_partition *, int); int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
int del_mtd_partitions(struct mtd_info *); int del_mtd_partitions(struct mtd_info *);
/* /*
......
/*
* $Id: inftl-user.h,v 1.1 2004/05/05 15:17:00 dwmw2 Exp $
*
* Parts of INFTL headers shared with userspace
*
*/
#ifndef __MTD_INFTL_USER_H__
#define __MTD_INFTL_USER_H__
#define OSAK_VERSION 0x5120
#define PERCENTUSED 98
#define SECTORSIZE 512
/* Block Control Information */
struct inftl_bci {
uint8_t ECCsig[6];
uint8_t Status;
uint8_t Status1;
} __attribute__((packed));
struct inftl_unithead1 {
uint16_t virtualUnitNo;
uint16_t prevUnitNo;
uint8_t ANAC;
uint8_t NACs;
uint8_t parityPerField;
uint8_t discarded;
} __attribute__((packed));
struct inftl_unithead2 {
uint8_t parityPerField;
uint8_t ANAC;
uint16_t prevUnitNo;
uint16_t virtualUnitNo;
uint8_t NACs;
uint8_t discarded;
} __attribute__((packed));
struct inftl_unittail {
uint8_t Reserved[4];
uint16_t EraseMark;
uint16_t EraseMark1;
} __attribute__((packed));
union inftl_uci {
struct inftl_unithead1 a;
struct inftl_unithead2 b;
struct inftl_unittail c;
};
struct inftl_oob {
struct inftl_bci b;
union inftl_uci u;
};
/* INFTL Media Header */
struct INFTLPartition {
__u32 virtualUnits;
__u32 firstUnit;
__u32 lastUnit;
__u32 flags;
__u32 spareUnits;
__u32 Reserved0;
__u32 Reserved1;
} __attribute__((packed));
struct INFTLMediaHeader {
char bootRecordID[8];
__u32 NoOfBootImageBlocks;
__u32 NoOfBinaryPartitions;
__u32 NoOfBDTLPartitions;
__u32 BlockMultiplierBits;
__u32 FormatFlags;
__u32 OsakVersion;
__u32 PercentUsed;
struct INFTLPartition Partitions[4];
} __attribute__((packed));
/* Partition flag types */
#define INFTL_BINARY 0x20000000
#define INFTL_BDTL 0x40000000
#define INFTL_LAST 0x80000000
#endif /* __MTD_INFTL_USER_H__ */
/*
* $Id: jffs2-user.h,v 1.1 2004/05/05 11:57:54 dwmw2 Exp $
*
* JFFS2 definitions for use in user space only
*/
#ifndef __JFFS2_USER_H__
#define __JFFS2_USER_H__
/* This file is blessed for inclusion by userspace */
#include <linux/jffs2.h>
#include <endian.h>
#include <byteswap.h>
#undef cpu_to_je16
#undef cpu_to_je32
#undef cpu_to_jemode
#undef je16_to_cpu
#undef je32_to_cpu
#undef jemode_to_cpu
extern int target_endian;
#define t16(x) ({ uint16_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); })
#define t32(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); })
#define cpu_to_je16(x) ((jint16_t){t16(x)})
#define cpu_to_je32(x) ((jint32_t){t32(x)})
#define cpu_to_jemode(x) ((jmode_t){t32(x)})
#define je16_to_cpu(x) (t16((x).v16))
#define je32_to_cpu(x) (t32((x).v32))
#define jemode_to_cpu(x) (t32((x).m))
#endif /* __JFFS2_USER_H__ */
/*
* $Id: mtd-abi.h,v 1.5 2004/06/22 09:29:35 gleixner Exp $
*
* Portions of MTD ABI definition which are shared by kernel and user space
*/
#ifndef __MTD_ABI_H__
#define __MTD_ABI_H__
struct erase_info_user {
uint32_t start;
uint32_t length;
};
struct mtd_oob_buf {
uint32_t start;
uint32_t length;
unsigned char *ptr;
};
#define MTD_ABSENT 0
#define MTD_RAM 1
#define MTD_ROM 2
#define MTD_NORFLASH 3
#define MTD_NANDFLASH 4
#define MTD_PEROM 5
#define MTD_OTHER 14
#define MTD_UNKNOWN 15
#define MTD_CLEAR_BITS 1 // Bits can be cleared (flash)
#define MTD_SET_BITS 2 // Bits can be set
#define MTD_ERASEABLE 4 // Has an erase function
#define MTD_WRITEB_WRITEABLE 8 // Direct IO is possible
#define MTD_VOLATILE 16 // Set for RAMs
#define MTD_XIP 32 // eXecute-In-Place possible
#define MTD_OOB 64 // Out-of-band data (NAND flash)
#define MTD_ECC 128 // Device capable of automatic ECC
// Some common devices / combinations of capabilities
#define MTD_CAP_ROM 0
#define MTD_CAP_RAM (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE)
#define MTD_CAP_NORFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE)
#define MTD_CAP_NANDFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB)
#define MTD_WRITEABLE (MTD_CLEAR_BITS|MTD_SET_BITS)
// Types of automatic ECC/Checksum available
#define MTD_ECC_NONE 0 // No automatic ECC available
#define MTD_ECC_RS_DiskOnChip 1 // Automatic ECC on DiskOnChip
#define MTD_ECC_SW 2 // SW ECC for Toshiba & Samsung devices
/* ECC byte placement */
#define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended)
#define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode)
#define MTD_NANDECC_AUTOPLACE 2 // Use the default placement scheme
#define MTD_NANDECC_PLACEONLY 3 // Use the given placement in the structure (Do not store ecc result on read)
struct mtd_info_user {
uint8_t type;
uint32_t flags;
uint32_t size; // Total size of the MTD
uint32_t erasesize;
uint32_t oobblock; // Size of OOB blocks (e.g. 512)
uint32_t oobsize; // Amount of OOB data per block (e.g. 16)
uint32_t ecctype;
uint32_t eccsize;
};
struct region_info_user {
uint32_t offset; /* At which this region starts,
* from the beginning of the MTD */
uint32_t erasesize; /* For this region */
uint32_t numblocks; /* Number of blocks in this region */
uint32_t regionindex;
};
#define MEMGETINFO _IOR('M', 1, struct mtd_info_user)
#define MEMERASE _IOW('M', 2, struct erase_info_user)
#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf)
#define MEMREADOOB _IOWR('M', 4, struct mtd_oob_buf)
#define MEMLOCK _IOW('M', 5, struct erase_info_user)
#define MEMUNLOCK _IOW('M', 6, struct erase_info_user)
#define MEMGETREGIONCOUNT _IOR('M', 7, int)
#define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user)
#define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo)
#define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo)
#define MEMGETBADBLOCK _IOW('M', 11, loff_t)
#define MEMSETBADBLOCK _IOW('M', 12, loff_t)
struct nand_oobinfo {
uint32_t useecc;
uint32_t eccbytes;
uint32_t oobfree[8][2];
uint32_t eccpos[32];
};
#endif /* __MTD_ABI_H__ */
/*
* $Id: mtd-user.h,v 1.2 2004/05/05 14:44:57 dwmw2 Exp $
*
* MTD ABI header for use by user space only.
*/
#ifndef __MTD_USER_H__
#define __MTD_USER_H__
#include <stdint.h>
/* This file is blessed for inclusion by userspace */
#include <mtd/mtd-abi.h>
typedef struct mtd_info_user mtd_info_t;
typedef struct erase_info_user erase_info_t;
typedef struct region_info_user region_info_t;
typedef struct nand_oobinfo nand_oobinfo_t;
#endif /* __MTD_USER_H__ */
/*
* $Id: nftl-user.h,v 1.1 2004/05/05 14:44:57 dwmw2 Exp $
*
* Parts of NFTL headers shared with userspace
*
*/
#ifndef __MTD_NFTL_USER_H__
#define __MTD_NFTL_USER_H__
/* Block Control Information */
struct nftl_bci {
unsigned char ECCSig[6];
uint8_t Status;
uint8_t Status1;
}__attribute__((packed));
/* Unit Control Information */
struct nftl_uci0 {
uint16_t VirtUnitNum;
uint16_t ReplUnitNum;
uint16_t SpareVirtUnitNum;
uint16_t SpareReplUnitNum;
} __attribute__((packed));
struct nftl_uci1 {
uint32_t WearInfo;
uint16_t EraseMark;
uint16_t EraseMark1;
} __attribute__((packed));
struct nftl_uci2 {
uint16_t FoldMark;
uint16_t FoldMark1;
uint32_t unused;
} __attribute__((packed));
union nftl_uci {
struct nftl_uci0 a;
struct nftl_uci1 b;
struct nftl_uci2 c;
};
struct nftl_oob {
struct nftl_bci b;
union nftl_uci u;
};
/* NFTL Media Header */
struct NFTLMediaHeader {
char DataOrgID[6];
uint16_t NumEraseUnits;
uint16_t FirstPhysicalEUN;
uint32_t FormattedSize;
unsigned char UnitSizeFactor;
} __attribute__((packed));
#define MAX_ERASE_ZONES (8192 - 512)
#define ERASE_MARK 0x3c69
#define SECTOR_FREE 0xff
#define SECTOR_USED 0x55
#define SECTOR_IGNORE 0x11
#define SECTOR_DELETED 0x00
#define FOLD_MARK_IN_PROGRESS 0x5555
#define ZONE_GOOD 0xff
#define ZONE_BAD_ORIGINAL 0
#define ZONE_BAD_MARKED 7
#endif /* __MTD_NFTL_USER_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment