[RFC/PATCH 2/3] NAND multiple plane feature
Alexey Korolev
akorolev at infradead.org
Wed May 28 09:08:01 EDT 2008
As NAND multiple plane architecture assumes simultaneous write/erase of
several pages/blocks at the same time, we have to modify page/eraseblock
sizes and report modified size to upper layers. In other words physical
erase block size/ page size != reported erase block size/ page size.
For example if we have dual plane device we have to extend erase block
size and page size in 2 times.
Also this patch keep track on bad block management. NAND datasheets
alows us combine only pair of neibour blocks into dual/multiple plane
operations. So if we have multiple plane device and one block is bad,
reported extended block should be considered as bad.
Signed-off-by: Vasiliy Leonenko <vasiliy.leonenko at gmail.com>
Signed-off-by: Alexey Korolev <akorolev at infradead.org>
--------
drivers/mtd/nand/diskonchip.c | 8 ++---
drivers/mtd/nand/nand_base.c | 60 ++++++++++++++++++++++++++----------------
drivers/mtd/nand/nand_bbt.c | 27 ++++++++++++++----
include/linux/mtd/nand.h | 14 ++++++---
4 files changed, 71 insertions(+), 38 deletions(-)
diff -Nurp linux-2.6.24.3/drivers/mtd/nand/diskonchip.c linux-2.6.24.3-dp/drivers/mtd/nand/diskonchip.c
--- linux-2.6.24.3/drivers/mtd/nand/diskonchip.c 2008-02-26 03:20:20.000000000 +0300
+++ linux-2.6.24.3-dp/drivers/mtd/nand/diskonchip.c 2008-05-28 14:18:43.000000000 +0400
@@ -1142,7 +1142,7 @@ static inline int __init nftl_partscan(s
mh->FirstPhysicalEUN, mh->FormattedSize,
mh->UnitSizeFactor);
- blocks = mtd->size >> this->phys_erase_shift;
+ blocks = mtd->size >> this->erase_shift;
maxblocks = min(32768U, mtd->erasesize - psize);
if (mh->UnitSizeFactor == 0x00) {
@@ -1226,7 +1226,7 @@ static inline int __init inftl_partscan(
int end = mtd->size;
if (inftl_bbt_write)
- end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift);
+ end -= (INFTL_BBT_RESERVED_BLOCKS << this->erase_shift);
buf = kmalloc(mtd->writesize, GFP_KERNEL);
if (!buf) {
@@ -1264,7 +1264,7 @@ static inline int __init inftl_partscan(
((unsigned char *) &mh->OsakVersion)[3] & 0xf,
mh->PercentUsed);
- vshift = this->phys_erase_shift + mh->BlockMultiplierBits;
+ vshift = this->erase_shift + mh->BlockMultiplierBits;
blocks = mtd->size >> vshift;
if (blocks > 32768) {
@@ -1272,7 +1272,7 @@ static inline int __init inftl_partscan(
goto out;
}
- blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift);
+ blocks = doc->chips_per_floor << (this->chip_shift - this->erase_shift);
if (inftl_bbt_write && (blocks > mtd->erasesize)) {
printk(KERN_ERR "Writeable BBTs spanning more than one erase block are not yet supported. FIX ME!\n");
goto out;
diff -Nurp linux-2.6.24.3/drivers/mtd/nand/nand_base.c linux-2.6.24.3-dp/drivers/mtd/nand/nand_base.c
--- linux-2.6.24.3/drivers/mtd/nand/nand_base.c 2008-05-28 14:04:59.000000000 +0400
+++ linux-2.6.24.3-dp/drivers/mtd/nand/nand_base.c 2008-05-28 14:43:15.000000000 +0400
@@ -554,7 +554,7 @@ static void nand_command_lp(struct mtd_i
/* Emulate NAND_CMD_READOOB */
if (command == NAND_CMD_READOOB) {
- column += mtd->writesize;
+ column += chip->page_phys_size;
command = NAND_CMD_READ0;
}
@@ -752,8 +752,8 @@ static int nand_wait(struct mtd_info *mt
static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf)
{
- chip->read_buf(mtd, buf, mtd->writesize);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->read_buf(mtd, buf, chip->page_phys_size);
+ chip->read_buf(mtd, chip->oob_poi, chip->oob_phys_size);
return 0;
}
@@ -958,7 +958,7 @@ static int nand_do_read_ops(struct mtd_i
int chipnr, page, realpage, col, bytes, aligned;
struct nand_chip *chip = mtd->priv;
struct mtd_ecc_stats stats;
- int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
+ int blkcheck = (1 << (chip->erase_shift - chip->page_shift)) - 1;
int sndcmd = 1;
int ret = 0;
uint32_t readlen = ops->len;
@@ -1267,7 +1267,7 @@ static int nand_do_read_oob(struct mtd_i
{
int page, realpage, chipnr, sndcmd = 1;
struct nand_chip *chip = mtd->priv;
- int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
+ int blkcheck = (1 << (chip->erase_shift - chip->page_shift)) - 1;
int readlen = ops->ooblen;
int len;
uint8_t *buf = ops->oobbuf;
@@ -1402,8 +1402,8 @@ static int nand_read_oob(struct mtd_info
static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf)
{
- chip->write_buf(mtd, buf, mtd->writesize);
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->write_buf(mtd, buf, chip->page_phys_size);
+ chip->write_buf(mtd, chip->oob_poi, chip->oob_phys_size);
}
/**
@@ -1655,7 +1655,7 @@ static int nand_do_write_ops(struct mtd_
realpage = (int)(to >> chip->page_shift);
page = realpage & chip->pagemask;
- blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
+ blockmask = (1 << (chip->erase_shift - chip->page_shift)) - 1;
/* Invalidate the page cache, when we write to the cached page */
if (to <= (chip->pagebuf << chip->page_shift) &&
@@ -1939,13 +1939,13 @@ int nand_erase_nand(struct mtd_info *mtd
(unsigned int)instr->addr, (unsigned int)instr->len);
/* Start address must align on block boundary */
- if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) {
+ if (instr->addr & ((1 << chip->erase_shift) - 1)) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
return -EINVAL;
}
/* Length must align on block boundary */
- if (instr->len & ((1 << chip->phys_erase_shift) - 1)) {
+ if (instr->len & ((1 << chip->erase_shift) - 1)) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_erase: "
"Length not block aligned\n");
return -EINVAL;
@@ -1968,7 +1968,7 @@ int nand_erase_nand(struct mtd_info *mtd
chipnr = (int)(instr->addr >> chip->chip_shift);
/* Calculate pages in each block */
- pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
+ pages_per_block = 1 << (chip->erase_shift - chip->page_shift);
/* Select the NAND device */
chip->select_chip(mtd, chipnr);
@@ -2045,7 +2045,7 @@ int nand_erase_nand(struct mtd_info *mtd
rewrite_bbt[chipnr] = (page << chip->page_shift);
/* Increment page address and decrement length */
- len -= (1 << chip->phys_erase_shift);
+ len -= (1 << chip->erase_shift);
page += pages_per_block;
/* Check, if we cross a chip boundary */
@@ -2269,13 +2269,13 @@ static struct nand_flash_dev *nand_get_f
/* Number of planes*/
chip->numplanes = 1 << ((planeid >> 2) & 0x03);
/* Calc pagesize */
- mtd->writesize = 1024 << (extid & 0x3);
+ mtd->writesize = (1024 << (extid & 0x3)) * chip->numplanes;
extid >>= 2;
/* Calc oobsize */
mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
extid >>= 2;
/* Calc blocksize. Blocksize is multiples of 64KiB */
- mtd->erasesize = (64 * 1024) << (extid & 0x03);
+ mtd->erasesize = ((64 * 1024) << (extid & 0x03)) * chip->numplanes;
extid >>= 2;
/* Get buswidth information */
busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
@@ -2290,6 +2290,8 @@ static struct nand_flash_dev *nand_get_f
busw = type->options & NAND_BUSWIDTH_16;
}
+ chip->oob_phys_size = mtd->oobsize / chip->numplanes;
+
/* Try to identify manufacturer */
for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
if (nand_manuf_ids[maf_idx].id == *maf_id)
@@ -2312,10 +2314,11 @@ static struct nand_flash_dev *nand_get_f
/* Calculate the address shift from the page size */
chip->page_shift = ffs(mtd->writesize) - 1;
+ chip->page_phys_size = mtd->writesize / chip->numplanes;
/* Convert chipsize to number of pages per chip -1. */
chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
- chip->bbt_erase_shift = chip->phys_erase_shift =
+ chip->bbt_erase_shift = chip->erase_shift =
ffs(mtd->erasesize) - 1;
chip->chip_shift = ffs(chip->chipsize) - 1;
@@ -2417,7 +2420,8 @@ int nand_scan_ident(struct mtd_info *mtd
*/
int nand_scan_tail(struct mtd_info *mtd)
{
- int i;
+ int i, j;
+ int oobfree_len;
struct nand_chip *chip = mtd->priv;
if (!(chip->options & NAND_OWN_BUFFERS))
@@ -2432,7 +2436,7 @@ int nand_scan_tail(struct mtd_info *mtd)
* If no default placement scheme is given, select an appropriate one
*/
if (!chip->ecc.layout) {
- switch (mtd->oobsize) {
+ switch (chip->oob_phys_size) {
case 8:
chip->ecc.layout = &nand_oob_8;
break;
@@ -2449,9 +2453,6 @@ int nand_scan_tail(struct mtd_info *mtd)
}
}
- if (!chip->write_page)
- chip->write_page = nand_write_page;
-
/*
* check ECC mode, default to software if 3byte/512byte hardware ECC is
* selected and we have 256 byte pagesize fallback to software ECC
@@ -2539,8 +2540,8 @@ int nand_scan_tail(struct mtd_info *mtd)
* Set the number of read / write steps for one page depending on ECC
* mode
*/
- chip->ecc.steps = mtd->writesize / chip->ecc.size;
- if(chip->ecc.steps * chip->ecc.size != mtd->writesize) {
+ chip->ecc.steps = chip->page_phys_size / chip->ecc.size;
+ if (chip->ecc.steps * chip->ecc.size != chip->page_phys_size) {
printk(KERN_WARNING "Invalid ecc parameters\n");
BUG();
}
@@ -2593,6 +2594,21 @@ int nand_scan_tail(struct mtd_info *mtd)
/* propagate ecc.layout to mtd_info */
mtd->ecclayout = chip->ecc.layout;
+ if (chip->numplanes > 1) {
+ for (oobfree_len = 0; chip->ecc.layout->oobfree[oobfree_len].length; oobfree_len++)
+ ;
+ i = 0;
+ while (i < chip->numplanes) {
+ for (j = 0; j < oobfree_len ; j++) {
+ chip->ecc.layout->oobfree[j + i * oobfree_len].length = chip->ecc.layout->oobfree[j].length;
+ chip->ecc.layout->oobfree[j + i * oobfree_len].offset = chip->ecc.layout->oobfree[j].offset + chip->oob_phys_size * i;
+ }
+ for (j = 0; j < chip->ecc.layout->eccbytes; j++)
+ chip->ecc.layout->eccpos[j + i * chip->ecc.layout->eccbytes] = chip->ecc.layout->eccpos[j] + chip->oob_phys_size * i;
+ i++;
+ }
+ chip->ecc.layout->eccbytes *= chip->numplanes;
+ }
/* Check, if we should skip the bad block table scan */
if (chip->options & NAND_SKIP_BBTSCAN)
diff -Nurp linux-2.6.24.3/drivers/mtd/nand/nand_bbt.c linux-2.6.24.3-dp/drivers/mtd/nand/nand_bbt.c
--- linux-2.6.24.3/drivers/mtd/nand/nand_bbt.c 2008-02-26 03:20:20.000000000 +0300
+++ linux-2.6.24.3-dp/drivers/mtd/nand/nand_bbt.c 2008-05-28 14:57:56.000000000 +0400
@@ -75,12 +75,15 @@
* pattern area contain 0xff
*
*/
-static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
+static int check_pattern(struct mtd_info *mtd, uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
{
int i, end = 0;
+ struct nand_chip *this = mtd->priv;
+ int plane_num = 0;
uint8_t *p = buf;
- end = paglen + td->offs;
+ do {
+ end = this->page_phys_size + td->offs;
if (td->options & NAND_BBT_SCANEMPTY) {
for (i = 0; i < end; i++) {
if (p[i] != 0xff)
@@ -103,6 +106,10 @@ static int check_pattern(uint8_t *buf, i
return -1;
}
}
+ p += len - end;
+ plane_num++;
+ }
+ while (plane_num < this->numplanes);
return 0;
}
@@ -116,16 +123,22 @@ static int check_pattern(uint8_t *buf, i
* no optional empty check
*
*/
-static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
+static int check_short_pattern(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
{
int i;
uint8_t *p = buf;
+ struct nand_chip *this = mtd->priv;
+ int plane_num = 0;
/* Compare the pattern */
+ do {
for (i = 0; i < td->len; i++) {
- if (p[td->offs + i] != td->pattern[i])
+ if (p[this->oob_phys_size * plane_num + td->offs + i] != td->pattern[i])
return -1;
}
+ plane_num++;
+ }
+ while (plane_num < this->numplanes);
return 0;
}
@@ -318,7 +331,7 @@ static int scan_block_full(struct mtd_in
return ret;
for (j = 0; j < len; j++, buf += scanlen) {
- if (check_pattern(buf, scanlen, mtd->writesize, bd))
+ if (check_pattern(mtd, buf, scanlen, mtd->writesize, bd))
return 1;
}
return 0;
@@ -349,7 +362,7 @@ static int scan_block_fast(struct mtd_in
if (ret)
return ret;
- if (check_short_pattern(buf, bd))
+ if (check_short_pattern(mtd, buf, bd))
return 1;
offs += mtd->writesize;
@@ -501,7 +514,7 @@ static int search_bbt(struct mtd_info *m
/* Read first page */
scan_read_raw(mtd, buf, offs, mtd->writesize);
- if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
+ if (!check_pattern(mtd, buf, scanlen, mtd->writesize, td)) {
td->pages[i] = actblock << blocktopage;
if (td->options & NAND_BBT_VERSION) {
td->version[i] = buf[mtd->writesize + td->veroffs];
diff -Nurp linux-2.6.24.3/include/linux/mtd/nand.h linux-2.6.24.3-dp/include/linux/mtd/nand.h
--- linux-2.6.24.3/include/linux/mtd/nand.h 2008-05-28 14:04:59.000000000 +0400
+++ linux-2.6.24.3-dp/include/linux/mtd/nand.h 2008-05-28 15:02:26.000000000 +0400
@@ -45,8 +45,8 @@ extern void nand_wait_ready(struct mtd_i
* is supported now. If you add a chip with bigger oobsize/page
* adjust this accordingly.
*/
-#define NAND_MAX_OOBSIZE 64
-#define NAND_MAX_PAGESIZE 2048
+#define NAND_MAX_OOBSIZE 128
+#define NAND_MAX_PAGESIZE 4096
/*
* Constants for hardware specific CLE/ALE/NCE function
@@ -332,8 +332,8 @@ struct nand_buffers {
* @wq: [INTERN] wait queue to sleep on if a NAND operation is in progress
* @state: [INTERN] the current state of the NAND device
* @oob_poi: poison value buffer
- * @page_shift: [INTERN] number of address bits in a page (column address bits)
- * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock
+ * @page_shift: [INTERN] number of address bits in a page reported to upper layer drivers
+ * @erase_shift: [INTERN] number of address bits in eraseblock reported to upper layer drivers
* @bbt_erase_shift: [INTERN] number of address bits in a bbt entry
* @chip_shift: [INTERN] number of address bits in one chip
* @datbuf: [INTERN] internal buffer for one page + oob
@@ -344,6 +344,8 @@ struct nand_buffers {
* special functionality. See the defines for further explanation
* @badblockpos: [INTERN] position of the bad block marker in the oob area
* @numplanes: [INTERN] number of ident planes on NAND device
+ * @oob_phys_size: [INTERN] physical size of NAND oob area
+ * @page_phys_size: [INTERN] physical size of NAND page
* @cellinfo: [INTERN] MLC/multichip data from chip ident
* @numchips: [INTERN] number of physical chips
* @chipsize: [INTERN] the size of one chip for multichip arrays
@@ -390,7 +392,7 @@ struct nand_chip {
unsigned int options;
int page_shift;
- int phys_erase_shift;
+ int erase_shift;
int bbt_erase_shift;
int chip_shift;
int numchips;
@@ -401,6 +403,8 @@ struct nand_chip {
uint8_t cellinfo;
int badblockpos;
int numplanes;
+ int oob_phys_size;
+ int page_phys_size;
nand_state_t state;
More information about the linux-mtd
mailing list