Have nand_base calculate a good ecc layout default. A benefit of this is allowing a software ecc other than 3 bytes long. For example, 4 bit software ecc correction.
Boards that don't specify a layout and don't use all ecc bytes in the current default layout could have their ecc position changed. Looking through the code, I can only see that Davinci is effected this way when using 512 bytes x 4 steps with hardware ecc. It has its ecc bytes moved from oob 40-63 to oob 52-63. So a Davinci authority will need to ack this, or request a change. Some boards will print warning messages. i.e. mxc_nand which specifies ecc.bytes = 3, but eccbytes = 5 (not a multiple of 3). I may have missed other boards being affected so it needs thorough testing. Signed-off-by: Troy Kisky <[email protected]> --- drivers/mtd/nand/nand_base.c | 206 ++++++++++++++++++++++++++---------------- 1 files changed, 130 insertions(+), 76 deletions(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 2780a9b..9a9fdfb 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -52,50 +52,6 @@ #include <linux/mtd/partitions.h> #endif -/* Define default oob placement schemes for large and small page devices */ -static struct nand_ecclayout nand_oob_8 = { - .eccbytes = 3, - .eccpos = {0, 1, 2}, - .oobfree = { - {.offset = 3, - .length = 2}, - {.offset = 6, - .length = 2}} -}; - -static struct nand_ecclayout nand_oob_16 = { - .eccbytes = 6, - .eccpos = {0, 1, 2, 3, 6, 7}, - .oobfree = { - {.offset = 8, - . length = 8}} -}; - -static struct nand_ecclayout nand_oob_64 = { - .eccbytes = 24, - .eccpos = { - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63}, - .oobfree = { - {.offset = 2, - .length = 38}} -}; - -static struct nand_ecclayout nand_oob_128 = { - .eccbytes = 48, - .eccpos = { - 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127}, - .oobfree = { - {.offset = 2, - .length = 78}} -}; - static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state); @@ -2627,7 +2583,10 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips) */ int nand_scan_tail(struct mtd_info *mtd) { - int i; + int i, len, bit, next_bit; + int bad_block_marker_offset; + int bad_block_marker_length; + DECLARE_BITMAP(in_use, 256); struct nand_chip *chip = mtd->priv; if (!(chip->options & NAND_OWN_BUFFERS)) @@ -2737,42 +2696,137 @@ int nand_scan_tail(struct mtd_info *mtd) BUG(); } chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; - - /* - * If no default placement scheme is given, select an appropriate one - */ - if (!chip->ecc.layout.eccbytes) { - const struct nand_ecclayout *layout = NULL; - switch (mtd->oobsize) { - case 8: - layout = &nand_oob_8; - break; - case 16: - layout = &nand_oob_16; - break; - case 64: - layout = &nand_oob_64; - break; - case 128: - layout = &nand_oob_128; + pr_info("%s ecc.total = %d, ecc.steps = %d, ecc.bytes = %d, " + "ecc.size = %d, writesize = %d\n", + __func__, chip->ecc.total, chip->ecc.steps, chip->ecc.bytes, + chip->ecc.size, mtd->writesize); +try_again: + bad_block_marker_offset = NAND_LARGE_BADBLOCK_POS; + bad_block_marker_length = 2; + if (mtd->oobsize == 8) { + bad_block_marker_offset = NAND_SMALL_BADBLOCK_POS; + bad_block_marker_length = 1; + } else if (mtd->oobsize == 16) { + bad_block_marker_offset = 4; /* length still 2 */ + } + if (chip->bbt_td) { + bad_block_marker_offset = chip->bbt_td->offs; + bad_block_marker_length = chip->bbt_td->len; + } + if (chip->badblock_pattern) { + bad_block_marker_offset = chip->badblock_pattern->offs; + bad_block_marker_length = chip->badblock_pattern->len; + } + /* Mark no oob bytes in_use */ + memset(in_use, 0, 256/8); + + /* Mark bad block indicator oob byte in_use */ + while (bad_block_marker_length--) + __set_bit(bad_block_marker_offset++, in_use); + + /* Mark reserved oobfree entries in_use */ + chip->ecc.layout.oobavail = 0; + i = 0; + do { + len = chip->ecc.layout.oobfree[i].length; + if (!len) break; - default: - printk(KERN_WARNING "No oob scheme defined for " - "oobsize %d\n", mtd->oobsize); - BUG(); + bit = chip->ecc.layout.oobfree[i].offset; + if (bit + len > mtd->oobsize) { + pr_warning("oob byte(%d) >= oobsize(%d)\n", + bit + len - 1, mtd->oobsize); + len = mtd->oobsize - bit; + if (len < 0) + len = 0; + chip->ecc.layout.oobfree[i].length = len; + if (!len) + break; + } + chip->ecc.layout.oobavail += len; + while (len) { + if (__test_and_set_bit(bit, in_use)) + pr_warning("free oob byte(%d) in use\n", bit); + bit++; + len--; + } + i++; + } while (i < MTD_MAX_OOBFREE_ENTRIES); + + if (chip->ecc.layout.eccbytes >= chip->ecc.total) { + /* Mark eccpos bytes in_use */ + int j = 0; + if (chip->ecc.layout.eccbytes > chip->ecc.total) + pr_warning("%s: layout.eccbytes(%d) > ecc.total(%d)," + " some oob space will be wasted\n", __func__, + chip->ecc.layout.eccbytes, chip->ecc.total); + for (len = chip->ecc.layout.eccbytes; len; len--) { + bit = chip->ecc.layout.eccpos[j++]; + if (bit >= mtd->oobsize) { + pr_warning("%s: eccpos(%d) too big, " + "defaulting\n", __func__, bit); + chip->ecc.layout.eccbytes = 0; + goto try_again; + } else if (__test_and_set_bit(bit, in_use)) + pr_warning("%s: eccpos(%d) in use\n", __func__, + bit); } - if (layout) - memcpy(&chip->ecc.layout, layout, sizeof(*layout)); + } else { + int j; + if (chip->ecc.layout.eccbytes) + pr_warning("%s: layout.eccbytes(%d) < ecc.total(%d)," + " eccpos will default\n", __func__, + chip->ecc.layout.eccbytes, chip->ecc.total); + /* + * Choose the default ecc byte positions. + * If the 1st oob byte is free, use the lowest + * oob bytes for the ecc. Otherwise, use the + * highest oob bytes for the ecc. + */ + if (!test_bit(0, in_use)) + bit = 0; + else { + bit = mtd->oobsize; + j = chip->ecc.total; + while (bit) { + bit--; + if (!test_bit(bit, in_use)) { + j--; + if (!j) + break; + } + } + } + j = 0; + while (j < chip->ecc.total) { + bit = find_next_zero_bit(in_use, mtd->oobsize, bit); + if (bit == mtd->oobsize) { + pr_warning("%s: oobfull\n", __func__); + break; + } + __set_bit(bit, in_use); + chip->ecc.layout.eccpos[j++] = bit++; + } + chip->ecc.layout.eccbytes = j; } - /* - * The number of bytes available for a client to place data into - * the out of band area - */ - chip->ecc.layout.oobavail = 0; - for (i = 0; chip->ecc.layout.oobfree[i].length; i++) + /* Add all oob bytes not in_use to the oobfree list */ + next_bit = 0; + while (i < MTD_MAX_OOBFREE_ENTRIES) { + bit = find_next_zero_bit(in_use, mtd->oobsize, next_bit); + if (bit == mtd->oobsize) + break; + next_bit = find_next_bit(in_use, mtd->oobsize, bit); + pr_info("%s oobfree[%d].offset=%d, .length=%d\n", __func__, i, + bit, next_bit - bit); + chip->ecc.layout.oobfree[i].offset = bit; chip->ecc.layout.oobavail += - chip->ecc.layout.oobfree[i].length; + chip->ecc.layout.oobfree[i++].length = next_bit - bit; + } + + if (i < MTD_MAX_OOBFREE_ENTRIES) { + chip->ecc.layout.oobfree[i].offset = 0; + chip->ecc.layout.oobfree[i].length = 0; + } mtd->oobavail = chip->ecc.layout.oobavail; /* -- 1.5.4.3 _______________________________________________ Davinci-linux-open-source mailing list [email protected] http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source
