mtd/drivers/mtd/nand nand_base.c,1.86,1.87
gleixner at infradead.org
gleixner at infradead.org
Wed May 26 09:35:33 EDT 2004
Update of /home/cvs/mtd/drivers/mtd/nand
In directory phoenix.infradead.org:/tmp/cvs-serv11150
Modified Files:
nand_base.c
Log Message:
Add bad block table support. Signed-off-by: Thomas Gleixner <tglx at linutronix.de>
Index: nand_base.c
===================================================================
RCS file: /home/cvs/mtd/drivers/mtd/nand/nand_base.c,v
retrieving revision 1.86
retrieving revision 1.87
diff -u -r1.86 -r1.87
--- nand_base.c 19 May 2004 20:17:40 -0000 1.86
+++ nand_base.c 26 May 2004 13:35:30 -0000 1.87
@@ -48,6 +48,7 @@
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/sched.h>
+#include <linux/slab.h>
#include <linux/types.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
@@ -57,6 +58,10 @@
#include <linux/bitops.h>
#include <asm/io.h>
+#ifdef CONFIG_MTD_PARTITIONS
+#include <linux/mtd/partitions.h>
+#endif
+
/* Define default oob placement schemes for large and small page devices */
static struct nand_oobinfo nand_oob_8 = {
.useecc = MTD_NANDECC_AUTOPLACE,
@@ -268,7 +273,6 @@
u16 bad;
if (getchip) {
- /* Shift to get page */
page = ((int) ofs) >> this->page_shift;
chipnr = (int)((unsigned long) ofs / this->chipsize);
@@ -309,10 +313,18 @@
struct nand_chip *this = mtd->priv;
u_char buf[2] = {0, 0};
size_t retlen;
+ int block;
- ofs += mtd->oobsize + (this->badblockpos & ~0x01);
-
+ /* Get block number */
+ block = ((int) ofs) >> this->erase_shift;
+ this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+
+ /* Do we have a flash based bad block table ? */
+ if (this->options & NAND_USE_FLASH_BBT)
+ return nand_update_bbt (mtd);
+
/* We write two bytes, so we dont have to mess with 16 bit access */
+ ofs += mtd->oobsize + (this->badblockpos & ~0x01);
return nand_write_oob (mtd, ofs , 2, &retlen, buf);
}
@@ -327,6 +339,29 @@
return (this->read_byte(mtd) & 0x80) ? 0 : 1;
}
+/* Check, if the block is already scanned in the bad block table.
+ * If not, call the scan function and store the result in
+ * the table.
+ *
+ * @mtd: MTD device structure
+ * @ofs: offset from device start
+ * @getchip: 0, if the chip is already selected
+ *
+ */
+static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+ struct nand_chip *this = mtd->priv;
+ int block;
+
+ if (!this->bbt)
+ return this->block_bad(mtd, ofs, getchip);
+
+ /* Return info from the table */
+ /* Get block number */
+ block = (int) (ofs >> (this->erase_shift - 1));
+ return this->bbt[block >> 3] & (1 << (block & 0x06)) ? 1 : 0;
+}
+
/*
* Send command to NAND device
*/
@@ -1088,16 +1123,14 @@
*/
i = 0;
while (i < len) {
- int thislen = (mtd->oobsize - col) & (mtd->oobsize - 1);
- if (!thislen)
- thislen = mtd->oobsize;
+ int thislen = mtd->oobsize - col;
thislen = min_t(int, thislen, len);
this->read_buf(mtd, &buf[i], thislen);
i += thislen;
- col += thislen;
/* Read more ? */
if (i < len) {
page++;
+ col = 0;
/* Apply delay or wait for ready/busy pin
* Do this before the AUTOINCR check, so no problems
* arise if a chip which does auto increment
@@ -1133,6 +1166,64 @@
return 0;
}
+/*
+ * Read raw data including oob into buffer
+ *
+ * @mtd: MTD device structure
+ * @buf: temporary buffer
+ * @from: offset to read from
+ * @len: number of bytes to read
+ */
+int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len)
+{
+ struct nand_chip *this = mtd->priv;
+ int page = (int) (from >> this->page_shift);
+ int chipshift = ffs(this->chipsize) - 1;
+ int chip = (int) (from >> chipshift);
+ int sndcmd = 1;
+ int erase_state = 0;
+ int cnt = 0;
+ int pagesize = mtd->oobblock + mtd->oobsize;
+ int blockcheck = (mtd->erasesize >> this->page_shift) - 1;
+
+ /* Do not allow reads past end of device */
+ if ((from + len) > mtd->size) {
+ DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n");
+ return -EINVAL;
+ }
+
+ /* Grab the lock and see if the device is available */
+ nand_get_chip (this, mtd , FL_READING, &erase_state);
+
+ this->select_chip (mtd, chip);
+
+ while (len) {
+ if (sndcmd)
+ this->cmdfunc (mtd, NAND_CMD_READ0, 0, page & this->pagemask);
+ sndcmd = 0;
+
+ this->read_buf (mtd, &buf[cnt], pagesize);
+
+ len -= pagesize;
+ cnt += pagesize;
+ page++;
+
+ if (!this->dev_ready)
+ udelay (this->chip_delay);
+ else
+ while (!this->dev_ready(mtd));
+
+ /* Check, if the chip supports auto page increment */
+ if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
+ sndcmd = 1;
+ }
+
+ /* Deselect and wake up anyone waiting on the device */
+ nand_release_chip(mtd);
+ return 0;
+}
+
+
/* Prepare the out of band buffer
*
* Return:
@@ -1642,6 +1733,7 @@
*/
static void single_erase_cmd (struct mtd_info *mtd, int page)
{
+ struct nand_chip *this = mtd->priv;
/* Send commands to erase a block */
this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
@@ -1653,6 +1745,7 @@
*/
static void multi_erase_cmd (struct mtd_info *mtd, int page)
{
+ struct nand_chip *this = mtd->priv;
/* Send commands to erase a block */
this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
@@ -1721,7 +1814,7 @@
while (len) {
/* Check if we have a bad block, we do not erase bad blocks ! */
- if (this->block_bad(mtd, (loff_t) page, 0)) {
+ if (nand_block_checkbad(mtd, (loff_t) page << this->page_shift, 0)) {
printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page);
instr->state = MTD_ERASE_FAILED;
goto erase_exit;
@@ -1758,7 +1851,7 @@
erase_exit:
- ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;;
+ ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
/* Do call back function */
if (!ret && instr->callback)
instr->callback (instr);
@@ -1817,6 +1910,7 @@
spin_unlock_bh (&this->chip_lock);
}
+
/*
* Check whether the block at the given offset is bad
*
@@ -1825,13 +1919,11 @@
*/
static int nand_block_isbad (struct mtd_info *mtd, loff_t ofs)
{
- struct nand_chip *this = mtd->priv;
-
/* Check for invalid offset */
if (ofs > mtd->size)
return -EINVAL;
-
- return this->block_bad(mtd, ofs, 1);
+
+ return nand_block_checkbad (mtd, ofs, 1);
}
/*
@@ -1902,6 +1994,8 @@
this->read_buf = busw ? nand_read_buf16 : nand_read_buf;
if (!this->verify_buf)
this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
+ if (!this->scan_bbt)
+ this->scan_bbt = nand_default_bbt;
/* Select the device */
this->select_chip(mtd, 0);
@@ -1972,8 +2066,9 @@
this->badblockpos = mtd->oobblock > 512 ?
NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
- /* Get chip options */
- this->options = nand_flash_ids[i].options;
+ /* Get chip options, preserve non chip based options */
+ this->options &= ~NAND_CHIPOPTIONS_MSK;
+ this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK;
/* Check if this is a not a samsung device. Do not clear the options
* for chips which are not having an extended id.
*/
@@ -1987,7 +2082,7 @@
this->erase_cmd = single_erase_cmd;
/* Do not replace user supplied command function ! */
- if (this->pagesize > 512 && this->cmdfunc == nand_command)
+ if (mtd->oobblock > 512 && this->cmdfunc == nand_command)
this->cmdfunc = nand_command_lp;
/* Try to identify manufacturer */
@@ -2021,9 +2116,29 @@
if (i > 1)
printk(KERN_INFO "%d NAND chips detected\n", i);
- if (!this->oob_buf || !this->data_buf) {
- printk (KERN_WARNING "nand_scan(): Buffers not set. Scan aborted\n");
- return 1;
+ /* Allocate buffers, if neccecary */
+ if (!this->oob_buf) {
+ size_t len;
+ len = mtd->oobsize << (this->erase_shift - this->page_shift);
+ this->oob_buf = kmalloc (len, GFP_KERNEL);
+ if (!this->oob_buf) {
+ printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n");
+ return -ENOMEM;
+ }
+ this->options |= NAND_OOBBUF_ALLOC;
+ }
+
+ if (!this->data_buf) {
+ size_t len;
+ len = mtd->oobblock + mtd->oobsize;
+ this->data_buf = kmalloc (len, GFP_KERNEL);
+ if (!this->data_buf) {
+ if (this->options & NAND_OOBBUF_ALLOC)
+ kfree (this->oob_buf);
+ printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n");
+ return -ENOMEM;
+ }
+ this->options |= NAND_DATABUF_ALLOC;
}
/* Store the number of chips and calc total size for mtd */
@@ -2164,11 +2279,38 @@
mtd->owner = THIS_MODULE;
- /* Return happy */
- return 0;
+ /* Build bad block table */
+ return this->scan_bbt (mtd);
+}
+
+/* Free resources held by the NAND device
+ *
+ * @mtd: MTD device structure
+*/
+void nand_release (struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+
+#ifdef CONFIG_MTD_PARTITIONS
+ /* Unregister partitions */
+ del_mtd_partitions (mtd);
+#endif
+ /* Unregister the device */
+ del_mtd_device (mtd);
+
+ /* Free bad block table memory, if allocated */
+ if (this->bbt)
+ kfree (this->bbt);
+ /* Buffer allocated by nand_scan ? */
+ if (this->options & NAND_OOBBUF_ALLOC)
+ kfree (this->oob_buf);
+ /* Buffer allocated by nand_scan ? */
+ if (this->options & NAND_DATABUF_ALLOC)
+ kfree (this->data_buf);
}
EXPORT_SYMBOL (nand_scan);
+EXPORT_SYMBOL (nand_release);
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("Steven J. Hill <sjhill at realitydiluted.com>, Thomas Gleixner <tglx at linutronix.de>");
More information about the linux-mtd-cvs
mailing list