mtd/drivers/mtd/nand nand.c,1.68,1.69
gleixner at infradead.org
gleixner at infradead.org
Sun Mar 28 19:39:33 EST 2004
Update of /home/cvs/mtd/drivers/mtd/nand
In directory phoenix.infradead.org:/tmp/cvs-serv7624
Modified Files:
nand.c
Log Message:
Fix multi chip support
Index: nand.c
===================================================================
RCS file: /home/cvs/mtd/drivers/mtd/nand/nand.c,v
retrieving revision 1.68
retrieving revision 1.69
diff -u -r1.68 -r1.69
--- nand.c 28 Mar 2004 23:34:07 -0000 1.68
+++ nand.c 29 Mar 2004 00:39:31 -0000 1.69
@@ -181,7 +181,7 @@
unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
static void nand_sync (struct mtd_info *mtd);
-static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel);
+static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr);
static u_char nand_read_byte(struct mtd_info *mtd)
{
@@ -379,7 +379,7 @@
this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
/* One more address cycle for higher density devices */
- if (mtd->size & 0x0c000000)
+ if (this->chipsize & 0x0c000000)
this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
}
/* Latch in address */
@@ -463,7 +463,7 @@
this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
/* One more address cycle for devices > 128MiB */
- if (mtd->size > (128 << 20))
+ if (this->chipsize > (128 << 20))
this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0xff));
}
/* Latch in address */
@@ -541,17 +541,7 @@
spin_unlock_bh (&this->chip_lock);
return;
}
-#if 0 /* This was broken. And of dubious utility */
- if (this->state == FL_ERASING) {
- if (new_state != FL_ERASING) {
- this->state = new_state;
- spin_unlock_bh (&this->chip_lock);
- this->select_chip(mtd, 0); /* select in any case */
- this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
- return;
- }
- }
-#endif
+
set_current_state (TASK_UNINTERRUPTIBLE);
add_wait_queue (&this->wq, &wait);
spin_unlock_bh (&this->chip_lock);
@@ -608,7 +598,7 @@
* This function will always program a full page of data
* If you call it with a non page aligned buffer, you're lost :)
*/
-static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel)
+static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr)
{
int i, status;
u_char ecc_code[6], *oob_data;
@@ -759,7 +749,7 @@
* This is not a problem for all chips, but I have found a bunch of them.
*/
this->select_chip(mtd, -1);
- this->select_chip(mtd, 0);
+ this->select_chip(mtd, chipnr);
#endif
return 0;
}
@@ -779,7 +769,7 @@
static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
{
- int j, col, page, end, ecc;
+ int j, col, page, end, ecc, chipnr;
int erase_state = 0;
int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;
struct nand_chip *this = mtd->priv;
@@ -810,7 +800,8 @@
nand_get_chip (this, mtd ,FL_READING, &erase_state);
/* Select the NAND device */
- this->select_chip(mtd, 0);
+ chipnr = (int)((unsigned long)from / this->chipsize);
+ this->select_chip(mtd, chipnr);
/* First we calculate the starting page */
page = from >> this->page_shift;
@@ -822,7 +813,7 @@
ecc = mtd->eccsize;
/* Send the read command */
- this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
+ this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page & this->pagemask);
/* Loop until all data read */
while (read < len) {
@@ -941,11 +932,19 @@
udelay (this->chip_delay);
else
while (!this->dev_ready(mtd));
+
+ /* Check, if we cross a chip boundary */
+ if (!(page & this->pagemask)) {
+ chipnr++;
+ this->select_chip(mtd, -1);
+ this->select_chip(mtd, chipnr);
+ }
+
/* Check, if the chip supports auto page increment
* or if we have hit a block boundary.
*/
- if (unlikely (!NAND_CANAUTOINCR(this) || !(page & blockcheck)))
- this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
+ if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
+ this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page & this->pagemask);
}
}
@@ -972,7 +971,7 @@
*/
static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
{
- int i, col, page;
+ int i, col, page, chipnr;
int erase_state = 0;
struct nand_chip *this = mtd->priv;
int blockcheck = (mtd->erasesize >> this->page_shift) - 1;
@@ -981,7 +980,8 @@
/* Shift to get page */
page = ((int) from) >> this->page_shift;
-
+ chipnr = (int)((unsigned long)from / this->chipsize);
+
/* Mask to get column */
col = from & (mtd->oobsize - 1);
@@ -999,10 +999,10 @@
nand_get_chip (this, mtd , FL_READING, &erase_state);
/* Select the NAND device */
- this->select_chip(mtd, 0);
+ this->select_chip(mtd, chipnr);
/* Send the read command */
- this->cmdfunc (mtd, NAND_CMD_READOOB, col, page);
+ this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->pagemask);
/*
* Read the data, if we read more than one page
* oob data, let the device transfer the data !
@@ -1028,13 +1028,21 @@
udelay (this->chip_delay);
else
while (!this->dev_ready(mtd));
+
+ /* Check, if we cross a chip boundary */
+ if (!(page & this->pagemask)) {
+ chipnr++;
+ this->select_chip(mtd, -1);
+ this->select_chip(mtd, chipnr);
+ }
+
/* Check, if the chip supports auto page increment
* or if we have hit a block boundary.
*/
- if (unlikely (!NAND_CANAUTOINCR(this) || !(page & blockcheck))) {
+ if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) {
/* For subsequent page reads set offset to 0 */
this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page);
- }
+ }
}
}
/* De-select the NAND device */
@@ -1066,7 +1074,7 @@
static int nand_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 *oobsel)
{
- int page, ret = 0, oob = 0, written = 0;
+ int page, ret = 0, oob = 0, written = 0, chipnr;
struct nand_chip *this = mtd->priv;
DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
@@ -1089,12 +1097,13 @@
/* Shift to get page */
page = ((int) to) >> this->page_shift;
+ chipnr = (int)((unsigned long)to / this->chipsize);
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd, FL_WRITING, NULL);
/* Select the NAND device */
- this->select_chip(mtd, 0);
+ this->select_chip(mtd, chipnr);
/* Check the WP bit */
this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
@@ -1110,10 +1119,10 @@
this->data_poi = (u_char*) &buf[written];
/* We use the same function for write and writev */
if (eccbuf) {
- ret = nand_write_page (mtd, this, page, &eccbuf[oob], oobsel);
+ ret = nand_write_page (mtd, this, page & this->pagemask, &eccbuf[oob], oobsel, chipnr);
oob += mtd->oobsize;
} else
- ret = nand_write_page (mtd, this, page, NULL, oobsel);
+ ret = nand_write_page (mtd, this, page & this->pagemask, NULL, oobsel, chipnr);
if (ret)
goto out;
@@ -1122,6 +1131,13 @@
written += cnt;
/* Increment page address */
page++;
+
+ /* Check, if we cross a chip boundary */
+ if (!(page & this->pagemask)) {
+ chipnr++;
+ this->select_chip(mtd, -1);
+ this->select_chip(mtd, chipnr);
+ }
}
out:
@@ -1154,7 +1170,7 @@
*/
static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
{
- int column, page, status, ret = 0;
+ int column, page, status, ret = 0, chipnr;
struct nand_chip *this = mtd->priv;
#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
int i;
@@ -1164,6 +1180,7 @@
/* Shift to get page */
page = ((int) to) >> this->page_shift;
+ chipnr = (int)((unsigned long)to / this->chipsize);
/* Mask to get column */
column = to & (mtd->oobsize - 1);
@@ -1181,7 +1198,7 @@
nand_get_chip (this, mtd, FL_WRITING, NULL);
/* Select the NAND device */
- this->select_chip(mtd, 0);
+ this->select_chip(mtd, chipnr);
/* Reset the chip. Some chips (like the Toshiba TC5832DC found
in one of my DiskOnChip 2000 test units) will clear the whole
@@ -1200,7 +1217,7 @@
if (NAND_MUST_PAD(this)) {
/* Write out desired data */
- this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page);
+ this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page & this->pagemask);
/* prepad 0xff for partial programming */
this->write_buf(mtd, ffchars, column);
/* write data */
@@ -1209,7 +1226,7 @@
this->write_buf(mtd, ffchars, mtd->oobsize - (len+column));
} else {
/* Write out desired data */
- this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page);
+ this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page & this->pagemask);
/* write data */
this->write_buf(mtd, buf, len);
}
@@ -1229,7 +1246,7 @@
#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
/* Send command to read back the data */
- this->cmdfunc (mtd, NAND_CMD_READOOB, column, page);
+ this->cmdfunc (mtd, NAND_CMD_READOOB, column, page & this->pagemask);
/* Loop through and verify the data */
for (i = 0; i < len; i++) {
@@ -1267,7 +1284,7 @@
static int nand_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)
{
- int i, page, len, total_len, ret = 0, written = 0;
+ int i, page, len, total_len, ret = 0, written = 0, chipnr;
struct nand_chip *this = mtd->priv;
/* Calculate total length of data */
@@ -1296,12 +1313,13 @@
/* Shift to get page */
page = ((int) to) >> this->page_shift;
+ chipnr = (int)((unsigned long)to / this->chipsize);
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd, FL_WRITING, NULL);
/* Select the NAND device */
- this->select_chip(mtd, 0);
+ this->select_chip(mtd, chipnr);
/* Check the WP bit */
this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
@@ -1350,7 +1368,7 @@
}
/* We use the same function for write and writev !) */
- ret = nand_write_page (mtd, this, page, NULL, oobsel);
+ ret = nand_write_page (mtd, this, page & this->pagemask, NULL, oobsel, chipnr);
if (ret)
goto out;
@@ -1359,6 +1377,13 @@
/* Increment page address */
page++;
+
+ /* Check, if we cross a chip boundary */
+ if (!(page & this->pagemask)) {
+ chipnr++;
+ this->select_chip(mtd, -1);
+ this->select_chip(mtd, chipnr);
+ }
}
out:
@@ -1380,7 +1405,7 @@
*/
static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
{
- int page, len, status, pages_per_block, ret;
+ int page, len, status, pages_per_block, ret, chipnr;
struct nand_chip *this = mtd->priv;
DECLARE_WAITQUEUE (wait, current);
@@ -1410,12 +1435,13 @@
/* Shift to get first page */
page = (int) (instr->addr >> this->page_shift);
+ chipnr = (int)((unsigned long)instr->addr / this->chipsize);
/* Calculate pages in each block */
pages_per_block = mtd->erasesize / mtd->oobblock;
/* Select the NAND device */
- this->select_chip(mtd, 0);
+ this->select_chip(mtd, chipnr);
/* Check the WP bit */
this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
@@ -1459,6 +1485,12 @@
/* Increment page address and decrement length */
len -= mtd->erasesize;
page += pages_per_block;
+ /* Check, if we cross a chip boundary */
+ if (!(page & this->pagemask)) {
+ chipnr++;
+ this->select_chip(mtd, -1);
+ this->select_chip(mtd, chipnr);
+ }
}
/* Release the spin lock */
spin_unlock_bh (&this->chip_lock);
@@ -1468,7 +1500,7 @@
if (this->state == FL_ERASING || this->state == FL_READY) {
/* Select the NAND device again, if we were interrupted */
this->state = FL_ERASING;
- this->select_chip(mtd, 0);
+ this->select_chip(mtd, chipnr);
continue;
} else {
set_current_state (TASK_UNINTERRUPTIBLE);
@@ -1605,7 +1637,7 @@
continue;
mtd->name = nand_flash_ids[i].name;
- mtd->size = nand_flash_ids[i].chipsize << 20;
+ this->chipsize = nand_flash_ids[i].chipsize << 20;
/* New devices have all the information in additional id bytes */
if (!nand_flash_ids[i].pagesize) {
@@ -1669,8 +1701,6 @@
printk (KERN_INFO "NAND device: Manufacturer ID:"
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
nand_manuf_ids[i].name , mtd->name);
-
-
break;
}
@@ -1692,7 +1722,13 @@
}
if (i > 1)
printk(KERN_INFO "%d NAND chips detected\n", i);
-
+
+ /* Store the number of chips and calc total size for mtd */
+ this->numchips = i;
+ mtd->size = i * this->chipsize;
+ /* Convert chipsize to number of pages per chip -1. */
+ this->pagemask = (this->chipsize >> this->page_shift) - 1;
+
/*
* check ECC mode, default to software
* if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize
More information about the linux-mtd-cvs
mailing list