|
@@ -7,7 +7,7 @@
|
|
|
* Basic support for AG-AND chips is provided.
|
|
|
*
|
|
|
* Additional technical information is available on
|
|
|
- * http://www.linux-mtd.infradead.org/tech/nand.html
|
|
|
+ * http://www.linux-mtd.infradead.org/doc/nand.html
|
|
|
*
|
|
|
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
|
|
|
* 2002-2006 Thomas Gleixner (tglx@linutronix.de)
|
|
@@ -24,6 +24,7 @@
|
|
|
* if we have HW ecc support.
|
|
|
* The AG-AND chips have nice features for speed improvement,
|
|
|
* which are not supported yet. Read / program 4 pages in one go.
|
|
|
+ * BBT table is not serialized, has to be fixed
|
|
|
*
|
|
|
* 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
|
|
@@ -128,7 +129,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
|
|
|
static int nand_wait(struct mtd_info *mtd, struct nand_chip *this);
|
|
|
|
|
|
/*
|
|
|
- * For devices which display every fart in the system on a seperate LED. Is
|
|
|
+ * For devices which display every fart in the system on a separate LED. Is
|
|
|
* compiled away when LED support is disabled.
|
|
|
*/
|
|
|
/* XXX U-BOOT XXX */
|
|
@@ -412,6 +413,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
|
|
/* We write two bytes, so we dont have to mess with 16 bit
|
|
|
* access
|
|
|
*/
|
|
|
+ nand_get_device(chip, mtd, FL_WRITING);
|
|
|
ofs += mtd->oobsize;
|
|
|
chip->ops.len = chip->ops.ooblen = 2;
|
|
|
chip->ops.datbuf = NULL;
|
|
@@ -419,9 +421,11 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
|
|
chip->ops.ooboffs = chip->badblockpos & ~0x01;
|
|
|
|
|
|
ret = nand_do_write_oob(mtd, ofs, &chip->ops);
|
|
|
+ nand_release_device(mtd);
|
|
|
}
|
|
|
if (!ret)
|
|
|
mtd->ecc_stats.badblocks++;
|
|
|
+
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -911,7 +915,88 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
int stat;
|
|
|
|
|
|
stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
|
|
|
- if (stat == -1)
|
|
|
+ if (stat < 0)
|
|
|
+ mtd->ecc_stats.failed++;
|
|
|
+ else
|
|
|
+ mtd->ecc_stats.corrected += stat;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function
|
|
|
+ * @mtd: mtd info structure
|
|
|
+ * @chip: nand chip info structure
|
|
|
+ * @dataofs offset of requested data within the page
|
|
|
+ * @readlen data length
|
|
|
+ * @buf: buffer to store read data
|
|
|
+ */
|
|
|
+static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
|
|
|
+{
|
|
|
+ int start_step, end_step, num_steps;
|
|
|
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
|
|
|
+ uint8_t *p;
|
|
|
+ int data_col_addr, i, gaps = 0;
|
|
|
+ int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
|
|
|
+ int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
|
|
|
+
|
|
|
+ /* Column address wihin the page aligned to ECC size (256bytes). */
|
|
|
+ start_step = data_offs / chip->ecc.size;
|
|
|
+ end_step = (data_offs + readlen - 1) / chip->ecc.size;
|
|
|
+ num_steps = end_step - start_step + 1;
|
|
|
+
|
|
|
+ /* Data size aligned to ECC ecc.size*/
|
|
|
+ datafrag_len = num_steps * chip->ecc.size;
|
|
|
+ eccfrag_len = num_steps * chip->ecc.bytes;
|
|
|
+
|
|
|
+ data_col_addr = start_step * chip->ecc.size;
|
|
|
+ /* If we read not a page aligned data */
|
|
|
+ if (data_col_addr != 0)
|
|
|
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1);
|
|
|
+
|
|
|
+ p = bufpoi + data_col_addr;
|
|
|
+ chip->read_buf(mtd, p, datafrag_len);
|
|
|
+
|
|
|
+ /* Calculate ECC */
|
|
|
+ for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)
|
|
|
+ chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]);
|
|
|
+
|
|
|
+ /* The performance is faster if to position offsets
|
|
|
+ according to ecc.pos. Let make sure here that
|
|
|
+ there are no gaps in ecc positions */
|
|
|
+ for (i = 0; i < eccfrag_len - 1; i++) {
|
|
|
+ if (eccpos[i + start_step * chip->ecc.bytes] + 1 !=
|
|
|
+ eccpos[i + start_step * chip->ecc.bytes + 1]) {
|
|
|
+ gaps = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (gaps) {
|
|
|
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
|
|
|
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
|
|
|
+ } else {
|
|
|
+ /* send the command to read the particular ecc bytes */
|
|
|
+ /* take care about buswidth alignment in read_buf */
|
|
|
+ aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1);
|
|
|
+ aligned_len = eccfrag_len;
|
|
|
+ if (eccpos[start_step * chip->ecc.bytes] & (busw - 1))
|
|
|
+ aligned_len++;
|
|
|
+ if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1))
|
|
|
+ aligned_len++;
|
|
|
+
|
|
|
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1);
|
|
|
+ chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < eccfrag_len; i++)
|
|
|
+ chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]];
|
|
|
+
|
|
|
+ p = bufpoi + data_col_addr;
|
|
|
+ for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
|
|
|
+ int stat;
|
|
|
+
|
|
|
+ stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
|
|
|
+ if (stat < 0)
|
|
|
mtd->ecc_stats.failed++;
|
|
|
else
|
|
|
mtd->ecc_stats.corrected += stat;
|
|
@@ -996,7 +1081,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
|
|
|
chip->read_buf(mtd, oob, eccbytes);
|
|
|
stat = chip->ecc.correct(mtd, p, oob, NULL);
|
|
|
|
|
|
- if (stat == -1)
|
|
|
+ if (stat < 0)
|
|
|
mtd->ecc_stats.failed++;
|
|
|
else
|
|
|
mtd->ecc_stats.corrected += stat;
|
|
@@ -1116,6 +1201,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
|
|
/* Now read the page into the buffer */
|
|
|
if (unlikely(ops->mode == MTD_OOB_RAW))
|
|
|
ret = chip->ecc.read_page_raw(mtd, chip, bufpoi);
|
|
|
+ else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
|
|
|
+ ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi);
|
|
|
else
|
|
|
ret = chip->ecc.read_page(mtd, chip, bufpoi);
|
|
|
if (ret < 0)
|
|
@@ -1123,7 +1210,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
|
|
|
|
|
|
/* Transfer not aligned data */
|
|
|
if (!aligned) {
|
|
|
- chip->pagebuf = realpage;
|
|
|
+ if (!NAND_SUBPAGE_READ(chip) && !oob)
|
|
|
+ chip->pagebuf = realpage;
|
|
|
memcpy(buf, chip->buffers->databuf + col, bytes);
|
|
|
}
|
|
|
|
|
@@ -2193,13 +2281,14 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
|
|
erase_exit:
|
|
|
|
|
|
ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
|
|
|
- /* Do call back function */
|
|
|
- if (!ret)
|
|
|
- mtd_erase_callback(instr);
|
|
|
|
|
|
/* Deselect and wake up anyone waiting on the device */
|
|
|
nand_release_device(mtd);
|
|
|
|
|
|
+ /* Do call back function */
|
|
|
+ if (!ret)
|
|
|
+ mtd_erase_callback(instr);
|
|
|
+
|
|
|
/*
|
|
|
* If BBT requires refresh and erase was successful, rewrite any
|
|
|
* selected bad block tables
|
|
@@ -2356,6 +2445,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
|
|
{
|
|
|
struct nand_flash_dev *type = NULL;
|
|
|
int i, dev_id, maf_idx;
|
|
|
+ int tmp_id, tmp_manf;
|
|
|
|
|
|
/* Select the device */
|
|
|
chip->select_chip(mtd, 0);
|
|
@@ -2367,6 +2457,26 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
|
|
|
*maf_id = chip->read_byte(mtd);
|
|
|
dev_id = chip->read_byte(mtd);
|
|
|
|
|
|
+ /* Try again to make sure, as some systems the bus-hold or other
|
|
|
+ * interface concerns can cause random data which looks like a
|
|
|
+ * possibly credible NAND flash to appear. If the two results do
|
|
|
+ * not match, ignore the device completely.
|
|
|
+ */
|
|
|
+
|
|
|
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
|
|
|
+
|
|
|
+ /* Read manufacturer and device IDs */
|
|
|
+
|
|
|
+ tmp_manf = chip->read_byte(mtd);
|
|
|
+ tmp_id = chip->read_byte(mtd);
|
|
|
+
|
|
|
+ if (tmp_manf != *maf_id || tmp_id != dev_id) {
|
|
|
+ printk(KERN_INFO "%s: second ID read did not match "
|
|
|
+ "%02x,%02x against %02x,%02x\n", __func__,
|
|
|
+ *maf_id, dev_id, tmp_manf, tmp_id);
|
|
|
+ return ERR_PTR(-ENODEV);
|
|
|
+ }
|
|
|
+
|
|
|
/* Lookup the flash id */
|
|
|
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
|
|
|
if (dev_id == nand_flash_ids[i].id) {
|
|
@@ -2630,6 +2740,7 @@ int nand_scan_tail(struct mtd_info *mtd)
|
|
|
chip->ecc.calculate = nand_calculate_ecc;
|
|
|
chip->ecc.correct = nand_correct_data;
|
|
|
chip->ecc.read_page = nand_read_page_swecc;
|
|
|
+ chip->ecc.read_subpage = nand_read_subpage;
|
|
|
chip->ecc.write_page = nand_write_page_swecc;
|
|
|
chip->ecc.read_oob = nand_read_oob_std;
|
|
|
chip->ecc.write_oob = nand_write_oob_std;
|