From 31fb1df8757177e47e3368f6e735623748449ef4 Mon Sep 17 00:00:00 2001 From: Angus CLARK Date: Wed, 8 Jun 2011 17:31:24 +0100 Subject: [PATCH (sh-2.6.32.y)] mtd_nand: Support for Micron on-die 4-bit ECC SLC LP NAND devices This patch adds support for the Micron on-die 4-bit ECC SLC LP family of devices. The main changes are: * Add support for the SET/GET FEATURES NAND CMD (required to enable/disable on-die ECC). * BBT signature moved so as not to clash with the on-die ECC layout * Check STATUS after READ operations for correctable/non-correctable ECC errors * Add a new ECC layout for on-die 4-bit ECC devices. Note, the use of on-die ECC brings a number of limitations to the way in which the OOB area can be used. In particular, some bytes in OOB are ECC-protected, and some are not. The ECC-protected bytes must be written at the same time as the page data. This breaks a number of assumptions made by existing MTD clients. As a result, it is not possible to define a ECC layout that is compatible with both JFFS2 and YAFFS2. Here we have chosen to support JFFS2, since it breaks fewer assumptions, and requires fewer changes to be made elsewhere. (UBI/UBIFS is fully supported, since it does not use the OOB area.) * Disable on-die ECC for 'RAW' page read/write operations * Disable on-die ECC for all OOB read/write operations (provides greatest level of compatibility with existing MTD utilities). * Extend nand_get_flash_type() to correctly distinguish between "legacy" and 4-bit on-die ECC Micron devices. Signed-off-by: Angus Clark --- drivers/mtd/nand/nand_base.c | 277 +++++++++++++++++++++++++++++++++++--- drivers/mtd/nand/nand_bbt.c | 30 ++++- drivers/mtd/nand/stm_nand_flex.c | 7 + include/linux/mtd/nand.h | 7 + 4 files changed, 302 insertions(+), 19 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 98060be..45634cb 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -103,6 +103,40 @@ static struct nand_ecclayout nand_oob_128 = { .length = 78}} }; +/* Micron 4-bit on-die ECC layout + * + * The 64-byte OOB is divided into 4 identical records. Each 16-byte record has + * the following layout: + * 0x00 - 0x01 : Reserved (for Bad Block Markers) + * 0x02 - 0x03 : User Metadata II (unprotected) + * 0x04 - 0x07 : User Metadata I (protected) + * 0x08 - 0x0f : ECC for main + Metadata I regions + * + * The use of on-die ECC brings a number of limitations to the way in which the + * OOB area can be used. In particular, some bytes in OOB are ECC-protected, + * and some are not. The ECC-protected bytes must be written at the same time + * as the page data. This breaks a number of assumptions made by existing MTD + * clients. As a result, it is not possible to define a ECC layout that is + * compatible with both JFFS2 and YAFFS2. Here we have chosen to support JFFS2, + * since it breaks fewer assumptions, and requires fewer changes to be made + * elsewhere. (UBI/UBIFS is fully supported, since it does not use the OOB + * area.) + */ +static struct nand_ecclayout nand_oob_64_4bitondie = { + .eccbytes = 32, + .eccpos = { + 8, 9, 10, 11, 12, 13, 14, 15, + 24, 25, 26, 27, 28, 29, 30, 31, + 40, 41, 42, 43, 44, 45, 46, 47, + 56, 57, 58, 59, 60, 61, 62, 63 }, + .oobfree = { + {.offset = 2, .length = 2}, + {.offset = 18, .length = 2}, + {.offset = 34, .length = 2}, + {.offset = 50, .length = 2} + } +}; + int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state); @@ -436,6 +470,104 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, return nand_isbad_bbt(mtd, ofs, allowbbt); } +/** + * nand_get_features - issue a "GET FEATURES" command + * @mtd: MTD device structure + * @feature: the feature address (FA) to be used + * @parameters: returned parameters (P1,P2,P3,P4) + * + * Send an entire "SET FEATURES" command to NAND device. This includes + * the feature address (FA), and the set of 4 parameters to use (P1,P2,P3,P4). + */ +static int nand_get_features(struct mtd_info *mtd, int feature, + uint8_t *parameters) +{ + struct nand_chip *chip = mtd->priv; + + /* issue the appropriate command + address */ + chip->cmdfunc(mtd, NAND_CMD_GETFEATURES, feature, -1); + + /* short delay */ + ndelay(100); /* tWB = 100ns */ + + /* wait until "GET FEATURES" command is processed */ + if (!chip->dev_ready) + udelay(chip->chip_delay); + else + while (!chip->dev_ready(mtd)) + ; + + /* read the 4 parameters */ + chip->read_buf(mtd, parameters, 4); + + DEBUG(MTD_DEBUG_LEVEL0, + "%s: with FA=0x%02x, P1=0x%02x, P2=0x%02x, " + "P3=0x%02x, P4=0x%02x\n", + __func__, feature, + parameters[0], parameters[1], parameters[2], parameters[3]); + + return 0; +} + +/** + * nand_set_features - issue a "SET FEATURES" command + * @mtd: MTD device structure + * @feature: the feature address (FA) to be used + * @parameters: the set of 4 parameters to use (P1,P2,P3,P4) + * + * Send an entire "SET FEATURES" command to NAND device. This includes + * the feature address (FA), and the set of 4 parameters to use (P1,P2,P3,P4). + */ +static int nand_set_features(struct mtd_info *mtd, int feature, + const uint8_t *parameters) +{ + struct nand_chip *chip = mtd->priv; + + DEBUG(MTD_DEBUG_LEVEL0, + "%s: with FA=0x%02x, P1=0x%02x, P2=0x%02x, " + "P3=0x%02x, P4=0x%02x\n", + __func__, feature, + parameters[0], parameters[1], parameters[2], parameters[3]); + + /* issue the appropriate command + address */ + chip->cmdfunc(mtd, NAND_CMD_SETFEATURES, feature, -1); + + /* write the 4 parameters */ + chip->write_buf(mtd, parameters, 4); + + /* short delay */ + ndelay(100); /* tWB = 100ns */ + + /* wait until "SET FEATURES" command is processed */ + if (!chip->dev_ready) + udelay(chip->chip_delay); + else + while (!chip->dev_ready(mtd)) + ; + + return 0; +} + +/* + * Micron 4-bit on-die ECC: enable/disable ECC Note, we use 'ecc.postpad' as a + * flag to indicate that on-die ECC is currently enabled; used by + * nand_command_lp() to check on-die ECC status after a read operation. + */ +static void nand_micron_4bit_ondie_ecc(struct mtd_info *mtd, int enable) +{ + struct nand_chip *chip = mtd->priv; + const uint8_t fp_ecc[2][4] = { + {0x0, 0x0, 0x0, 0x0}, + {0x8, 0x0, 0x0, 0x0} + }; + + BUG_ON(enable != 0 && enable != 1); + + nand_set_features(mtd, NAND_FEATURE_MICRON_ARRAY_OP_MODE, + fp_ecc[enable]); + chip->ecc.postpad = enable; +} + /* * Wait for the ready pin, after a command * The timeout is catched later. @@ -587,23 +719,30 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, if (column != -1 || page_addr != -1) { int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE; - /* Serially input address */ - if (column != -1) { - /* Adjust columns for 16 bit buswidth */ - if (chip->options & NAND_BUSWIDTH_16) - column >>= 1; - chip->cmd_ctrl(mtd, column, ctrl); - ctrl &= ~NAND_CTRL_CHANGE; - chip->cmd_ctrl(mtd, column >> 8, ctrl); - } - if (page_addr != -1) { - chip->cmd_ctrl(mtd, page_addr, ctrl); - chip->cmd_ctrl(mtd, page_addr >> 8, - NAND_NCE | NAND_ALE); - /* One more address cycle for devices > 128MiB */ - if (chip->chipsize > (128 << 20)) - chip->cmd_ctrl(mtd, page_addr >> 16, + if (command == NAND_CMD_SETFEATURES || + command == NAND_CMD_GETFEATURES) { + /* Write Feature Address */ + chip->cmd_ctrl(mtd, column & 0xff, ctrl); + } else { + /* Serially input address */ + if (column != -1) { + /* Adjust columns for 16 bit buswidth */ + if (chip->options & NAND_BUSWIDTH_16) + column >>= 1; + chip->cmd_ctrl(mtd, column, ctrl); + ctrl &= ~NAND_CTRL_CHANGE; + chip->cmd_ctrl(mtd, column >> 8, ctrl); + } + if (page_addr != -1) { + chip->cmd_ctrl(mtd, page_addr, ctrl); + chip->cmd_ctrl(mtd, page_addr >> 8, NAND_NCE | NAND_ALE); + /* One more address cycle for devices > 128MiB + */ + if (chip->chipsize > (128 << 20)) + chip->cmd_ctrl(mtd, page_addr >> 16, + NAND_NCE | NAND_ALE); + } } } chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); @@ -624,6 +763,13 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command, case NAND_CMD_DEPLETE1: return; + case NAND_CMD_SETFEATURES: + ndelay(70); /* tADL = 70ns */ + return; + + case NAND_CMD_GETFEATURES: + break; + /* * read error status commands require only a short delay */ @@ -668,6 +814,30 @@ 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 using 4-bit on-die ECC, check status for + * correctable/uncorrectable ECC errors. (ecc.postpad is used as + * a flag to indicate on-die ECC is currently enabled) + */ + if (chip->ecc.mode == NAND_ECC_4BITONDIE && chip->ecc.postpad) { + int status; + + status = chip->waitfunc(mtd, chip); + + if (status & NAND_STATUS_FAIL) + mtd->ecc_stats.failed++; + else if (status & NAND_STATUS_ECCREWRITE) + mtd->ecc_stats.corrected++; + + /* Re-issue CMD0 after STATUS Check */ + 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); + + /* Device now ready for reading, return immediately */ + return; + } + /* This applies to read commands */ default: /* @@ -1172,6 +1342,7 @@ int nand_do_read_ops(struct mtd_info *mtd, loff_t from, uint32_t readlen = ops->len; uint32_t oobreadlen = ops->ooblen; uint8_t *bufpoi, *oob, *buf; + int reenable_ondie_ecc = 0; stats = mtd->ecc_stats; @@ -1186,6 +1357,12 @@ int nand_do_read_ops(struct mtd_info *mtd, loff_t from, buf = ops->datbuf; oob = ops->oobbuf; + /* For 'RAW' reads, disable on-die ECC if necessary */ + if (ops->mode == MTD_OOB_RAW && chip->ecc.mode == NAND_ECC_4BITONDIE) { + nand_micron_4bit_ondie_ecc(mtd, 0); + reenable_ondie_ecc = 1; + } + while(1) { bytes = min(mtd->writesize - col, readlen); aligned = (bytes == mtd->writesize); @@ -1282,6 +1459,10 @@ int nand_do_read_ops(struct mtd_info *mtd, loff_t from, if (oob) ops->oobretlen = ops->ooblen - oobreadlen; + /* Re-enable on-die ECC if necessary */ + if (reenable_ondie_ecc) + nand_micron_4bit_ondie_ecc(mtd, 1); + if (ret) return ret; @@ -1485,6 +1666,7 @@ int nand_do_read_oob(struct mtd_info *mtd, loff_t from, int readlen = ops->ooblen; int len; uint8_t *buf = ops->oobbuf; + int reenable_ondie_ecc = 0; DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08Lx, len = %i\n", __func__, (unsigned long long)from, readlen); @@ -1516,6 +1698,12 @@ int nand_do_read_oob(struct mtd_info *mtd, loff_t from, realpage = (int)(from >> chip->page_shift); page = realpage & chip->pagemask; + /* Disable on-die ECC if necessary */ + if (chip->ecc.mode == NAND_ECC_4BITONDIE) { + nand_micron_4bit_ondie_ecc(mtd, 0); + reenable_ondie_ecc = 1; + } + while(1) { sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd); @@ -1557,6 +1745,10 @@ int nand_do_read_oob(struct mtd_info *mtd, loff_t from, sndcmd = 1; } + /* Re-enable on-die ECC if necessary */ + if (reenable_ondie_ecc) + nand_micron_4bit_ondie_ecc(mtd, 1); + ops->oobretlen = ops->ooblen; return 0; } @@ -1883,6 +2075,7 @@ int nand_do_write_ops(struct mtd_info *mtd, loff_t to, uint8_t *oob = ops->oobbuf; uint8_t *buf = ops->datbuf; int ret, subpage; + int reenable_ondie_ecc = 0; ops->retlen = 0; if (!writelen) @@ -1921,6 +2114,12 @@ int nand_do_write_ops(struct mtd_info *mtd, loff_t to, if (likely(!oob)) memset(chip->oob_poi, 0xff, mtd->oobsize); + /* For 'RAW' writes, disable on-die ECC if necessary */ + if (ops->mode == MTD_OOB_RAW && chip->ecc.mode == NAND_ECC_4BITONDIE) { + nand_micron_4bit_ondie_ecc(mtd, 0); + reenable_ondie_ecc = 1; + } + while(1) { int bytes = mtd->writesize; int cached = writelen > bytes && page != blockmask; @@ -1961,6 +2160,10 @@ int nand_do_write_ops(struct mtd_info *mtd, loff_t to, } } + /* Re-enable on-die ECC if necessary */ + if (reenable_ondie_ecc) + nand_micron_4bit_ondie_ecc(mtd, 1); + ops->retlen = ops->len - writelen; if (unlikely(oob)) ops->oobretlen = ops->ooblen; @@ -2018,6 +2221,7 @@ int nand_do_write_oob(struct mtd_info *mtd, loff_t to, { int chipnr, page, status, len; struct nand_chip *chip = mtd->priv; + int reenable_ondie_ecc = 0; DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to, (int)ops->ooblen); @@ -2072,11 +2276,21 @@ int nand_do_write_oob(struct mtd_info *mtd, loff_t to, if (page == chip->pagebuf) chip->pagebuf = -1; + /* Disable on-die ECC */ + if (chip->ecc.mode == NAND_ECC_4BITONDIE) { + nand_micron_4bit_ondie_ecc(mtd, 0); + reenable_ondie_ecc = 1; + } + memset(chip->oob_poi, 0xff, mtd->oobsize); nand_fill_oob(chip, ops->oobbuf, ops); status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); memset(chip->oob_poi, 0xff, mtd->oobsize); + /* Re-enable on-die ECC if necessary */ + if (reenable_ondie_ecc) + nand_micron_4bit_ondie_ecc(mtd, 1); + if (status) return status; @@ -2566,6 +2780,18 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, /* Get buswidth information */ busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + /* Micron device: check for 4-bit on-die ECC */ + if (*maf_id == NAND_MFR_MICRON) { + u8 id4, id5; + id4 = chip->read_byte(mtd); + id5 = chip->read_byte(mtd); + + /* Do we have a 5-byte ID ? */ + if (!(id4 == *maf_id && id5 == dev_id)) + /* ECC level in id4[1:0] */ + if ((id4 & 0x3) == 0x2) + chip->ecc.mode = NAND_ECC_4BITONDIE; + } } else { /* * Old devices have chip data hardcoded in the device id table @@ -2730,7 +2956,10 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.layout = &nand_oob_16; break; case 64: - chip->ecc.layout = &nand_oob_64; + if (chip->ecc.mode == NAND_ECC_4BITONDIE) + chip->ecc.layout = &nand_oob_64_4bitondie; + else + chip->ecc.layout = &nand_oob_64; break; case 128: chip->ecc.layout = &nand_oob_128; @@ -2837,6 +3066,20 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.bytes = 0; break; + case NAND_ECC_4BITONDIE: + chip->ecc.read_page = nand_read_page_raw; + chip->ecc.write_page = nand_write_page_raw; + chip->ecc.read_page_raw = nand_read_page_raw; + chip->ecc.write_page_raw = nand_write_page_raw; + chip->ecc.read_oob = nand_read_oob_std; + chip->ecc.write_oob = nand_write_oob_std; + chip->ecc.size = 512; + chip->ecc.bytes = 8; + + /* Turn on on-die ECC */ + nand_micron_4bit_ondie_ecc(mtd, 1); + break; + default: printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n", chip->ecc.mode); diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 6b1942b..fee510a 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -1165,6 +1165,27 @@ static struct nand_bbt_descr bbt_mirror_descr = { .pattern = mirror_pattern }; +/* BBT descriptors for (Micron) 4-bit on-die ECC */ +static struct nand_bbt_descr bbt_main_descr_ode = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8 + 8, /* need to shift by 8 due to on-die ECC */ + .len = 4, + .veroffs = 12 + 8, /* need to shift by 8 due to on-die ECC */ + .maxblocks = 4, + .pattern = bbt_pattern +}; + +static struct nand_bbt_descr bbt_mirror_descr_ode = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8 + 8, /* need to shift by 8 due to on-die ECC */ + .len = 4, + .veroffs = 12 + 8, /* need to shift by 8 due to on-die ECC */ + .maxblocks = 4, + .pattern = mirror_pattern +}; + /** * nand_default_bbt - [NAND Interface] Select a default bad block table for the device * @mtd: MTD device structure @@ -1198,8 +1219,13 @@ int nand_default_bbt(struct mtd_info *mtd) if (this->options & NAND_USE_FLASH_BBT) { /* Use the default pattern descriptors */ if (!this->bbt_td) { - this->bbt_td = &bbt_main_descr; - this->bbt_md = &bbt_mirror_descr; + if (this->ecc.mode == NAND_ECC_4BITONDIE) { + this->bbt_td = &bbt_main_descr_ode; + this->bbt_md = &bbt_mirror_descr_ode; + } else { + this->bbt_td = &bbt_main_descr; + this->bbt_md = &bbt_mirror_descr; + } } if (!this->badblock_pattern) { this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 0471f01..67144b7 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -80,6 +80,8 @@ extern void nand_wait_ready(struct mtd_info *mtd); #define NAND_CMD_READID 0x90 #define NAND_CMD_ERASE2 0xd0 #define NAND_CMD_RESET 0xff +#define NAND_CMD_SETFEATURES 0xef +#define NAND_CMD_GETFEATURES 0xee /* Extended commands for large page devices */ #define NAND_CMD_READSTART 0x30 @@ -107,12 +109,16 @@ extern void nand_wait_ready(struct mtd_info *mtd); #define NAND_CMD_NONE -1 +/* Feature Addresses (for the "SET/GET FEATURES" commands) */ +#define NAND_FEATURE_MICRON_ARRAY_OP_MODE 0x90 + /* Status bits */ #define NAND_STATUS_FAIL 0x01 #define NAND_STATUS_FAIL_N1 0x02 #define NAND_STATUS_TRUE_READY 0x20 #define NAND_STATUS_READY 0x40 #define NAND_STATUS_WP 0x80 +#define NAND_STATUS_ECCREWRITE 0x08 /* * Constants for ECC_MODES @@ -123,6 +129,7 @@ typedef enum { NAND_ECC_HW, NAND_ECC_HW_SYNDROME, NAND_ECC_HW_OOB_FIRST, + NAND_ECC_4BITONDIE, } nand_ecc_modes_t; /* -- 1.7.7