From 69cc97f8dbf898d732fbd04852cf1068aeb991ba Mon Sep 17 00:00:00 2001 From: pekon gupta Date: Thu, 5 Dec 2013 17:54:21 +0530 Subject: mtd: nand: omap: fix ecc-layout for HAM1 ecc-scheme As per OMAP3530 TRM referenced below [1] For large-page NAND, ROM code expects following ecc-layout for HAM1 ecc-scheme - OOB[1] (offset of 1 *byte* from start of OOB) for x8 NAND device - OOB[2] (offset of 1 *word* from start of OOB) for x16 NAND device Thus ecc-layout expected by ROM code for HAM1 ecc-scheme is: *for x8 NAND Device* +--------+---------+---------+---------+---------+---------+---------+ | xxxx | ECC[A0] | ECC[A1] | ECC[A2] | ECC[B0] | ECC[B1] | ECC[B2] | ... +--------+---------+---------+---------+---------+---------+---------+ *for x16 NAND Device* +--------+--------+---------+---------+---------+---------+---------+---------+ | xxxxx | xxxxx | ECC[A0] | ECC[A1] | ECC[A2] | ECC[B0] | ECC[B1] | ECC[B2] | +--------+--------+---------+---------+---------+---------+---------+---------+ This patch fixes ecc-layout *only* for HAM1, as required by ROM-code For other ecc-schemes like (BCH8) ecc-layout is same for x8 or x16 devices. [1] OMAP3530: http://www.ti.com/product/omap3530 TRM: http://www.ti.com/litv/pdf/spruf98x Chapter-25: Initialization Sub-topic: Memory Booting Section: 25.4.7.4 NAND Figure 25-19. ECC Locations in NAND Spare Areas Reported-by: Stefan Roese Signed-off-by: Pekon Gupta Tested-by: Stefan Roese diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index 5e7e6b3..23a961c 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -798,8 +798,12 @@ static int omap_select_ecc_scheme(struct nand_chip *nand, nand->ecc.calculate = omap_calculate_ecc; /* define ecc-layout */ ecclayout->eccbytes = nand->ecc.bytes * eccsteps; - for (i = 0; i < ecclayout->eccbytes; i++) - ecclayout->eccpos[i] = i + BADBLOCK_MARKER_LENGTH; + for (i = 0; i < ecclayout->eccbytes; i++) { + if (nand->options & NAND_BUSWIDTH_16) + ecclayout->eccpos[i] = i + 2; + else + ecclayout->eccpos[i] = i + 1; + } ecclayout->oobfree[0].offset = i + BADBLOCK_MARKER_LENGTH; ecclayout->oobfree[0].length = oobsize - ecclayout->eccbytes - BADBLOCK_MARKER_LENGTH; -- cgit v0.10.2 From 5d7a49b930156514ce9d418e26b5bf21673fd399 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Thu, 5 Dec 2013 07:58:06 +0100 Subject: mtd: nand: omap_gpmc: cosmetic: Fix indentation Signed-off-by: Stefan Roese Cc: Pekon Gupta Cc: Scott Wood [scottwood@freescale.com: wrap some long lines] Signed-off-by: Scott Wood diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index 23a961c..2067969 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -283,53 +283,55 @@ static void omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode) if (bch->ecc_scheme == OMAP_ECC_BCH8_CODE_HW) { wr_mode = BCH_WRAPMODE_1; - switch (bch->nibbles) { - case ECC_BCH4_NIBBLES: - unused_length = 3; - break; - case ECC_BCH8_NIBBLES: - unused_length = 2; - break; - case ECC_BCH16_NIBBLES: - unused_length = 0; - break; - } - - /* - * This is ecc_size_config for ELM mode. - * Here we are using different settings for read and write access and - * also depending on BCH strength. - */ - switch (mode) { - case NAND_ECC_WRITE: - /* write access only setup eccsize1 config */ - val = ((unused_length + bch->nibbles) << 22); - break; + switch (bch->nibbles) { + case ECC_BCH4_NIBBLES: + unused_length = 3; + break; + case ECC_BCH8_NIBBLES: + unused_length = 2; + break; + case ECC_BCH16_NIBBLES: + unused_length = 0; + break; + } - case NAND_ECC_READ: - default: /* - * by default eccsize0 selected for ecc1resultsize - * eccsize0 config. + * This is ecc_size_config for ELM mode. Here we are using + * different settings for read and write access and also + * depending on BCH strength. */ - val = (bch->nibbles << 12); - /* eccsize1 config */ - val |= (unused_length << 22); - break; - } + switch (mode) { + case NAND_ECC_WRITE: + /* write access only setup eccsize1 config */ + val = ((unused_length + bch->nibbles) << 22); + break; + + case NAND_ECC_READ: + default: + /* + * by default eccsize0 selected for ecc1resultsize + * eccsize0 config. + */ + val = (bch->nibbles << 12); + /* eccsize1 config */ + val |= (unused_length << 22); + break; + } } else { - /* - * This ecc_size_config setting is for BCH sw library. - * - * Note: we only support BCH8 currently with BCH sw library! - * Should be really easy to adobt to BCH4, however some omap3 have - * flaws with BCH4. - * - * Here we are using wrapping mode 6 both for reading and writing, with: - * size0 = 0 (no additional protected byte in spare area) - * size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) - */ - val = (32 << 22) | (0 << 12); + /* + * This ecc_size_config setting is for BCH sw library. + * + * Note: we only support BCH8 currently with BCH sw library! + * Should be really easy to adobt to BCH4, however some omap3 + * have flaws with BCH4. + * + * Here we are using wrapping mode 6 both for reading and + * writing, with: + * size0 = 0 (no additional protected byte in spare area) + * size1 = 32 (skip 32 nibbles = 16 bytes per sector in + * spare area) + */ + val = (32 << 22) | (0 << 12); } /* ecc size configuration */ writel(val, &gpmc_cfg->ecc_size_config); -- cgit v0.10.2 From 2528460c38dfaffe7ae430244d1fa119087c1b01 Mon Sep 17 00:00:00 2001 From: Nikita Kiryanov Date: Thu, 12 Dec 2013 15:19:31 +0200 Subject: mtd: nand: omap: fix HAM1_SW ecc using default value for ecc.size Commit "mtd: nand: omap: enable BCH ECC scheme using ELM for generic platform" (d016dc42cedbf6102e100fa9ecb58462edfb14f8) changed the way software ECC is configured, both during boot, and during ecc switch, in a way that is not backwards compatible with older systems: Older version of omap_gpmc.c always assigned ecc.size = 0 when configuring for software ecc, relying on nand_scan_tail() to select a default for ecc.size (256), while the new version of omap_gpmc.c assigns ecc.size = pagesize, which is likely to not be 256. Since 1 bit hamming sw ecc is only meant to be used by legacy devices, revert to the original behavior. Cc: Igor Grinberg Cc: Tom Rini Cc: Scott Wood Cc: Pekon Gupta Signed-off-by: Nikita Kiryanov Acked-by: Pekon Gupta diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index 2067969..d5c4c22 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -776,7 +776,7 @@ static int omap_select_ecc_scheme(struct nand_chip *nand, bch_priv.type = 0; nand->ecc.mode = NAND_ECC_SOFT; nand->ecc.layout = NULL; - nand->ecc.size = pagesize; + nand->ecc.size = 0; bch->ecc_scheme = OMAP_ECC_HAM1_CODE_SW; break; -- cgit v0.10.2 From 3ef1eadb4466a68a32cb56e6be98a98061c07673 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 16 Dec 2013 09:59:34 -0500 Subject: nand_util.c: Use '%zd' for length in nand_unlock debug print length is size_t so needs to be '%zd' not '%d' to avoid warnings. Cc: Scott Wood Signed-off-by: Tom Rini diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index eeaa7e8..b292826 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -315,7 +315,7 @@ int nand_unlock(struct mtd_info *mtd, loff_t start, size_t length, int page; struct nand_chip *chip = mtd->priv; - debug("nand_unlock%s: start: %08llx, length: %d!\n", + debug("nand_unlock%s: start: %08llx, length: %zd!\n", allexcept ? " (allexcept)" : "", start, length); /* select the NAND device */ -- cgit v0.10.2 From eb237a15bd793a785feb0c77c459c9f97c6fbe4c Mon Sep 17 00:00:00 2001 From: Nikita Kiryanov Date: Mon, 16 Dec 2013 19:19:01 +0200 Subject: mtd: nand: omap: fix sw->hw->sw ecc switch When switching ecc mode, omap_select_ecc_scheme() assigns the appropriate values into the current nand chip's ecc.layout struct. This is done under the assumption that the struct exists only to store values, so it is OK to overwrite it, but there is at least one situation where this assumption is incorrect: When switching to 1 bit hamming code sw ecc, the job of assigning layout data is outsourced to nand_scan_tail(), which simply assigns into ecc.layout a pointer to an existing struct prefilled with the appropriate values. This struct doubles as both data and layout definition, and therefore shouldn't be overwritten, but on the next switch to hardware ecc, this is exactly what's going to happen. The next time the user switches to software ecc, they're going to get a messed up ecc layout. Prevent this and possible similar bugs by explicitly using the private-to-omap_gpmc.c omap_ecclayout struct when switching ecc mode. Cc: Scott Wood Cc: Pekon Gupta Signed-off-by: Nikita Kiryanov diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index d5c4c22..3405ed5 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -763,7 +763,7 @@ static void __maybe_unused omap_free_bch(struct mtd_info *mtd) static int omap_select_ecc_scheme(struct nand_chip *nand, enum omap_ecc ecc_scheme, unsigned int pagesize, unsigned int oobsize) { struct nand_bch_priv *bch = nand->priv; - struct nand_ecclayout *ecclayout = nand->ecc.layout; + struct nand_ecclayout *ecclayout = &omap_ecclayout; int eccsteps = pagesize / SECTOR_BYTES; int i; @@ -897,6 +897,11 @@ static int omap_select_ecc_scheme(struct nand_chip *nand, debug("nand: error: ecc scheme not enabled or supported\n"); return -EINVAL; } + + /* nand_scan_tail() sets ham1 sw ecc; hw ecc layout is set by driver */ + if (ecc_scheme != OMAP_ECC_HAM1_CODE_SW) + nand->ecc.layout = ecclayout; + return 0; } -- cgit v0.10.2 From fcd052457434d1f86c4e6d5f90d838210ab87d8b Mon Sep 17 00:00:00 2001 From: Nikita Kiryanov Date: Tue, 17 Dec 2013 15:18:01 +0200 Subject: mtd: nand: omap: fix ecc ops assignment when changing ecc If we change to software ecc and then back to hardware ecc, the nand ecc ops pointers are populated with incorrect function pointers. This is related to the way nand_scan_tail() handles assigning functions to ecc ops: If we are switching to software ecc/no ecc, it assigns default functions to the ecc ops pointers unconditionally, but if we are switching to hardware ecc, the default hardware ecc functions are assigned to ops pointers only if these pointers are NULL (so that drivers could set their own functions). In the case of omap_gpmc.c driver, when we switch to sw ecc, sw ecc functions are assigned to ecc ops by nand_scan_tail(), and when we later switch to hw ecc, the ecc ops pointers are not NULL, so nand_scan_tail() does not overwrite them with hw ecc functions. The result: sw ecc functions used to write hw ecc data. Clear the ecc ops pointers in omap_gpmc.c when switching ecc types, so that ops which were not assigned by the driver will get the correct default values from nand_scan_tail(). Cc: Scott Wood Cc: Pekon Gupta Signed-off-by: Nikita Kiryanov diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index 3405ed5..790d538 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -791,6 +791,7 @@ static int omap_select_ecc_scheme(struct nand_chip *nand, bch_priv.control = NULL; bch_priv.type = 0; /* populate ecc specific fields */ + memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl)); nand->ecc.mode = NAND_ECC_HW; nand->ecc.strength = 1; nand->ecc.size = SECTOR_BYTES; @@ -829,6 +830,7 @@ static int omap_select_ecc_scheme(struct nand_chip *nand, } bch_priv.type = ECC_BCH8; /* populate ecc specific fields */ + memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl)); nand->ecc.mode = NAND_ECC_HW; nand->ecc.strength = 8; nand->ecc.size = SECTOR_BYTES; @@ -871,6 +873,7 @@ static int omap_select_ecc_scheme(struct nand_chip *nand, elm_init(); bch_priv.type = ECC_BCH8; /* populate ecc specific fields */ + memset(&nand->ecc, 0, sizeof(struct nand_ecc_ctrl)); nand->ecc.mode = NAND_ECC_HW; nand->ecc.strength = 8; nand->ecc.size = SECTOR_BYTES; -- cgit v0.10.2