Commit c35e6da4 authored by David Woodhouse's avatar David Woodhouse

MTD: NAND driver updates

 - Support 2048-byte HW ECC (from Juha Yrjölä <juha.yrjola@nokia.com>)
 - Allow board drivers to provide pattern for bad block scanning
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarDavid Woodhouse <dwmw2@infradead.org>
parent 4d2a6a76
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
* The AG-AND chips have nice features for speed improvement, * The AG-AND chips have nice features for speed improvement,
* which are not supported yet. Read / program 4 pages in one go. * which are not supported yet. Read / program 4 pages in one go.
* *
* $Id: nand_base.c,v 1.121 2004/10/06 19:53:11 gleixner Exp $ * $Id: nand_base.c,v 1.123 2004/11/02 22:36:59 gleixner Exp $
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
...@@ -840,18 +840,8 @@ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int pa ...@@ -840,18 +840,8 @@ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int pa
} }
this->write_buf(mtd, this->data_poi, mtd->oobblock); this->write_buf(mtd, this->data_poi, mtd->oobblock);
break; break;
default:
/* Hardware ecc 8 byte / 512 byte data */ eccbytes = this->eccbytes;
case NAND_ECC_HW8_512:
eccbytes += 2;
/* Hardware ecc 6 byte / 512 byte data */
case NAND_ECC_HW6_512:
eccbytes += 3;
/* Hardware ecc 3 byte / 256 data */
/* Hardware ecc 3 byte / 512 byte data */
case NAND_ECC_HW3_256:
case NAND_ECC_HW3_512:
eccbytes += 3;
for (; eccsteps; eccsteps--) { for (; eccsteps; eccsteps--) {
/* enable hardware ecc logic for write */ /* enable hardware ecc logic for write */
this->enable_hwecc(mtd, NAND_ECC_WRITE); this->enable_hwecc(mtd, NAND_ECC_WRITE);
...@@ -864,14 +854,9 @@ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int pa ...@@ -864,14 +854,9 @@ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int pa
* the data bytes (words) */ * the data bytes (words) */
if (this->options & NAND_HWECC_SYNDROME) if (this->options & NAND_HWECC_SYNDROME)
this->write_buf(mtd, ecc_code, eccbytes); this->write_buf(mtd, ecc_code, eccbytes);
datidx += this->eccsize; datidx += this->eccsize;
} }
break; break;
default:
printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
BUG();
} }
/* Write out OOB data */ /* Write out OOB data */
...@@ -1051,7 +1036,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, ...@@ -1051,7 +1036,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
int eccmode, eccsteps; int eccmode, eccsteps;
int *oob_config, datidx; int *oob_config, datidx;
int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1; int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
int eccbytes = 3; int eccbytes;
int compareecc = 1; int compareecc = 1;
int oobreadlen; int oobreadlen;
...@@ -1092,19 +1077,9 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, ...@@ -1092,19 +1077,9 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
end = mtd->oobblock; end = mtd->oobblock;
ecc = this->eccsize; ecc = this->eccsize;
switch (eccmode) { eccbytes = this->eccbytes;
case NAND_ECC_HW6_512: /* Hardware ECC 6 byte / 512 byte data */
eccbytes = 6; if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME))
break;
case NAND_ECC_HW8_512: /* Hardware ECC 8 byte / 512 byte data */
eccbytes = 8;
break;
case NAND_ECC_NONE:
compareecc = 0;
break;
}
if (this->options & NAND_HWECC_SYNDROME)
compareecc = 0; compareecc = 0;
oobreadlen = mtd->oobsize; oobreadlen = mtd->oobsize;
...@@ -1164,13 +1139,10 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, ...@@ -1164,13 +1139,10 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc)
this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]); this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
break; break;
case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data */ default:
case NAND_ECC_HW3_512: /* Hardware ECC 3 byte /512 byte data */
case NAND_ECC_HW6_512: /* Hardware ECC 6 byte / 512 byte data */
case NAND_ECC_HW8_512: /* Hardware ECC 8 byte / 512 byte data */
for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) { for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) {
this->enable_hwecc(mtd, NAND_ECC_READ); this->enable_hwecc(mtd, NAND_ECC_READ);
this->read_buf(mtd, &data_poi[datidx], ecc); this->read_buf(mtd, &data_poi[datidx], ecc);
/* HW ecc with syndrome calculation must read the /* HW ecc with syndrome calculation must read the
...@@ -1193,10 +1165,6 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, ...@@ -1193,10 +1165,6 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
} }
} }
break; break;
default:
printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
BUG();
} }
/* read oobdata */ /* read oobdata */
...@@ -2433,8 +2401,19 @@ int nand_scan (struct mtd_info *mtd, int maxchips) ...@@ -2433,8 +2401,19 @@ int nand_scan (struct mtd_info *mtd, int maxchips)
* fallback to software ECC * fallback to software ECC
*/ */
this->eccsize = 256; /* set default eccsize */ this->eccsize = 256; /* set default eccsize */
this->eccbytes = 3;
switch (this->eccmode) { switch (this->eccmode) {
case NAND_ECC_HW12_2048:
if (mtd->oobblock < 2048) {
printk(KERN_WARNING "2048 byte HW ECC not possible on %d byte page size, fallback to SW ECC\n",
mtd->oobblock);
this->eccmode = NAND_ECC_SOFT;
this->calculate_ecc = nand_calculate_ecc;
this->correct_data = nand_correct_data;
} else
this->eccsize = 2048;
break;
case NAND_ECC_HW3_512: case NAND_ECC_HW3_512:
case NAND_ECC_HW6_512: case NAND_ECC_HW6_512:
...@@ -2444,16 +2423,13 @@ int nand_scan (struct mtd_info *mtd, int maxchips) ...@@ -2444,16 +2423,13 @@ int nand_scan (struct mtd_info *mtd, int maxchips)
this->eccmode = NAND_ECC_SOFT; this->eccmode = NAND_ECC_SOFT;
this->calculate_ecc = nand_calculate_ecc; this->calculate_ecc = nand_calculate_ecc;
this->correct_data = nand_correct_data; this->correct_data = nand_correct_data;
break;
} else } else
this->eccsize = 512; /* set eccsize to 512 and fall through for function check */ this->eccsize = 512; /* set eccsize to 512 */
break;
case NAND_ECC_HW3_256: case NAND_ECC_HW3_256:
if (this->calculate_ecc && this->correct_data && this->enable_hwecc) break;
break;
printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n");
BUG();
case NAND_ECC_NONE: case NAND_ECC_NONE:
printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n"); printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");
this->eccmode = NAND_ECC_NONE; this->eccmode = NAND_ECC_NONE;
...@@ -2468,11 +2444,32 @@ int nand_scan (struct mtd_info *mtd, int maxchips) ...@@ -2468,11 +2444,32 @@ int nand_scan (struct mtd_info *mtd, int maxchips)
printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
BUG(); BUG();
} }
/* Check hardware ecc function availability and adjust number of ecc bytes per
* calculation step
*/
switch (this->eccmode) {
case NAND_ECC_HW12_2048:
this->eccbytes += 4;
case NAND_ECC_HW8_512:
this->eccbytes += 2;
case NAND_ECC_HW6_512:
this->eccbytes += 3;
case NAND_ECC_HW3_512:
case NAND_ECC_HW3_256:
if (this->calculate_ecc && this->correct_data && this->enable_hwecc)
break;
printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n");
BUG();
}
mtd->eccsize = this->eccsize; mtd->eccsize = this->eccsize;
/* Set the number of read / write steps for one page to ensure ECC generation */ /* Set the number of read / write steps for one page to ensure ECC generation */
switch (this->eccmode) { switch (this->eccmode) {
case NAND_ECC_HW12_2048:
this->eccsteps = mtd->oobblock / 2048;
break;
case NAND_ECC_HW3_512: case NAND_ECC_HW3_512:
case NAND_ECC_HW6_512: case NAND_ECC_HW6_512:
case NAND_ECC_HW8_512: case NAND_ECC_HW8_512:
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* *
* Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
* *
* $Id: nand_bbt.c,v 1.26 2004/10/05 13:50:20 gleixner Exp $ * $Id: nand_bbt.c,v 1.28 2004/11/13 10:19:09 gleixner Exp $
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
...@@ -1001,25 +1001,27 @@ int nand_default_bbt (struct mtd_info *mtd) ...@@ -1001,25 +1001,27 @@ int nand_default_bbt (struct mtd_info *mtd)
return nand_scan_bbt (mtd, &agand_flashbased); return nand_scan_bbt (mtd, &agand_flashbased);
} }
/* Is a flash based bad block table requested ? */ /* Is a flash based bad block table requested ? */
if (this->options & NAND_USE_FLASH_BBT) { if (this->options & NAND_USE_FLASH_BBT) {
/* Use the default pattern descriptors */ /* Use the default pattern descriptors */
if (!this->bbt_td) { if (!this->bbt_td) {
this->bbt_td = &bbt_main_descr; this->bbt_td = &bbt_main_descr;
this->bbt_md = &bbt_mirror_descr; this->bbt_md = &bbt_mirror_descr;
} }
if (mtd->oobblock > 512) if (!this->badblock_pattern) {
return nand_scan_bbt (mtd, &largepage_flashbased); this->badblock_pattern = (mtd->oobblock > 512) ?
else &largepage_flashbased : &smallpage_flashbased;
return nand_scan_bbt (mtd, &smallpage_flashbased); }
} else { } else {
this->bbt_td = NULL; this->bbt_td = NULL;
this->bbt_md = NULL; this->bbt_md = NULL;
if (mtd->oobblock > 512) if (!this->badblock_pattern) {
return nand_scan_bbt (mtd, &largepage_memorybased); this->badblock_pattern = (mtd->oobblock > 512) ?
else &largepage_memorybased : &smallpage_memorybased;
return nand_scan_bbt (mtd, &smallpage_memorybased); }
} }
return nand_scan_bbt (mtd, this->badblock_pattern);
} }
/** /**
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* Steven J. Hill <sjhill@realitydiluted.com> * Steven J. Hill <sjhill@realitydiluted.com>
* Thomas Gleixner <tglx@linutronix.de> * Thomas Gleixner <tglx@linutronix.de>
* *
* $Id: nand.h,v 1.66 2004/10/02 10:07:08 gleixner Exp $ * $Id: nand.h,v 1.68 2004/11/12 10:40:37 gleixner Exp $
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
...@@ -138,6 +138,8 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_ ...@@ -138,6 +138,8 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_
#define NAND_ECC_HW6_512 4 #define NAND_ECC_HW6_512 4
/* Hardware ECC 8 byte ECC per 512 Byte data */ /* Hardware ECC 8 byte ECC per 512 Byte data */
#define NAND_ECC_HW8_512 6 #define NAND_ECC_HW8_512 6
/* Hardware ECC 12 byte ECC per 2048 Byte data */
#define NAND_ECC_HW12_2048 7
/* /*
* Constants for Hardware ECC * Constants for Hardware ECC
...@@ -253,6 +255,7 @@ struct nand_hw_control { ...@@ -253,6 +255,7 @@ struct nand_hw_control {
* @scan_bbt: [REPLACEABLE] function to scan bad block table * @scan_bbt: [REPLACEABLE] function to scan bad block table
* @eccmode: [BOARDSPECIFIC] mode of ecc, see defines * @eccmode: [BOARDSPECIFIC] mode of ecc, see defines
* @eccsize: [INTERN] databytes used per ecc-calculation * @eccsize: [INTERN] databytes used per ecc-calculation
* @eccbytes: [INTERN] number of ecc bytes per ecc-calculation step
* @eccsteps: [INTERN] number of ecc calculation steps per page * @eccsteps: [INTERN] number of ecc calculation steps per page
* @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR) * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)
* @chip_lock: [INTERN] spinlock used to protect access to this structure and the chip * @chip_lock: [INTERN] spinlock used to protect access to this structure and the chip
...@@ -277,6 +280,7 @@ struct nand_hw_control { ...@@ -277,6 +280,7 @@ struct nand_hw_control {
* @bbt: [INTERN] bad block table pointer * @bbt: [INTERN] bad block table pointer
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup * @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup
* @bbt_md: [REPLACEABLE] bad block table mirror descriptor * @bbt_md: [REPLACEABLE] bad block table mirror descriptor
* @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan
* @controller: [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices * @controller: [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices
* @priv: [OPTIONAL] pointer to private chip date * @priv: [OPTIONAL] pointer to private chip date
*/ */
...@@ -307,6 +311,7 @@ struct nand_chip { ...@@ -307,6 +311,7 @@ struct nand_chip {
int (*scan_bbt)(struct mtd_info *mtd); int (*scan_bbt)(struct mtd_info *mtd);
int eccmode; int eccmode;
int eccsize; int eccsize;
int eccbytes;
int eccsteps; int eccsteps;
int chip_delay; int chip_delay;
spinlock_t chip_lock; spinlock_t chip_lock;
...@@ -330,6 +335,7 @@ struct nand_chip { ...@@ -330,6 +335,7 @@ struct nand_chip {
uint8_t *bbt; uint8_t *bbt;
struct nand_bbt_descr *bbt_td; struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md; struct nand_bbt_descr *bbt_md;
struct nand_bbt_descr *badblock_pattern;
struct nand_hw_control *controller; struct nand_hw_control *controller;
void *priv; void *priv;
}; };
......
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