From: David Brownell <[email protected]> Support the 4-bit ECC engine found on DM355 and OMAP-L137 chips.
This is limited to small-page flash for now; there are separate issues specific to large page. Note that the DM355 EVM uses a large-page chip -- unless you swap socketed chips. Note that this includes part of the OOB in the ECC code. Which seems uncommon in Linux, and may be making some trouble. This way does seem to interoperate with the original code from TI, in at least some cases. # FIXME locking with 4-bit ECC hardware is insufficient... # it needs to prevent all access from other chipselects, # since the hardware doesn't know which one matters. # NYET Signed-off-by: David Brownell <[email protected]> --- drivers/mtd/nand/davinci_nand.c | 295 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 286 insertions(+), 9 deletions(-) --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -58,7 +58,7 @@ static inline int mtd_has_cmdlinepart(vo * and some flavors of secondary chipselect (e.g. based on A12) as used * with multichip packages. * - * The 1-bit ECC hardware is supported, but not yet the newer 4-bit ECC + * The 1-bit ECC hardware is supported, as well as the newer 4-bit ECC * available on chips like the DM355 and OMAP-L137 and needed with the * more error-prone MLC NAND chips. * @@ -68,6 +68,7 @@ static inline int mtd_has_cmdlinepart(vo struct davinci_nand_info { struct mtd_info mtd; struct nand_chip chip; + struct nand_ecclayout ecclayout; struct device *dev; struct clk *clk; @@ -86,7 +87,13 @@ struct davinci_nand_info { u32 core_chipsel; }; +/* With 4-bit ECC mode, match the page layout of the + * DM355 boot ROM and DM355 DVSDK code. + */ +#define ECC_PREPAD 6 + static DEFINE_SPINLOCK(davinci_nand_lock); +static bool ecc4_busy; #define to_davinci_nand(m) container_of(m, struct davinci_nand_info, mtd) @@ -232,6 +239,192 @@ static int nand_davinci_correct_1bit(str /*----------------------------------------------------------------------*/ /* + * 4-bit hardware ECC ... context maintained over entire AEMIF + * + * This is a syndrome engine, so the page layout is automatically + * N * { data + prepad + ECC + postpad } + remaining_OOB + * + * That ECC covers the prepad as well as the data. When N != 1 + * (large page NAND), the spare area is overrun with data and + * hence manufacture bad block markings get trashed. + */ + +static void nand_davinci_hwctl_4bit(struct mtd_info *mtd, int mode) +{ + struct davinci_nand_info *info = to_davinci_nand(mtd); + unsigned long flags; + u32 val; + + switch (mode) { + case NAND_ECC_WRITE: + case NAND_ECC_READ: + spin_lock_irqsave(&davinci_nand_lock, flags); + + /* Start 4-bit ECC calculation for read/write on CS0 */ + val = davinci_nand_readl(info, NANDFCR_OFFSET); + val &= ~(0x03 << 4); + val |= (info->core_chipsel << 4) | BIT(12); + davinci_nand_writel(info, NANDFCR_OFFSET, val); + + spin_unlock_irqrestore(&davinci_nand_lock, flags); + break; + case NAND_ECC_READSYN: + /* Terminate ECC calculation by performing a dummy read of + * some 4-bit ECC register. ECC covers data and pre-pad. + */ + davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET); + break; + } +} + +/* read raw ECC code (NAND writes) or syndrome (NAND reads) */ +static void +nand_davinci_readecc_4bit(struct davinci_nand_info *info, u32 code[4]) +{ + const u32 mask = 0x03ff03ff; + + code[0] = davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET) & mask; + code[1] = davinci_nand_readl(info, NAND_4BIT_ECC2_OFFSET) & mask; + code[2] = davinci_nand_readl(info, NAND_4BIT_ECC3_OFFSET) & mask; + code[3] = davinci_nand_readl(info, NAND_4BIT_ECC4_OFFSET) & mask; +} + +/* return ECC as bytes after data is written to NAND */ +static int nand_davinci_calculate_4bit(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_code) +{ + u32 raw_ecc[4], *p; + unsigned i; + + /* Pack eight raw 10-bit ecc values into ten bytes, making + * two passes which each convert four values into five bytes + */ + nand_davinci_readecc_4bit(to_davinci_nand(mtd), raw_ecc); + for (i = 0, p = raw_ecc; i < 2; i++, p += 2) { + *ecc_code++ = p[0] & 0xff; + *ecc_code++ = ((p[0] >> 8) & 0x03) | ((p[0] >> 14) & 0xfc); + *ecc_code++ = ((p[0] >> 22) & 0x0f) | ((p[1] << 4) & 0xf0); + *ecc_code++ = ((p[1] >> 4) & 0x3f) | ((p[1] >> 10) & 0xc0); + *ecc_code++ = (p[1] >> 18) & 0xff; + } + + return 0; +} + +/* correct up to 4 bits in data and oob, using hardware state and ecc_code */ +static int nand_davinci_correct_4bit(struct mtd_info *mtd, + u_char *data, u_char *ecc_code, u_char *null) +{ + int i; + struct davinci_nand_info *info = to_davinci_nand(mtd); + unsigned short ecc10[8]; + unsigned short *ecc16; + u32 syndrome[4]; + unsigned num_errors; + u8 *prepad; + + /* All bytes 0xff? It's an erased page; ignore its ECC. */ + for (i = 0; i < 10; i++) { + if (ecc_code[i] != 0xff) + goto compare; + } + return 0; + +compare: + /* Unpack ten bytes into eight 10 bit values. We know we're + * little-endian, and use type punning for less shifting/masking. + */ + if (WARN_ON(0x01 & (unsigned) ecc_code)) + return -EINVAL; + ecc16 = (unsigned short *)ecc_code; + + ecc10[0] = (ecc16[0] >> 0) & 0x3ff; + ecc10[1] = ((ecc16[0] >> 10) & 0x3f) | ((ecc16[1] << 6) & 0x3c0); + ecc10[2] = (ecc16[1] >> 4) & 0x3ff; + ecc10[3] = ((ecc16[1] >> 14) & 0x3) | ((ecc16[2] << 2) & 0x3fc); + ecc10[4] = (ecc16[2] >> 8) | ((ecc16[3] << 8) & 0x300); + ecc10[5] = (ecc16[3] >> 2) & 0x3ff; + ecc10[6] = ((ecc16[3] >> 12) & 0xf) | ((ecc16[4] << 4) & 0x3f0); + ecc10[7] = (ecc16[4] >> 6) & 0x3ff; + + /* Tell ECC controller about the expected ECC codes. */ + for (i = 7; i >= 0; i--) + davinci_nand_writel(info, NAND_4BIT_ECC_LOAD_OFFSET, ecc10[i]); + + /* Allow time for syndrome calculation ... then read it. + * A syndrome of all zeroes 0 means no detected errors. + */ + davinci_nand_readl(info, NANDFSR_OFFSET); + nand_davinci_readecc_4bit(info, syndrome); + if (!syndrome[0] && !syndrome[1] && !syndrome[2] && !syndrome[3]) + return 0; + + /* Start address calculation, and wait for it to complete. + * We _could_ start reading more data while this is working, + * to speed up the overall page read. + */ + davinci_nand_writel(info, NANDFCR_OFFSET, + davinci_nand_readl(info, NANDFCR_OFFSET) | BIT(13)); + for (;;) { + u32 fsr = davinci_nand_readl(info, NANDFSR_OFFSET); + + switch ((fsr >> 8) & 0x0f) { + case 0: /* no error, should not happen */ + return 0; + case 1: /* five or more errors detected */ + return -EIO; + case 2: /* error addresses computed */ + case 3: + num_errors = 1 + ((fsr >> 16) & 0x03); + break; + default: /* still working on it */ + cpu_relax(); + continue; + } + + /* ready to correct up to four errors */ + break; + } + + /* correct each error */ + prepad = ecc_code - ECC_PREPAD; + for (i = 0; i < num_errors; i++) { + int error_address, error_value; + + if (i > 1) { + error_address = davinci_nand_readl(info, + NAND_ERR_ADD2_OFFSET); + error_value = davinci_nand_readl(info, + NAND_ERR_ERRVAL2_OFFSET); + } else { + error_address = davinci_nand_readl(info, + NAND_ERR_ADD1_OFFSET); + error_value = davinci_nand_readl(info, + NAND_ERR_ERRVAL1_OFFSET); + } + + if (i & 1) { + error_address >>= 16; + error_value >>= 16; + } + error_address &= 0x3ff; + error_address = (512 + ECC_PREPAD + 7) - error_address; + + if (error_address < 512) + data[error_address] ^= error_value; + else { + error_address -= 512; + if (ECC_PREPAD && error_address < ECC_PREPAD) + prepad[error_address] ^= error_value; + } + } + + return num_errors; +} + +/*----------------------------------------------------------------------*/ + +/* * NOTE: NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's * how these chips are normally wired. This translates to both 8 and 16 * bit busses using ALE == BIT(3) in byte addresses, and CLE == BIT(4). @@ -308,6 +501,21 @@ static void __init nand_dm6446evm_flash_ /*----------------------------------------------------------------------*/ +/* An ECC layout for using ECC_HW_SYNDROME with small-page flash, + * which won't trash manufacturer bad block markers. + */ +static struct nand_ecclayout hwecc_small __initconst = { + .eccbytes = 10, + .eccpos = { 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, }, + .oobfree = { + {.offset = 0, .length = 4, }, + /* offset 5 holds the badblock marker */ + {.offset = 6, .length = 1, }, + {.offset = 16, }, + }, +}; + + static int __init nand_davinci_probe(struct platform_device *pdev) { struct davinci_nand_pdata *pdata = pdev->dev.platform_data; @@ -333,11 +541,12 @@ static int __init nand_davinci_probe(str platform_set_drvdata(pdev, info); + ret = -EINVAL; + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res1 || !res2) { dev_err(&pdev->dev, "resource missing\n"); - ret = -EINVAL; goto err_res; } @@ -345,7 +554,6 @@ static int __init nand_davinci_probe(str base = ioremap(res2->start, res2->end - res2->start); if (!vaddr || !base) { dev_err(&pdev->dev, "ioremap failed\n"); - ret = -EINVAL; goto err_ioremap; } @@ -410,14 +618,29 @@ static int __init nand_davinci_probe(str info->chip.ecc.bytes = 3; break; case NAND_ECC_HW_SYNDROME: - /* FIXME implement */ + if ((info->chip.options & NAND_BUSWIDTH_16) + || !cpu_is_davinci_dm355()) + goto err_ecc; + + /* no sharing syndrome hardware between chipselects yet */ + spin_lock_irq(&davinci_nand_lock); + if (ecc4_busy) + ret = -EBUSY; + else + ecc4_busy = true; + spin_unlock_irq(&davinci_nand_lock); + + if (ret == -EBUSY) + goto err_ecc; + + info->chip.ecc.calculate = nand_davinci_calculate_4bit; + info->chip.ecc.correct = nand_davinci_correct_4bit; + info->chip.ecc.hwctl = nand_davinci_hwctl_4bit; info->chip.ecc.size = 512; info->chip.ecc.bytes = 10; - - dev_warn(&pdev->dev, "4-bit ECC nyet supported\n"); - /* FALL THROUGH */ + info->chip.ecc.prepad = ECC_PREPAD; + break; default: - ret = -EINVAL; goto err_ecc; } info->chip.ecc.mode = ecc_mode; @@ -456,12 +679,56 @@ static int __init nand_davinci_probe(str spin_unlock_irq(&davinci_nand_lock); /* Scan to find existence of the device(s) */ - ret = nand_scan(&info->mtd, pdata->mask_chipsel ? 2 : 1); + ret = nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1); if (ret < 0) { dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); goto err_scan; } + /* update ECC layout if needed ... for ECC_HW, the default is OK, + * but it allocates twice as much space as needed: 6 bytes ECC + * every 512 bytes, vs just 3. + */ + if (ecc_mode == NAND_ECC_HW_SYNDROME) { + int chunks = info->mtd.writesize / 512; + int i, oobindex; + + if (!chunks || info->mtd.oobsize < 16) { + dev_dbg(&pdev->dev, "too small\n"); + ret = -EINVAL; + goto err_scan; + } + + /* For small page chips, preserve the manufacturer's + * badblock marking data ... and reject flash BBT tables + * for now, since the defaults want to write table tags + * over the ECC data. + */ + if (chunks == 1) { + if (info->chip.options & NAND_USE_FLASH_BBT) { + dev_dbg(&pdev->dev, "using FLASH_BBT?\n"); + ret = -EOPNOTSUPP; + goto err_scan; + } + info->ecclayout = hwecc_small; + info->ecclayout.oobfree[2].length = + info->mtd.oobsize - 16; + goto syndrome_done; + } + + dev_warn(&pdev->dev, "no 4-bit ECC support yet " + "for large page NAND\n"); + ret = -EIO; + goto err_scan; + +syndrome_done: + info->chip.ecc.layout = &info->ecclayout; + } + + ret = nand_scan_tail(&info->mtd); + if (ret < 0) + goto err_scan; + if (mtd_has_partitions()) { struct mtd_partition *mtd_parts = NULL; int mtd_parts_nb = 0; @@ -524,6 +791,11 @@ err_scan: err_clk_enable: clk_put(info->clk); + spin_lock_irq(&davinci_nand_lock); + if (ecc_mode == NAND_ECC_HW_SYNDROME) + ecc4_busy = false; + spin_unlock_irq(&davinci_nand_lock); + err_ecc: err_clk: if (base) @@ -549,6 +821,11 @@ static int __exit nand_davinci_remove(st else status = del_mtd_device(&info->mtd); + spin_lock_irq(&davinci_nand_lock); + if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME) + ecc4_busy = false; + spin_unlock_irq(&davinci_nand_lock); + iounmap(info->base); iounmap(info->vaddr); _______________________________________________ Davinci-linux-open-source mailing list [email protected] http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source
