on-die ECC support
David Mosberger-Tang
dmosberger at gmail.com
Fri Mar 15 12:17:58 EDT 2013
Hopefully this attachment comes through...
On Fri, Mar 15, 2013 at 10:08 AM, David Mosberger-Tang
<dmosberger at gmail.com> wrote:
> We need to be able to support 4-bit-correcting ECC with a
> micro-controller that doesn't have hardware-support for that. We are
> planning to use the on-die ECC controller supported by Micron flash
> chips. I suppose we could use the BCH swecc support in the Linux
> kernel, but I'm concerned about the performance implication of that
> and we'd also have to add BCH ecc to our bootloader, which would mean
> more development and testing.
>
> For backwards-compatibility, we need to be able to support a
> subpage-size of 512 bytes. From the Micron data sheets, it's not
> clear to me whether the on-die controller really supports reading 512
> byte subpages but the attached patch certainly *seems* to work fine so
> far.
>
> I'd love to get some feedback as to whether I'm on the right track
> here or whether I should pursue a different path. The patch does the
> following:
>
> - Add NAND_ECC_HW_ON_DIE ecc mode
> - Add nand_read_subpage_raw() to read a subpage without worrying about ECC
> - Hack atmel_nand.c to use on-die ECC.
>
> Thanks,
>
> --david
-------------- next part --------------
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index 92623ac..8043fde 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -1462,7 +1462,12 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
}
nand_chip->ecc.mode = host->board.ecc_mode;
+nand_chip->ecc.mode = NAND_ECC_HW_ON_DIE; // XXX
+nand_chip->ecc.size = 512; // XXX
+nand_chip->ecc.strength = 4; // XXX
+printk("nand_chip->ecc.mode=%d\n", nand_chip->ecc.mode);
nand_chip->chip_delay = 20; /* 20us command delay time */
+ nand_chip->chip_delay = 70; // XXX t_R_ECC
if (host->board.bus_width_16) /* 16-bit bus width */
nand_chip->options |= NAND_BUSWIDTH_16;
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 1a03b7f..88ee536 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -711,6 +711,29 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
chip->cmd_ctrl(mtd, NAND_CMD_NONE,
NAND_NCE | NAND_CTRL_CHANGE);
+ if (1) {
+ u8 status;
+
+ udelay(chip->chip_delay);
+
+ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+ status = chip->read_byte(mtd);
+
+ if (status & 0x01) {
+ printk("ERROR: on-die ECC read failure\n");
+ } else if (status & 0x08) {
+ // bit flip
+ printk("ERROR: on-die ECC rewrite recommended\n"
+);
+ }
+
+ chip->cmd_ctrl(mtd, NAND_CMD_READ0,
+ NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+ chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+ NAND_NCE | NAND_CTRL_CHANGE);
+ return;
+ }
+
/* This applies to read commands */
default:
/*
@@ -1222,6 +1245,42 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
+ * nand_read_subpage_raw - [REPLACEABLE] raw sub-page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @data_offs: offset of requested data within the page
+ * @readlen: data length
+ * @bufpoi: buffer to store read data
+ */
+static int nand_read_subpage_raw(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;
+ uint8_t *p;
+ int data_col_addr;
+ int datafrag_len;
+ unsigned int max_bitflips = 0;
+
+ /* Column address within the page aligned to ECC size */
+ 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;
+
+ 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);
+
+ return max_bitflips;
+}
+
+/**
* nand_read_page_hwecc - [REPLACEABLE] hardware ECC based page read function
* @mtd: mtd info structure
* @chip: nand chip info structure
@@ -3530,6 +3589,21 @@ int nand_scan_tail(struct mtd_info *mtd)
chip->ecc.bytes * 8 / fls(8 * chip->ecc.size);
break;
+ case NAND_ECC_HW_ON_DIE:
+ chip->ecc.read_page = nand_read_page_raw;
+ chip->ecc.read_subpage = nand_read_subpage_raw;
+ chip->ecc.write_page = nand_write_page_raw;
+ chip->ecc.read_oob = nand_read_oob_std;
+ chip->ecc.read_page_raw = nand_read_page_raw;
+ chip->ecc.write_page_raw = nand_write_page_raw;
+ chip->ecc.write_oob = nand_write_oob_std;
+ chip->ecc.bytes = 0;
+/*
+ chip->ecc.size = mtd->writesize;
+ chip->ecc.strength = 0;
+*/
+ break;
+
case NAND_ECC_NONE:
pr_warn("NAND_ECC_NONE selected by board driver. "
"This is not recommended!\n");
@@ -3591,6 +3665,7 @@ int nand_scan_tail(struct mtd_info *mtd)
break;
}
}
+mtd->subpage_sft = 2; // XXX
chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
/* Initialize state */
@@ -3603,7 +3678,9 @@ int nand_scan_tail(struct mtd_info *mtd)
chip->pagebuf = -1;
/* Large page NAND with SOFT_ECC should support subpage reads */
- if ((chip->ecc.mode == NAND_ECC_SOFT) && (chip->page_shift > 9))
+ if (((chip->ecc.mode == NAND_ECC_SOFT)
+ || (chip->ecc.mode == NAND_ECC_HW_ON_DIE))
+ && (chip->page_shift > 9))
chip->options |= NAND_SUBPAGE_READ;
/* Fill in remaining MTD driver data */
diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c
index a27ec94..79ea026 100644
--- a/drivers/of/of_mtd.c
+++ b/drivers/of/of_mtd.c
@@ -23,6 +23,7 @@ static const char *nand_ecc_modes[] = {
[NAND_ECC_HW_SYNDROME] = "hw_syndrome",
[NAND_ECC_HW_OOB_FIRST] = "hw_oob_first",
[NAND_ECC_SOFT_BCH] = "soft_bch",
+ [NAND_ECC_HW_ON_DIE] = "hw_on_die",
};
/**
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 24e9159..2c62b2a 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -143,6 +143,7 @@ typedef enum {
NAND_ECC_HW_SYNDROME,
NAND_ECC_HW_OOB_FIRST,
NAND_ECC_SOFT_BCH,
+ NAND_ECC_HW_ON_DIE,
} nand_ecc_modes_t;
/*
More information about the linux-mtd
mailing list