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 @@
This is access code for flashes using ARM's flash partitioning
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
*
......@@ -10,7 +10,7 @@
* mtdparts=<mtddef>[;<mtddef]
* <mtddef> := <mtd-id>:<partdef>[,<partdef>]
* <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
* <name> := '(' NAME ')'
*
......@@ -358,14 +358,7 @@ static int __init cmdline_parser_init(void)
return register_mtd_parser(&cmdline_parser);
}
static void __exit cmdline_parser_exit(void)
{
deregister_mtd_parser(&cmdline_parser);
}
module_init(cmdline_parser_init);
module_exit(cmdline_parser_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>");
......
# 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"
depends on MTD!=n
......@@ -9,9 +9,9 @@ config MTD_PMC551
depends on MTD && PCI
---help---
This provides a MTD device driver for the Ramix PMC551 RAM PCI card
from Ramix Inc. <http://www.ramix.com/products/>. These devices come
in memory configurations from 32M - 1G. If you have one, you
probably want to enable this.
from Ramix Inc. <http://www.ramix.com/products/memory/pmc551.html>.
These devices come in memory configurations from 32M - 1G. If you
have one, you probably want to enable this.
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.
......@@ -40,9 +40,12 @@ config MTD_PMC551_DEBUG
config MTD_MS02NV
tristate "DEC MS02-NV NVRAM module support"
depends on CONFIG_MACH_DECSTATION
depends on MTD && MACH_DECSTATION
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
tristate "Uncached system RAM"
......@@ -52,6 +55,16 @@ config MTD_SLRAM
you can still use it for storage or swap by using this driver to
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
tristate "28F160xx flash driver for LART"
depends on SA1100_LART && MTD
......@@ -161,11 +174,18 @@ config MTD_DOC2001PLUS
config MTD_DOCPROBE
tristate
default m if MTD_DOC2001!=y && MTD_DOC2000!=y && MTD_DOC2001PLUS!=y && (MTD_DOC2001=m || MTD_DOC2000=m || 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
help
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
bool "Advanced detection options for DiskOnChip"
depends on MTD_DOCPROBE
......
#
# 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 ***
#
......@@ -13,8 +13,10 @@
obj-$(CONFIG_MTD_DOC2000) += doc2000.o
obj-$(CONFIG_MTD_DOC2001) += doc2001.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_PHRAM) += phram.o
obj-$(CONFIG_MTD_PMC551) += pmc551.o
obj-$(CONFIG_MTD_MS02NV) += ms02-nv.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
*
......@@ -39,7 +39,7 @@
/* Default erase size in K, always make it a multiple of PAGE_SIZE */
#define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10) /* 128KiB */
#define VERSION "$Revision: 1.5 $"
#define VERSION "$Revision: 1.6 $"
/* Info for the block device */
struct blkmtd_dev {
......
......@@ -4,7 +4,7 @@
* (c) 1999 Machine Vision Holdings, Inc.
* (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>
......@@ -19,12 +19,14 @@
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/doc2000.h>
#define DOC_SUPPORT_2000
#define DOC_SUPPORT_2000TSOP
#define DOC_SUPPORT_MILLENNIUM
#ifdef DOC_SUPPORT_2000
......@@ -33,7 +35,7 @@
#define DoC_is_2000(doc) (0)
#endif
#ifdef DOC_SUPPORT_MILLENNIUM
#if defined(DOC_SUPPORT_2000TSOP) || defined(DOC_SUPPORT_MILLENNIUM)
#define DoC_is_Millennium(doc) (doc->ChipID == DOC_ChipID_DocMil)
#else
#define DoC_is_Millennium(doc) (0)
......@@ -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,
size_t *retlen, const u_char *buf);
static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf, u_char *eccbuf,
struct nand_oobinfo *unused);
size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf, u_char *eccbuf,
struct nand_oobinfo *unused);
size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
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,
size_t *retlen, u_char *buf);
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)
/* Out-of-line routine to wait for chip response */
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)) {
DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n");
return -EIO;
......@@ -147,6 +154,8 @@ static inline int DoC_Command(struct DiskOnChip *doc, unsigned char command,
/* Send the command */
WriteDOC_(command, docptr, doc->ioreg);
if (DoC_is_Millennium(doc))
WriteDOC(command, docptr, WritePipeTerm);
/* Lower the CLE line */
WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl);
......@@ -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. */
/* 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)
/* Read the manufacturer and device id codes from the device */
/* CDSN Slow IO register see Software Requirement 11.4 item 5. */
dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
DoC_Delay(doc, 2);
mfr = ReadDOC_(doc->virtadr, doc->ioreg);
if (DoC_is_Millennium(doc)) {
DoC_Delay(doc, 2);
dummy = ReadDOC(doc->virtadr, ReadPipeInit);
mfr = ReadDOC(doc->virtadr, LastDataRead);
/* CDSN Slow IO register see Software Requirement 11.4 item 5. */
dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
DoC_Delay(doc, 2);
id = ReadDOC_(doc->virtadr, doc->ioreg);
DoC_Delay(doc, 2);
dummy = ReadDOC(doc->virtadr, ReadPipeInit);
id = ReadDOC(doc->virtadr, LastDataRead);
} 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 */
if (mfr == 0xff || mfr == 0)
......@@ -388,11 +410,10 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
if (!doc->mfr) {
doc->mfr = mfr;
doc->id = id;
doc->chipshift =
nand_flash_ids[i].chipshift;
doc->page256 = nand_flash_ids[i].page256;
doc->pageadrlen =
nand_flash_ids[i].chipshift > 25 ? 3 : 2;
doc->chipshift =
ffs((nand_flash_ids[i].chipsize << 20)) - 1;
doc->page256 = (nand_flash_ids[i].pagesize == 256) ? 1 : 0;
doc->pageadrlen = doc->chipshift > 25 ? 3 : 2;
doc->erasesize =
nand_flash_ids[i].erasesize;
return 1;
......@@ -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 */
static void DoC_ScanChips(struct DiskOnChip *this)
static void DoC_ScanChips(struct DiskOnChip *this, int maxchips)
{
int floor, chip;
int numchips[MAX_FLOORS];
int maxchips = MAX_CHIPS;
int ret = 1;
this->numchips = 0;
this->mfr = 0;
this->id = 0;
if (DoC_is_Millennium(this))
maxchips = MAX_CHIPS_MIL;
/* For each floor, find the number of valid chips it contains */
for (floor = 0; floor < MAX_FLOORS; floor++) {
ret = 1;
......@@ -517,6 +534,7 @@ static void DoC2k_init(struct mtd_info *mtd)
{
struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
struct DiskOnChip *old = NULL;
int maxchips;
/* We must avoid being called twice for the same device. */
......@@ -540,14 +558,28 @@ static void DoC2k_init(struct mtd_info *mtd)
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:
mtd->name = "DiskOnChip 2000";
this->ioreg = DoC_2k_CDSN_IO;
maxchips = MAX_CHIPS;
break;
case DOC_ChipID_DocMil:
mtd->name = "DiskOnChip Millennium";
this->ioreg = DoC_Mil_CDSN_IO;
maxchips = MAX_CHIPS_MIL;
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,
......@@ -568,6 +600,7 @@ static void DoC2k_init(struct mtd_info *mtd)
mtd->write = doc_write;
mtd->read_ecc = doc_read_ecc;
mtd->write_ecc = doc_write_ecc;
mtd->writev_ecc = doc_writev_ecc;
mtd->read_oob = doc_read_oob;
mtd->write_oob = doc_write_oob;
mtd->sync = NULL;
......@@ -580,7 +613,7 @@ static void DoC2k_init(struct mtd_info *mtd)
init_MUTEX(&this->lock);
/* Ident all the chips present. */
DoC_ScanChips(this);
DoC_ScanChips(this, maxchips);
if (!this->totlen) {
kfree(mtd);
......@@ -599,12 +632,11 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t * retlen, u_char * buf)
{
/* Just a special case of doc_read_ecc */
return doc_read_ecc(mtd, from, len, retlen, buf, NULL, 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,
size_t * retlen, u_char * buf, u_char * eccbuf,
struct nand_oobinfo *unused)
size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel)
{
struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
unsigned long docptr;
......@@ -612,6 +644,7 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
unsigned char syndrome[6];
volatile char dummy;
int i, len256 = 0, ret=0;
size_t left = len;
docptr = this->virtadr;
......@@ -621,122 +654,131 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
down(&this->lock);
/* Don't allow a single read to cross a 512-byte block boundary */
if (from + len > ((from | 0x1ff) + 1))
len = ((from | 0x1ff) + 1) - from;
/* The ECC will not be calculated correctly if less than 512 is read */
if (len != 0x200 && eccbuf)
printk(KERN_WARNING
"ECC needs a full sector read (adr: %lx size %lx)\n",
(long) from, (long) len);
/* printk("DoC_Read (adr: %lx size %lx)\n", (long) from, (long) len); */
*retlen = 0;
while (left) {
len = left;
/* 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 */
mychip = &this->chips[from >> (this->chipshift)];
/* 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);
if (this->curfloor != mychip->floor) {
DoC_SelectFloor(this, mychip->floor);
DoC_SelectChip(this, mychip->chip);
} else if (this->curchip != mychip->chip) {
DoC_SelectChip(this, mychip->chip);
}
/* printk("DoC_Read (adr: %lx size %lx)\n", (long) from, (long) len); */
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
DoC_Command(this,
(!this->page256
&& (from & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0,
CDSN_CTRL_WP);
DoC_Address(this, ADDR_COLUMN_PAGE, from, CDSN_CTRL_WP,
CDSN_CTRL_ECC_IO);
if (eccbuf) {
/* Prime the ECC engine */
WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
WriteDOC(DOC_ECC_EN, docptr, ECCConf);
} else {
/* disable the ECC engine */
WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
}
/* Find the chip which is to be used and select it */
mychip = &this->chips[from >> (this->chipshift)];
/* treat crossing 256-byte sector for 2M x 8bits devices */
if (this->page256 && from + len > (from | 0xff) + 1) {
len256 = (from | 0xff) + 1 - from;
DoC_ReadBuf(this, buf, len256);
if (this->curfloor != mychip->floor) {
DoC_SelectFloor(this, mychip->floor);
DoC_SelectChip(this, mychip->chip);
} else if (this->curchip != mychip->chip) {
DoC_SelectChip(this, mychip->chip);
}
DoC_Command(this, NAND_CMD_READ0, CDSN_CTRL_WP);
DoC_Address(this, ADDR_COLUMN_PAGE, from + len256,
CDSN_CTRL_WP, CDSN_CTRL_ECC_IO);
}
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
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 */
*retlen = len;
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);
}
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);
/* treat crossing 256-byte sector for 2M x 8bits devices */
if (this->page256 && from + len > (from | 0xff) + 1) {
len256 = (from | 0xff) + 1 - from;
DoC_ReadBuf(this, buf, len256);
/* 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);
DoC_Command(this, NAND_CMD_READ0, CDSN_CTRL_WP);
DoC_Address(this, ADDR_COLUMN_PAGE, from + len256,
CDSN_CTRL_WP, CDSN_CTRL_ECC_IO);
}
/* Check the ECC Status */
if (i & 0x80) {
int nb_errors;
/* There was an ECC error */
DoC_ReadBuf(this, &buf[len256], len - len256);
/* Let the caller know we completed it */
*retlen += len;
if (eccbuf) {
/* Read the ECC data through the DiskOnChip ECC logic */
/* 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
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
/* Read the ECC syndrom through the DiskOnChip ECC logic.
These syndrome will be all ZERO when there is no error */
for (i = 0; i < 6; i++) {
syndrome[i] =
ReadDOC(docptr, ECCSyndrome0 + i);
}
nb_errors = doc_decode_ecc(buf, syndrome);
/* Read the ECC syndrom through the DiskOnChip ECC logic.
These syndrome will be all ZERO when there is no error */
for (i = 0; i < 6; i++) {
syndrome[i] =
ReadDOC(docptr, ECCSyndrome0 + i);
}
nb_errors = doc_decode_ecc(buf, syndrome);
#ifdef ECC_DEBUG
printk(KERN_ERR "Errors corrected: %x\n", nb_errors);
printk(KERN_ERR "Errors corrected: %x\n", nb_errors);
#endif
if (nb_errors < 0) {
/* We return error, but have actually done the read. Not that
this can be told to user-space, via sys_read(), but at least
MTD-aware stuff can know about it by checking *retlen */
ret = -EIO;
}
}
if (nb_errors < 0) {
/* We return error, but have actually done the read. Not that
this can be told to user-space, via sys_read(), but at least
MTD-aware stuff can know about it by checking *retlen */
ret = -EIO;
}
}
#ifdef PSYCHO_DEBUG
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],
eccbuf[3], eccbuf[4], eccbuf[5]);
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],
eccbuf[3], eccbuf[4], eccbuf[5]);
#endif
/* disable the ECC engine */
WriteDOC(DOC_ECC_DIS, docptr , ECCConf);
}
/* disable the ECC engine */
WriteDOC(DOC_ECC_DIS, docptr , ECCConf);
}
/* according to 11.4.1, we need to wait for the busy line
* drop if we read to the end of the page. */
if(0 == ((from + *retlen) & 0x1ff))
{
DoC_WaitReady(this);
/* according to 11.4.1, we need to wait for the busy line
* drop if we read to the end of the page. */
if(0 == ((from + len) & 0x1ff))
{
DoC_WaitReady(this);
}
from += len;
left -= len;
buf += len;
}
up(&this->lock);
......@@ -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)
{
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,
size_t * retlen, const u_char * buf,
u_char * eccbuf,
struct nand_oobinfo *unused)
u_char * eccbuf, struct nand_oobinfo *oobsel)
{
struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
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,
volatile char dummy;
int len256 = 0;
struct Nand *mychip;
size_t left = len;
int status;
docptr = this->virtadr;
......@@ -771,65 +814,133 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
down(&this->lock);
/* Don't allow a single write to cross a 512-byte block boundary */
if (to + len > ((to | 0x1ff) + 1))
len = ((to | 0x1ff) + 1) - to;
*retlen = 0;
while (left) {
len = left;
/* The ECC will not be calculated correctly if less than 512 is written */
if (len != 0x200 && eccbuf)
printk(KERN_WARNING
"ECC needs a full sector write (adr: %lx size %lx)\n",
(long) to, (long) len);
/* Don't allow a single write to cross a 512-byte block boundary */
if (to + len > ((to | 0x1ff) + 1))
len = ((to | 0x1ff) + 1) - to;
/* 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 */
mychip = &this->chips[to >> (this->chipshift)];
/* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */
if (this->curfloor != mychip->floor) {
DoC_SelectFloor(this, mychip->floor);
DoC_SelectChip(this, mychip->chip);
} else if (this->curchip != mychip->chip) {
DoC_SelectChip(this, mychip->chip);
}
/* Find the chip which is to be used and select it */
mychip = &this->chips[to >> (this->chipshift)];
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
if (this->curfloor != mychip->floor) {
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 */
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);
this->curfloor = mychip->floor;
this->curchip = mychip->chip;
DoC_Command(this, NAND_CMD_SEQIN, 0);
DoC_Address(this, ADDR_COLUMN_PAGE, to, 0, CDSN_CTRL_ECC_IO);
/* Set device to main plane of flash */
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) {
/* Prime the ECC engine */
WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
} else {
/* disable the ECC engine */
WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
}
DoC_Command(this, NAND_CMD_SEQIN, 0);
DoC_Address(this, ADDR_COLUMN_PAGE, to, 0, CDSN_CTRL_ECC_IO);
/* treat crossing 256-byte sector for 2M x 8bits devices */
if (this->page256 && to + len > (to | 0xff) + 1) {
len256 = (to | 0xff) + 1 - to;
DoC_WriteBuf(this, buf, len256);
if (eccbuf) {
/* Prime the ECC engine */
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 (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_STATUS, CDSN_CTRL_WP);
/* There's an implicit DoC_WaitReady() in DoC_Command */
dummy = ReadDOC(docptr, CDSNSlowIO);
DoC_Delay(this, 2);
if (DoC_is_Millennium(this)) {
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");
/* Error in programming */
*retlen = 0;
......@@ -837,82 +948,97 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
return -EIO;
}
DoC_Command(this, NAND_CMD_SEQIN, 0);
DoC_Address(this, ADDR_COLUMN_PAGE, to + len256, 0,
CDSN_CTRL_ECC_IO);
/* Let the caller know we completed it */
*retlen += len;
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) {
WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_CE, docptr,
CDSNControl);
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 char static_buf[512];
static DECLARE_MUTEX(writev_buf_sem);
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);
}
size_t totretlen = 0;
size_t thisvecofs = 0;
int ret= 0;
/* Read the ECC data through the DiskOnChip ECC logic */
for (di = 0; di < 6; di++) {
eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di);
}
down(&writev_buf_sem);
/* Reset the ECC engine */
WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
while(count) {
size_t thislen, thisretlen;
unsigned char *buf;
#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
}
buf = vecs->iov_base + thisvecofs;
thislen = vecs->iov_len - thisvecofs;
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 */
if (thislen >= 512) {
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);
DoC_Delay(this, 2);
totretlen += thisretlen;
if (ReadDOC_(docptr, this->ioreg) & 1) {
printk(KERN_ERR "Error programming flash\n");
/* Error in programming */
*retlen = 0;
up(&this->lock);
return -EIO;
}
if (ret || thisretlen != thislen)
break;
/* Let the caller know we completed it */
*retlen = len;
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);
up(&this->lock);
return ret;
}
up(&this->lock);
return 0;
to += thislen;
}
up(&writev_buf_sem);
*retlen = totretlen;
return ret;
}
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
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,
unsigned long docptr = this->virtadr;
struct Nand *mychip = &this->chips[ofs >> this->chipshift];
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,
// 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,
DoC_Command(this, NAND_CMD_STATUS, 0);
/* DoC_WaitReady() is implicit in DoC_Command */
dummy = ReadDOC(docptr, CDSNSlowIO);
DoC_Delay(this, 2);
if (DoC_is_Millennium(this)) {
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");
/* There was an error */
*retlen = 0;
......@@ -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_WaitReady() is implicit in DoC_Command */
dummy = ReadDOC(docptr, CDSNSlowIO);
DoC_Delay(this, 2);
if (DoC_is_Millennium(this)) {
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");
/* There was an error */
*retlen = 0;
......@@ -1085,6 +1224,7 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
volatile int dummy;
unsigned long docptr;
struct Nand *mychip;
int status;
down(&this->lock);
......@@ -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);
dummy = ReadDOC(docptr, CDSNSlowIO);
DoC_Delay(this, 2);
if (ReadDOC_(docptr, this->ioreg) & 1) {
if (DoC_is_Millennium(this)) {
ReadDOC(docptr, ReadPipeInit);
status = ReadDOC(docptr, LastDataRead);
} 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);
/* There was an error */
instr->state = MTD_ERASE_FAILED;
......
......@@ -4,7 +4,7 @@
* (c) 1999 Machine Vision Holdings, Inc.
* (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>
......@@ -19,6 +19,7 @@
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
......@@ -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,
size_t *retlen, const u_char *buf);
static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf, u_char *eccbuf,
struct nand_oobinfo *unused);
size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel);
static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf, u_char *eccbuf,
struct nand_oobinfo *unused);
size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel);
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
size_t *retlen, u_char *buf);
static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
......@@ -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);
doc->mfr = mfr;
doc->id = id;
doc->chipshift = nand_flash_ids[i].chipshift;
doc->chipshift = ffs((nand_flash_ids[i].chipsize << 20)) - 1;
break;
}
}
......@@ -406,12 +404,11 @@ static int doc_read (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
/* Just a special case of doc_read_ecc */
return doc_read_ecc(mtd, from, len, retlen, buf, NULL, 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,
size_t *retlen, u_char *buf, u_char *eccbuf,
struct nand_oobinfo *unused)
size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel)
{
int i, ret;
volatile char dummy;
......@@ -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)
{
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,
size_t *retlen, const u_char *buf, u_char *eccbuf,
struct nand_oobinfo *unused)
size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel)
{
int i,ret = 0;
volatile char dummy;
......
......@@ -6,7 +6,9 @@
* (c) 1999 Machine Vision Holdings, Inc.
* (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>
......@@ -21,6 +23,7 @@
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/bitops.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
......@@ -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 |
* +-----------+-------+-------+-------+--------------+---------+-----------+
*/
/* 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)
{
unsigned int ofs = *from & 0x3ff;
unsigned int cmd;
struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
if (ofs < 512) {
cmd = NAND_CMD_READ0;
ofs &= 0x1ff;
} else if (ofs < 1014) {
cmd = NAND_CMD_READ1;
ofs = (ofs & 0x1ff) + 10;
if (this->interleave) {
unsigned int ofs = *from & 0x3ff;
unsigned int cmd;
if (ofs < 512) {
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 {
cmd = NAND_CMD_READOOB;
ofs = ofs - 1014;
/* No interleave */
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)
......@@ -294,10 +308,12 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
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);
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);
......@@ -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);
doc->mfr = mfr;
doc->id = id;
doc->interleave = 0;
if (doc->ChipID == DOC_ChipID_DocMilPlus32)
doc->interleave = 1;
doc->chipshift = nand_flash_ids[i].chipshift;
doc->chipshift = ffs((nand_flash_ids[i].chipsize << 20)) - 1;
doc->erasesize = nand_flash_ids[i].erasesize << doc->interleave;
break;
}
......@@ -346,6 +359,21 @@ static void DoC_ScanChips(struct DiskOnChip *this)
this->mfr = 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 (floor = 0,ret = 1; floor < MAX_FLOORS_MPLUS; floor++) {
numchips[floor] = 0;
......@@ -739,7 +767,7 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
return -EINVAL;
/* Determine position of OOB flags, before or after data */
before = to & 0x200;
before = (this->interleave && (to & 0x200));
DoC_CheckASIC(docptr);
......@@ -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... */
fofs = ofs;
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);
size = 6 - base;
} else if (base < 8) {
......@@ -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... */
fofs = ofs;
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);
size = 6 - base;
} else if (base < 8) {
......
......@@ -4,7 +4,7 @@
/* (C) 1999 Machine Vision Holdings, Inc. */
/* (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
window, DOCControl);
#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);
switch (ChipID) {
......@@ -148,6 +151,12 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr
break;
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 */
tmp = 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
tmpc = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT;
if (tmp != tmpb && tmp == tmpc)
return ChipID;
break;
default:
break;
}
......@@ -199,8 +207,8 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr
default:
#ifndef CONFIG_MTD_DOCPROBE_55AA
printk(KERN_WARNING "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n",
#ifdef CONFIG_MTD_DOCPROBE_55AA
printk(KERN_DEBUG "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n",
ChipID, physadr);
#endif
#ifndef DOC_PASSIVE_PROBE
......@@ -241,6 +249,12 @@ static void __init DoC_Probe(unsigned long physadr)
return;
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;
mtd = kmalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL);
......@@ -262,6 +276,12 @@ static void __init DoC_Probe(unsigned long physadr)
sprintf(namebuf, "with ChipID %2.2X", ChipID);
switch(ChipID) {
case DOC_ChipID_Doc2kTSOP:
name="2000 TSOP";
im_funcname = "DoC2k_init";
im_modname = "doc2000";
break;
case DOC_ChipID_Doc2k:
name="2000";
im_funcname = "DoC2k_init";
......
......@@ -2,7 +2,7 @@
/*
* 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>
*
......
/*
* Copyright (c) 2001 Maciej W. Rozycki
* Copyright (c) 2001 Maciej W. Rozycki
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
* This program is 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.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>
......@@ -29,7 +29,7 @@
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_DESCRIPTION("DEC MS02-NV NVRAM module driver");
......@@ -38,9 +38,9 @@ MODULE_LICENSE("GPL");
/*
* 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
* boundary within a 0MB up to 448MB range. We don't support a module
* at 0MB, though.
* at any 8MiB boundary within a 0MiB up to 112MiB range or at any 32MiB
* boundary within a 0MiB up to 448MiB range. We don't support a module
* at 0MiB, though.
*/
static ulong ms02nv_addrs[] __initdata = {
0x07000000, 0x06800000, 0x06000000, 0x05800000, 0x05000000,
......@@ -130,7 +130,7 @@ static int __init ms02nv_init_one(ulong addr)
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);
if (!mod_res)
return -ENOMEM;
......@@ -233,7 +233,7 @@ static int __init ms02nv_init_one(ulong addr)
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);
mp->next = root_ms02nv_mtd;
......@@ -293,12 +293,12 @@ static int __init ms02nv_init(void)
switch (mips_machtype) {
case MACH_DS5000_200:
csr = (volatile u32 *)KN02_CSR_ADDR;
csr = (volatile u32 *)KN02_CSR_BASE;
if (*csr & KN02_CSR_BNK32M)
stride = 2;
break;
case MACH_DS5000_2X0:
case MACH_DS5000:
case MACH_DS5900:
csr = (volatile u32 *)KN03_MCR_BASE;
if (*csr & KN03_MCR_BNK32M)
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
* 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.
* DEC MS02-NV (54-20948-01) battery backed-up NVRAM module for
* DECstation/DECsystem 5000/2x0 and DECsystem 5900 and 5900/260
* systems.
*
* $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/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. */
#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. */
#define MS02NV_DIAG 0x0003f8 /* diagnostic status */
#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. */
#define MS02NV_DIAG_SIZE_MASK 0xf0 /* RAM size mask */
#define MS02NV_DIAG_SIZE_SHIFT 0x10 /* RAM size shift (left) */
/* MS02-NV diagnostic status bits. */
#define MS02NV_DIAG_TEST 0x01 /* SRAM test done (?) */
#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. */
#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
decoded by the module */
typedef volatile u32 ms02nv_uint;
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
*
......
/* 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 some leaks on failure in build_maps and ftl_notify_add, cleanups
......@@ -1093,7 +1093,7 @@ struct mtd_blktrans_ops ftl_tr = {
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);
}
......
......@@ -7,7 +7,7 @@
* (c) 1999 Machine Vision Holdings, Inc.
* 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
* 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)
struct INFTLrecord *inftl;
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;
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);
inftl = kmalloc(sizeof(*inftl), GFP_KERNEL);
......@@ -72,6 +82,8 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
inftl->mbd.devnum = -1;
inftl->mbd.blksize = 512;
inftl->mbd.tr = tr;
memcpy(&inftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo));
inftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY;
if (INFTL_mount(inftl) < 0) {
printk(KERN_WARNING "INFTL: could not mount device\n");
......@@ -284,21 +296,22 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
if (BlockMap[block] == BLOCK_NIL)
continue;
ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize *
ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize *
BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE,
&retlen, movebuf, (char *)&oob, NULL);
&retlen, movebuf);
if (ret < 0) {
ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize *
ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize *
BlockMap[block]) + (block * SECTORSIZE),
SECTORSIZE, &retlen, movebuf, (char *)&oob,
NULL);
SECTORSIZE, &retlen, movebuf);
if (ret != -EIO)
DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "
"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) +
(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
if (INFTL_formatblock(inftl, thisEUN) < 0) {
/*
* Could not erase : mark block as reserved.
* FixMe: Update Bad Unit Table on disk.
*/
inftl->PUtable[thisEUN] = BLOCK_RESERVED;
} else {
......@@ -668,7 +680,6 @@ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
if (INFTL_formatblock(inftl, thisEUN) < 0) {
/*
* Could not erase : mark block as reserved.
* FixMe: Update Bad Unit Table on medium.
*/
inftl->PUtable[thisEUN] = BLOCK_RESERVED;
} else {
......@@ -754,7 +765,7 @@ static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
unsigned int writeEUN;
unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
size_t retlen;
u8 eccbuf[6];
struct inftl_oob oob;
char *p, *pend;
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,
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) +
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
*/
} else {
......@@ -846,9 +859,8 @@ static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
} else {
size_t retlen;
loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
u_char eccbuf[6];
if (MTD_READECC(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen,
buffer, eccbuf, NULL))
if (MTD_READ(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen,
buffer))
return -EIO;
}
return 0;
......@@ -881,7 +893,7 @@ extern char inftlmountrev[];
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);
return register_mtd_blktrans(&inftl_tr);
......
......@@ -8,7 +8,7 @@
* Author: Fabrice Bellard (fabrice.bellard@netgem.com)
* 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
* it under the terms of the GNU General Public License as published by
......@@ -41,7 +41,7 @@
#include <linux/mtd/inftl.h>
#include <linux/mtd/compatmac.h>
char inftlmountrev[]="$Revision: 1.11 $";
char inftlmountrev[]="$Revision: 1.13 $";
/*
* 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)
{
struct inftl_unittail h1;
//struct inftl_oob oob;
unsigned int i, block, boot_record_count = 0;
unsigned int i, block;
u8 buf[SECTORSIZE];
struct INFTLMediaHeader *mh = &inftl->MediaHdr;
struct INFTLPartition *ip;
......@@ -72,7 +72,6 @@ static int find_boot_record(struct INFTLrecord *inftl)
inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize;
inftl->MediaUnit = BLOCK_NIL;
inftl->SpareMediaUnit = BLOCK_NIL;
/* Search for a valid boot record */
for (block = 0; block < inftl->nb_blocks; block++) {
......@@ -82,8 +81,11 @@ static int find_boot_record(struct INFTLrecord *inftl)
* Check for BNAND header first. Then whinge if it's found
* but later checks fail.
*/
if ((ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize,
SECTORSIZE, &retlen, buf))) {
ret = MTD_READ(inftl->mbd.mtd, block * inftl->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;
if (warncount) {
......@@ -114,36 +116,28 @@ static int find_boot_record(struct INFTLrecord *inftl)
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.
* Copy the media header structure into place.
*/
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->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions);
mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions);
......@@ -197,8 +191,9 @@ static int find_boot_record(struct INFTLrecord *inftl)
"UnitSizeFactor 0x%02x is experimental\n",
mh->BlockMultiplierBits);
inftl->EraseSize = inftl->mbd.mtd->erasesize <<
(0xff - mh->BlockMultiplierBits);
mh->BlockMultiplierBits;
inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize;
block >>= mh->BlockMultiplierBits;
}
/* Scan the partitions */
......@@ -317,34 +312,23 @@ static int find_boot_record(struct INFTLrecord *inftl)
/* Mark this boot record (NFTL MediaHeader) block as reserved */
inftl->PUtable[block] = BLOCK_RESERVED;
#if 0
/* Read Bad Erase Unit Table and modify PUtable[] accordingly */
for (i = 0; i < inftl->nb_blocks; i++) {
if ((i & (SECTORSIZE - 1)) == 0) {
/* read one sector for every SECTORSIZE of blocks */
if ((ret = MTD_READECC(inftl->mbd.mtd,
block * inftl->EraseSize + i + SECTORSIZE,
SECTORSIZE, &retlen, buf,
(char *)&oob, NULL)) < 0) {
printk(KERN_WARNING "INFTL: read of "
"bad sector table failed "
"(err %d)\n", ret);
kfree(inftl->VUtable);
kfree(inftl->PUtable);
return -1;
}
int physblock;
/* If any of the physical eraseblocks are bad, don't
use the unit. */
for (physblock = 0; physblock < inftl->EraseSize; physblock += inftl->mbd.mtd->erasesize) {
if (inftl->mbd.mtd->block_isbad(inftl->mbd.mtd, i * inftl->EraseSize + physblock))
inftl->PUtable[i] = BLOCK_RESERVED;
}
/* Mark the Bad Erase Unit as RESERVED in PUtable */
if (buf[i & (SECTORSIZE - 1)] != 0xff)
inftl->PUtable[i] = BLOCK_RESERVED;
}
#endif
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)
......@@ -365,27 +349,20 @@ static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address,
int len, int check_oob)
{
int i, retlen;
u8 buf[SECTORSIZE];
u8 buf[SECTORSIZE + inftl->mbd.mtd->oobsize];
DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=0x%x,"
"address=0x%x,len=%d,check_oob=%d)\n", (int)inftl,
address, len, check_oob);
for (i = 0; i < len; i += SECTORSIZE) {
/*
* We want to read the sector without ECC check here since a
* free sector does not have ECC syndrome on it yet.
*/
if (MTD_READ(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf) < 0)
if (MTD_READECC(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &inftl->oobinfo) < 0)
return -1;
if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
return -1;
if (check_oob) {
if (MTD_READOOB(inftl->mbd.mtd, address,
inftl->mbd.mtd->oobsize, &retlen, buf) < 0)
return -1;
if (memcmpb(buf, 0xff, inftl->mbd.mtd->oobsize) != 0)
if (memcmpb(buf + SECTORSIZE, 0xff, inftl->mbd.mtd->oobsize) != 0)
return -1;
}
address += SECTORSIZE;
......@@ -402,52 +379,62 @@ static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address,
* Return: 0 when succeed, -1 on error.
*
* ToDo: 1. Is it neceressary to check_free_sector after erasing ??
* 2. UnitSizeFactor != 0xFF
*/
int INFTL_formatblock(struct INFTLrecord *inftl, int block)
{
int retlen;
struct inftl_unittail uci;
struct erase_info *instr = &inftl->instr;
int physblock;
DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=0x%x,"
"block=%d)\n", (int)inftl, block);
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 */
instr->addr = block * inftl->EraseSize;
instr->len = inftl->EraseSize;
MTD_ERASE(inftl->mbd.mtd, instr);
instr->len = inftl->mbd.mtd->erasesize;
/* 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
* both in memory and on disk.
*/
printk(KERN_WARNING "INFTL: error while formatting block %d\n",
block);
return -1;
* Check the "freeness" of Erase Unit before updating metadata.
* FixMe: is this check really necessary? Since we have check the
* return code after the erase operation.
*/
if (check_free_sectors(inftl, instr->addr, instr->len, 1) != 0)
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.EraseMark1 = cpu_to_le16(ERASE_MARK);
uci.Reserved[0] = 0;
uci.Reserved[1] = 0;
uci.Reserved[2] = 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)
return -1;
goto fail;
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)
if (INFTL_formatblock(inftl, block) < 0) {
/*
* Cannot format !!!! Mark it as Bad Unit,
* FixMe: update the BadUnitTable on disk.
*/
inftl->PUtable[block] = BLOCK_RESERVED;
} 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>
*
......@@ -295,7 +295,10 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
snprintf(gd->devfs_name, sizeof(gd->devfs_name),
"%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;
new->blkcore_priv = gd;
gd->queue = tr->blkcore_priv->rq;
......
/*
* 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) 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.
*
......@@ -9,6 +9,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/compatmac.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/fs.h>
......@@ -16,14 +17,46 @@
#ifdef CONFIG_DEVFS_FS
#include <linux/devfs_fs_kernel.h>
static void mtd_notify_add(struct mtd_info* mtd);
static void mtd_notify_remove(struct mtd_info* mtd);
static 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 = {
.add = mtd_notify_add,
.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
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,
memset (erase,0,sizeof(struct erase_info));
if (copy_from_user(&erase->addr, argp,
2 * sizeof(u_long))) {
sizeof(struct erase_info_user))) {
kfree(erase);
return -EFAULT;
}
......@@ -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);
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;
kfree(databuf);
......@@ -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);
if (copy_to_user(argp + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
if (put_user(retlen, (uint32_t __user *)argp))
ret = -EFAULT;
else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
ret = -EFAULT;
......@@ -411,29 +444,29 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
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;
if (!mtd->lock)
ret = -EOPNOTSUPP;
else
ret = mtd->lock(mtd, adrs[0], adrs[1]);
ret = mtd->lock(mtd, info.start, info.length);
break;
}
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;
if (!mtd->unlock)
ret = -EOPNOTSUPP;
else
ret = mtd->unlock(mtd, adrs[0], adrs[1]);
ret = mtd->unlock(mtd, info.start, info.length);
break;
}
......@@ -443,7 +476,40 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
return -EFAULT;
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:
DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO);
ret = -ENOTTY;
......@@ -462,30 +528,6 @@ static struct file_operations mtd_fops = {
.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)
{
if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) {
......@@ -494,20 +536,13 @@ static int __init init_mtdchar(void)
return -EAGAIN;
}
#ifdef CONFIG_DEVFS_FS
devfs_mk_dir("mtd");
register_mtd_user(&notifier);
#endif
mtdchar_devfs_init();
return 0;
}
static void __exit cleanup_mtdchar(void)
{
#ifdef CONFIG_DEVFS_FS
unregister_mtd_user(&notifier);
devfs_remove("mtd");
#endif
mtdchar_devfs_exit();
unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
}
......
......@@ -7,7 +7,7 @@
*
* 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>
......@@ -391,7 +391,7 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
struct mtd_concat *concat = CONCAT(mtd);
struct mtd_info *subdev;
int i, err;
u_int32_t length;
u_int32_t length, offset = 0;
struct erase_info *erase;
if (!(mtd->flags & MTD_WRITEABLE))
......@@ -450,6 +450,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
return -EINVAL;
}
instr->fail_addr = 0xffffffff;
/* make a local copy of instr to avoid modifying the caller's struct */
erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL);
......@@ -465,10 +467,12 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
*/
for (i = 0; i < concat->num_subdev; i++) {
subdev = concat->subdev[i];
if (subdev->size <= erase->addr)
if (subdev->size <= erase->addr) {
erase->addr -= subdev->size;
else
offset += subdev->size;
} else {
break;
}
}
/* 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)
* block alignment has been checked above */
if (err == -EINVAL)
BUG();
if (erase->fail_addr != 0xffffffff)
instr->fail_addr = erase->fail_addr + offset;
break;
}
/*
......@@ -508,12 +514,13 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
* current subdevice, i.e. at offset zero.
*/
erase->addr = 0;
offset += subdev->size;
}
instr->state = erase->state;
kfree(erase);
if (err)
return err;
instr->state = MTD_ERASE_DONE;
if (instr->callback)
instr->callback(instr);
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
* drivers and users.
......@@ -334,10 +334,7 @@ static int mtd_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
/* Support for /proc/mtd */
#ifdef CONFIG_PROC_FS
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
static struct proc_dir_entry *proc_mtd;
#endif
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);
}
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
#else
,int unused
#endif
)
static int mtd_read_proc (char *page, char **start, off_t off, int count,
int *eof, void *data_unused)
{
int len, l, i;
off_t begin = 0;
......@@ -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;
#endif
done:
up(&mtd_table_mutex);
......@@ -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);
}
#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 */
/*====================================================================*/
......@@ -408,16 +386,8 @@ struct proc_dir_entry mtd_proc_entry = {
int __init init_mtd(void)
{
#ifdef CONFIG_PROC_FS
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
if ((proc_mtd = create_proc_entry( "mtd", 0, NULL )))
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();
proc_mtd->read_proc = mtd_read_proc;
#endif
#ifdef CONFIG_PM
......@@ -436,12 +406,8 @@ static void __exit cleanup_mtd(void)
#endif
#ifdef CONFIG_PROC_FS
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
if (proc_mtd)
remove_proc_entry( "mtd", NULL);
#else
proc_unregister(&proc_root,mtd_proc_entry.low_ino);
#endif
remove_proc_entry( "mtd", NULL);
#endif
}
......
/* Linux driver for NAND Flash Translation Layer */
/* (c) 1999 Machine Vision Holdings, Inc. */
/* 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
......@@ -43,9 +43,19 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
struct NFTLrecord *nftl;
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;
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);
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)
nftl->mbd.devnum = -1;
nftl->mbd.blksize = 512;
nftl->mbd.tr = tr;
memcpy(&nftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo));
nftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY;
if (NFTL_mount(nftl) < 0) {
printk(KERN_WARNING "NFTL: could not mount device\n");
......@@ -350,17 +362,19 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
if (BlockMap[block] == BLOCK_NIL)
continue;
ret = MTD_READECC(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP);
ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
512, &retlen, movebuf);
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,
movebuf, (char *)&oob, NAND_ECC_DISKONCHIP);
movebuf);
if (ret != -EIO)
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),
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 */
......@@ -390,7 +404,6 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
if (NFTL_formatblock(nftl, thisEUN) < 0) {
/* could not erase : mark block as reserved
* FixMe: Update Bad Unit Table on disk
*/
nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
} else {
......@@ -617,7 +630,7 @@ static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
u16 writeEUN;
unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
size_t retlen;
u8 eccbuf[6];
struct nftl_oob oob;
writeEUN = NFTL_findwriteunit(nftl, block);
......@@ -628,9 +641,11 @@ static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
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,
512, &retlen, (char *)buffer, (char *)eccbuf, NAND_ECC_DISKONCHIP);
/* no need to write SECTOR_USED flags since they are written in mtd_writeecc */
512, &retlen, (char *)buffer, (char *)&oob, &nftl->oobinfo);
/* need to write SECTOR_USED flags since they are not written in mtd_writeecc */
return 0;
}
......@@ -692,8 +707,7 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
} else {
loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
size_t retlen;
u_char eccbuf[6];
if (MTD_READECC(nftl->mbd.mtd, ptr, 512, &retlen, buffer, eccbuf, NAND_ECC_DISKONCHIP))
if (MTD_READ(nftl->mbd.mtd, ptr, 512, &retlen, buffer))
return -EIO;
}
return 0;
......@@ -735,7 +749,7 @@ extern char nftlmountrev[];
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);
}
......
......@@ -4,7 +4,7 @@
* Author: Fabrice Bellard (fabrice.bellard@netgem.com)
* 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
* it under the terms of the GNU General Public License as published by
......@@ -31,7 +31,7 @@
#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
* various device information of the NFTL partition and Bad Unit Table. Update
......@@ -50,6 +50,10 @@ static int find_boot_record(struct NFTLrecord *nftl)
/* Assume logical EraseSize == physical erasesize for starting the scan.
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->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize;
......@@ -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
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;
if (warncount) {
......@@ -104,7 +111,7 @@ static int find_boot_record(struct NFTLrecord *nftl)
/* Finally reread to check ECC */
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",
block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
continue;
......@@ -149,6 +156,10 @@ static int find_boot_record(struct NFTLrecord *nftl)
memcpy(mh, buf, sizeof(struct NFTLMediaHeader));
/* 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) {
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) {
......@@ -161,6 +172,7 @@ static int find_boot_record(struct NFTLrecord *nftl)
nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor);
nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize;
}
#endif
nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN);
if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) {
printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n");
......@@ -213,11 +225,13 @@ static int find_boot_record(struct NFTLrecord *nftl)
/* read the Bad Erase Unit Table and modify ReplUnitTable[] accordingly */
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) {
/* read one sector for every SECTORSIZE of blocks */
if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize +
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",
ret);
kfree(nftl->ReplUnitTable);
......@@ -228,6 +242,9 @@ static int find_boot_record(struct NFTLrecord *nftl)
/* mark the Bad Erase Unit as RESERVED in ReplUnitTable */
if (buf[i & (SECTORSIZE - 1)] != 0xff)
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;
......@@ -253,21 +270,16 @@ static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int
int check_oob)
{
int i, retlen;
u8 buf[SECTORSIZE];
u8 buf[SECTORSIZE + nftl->mbd.mtd->oobsize];
for (i = 0; i < len; i += SECTORSIZE) {
/* we want to read the sector without ECC check here since a free
sector does not have ECC syndrome on it yet */
if (MTD_READ(nftl->mbd.mtd, address, SECTORSIZE, &retlen, buf) < 0)
if (MTD_READECC(nftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &nftl->oobinfo) < 0)
return -1;
if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
return -1;
if (check_oob) {
if (MTD_READOOB(nftl->mbd.mtd, address, nftl->mbd.mtd->oobsize,
&retlen, buf) < 0)
return -1;
if (memcmpb(buf, 0xff, nftl->mbd.mtd->oobsize) != 0)
if (memcmpb(buf + SECTORSIZE, 0xff, nftl->mbd.mtd->oobsize) != 0)
return -1;
}
address += SECTORSIZE;
......@@ -282,7 +294,6 @@ static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int
* Return: 0 when succeed, -1 on error.
*
* ToDo: 1. Is it neceressary to check_free_sector after erasing ??
* 2. UnitSizeFactor != 0xFF
*/
int NFTL_formatblock(struct NFTLrecord *nftl, int block)
{
......@@ -312,11 +323,10 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block)
MTD_ERASE(nftl->mbd.mtd, instr);
if (instr->state == MTD_ERASE_FAILED) {
/* could not format, FixMe: We should update the BadUnitTable
both in memory and on disk */
printk("Error while formatting block %d\n", block);
return -1;
} else {
goto fail;
}
/* increase and write Wear-Leveling info */
nb_erases = le32_to_cpu(uci.WearInfo);
nb_erases++;
......@@ -329,14 +339,18 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block)
* FixMe: is this check really necessary ? since we have check the
* return code after the erase operation. */
if (check_free_sectors(nftl, instr->addr, nftl->EraseSize, 1) != 0)
return -1;
goto fail;
uci.WearInfo = le32_to_cpu(nb_erases);
if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
&retlen, (char *)&uci) < 0)
return -1;
goto fail;
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.
......@@ -441,8 +455,7 @@ static void format_chain(struct NFTLrecord *nftl, unsigned int first_block)
printk("Formatting block %d\n", block);
if (NFTL_formatblock(nftl, block) < 0) {
/* cannot format !!!! Mark it as Bad Unit,
FixMe: update the BadUnitTable on disk */
/* cannot format !!!! Mark it as Bad Unit */
nftl->ReplUnitTable[block] = BLOCK_RESERVED;
} else {
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
* produce a Linux partition array to match.
......@@ -48,21 +48,24 @@ static int parse_redboot_partitions(struct mtd_info *master,
char *names;
char *nullname;
int namelen = 0;
int nulllen = 0;
#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
static char nullstring[] = "unallocated";
#endif
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
buf = kmalloc(master->erasesize, GFP_KERNEL);
if (!buf)
return -ENOMEM;
/* Read the start of the last erase block */
ret = master->read(master, master->size - master->erasesize,
PAGE_SIZE, &retlen, (void *)buf);
master->erasesize, &retlen, (void *)buf);
if (ret)
goto out;
if (retlen != PAGE_SIZE) {
if (retlen != master->erasesize) {
ret = -EIO;
goto out;
}
......@@ -80,7 +83,7 @@ static int parse_redboot_partitions(struct mtd_info *master,
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;
if (buf[i].name[0] == 0xff)
......@@ -112,48 +115,69 @@ static int parse_redboot_partitions(struct mtd_info *master,
nrparts++;
}
if (fl->img->flash_base)
#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
if (fl->img->flash_base) {
nrparts++;
nulllen = sizeof(nullstring);
}
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++;
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) {
ret = -ENOMEM;
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];
sprintf(nullname, nullstring);
names = nullname + sizeof(nullstring);
#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
if (nulllen > 0) {
strcpy(nullname, nullstring);
}
#endif
names = nullname + nulllen;
i=0;
#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
if (fl->img->flash_base) {
parts[0].name = nullname;
parts[0].size = fl->img->flash_base;
parts[0].offset = 0;
i++;
}
#endif
for ( ; i<nrparts; i++) {
parts[i].size = fl->img->size;
parts[i].offset = fl->img->flash_base;
parts[i].name = names;
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;
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++;
parts[i].offset = parts[i-1].size + parts[i-1].offset;
parts[i].size = fl->next->img->flash_base - parts[i].offset;
parts[i].name = nullname;
}
#endif
tmp_fl = fl;
fl = fl->next;
kfree(tmp_fl);
......
/* Linux driver for Disk-On-Chip 2000 */
/* (c) 1999 Machine Vision Holdings, Inc. */
/* Author: David Woodhouse <dwmw2@mvhi.com> */
/* $Id: doc2000.h,v 1.17 2003/06/12 01:20:46 gerg Exp $ */
/*
* Linux driver for Disk-On-Chip devices
*
* Copyright (C) 1999 Machine Vision Holdings, Inc.
* 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__
#define __MTD_DOC2000_H__
#include <linux/mtd/mtd.h>
#include <asm/semaphore.h>
#define DoC_Sig1 0
#define DoC_Sig2 1
......@@ -73,12 +81,12 @@
* Others use readb/writeb
*/
#if defined(__arm__)
#define ReadDOC_(adr, reg) ((unsigned char)(*(__u32 *)(((unsigned long)adr)+((reg)<<2))))
#define WriteDOC_(d, adr, reg) do{ *(__u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0)
#define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u32 *)(((unsigned long)adr)+((reg)<<2))))
#define WriteDOC_(d, adr, reg) do{ *(volatile __u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0)
#define DOC_IOREMAP_LEN 0x8000
#elif defined(__ppc__)
#define ReadDOC_(adr, reg) ((unsigned char)(*(__u16 *)(((unsigned long)adr)+((reg)<<1))))
#define WriteDOC_(d, adr, reg) do{ *(__u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0)
#define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u16 *)(((unsigned long)adr)+((reg)<<1))))
#define WriteDOC_(d, adr, reg) do{ *(volatile __u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0)
#define DOC_IOREMAP_LEN 0x4000
#else
#define ReadDOC_(adr, reg) readb(((unsigned long)adr) + (reg))
......@@ -106,6 +114,7 @@
#define DOC_MODE_MDWREN 0x04
#define DOC_ChipID_Doc2k 0x20
#define DOC_ChipID_Doc2kTSOP 0x21 /* internal number for MTD */
#define DOC_ChipID_DocMil 0x30
#define DOC_ChipID_DocMilPlus32 0x40
#define DOC_ChipID_DocMilPlus16 0x41
......@@ -147,10 +156,10 @@ struct Nand {
#define MAX_FLOORS 4
#define MAX_CHIPS 4
#define MAX_FLOORS_MIL 4
#define MAX_FLOORS_MIL 1
#define MAX_CHIPS_MIL 1
#define MAX_FLOORS_MPLUS 1
#define MAX_FLOORS_MPLUS 2
#define MAX_CHIPS_MPLUS 1
#define ADDR_COLUMN 1
......@@ -161,7 +170,7 @@ struct DiskOnChip {
unsigned long physadr;
unsigned long virtadr;
unsigned long totlen;
char ChipID; /* Type of DiskOnChip */
unsigned char ChipID; /* Type of DiskOnChip */
int ioreg;
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):
* ftl.h 1.7 1999/10/25 20:23:17
......
......@@ -3,105 +3,32 @@
*
* (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__
#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/mtd.h>
#include <linux/mtd/nftl.h>
#define OSAK_VERSION 0x5120
#define PERCENTUSED 98
#define SECTORSIZE 512
#include <mtd/inftl-user.h>
#ifndef INFTL_MAJOR
#define INFTL_MAJOR 93 /* FIXME */
#define INFTL_MAJOR 94
#endif
#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__
struct INFTLrecord {
struct mtd_blktrans_dev mbd;
__u16 MediaUnit, SpareMediaUnit;
__u16 MediaUnit;
__u32 EraseSize;
struct INFTLMediaHeader MediaHdr;
int usecount;
......@@ -119,6 +46,7 @@ struct INFTLrecord {
unsigned int nb_blocks; /* number of physical blocks */
unsigned int nb_boot_blocks; /* number of blocks used by the bios */
struct erase_info instr;
struct nand_oobinfo oobinfo;
};
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__
#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/version.h>
......@@ -12,115 +19,26 @@
#include <linux/module.h>
#include <linux/uio.h>
#endif /* __KERNEL__ */
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;
};
#include <mtd/mtd-abi.h>
#define MTD_CHAR_MAJOR 90
#define MTD_BLOCK_MAJOR 31
#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_ERASING 0x02
#define MTD_ERASE_SUSPEND 0x04
#define MTD_ERASE_DONE 0x08
#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 mtd_info *mtd;
u_int32_t addr;
u_int32_t len;
u_int32_t fail_addr;
u_long time;
u_long retries;
u_int dev;
......@@ -150,6 +68,7 @@ struct mtd_info {
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 oobavail; // Number of bytes in OOB area available for fs
u_int32_t ecctype;
u_int32_t eccsize;
......@@ -223,6 +142,10 @@ struct mtd_info {
int (*suspend) (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;
struct module *owner;
......@@ -288,6 +211,4 @@ int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs,
#endif /* CONFIG_MTD_DEBUG */
#endif /* __KERNEL__ */
#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>
*/
......@@ -10,71 +10,7 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/blktrans.h>
/* Block Control Information */
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__
#include <mtd/nftl-user.h>
/* these info are used in ReplUnitTable */
#define BLOCK_NIL 0xffff /* last block of a chain */
......@@ -101,6 +37,7 @@ struct NFTLrecord {
unsigned int nb_blocks; /* number of physical blocks */
unsigned int nb_boot_blocks; /* number of blocks used by the bios */
struct erase_info instr;
struct nand_oobinfo oobinfo;
};
int NFTL_mount(struct NFTLrecord *s);
......@@ -114,6 +51,4 @@ int NFTL_formatblock(struct NFTLrecord *s, int block);
#define MAX_SECTORS_PER_UNIT 64
#define NFTL_PARTN_BITS 4
#endif /* __KERNEL__ */
#endif /* __MTD_NFTL_H__ */
......@@ -5,7 +5,7 @@
*
* 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
......@@ -50,7 +50,7 @@ struct mtd_partition {
#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 *);
/*
......
/*
* $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