* [U-Boot] [PATCH v6 (cosmetic) 1/5] mtd: nand: omap: enable BCH ECC scheme using ELM for generic platform
2013-09-20 9:40 [U-Boot] [PATCH v6 (cosmetic) 0/5] mtd: nand: omap: optimize and clean-up of OMAP NAND driver Pekon Gupta
@ 2013-09-20 9:40 ` Pekon Gupta
2013-09-20 9:40 ` [U-Boot] [PATCH v6 (cosmetic) 2/5] mtd: nand: omap: optimize chip->ecc.hwctl() for H/W ECC schemes Pekon Gupta
` (3 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Pekon Gupta @ 2013-09-20 9:40 UTC (permalink / raw)
To: u-boot
BCH8_ECC scheme implemented in omap_gpmc.c driver has following favours
+-----------------------------------+-----------------+-----------------+
|ECC Scheme | ECC Calculation | Error Detection |
+-----------------------------------+-----------------+-----------------+
|OMAP_ECC_BCH8_CODE_HW |GPMC |ELM H/W engine |
|OMAP_ECC_BCH8_CODE_HW_DETECTION_SW |GPMC |S/W BCH library |
+-----------------------------------+-----------------+-----------------+
Current implementation limits the BCH8_CODE_HW only for AM33xx device family.
(using CONFIG_AM33XX). However, other SoC families (like TI81xx) also have
ELM hardware module, and can support ECC error detection using ELM.
This patch
- removes CONFIG_AM33xx
Thus this driver can be reused by all devices having ELM h/w engine.
- removes CONFIG_NAND_OMAP_BCH8 and adds new CONFIG_NAND_OMAP_ECCSCHEME
CONFIG_NAND_OMAP_ECCSCHEME determines ecc-scheme to be used
refer doc/README.nand and board/ti/am335x/README
- adds omap_select_ecc_scheme()
A common function to handle ecc-scheme related configurations. This
can be used both during device-probe and via user-space u-boot commads
to change ecc-scheme. During device probe ecc-scheme is selected based
on CONFIG_NAND_OMAP_ECCSCHEME.
- enables CONFIG_BCH
S/W library (lib/bch.c) required by OMAP_ECC_BCH8_CODE_HW_DETECTION_SW
is enabled by CONFIG_BCH.
- enables CONFIG_SYS_NAND_ONFI_DETECTION
for auto-detection of ONFI compliant NAND devices
- updates following README doc
doc/README.nand
board/ti/am335x/README
doc/README.omap3
Signed-off-by: Pekon Gupta <pekon@ti.com>
---
arch/arm/include/asm/arch-am33xx/omap_gpmc.h | 52 ++---
doc/README.nand | 30 +++
doc/README.omap3 | 4 +-
drivers/mtd/nand/omap_gpmc.c | 292 ++++++++++++++++-----------
include/configs/am335x_evm.h | 9 +-
include/configs/tricorder.h | 3 +-
6 files changed, 223 insertions(+), 167 deletions(-)
diff --git a/arch/arm/include/asm/arch-am33xx/omap_gpmc.h b/arch/arm/include/asm/arch-am33xx/omap_gpmc.h
index 00ad1d0..62f608e 100644
--- a/arch/arm/include/asm/arch-am33xx/omap_gpmc.h
+++ b/arch/arm/include/asm/arch-am33xx/omap_gpmc.h
@@ -7,42 +7,20 @@
#ifndef __ASM_ARCH_OMAP_GPMC_H
#define __ASM_ARCH_OMAP_GPMC_H
-/* These GPMC_NAND_HW_BCHx_ECC_LAYOUT defines are based on AM33xx ELM */
-#define GPMC_NAND_HW_BCH4_ECC_LAYOUT {\
- .eccbytes = 32,\
- .eccpos = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\
- 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,\
- 28, 29, 30, 31, 32, 33},\
- .oobfree = {\
- {.offset = 34,\
- .length = 30 } } \
-}
+enum omap_ecc {
+ /* 1-bit ECC calculation by Software, Error detection by Software */
+ OMAP_ECC_HAM1_CODE_SW = 1, /* avoid un-initialized int can be 0x0 */
+ /* 1-bit ECC calculation by GPMC, Error detection by Software */
+ /* ECC layout compatible to legacy ROMCODE. */
+ OMAP_ECC_HAM1_CODE_HW,
+ /* 4-bit ECC calculation by GPMC, Error detection by Software */
+ OMAP_ECC_BCH4_CODE_HW_DETECTION_SW,
+ /* 4-bit ECC calculation by GPMC, Error detection by ELM */
+ OMAP_ECC_BCH4_CODE_HW,
+ /* 8-bit ECC calculation by GPMC, Error detection by Software */
+ OMAP_ECC_BCH8_CODE_HW_DETECTION_SW,
+ /* 8-bit ECC calculation by GPMC, Error detection by ELM */
+ OMAP_ECC_BCH8_CODE_HW,
+};
-#define GPMC_NAND_HW_BCH8_ECC_LAYOUT {\
- .eccbytes = 56,\
- .eccpos = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\
- 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,\
- 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,\
- 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,\
- 52, 53, 54, 55, 56, 57},\
- .oobfree = {\
- {.offset = 58,\
- .length = 6 } } \
-}
-
-#define GPMC_NAND_HW_BCH16_ECC_LAYOUT {\
- .eccbytes = 104,\
- .eccpos = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\
- 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,\
- 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,\
- 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,\
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,\
- 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,\
- 76, 77, 78, 79, 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},\
- .oobfree = {\
- {.offset = 106,\
- .length = 8 } } \
-}
#endif /* __ASM_ARCH_OMAP_GPMC_H */
diff --git a/doc/README.nand b/doc/README.nand
index 913e9b5..f9c921e 100644
--- a/doc/README.nand
+++ b/doc/README.nand
@@ -169,6 +169,36 @@ Configuration Options:
Please convert your driver even if you don't need the extra
flexibility, so that one day we can eliminate the old mechanism.
+ CONFIG_SYS_NAND_ONFI_DETECTION
+ Enables detection of ONFI compliant devices during probe.
+ And fetching device parameters flashed on device, by parsing
+ ONFI parameter page.
+
+ CONFIG_BCH
+ Enables software based BCH ECC algorithm present in lib/bch.c
+ This is used by SoC platforms which do not have built-in ELM
+ hardware engine required for BCH ECC correction.
+
+Platform specific options
+=========================
+
+ CONFIG_NAND_OMAP_ECCSCHEME
+ On OMAP platforms, this specifies NAND ECC scheme.
+ 1 - HAM1_SW 1-bit Hamming code using software library
+ (for legacy devices only)
+ 2 - HAM1_HW 1-bit Hamming code using GPMC hardware engine
+ (for legacy devices only)
+ 3 - BCH4_SW 4-bit BCH code (unsupported)
+ 4 - BCH4_HW 4-bit BCH code (unsupported)
+ 5 - BCH8_SW 8-bit BCH code with
+ - ecc calculation using GPMC hardware engine,
+ - error detection using software library.
+ - requires CONFIG_BCH to enable software BCH library
+ (For legacy device which do not have ELM h/w engine)
+ 6 - BCH8_HW 8-bit BCH code with
+ - ecc calculation using GPMC hardware engine,
+ - error detection using ELM hardware engine.
+
NOTE:
=====
diff --git a/doc/README.omap3 b/doc/README.omap3
index 1fbe79d..fd85b22 100644
--- a/doc/README.omap3
+++ b/doc/README.omap3
@@ -161,8 +161,8 @@ BCH8
To enable hardware assisted BCH8 (8-bit BCH [Bose, Chaudhuri, Hocquenghem]) on
OMAP3 devices we can use the BCH library in lib/bch.c. To do so add CONFIG_BCH
-to enable the library and CONFIG_NAND_OMAP_BCH8 to to enable hardware assisted
-syndrom generation to your board config.
+to enable the library and set CONFIG_NAND_OMAP_ECCSCHEME=5 (as per README.nand)
+to enable BCH8_SW ecc scheme.
The NAND OOB layout is the same as in linux kernel, if the linux kernel BCH8
implementation for OMAP3 works for you so the u-boot version should also.
When you require the SPL to read with BCH8 there are two more configs to
diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c
index ec1787f..6b86c49 100644
--- a/drivers/mtd/nand/omap_gpmc.c
+++ b/drivers/mtd/nand/omap_gpmc.c
@@ -15,15 +15,13 @@
#include <linux/bch.h>
#include <linux/compiler.h>
#include <nand.h>
-#ifdef CONFIG_AM33XX
#include <asm/arch/elm.h>
-#endif
+
+#define BADBLOCK_MARKER_LENGTH 2
+#define SECTOR_BYTES 512
static uint8_t cs;
-static __maybe_unused struct nand_ecclayout hw_nand_oob =
- GPMC_NAND_HW_ECC_LAYOUT;
-static __maybe_unused struct nand_ecclayout hw_bch8_nand_oob =
- GPMC_NAND_HW_BCH8_ECC_LAYOUT;
+static __maybe_unused struct nand_ecclayout omap_ecclayout;
/*
* omap_nand_hwcontrol - Set the address pointers corretly for the
@@ -233,6 +231,7 @@ struct nand_bch_priv {
uint8_t type;
uint8_t nibbles;
struct bch_control *control;
+ enum omap_ecc ecc_scheme;
};
/* bch types */
@@ -274,17 +273,15 @@ static void omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode)
{
uint32_t val;
uint32_t dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1;
-#ifdef CONFIG_AM33XX
uint32_t unused_length = 0;
-#endif
uint32_t wr_mode = BCH_WRAPMODE_6;
struct nand_bch_priv *bch = chip->priv;
/* Clear the ecc result registers, select ecc reg as 1 */
writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control);
-#ifdef CONFIG_AM33XX
- wr_mode = BCH_WRAPMODE_1;
+ if (bch->ecc_scheme == OMAP_ECC_BCH8_CODE_HW) {
+ wr_mode = BCH_WRAPMODE_1;
switch (bch->nibbles) {
case ECC_BCH4_NIBBLES:
@@ -320,7 +317,7 @@ static void omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode)
val |= (unused_length << 22);
break;
}
-#else
+ } else {
/*
* This ecc_size_config setting is for BCH sw library.
*
@@ -333,7 +330,7 @@ static void omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode)
* size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area)
*/
val = (32 << 22) | (0 << 12);
-#endif
+ }
/* ecc size configuration */
writel(val, &gpmc_cfg->ecc_size_config);
@@ -376,9 +373,8 @@ static void __maybe_unused omap_ecc_disable(struct mtd_info *mtd)
}
/*
- * BCH8 support (needs ELM and thus AM33xx-only)
+ * BCH support using ELM module
*/
-#ifdef CONFIG_AM33XX
/*
* omap_read_bch8_result - Read BCH result for BCH8 level
*
@@ -631,20 +627,19 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
}
return 0;
}
-#endif /* CONFIG_AM33XX */
/*
* OMAP3 BCH8 support (with BCH library)
*/
-#ifdef CONFIG_NAND_OMAP_BCH8
+#ifdef CONFIG_BCH
/*
- * omap_calculate_ecc_bch - Read BCH ECC result
+ * omap_calculate_ecc_bch_sw - Read BCH ECC result
*
* @mtd: MTD device structure
* @dat: The pointer to data on which ecc is computed (unused here)
* @ecc: The ECC output buffer
*/
-static int omap_calculate_ecc_bch(struct mtd_info *mtd, const uint8_t *dat,
+static int omap_calculate_ecc_bch_sw(struct mtd_info *mtd, const uint8_t *dat,
uint8_t *ecc)
{
int ret = 0;
@@ -689,13 +684,13 @@ static int omap_calculate_ecc_bch(struct mtd_info *mtd, const uint8_t *dat,
}
/**
- * omap_correct_data_bch - Decode received data and correct errors
+ * omap_correct_data_bch_sw - Decode received data and correct errors
* @mtd: MTD device structure
* @data: page data
* @read_ecc: ecc read from nand flash
* @calc_ecc: ecc read from HW ECC registers
*/
-static int omap_correct_data_bch(struct mtd_info *mtd, u_char *data,
+static int omap_correct_data_bch_sw(struct mtd_info *mtd, u_char *data,
u_char *read_ecc, u_char *calc_ecc)
{
int i, count;
@@ -752,7 +747,117 @@ static void __maybe_unused omap_free_bch(struct mtd_info *mtd)
chip_priv->control = NULL;
}
}
-#endif /* CONFIG_NAND_OMAP_BCH8 */
+#endif /* CONFIG_BCH */
+
+/**
+ * omap_select_ecc_scheme - configures driver for particular ecc-scheme
+ * @nand: NAND chip device structure
+ * @ecc_scheme: ecc scheme to configure
+ * @pagesize: number of main-area bytes per page of NAND device
+ * @oobsize: number of OOB/spare bytes per page of NAND device
+ */
+static int omap_select_ecc_scheme(struct nand_chip *nand, int ecc_scheme,
+ unsigned int pagesize, unsigned int oobsize) {
+ struct nand_bch_priv *bch = nand->priv;
+ struct nand_ecclayout *ecclayout = nand->ecc.layout;
+ int i;
+
+ switch (ecc_scheme) {
+ case OMAP_ECC_HAM1_CODE_SW:
+ debug("nand: selected OMAP_ECC_HAM1_CODE_SW\n");
+ nand->ecc.mode = NAND_ECC_SOFT;
+ nand->ecc.layout = NULL;
+ nand->ecc.size = 0;
+ nand->ecc.strength = 1;
+ bch->ecc_scheme = OMAP_ECC_HAM1_CODE_SW;
+ break;
+ case OMAP_ECC_HAM1_CODE_HW:
+ debug("nand: selected OMAP_ECC_HAM1_CODE_HW\n");
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.strength = 1;
+ nand->ecc.size = 512;
+ nand->ecc.bytes = 3;
+ nand->ecc.hwctl = omap_enable_hwecc;
+ nand->ecc.correct = omap_correct_data;
+ nand->ecc.calculate = omap_calculate_ecc;
+ /* define ecc-layout */
+ ecclayout->eccbytes = nand->ecc.bytes *
+ (pagesize / SECTOR_BYTES);
+ for (i = 0; i < ecclayout->eccbytes; i++)
+ ecclayout->eccpos[i] = i +
+ BADBLOCK_MARKER_LENGTH;
+ ecclayout->oobfree[0].offset = i +
+ BADBLOCK_MARKER_LENGTH;
+ ecclayout->oobfree[0].length = oobsize - nand->ecc.bytes -
+ BADBLOCK_MARKER_LENGTH;
+ bch->ecc_scheme = OMAP_ECC_HAM1_CODE_HW;
+ break;
+#ifdef CONFIG_BCH
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+ debug("nand: selected OMAP_ECC_BCH8_CODE_HW_DETECTION_SW\n");
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.strength = 8;
+ nand->ecc.size = 512;
+ nand->ecc.bytes = 13;
+ nand->ecc.hwctl = omap_enable_ecc_bch;
+ nand->ecc.correct = omap_correct_data_bch_sw;
+ nand->ecc.calculate = omap_calculate_ecc_bch_sw;
+ /* BCH SW library is used for error detection */
+ bch_priv.control = init_bch(13, 8, 0x201b);
+ if (!bch_priv.control) {
+ printf("nand: error: could not init_bch()\n");
+ return -ENODEV;
+ }
+ /* define ecc-layout */
+ ecclayout->eccbytes = nand->ecc.bytes *
+ (pagesize / SECTOR_BYTES);
+ for (i = 0; i < ecclayout->eccbytes; i++)
+ ecclayout->eccpos[i] = i + (oobsize -
+ ecclayout->eccbytes);
+ ecclayout->oobfree[0].offset = BADBLOCK_MARKER_LENGTH;
+ ecclayout->oobfree[0].length = oobsize - nand->ecc.bytes -
+ BADBLOCK_MARKER_LENGTH;
+ omap_hwecc_init_bch(nand, NAND_ECC_READ);
+ bch->ecc_scheme = OMAP_ECC_BCH8_CODE_HW_DETECTION_SW;
+ break;
+#endif
+ case OMAP_ECC_BCH8_CODE_HW:
+ debug("nand: selected OMAP_ECC_BCH8_CODE_HW\n");
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.strength = 8;
+ nand->ecc.size = 512;
+ nand->ecc.bytes = 14;
+ nand->ecc.hwctl = omap_enable_ecc_bch;
+ nand->ecc.correct = omap_correct_data_bch;
+ nand->ecc.calculate = omap_calculate_ecc_bch;
+ nand->ecc.read_page = omap_read_page_bch;
+ /* ELM is used for ECC error detection */
+ elm_init();
+ /* define ecc-layout */
+ ecclayout->eccbytes = nand->ecc.bytes *
+ (pagesize / SECTOR_BYTES);
+ for (i = 0; i < ecclayout->eccbytes; i++)
+ ecclayout->eccpos[i] = i +
+ BADBLOCK_MARKER_LENGTH;
+ ecclayout->oobfree[0].offset = i +
+ BADBLOCK_MARKER_LENGTH;
+ ecclayout->oobfree[0].length = oobsize - nand->ecc.bytes -
+ BADBLOCK_MARKER_LENGTH;
+ bch->ecc_scheme = OMAP_ECC_BCH8_CODE_HW;
+ break;
+ default:
+ debug("nand: error: ecc scheme not enabled or supported\n");
+ return -EINVAL;
+ }
+
+ /* check if NAND spare/OOB has enough bytes to accomodate ecclayout */
+ if ((ecclayout->eccbytes + BADBLOCK_MARKER_LENGTH) > oobsize) {
+ printf("nand: error: insufficient OOB bytes. require=%d\n", (
+ ecclayout->eccbytes + BADBLOCK_MARKER_LENGTH));
+ return -EINVAL;
+ }
+ return 0;
+}
#ifndef CONFIG_SPL_BUILD
/*
@@ -763,77 +868,53 @@ static void __maybe_unused omap_free_bch(struct mtd_info *mtd)
* @eccstrength - the number of bits that could be corrected
* (1 - hamming, 4 - BCH4, 8 - BCH8, 16 - BCH16)
*/
-void omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength)
+int omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength)
{
struct nand_chip *nand;
struct mtd_info *mtd;
+ struct nand_bch_priv *bch;
+ int err = 0;
if (nand_curr_device < 0 ||
nand_curr_device >= CONFIG_SYS_MAX_NAND_DEVICE ||
!nand_info[nand_curr_device].name) {
- printf("Error: Can't switch ecc, no devices available\n");
- return;
+ printf("nand: error: no NAND devices found\n");
+ return -ENODEV;
}
mtd = &nand_info[nand_curr_device];
nand = mtd->priv;
-
+ bch = nand->priv;
nand->options |= NAND_OWN_BUFFERS;
-
- /* Reset ecc interface */
- nand->ecc.mode = NAND_ECC_NONE;
- nand->ecc.read_page = NULL;
- nand->ecc.write_page = NULL;
- nand->ecc.read_oob = NULL;
- nand->ecc.write_oob = NULL;
- nand->ecc.hwctl = NULL;
- nand->ecc.correct = NULL;
- nand->ecc.calculate = NULL;
- nand->ecc.strength = eccstrength;
-
/* Setup the ecc configurations again */
if (hardware) {
if (eccstrength == 1) {
- nand->ecc.mode = NAND_ECC_HW;
- nand->ecc.layout = &hw_nand_oob;
- nand->ecc.size = 512;
- nand->ecc.bytes = 3;
- nand->ecc.hwctl = omap_enable_hwecc;
- nand->ecc.correct = omap_correct_data;
- nand->ecc.calculate = omap_calculate_ecc;
- omap_hwecc_init(nand);
- printf("1-bit hamming HW ECC selected\n");
- }
-#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8)
- else if (eccstrength == 8) {
- nand->ecc.mode = NAND_ECC_HW;
- nand->ecc.layout = &hw_bch8_nand_oob;
- nand->ecc.size = 512;
-#ifdef CONFIG_AM33XX
- nand->ecc.bytes = 14;
- nand->ecc.read_page = omap_read_page_bch;
-#else
- nand->ecc.bytes = 13;
-#endif
- nand->ecc.hwctl = omap_enable_ecc_bch;
- nand->ecc.correct = omap_correct_data_bch;
- nand->ecc.calculate = omap_calculate_ecc_bch;
- omap_hwecc_init_bch(nand, NAND_ECC_READ);
- printf("8-bit BCH HW ECC selected\n");
+ err = omap_select_ecc_scheme(nand,
+ OMAP_ECC_HAM1_CODE_HW,
+ mtd->writesize, mtd->oobsize);
+ } else if (eccstrength == 8) {
+ err = omap_select_ecc_scheme(nand,
+ OMAP_ECC_BCH8_CODE_HW,
+ mtd->writesize, mtd->oobsize);
+ } else {
+ printf("nand: error: unsupported ECC scheme\n");
+ return -EINVAL;
}
-#endif
} else {
- nand->ecc.mode = NAND_ECC_SOFT;
- /* Use mtd default settings */
- nand->ecc.layout = NULL;
- nand->ecc.size = 0;
- printf("SW ECC selected\n");
+ err = omap_select_ecc_scheme(nand, OMAP_ECC_HAM1_CODE_SW,
+ mtd->writesize, mtd->oobsize);
+ }
+ if (err) {
+ printf("nand: error: could not switch ecc, reverting\n");
+ omap_select_ecc_scheme(nand, bch->ecc_scheme,
+ mtd->writesize, mtd->oobsize);
+ return -EINVAL;
}
/* Update NAND handling after ECC mode switch */
nand_scan_tail(mtd);
-
nand->options &= ~NAND_OWN_BUFFERS;
+ return 0;
}
#endif /* CONFIG_SPL_BUILD */
@@ -854,6 +935,10 @@ void omap_nand_switch_ecc(uint32_t hardware, uint32_t eccstrength)
*/
int board_nand_init(struct nand_chip *nand)
{
+ struct mtd_info *mtd;
+#ifdef CONFIG_SPL_BUILD
+ struct mtd_info nand_info[CONFIG_SYS_MAX_NAND_DEVICE];
+#endif
int32_t gpmc_config = 0;
cs = 0;
@@ -873,7 +958,7 @@ int board_nand_init(struct nand_chip *nand)
cs++;
}
if (cs >= GPMC_MAX_CS) {
- printf("NAND: Unable to find NAND settings in "
+ printf("nand: error: Unable to find NAND settings in "
"GPMC Configuration - quitting\n");
return -ENODEV;
}
@@ -883,66 +968,27 @@ int board_nand_init(struct nand_chip *nand)
gpmc_config |= 0x10;
writel(gpmc_config, &gpmc_cfg->config);
+ mtd = &nand_info[0];
+ mtd->priv = nand;
nand->IO_ADDR_R = (void __iomem *)&gpmc_cfg->cs[cs].nand_dat;
nand->IO_ADDR_W = (void __iomem *)&gpmc_cfg->cs[cs].nand_cmd;
-
- nand->cmd_ctrl = omap_nand_hwcontrol;
- nand->options = NAND_NO_PADDING | NAND_CACHEPRG;
+ nand->priv = &bch_priv;
+ nand->cmd_ctrl = omap_nand_hwcontrol;
+ nand->options |= NAND_NO_PADDING | NAND_CACHEPRG;
/* If we are 16 bit dev, our gpmc config tells us that */
if ((readl(&gpmc_cfg->cs[cs].config1) & 0x3000) == 0x1000)
nand->options |= NAND_BUSWIDTH_16;
nand->chip_delay = 100;
-
-#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8)
-#ifdef CONFIG_AM33XX
- /* AM33xx uses the ELM */
- /* required in case of BCH */
- elm_init();
-#else
- /*
- * Whereas other OMAP based SoC do not have the ELM, they use the BCH
- * SW library.
- */
- bch_priv.control = init_bch(13, 8, 0x201b /* hw polynominal */);
- if (!bch_priv.control) {
- puts("Could not init_bch()\n");
- return -ENODEV;
+ nand->ecc.layout = &omap_ecclayout;
+
+ /* select ECC scheme */
+ if (omap_select_ecc_scheme(nand, CONFIG_NAND_OMAP_ECCSCHEME,
+ CONFIG_SYS_NAND_PAGE_SIZE,
+ CONFIG_SYS_NAND_OOBSIZE)) {
+ printf("nand: error: unsupported ecc scheme specified\n");
+ return -EINVAL;
}
-#endif
- /* BCH info that will be correct for SPL or overridden otherwise. */
- nand->priv = &bch_priv;
-#endif
-
- /* Default ECC mode */
-#if defined(CONFIG_AM33XX) || defined(CONFIG_NAND_OMAP_BCH8)
- nand->ecc.mode = NAND_ECC_HW;
- nand->ecc.layout = &hw_bch8_nand_oob;
- nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
- nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
- nand->ecc.strength = 8;
- nand->ecc.hwctl = omap_enable_ecc_bch;
- nand->ecc.correct = omap_correct_data_bch;
- nand->ecc.calculate = omap_calculate_ecc_bch;
-#ifdef CONFIG_AM33XX
- nand->ecc.read_page = omap_read_page_bch;
-#endif
- omap_hwecc_init_bch(nand, NAND_ECC_READ);
-#else
-#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_NAND_SOFTECC)
- nand->ecc.mode = NAND_ECC_SOFT;
-#else
- nand->ecc.mode = NAND_ECC_HW;
- nand->ecc.layout = &hw_nand_oob;
- nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
- nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
- nand->ecc.hwctl = omap_enable_hwecc;
- nand->ecc.correct = omap_correct_data;
- nand->ecc.calculate = omap_calculate_ecc;
- nand->ecc.strength = 1;
- omap_hwecc_init(nand);
-#endif
-#endif
#ifdef CONFIG_SPL_BUILD
if (nand->options & NAND_BUSWIDTH_16)
diff --git a/include/configs/am335x_evm.h b/include/configs/am335x_evm.h
index 3de30fc..978bca7 100644
--- a/include/configs/am335x_evm.h
+++ b/include/configs/am335x_evm.h
@@ -216,11 +216,11 @@
#ifdef CONFIG_NAND
#define CONFIG_SYS_NAND_5_ADDR_CYCLE
-#define CONFIG_SYS_NAND_PAGE_COUNT (CONFIG_SYS_NAND_BLOCK_SIZE / \
- CONFIG_SYS_NAND_PAGE_SIZE)
+#define CONFIG_SYS_NAND_BLOCK_SIZE 131072
#define CONFIG_SYS_NAND_PAGE_SIZE 2048
#define CONFIG_SYS_NAND_OOBSIZE 64
-#define CONFIG_SYS_NAND_BLOCK_SIZE (128*1024)
+#define CONFIG_SYS_NAND_PAGE_COUNT (CONFIG_SYS_NAND_BLOCK_SIZE / \
+ CONFIG_SYS_NAND_PAGE_SIZE)
#define CONFIG_SYS_NAND_BAD_BLOCK_POS NAND_LARGE_BADBLOCK_POS
#define CONFIG_SYS_NAND_ECCPOS { 2, 3, 4, 5, 6, 7, 8, 9, \
10, 11, 12, 13, 14, 15, 16, 17, \
@@ -232,9 +232,10 @@
#define CONFIG_SYS_NAND_ECCSIZE 512
#define CONFIG_SYS_NAND_ECCBYTES 14
-
+#define CONFIG_SYS_NAND_ONFI_DETECTION
#define CONFIG_SYS_NAND_U_BOOT_START CONFIG_SYS_TEXT_BASE
#define CONFIG_SYS_NAND_U_BOOT_OFFS 0x80000
+#define CONFIG_NAND_OMAP_ECCSCHEME 6
#endif
#endif
diff --git a/include/configs/tricorder.h b/include/configs/tricorder.h
index a9b2714..6daea50 100644
--- a/include/configs/tricorder.h
+++ b/include/configs/tricorder.h
@@ -110,7 +110,6 @@
#define CONFIG_SYS_MAX_NAND_DEVICE 1 /* Max number of NAND */
/* devices */
-#define CONFIG_NAND_OMAP_BCH8
#define CONFIG_BCH
/* commands to include */
@@ -298,6 +297,8 @@
#define CONFIG_SYS_NAND_ECCSIZE 512
#define CONFIG_SYS_NAND_ECCBYTES 13
+#define CONFIG_SYS_NAND_ECCSCHEME 5
+#define CONFIG_SYS_NAND_ONFI_DETECTION
#define CONFIG_SYS_NAND_U_BOOT_START CONFIG_SYS_TEXT_BASE
--
1.8.1
^ permalink raw reply related [flat|nested] 7+ messages in thread* [U-Boot] [PATCH v6 (cosmetic) 3/5] mtd: nand: omap: optimize chip->ecc.calculate() for H/W ECC schemes
2013-09-20 9:40 [U-Boot] [PATCH v6 (cosmetic) 0/5] mtd: nand: omap: optimize and clean-up of OMAP NAND driver Pekon Gupta
2013-09-20 9:40 ` [U-Boot] [PATCH v6 (cosmetic) 1/5] mtd: nand: omap: enable BCH ECC scheme using ELM for generic platform Pekon Gupta
2013-09-20 9:40 ` [U-Boot] [PATCH v6 (cosmetic) 2/5] mtd: nand: omap: optimize chip->ecc.hwctl() for H/W ECC schemes Pekon Gupta
@ 2013-09-20 9:40 ` Pekon Gupta
2013-09-20 9:40 ` [U-Boot] [PATCH v6 (cosmetic) 4/5] mtd: nand: omap: optimized chip->ecc.correct() " Pekon Gupta
2013-09-20 9:40 ` [U-Boot] [PATCH v6 (cosmetic) 5/5] board/ti/am335x/README: update for NAND boot Pekon Gupta
4 siblings, 0 replies; 7+ messages in thread
From: Pekon Gupta @ 2013-09-20 9:40 UTC (permalink / raw)
To: u-boot
chip->ecc.calculate() is used for calculating and fetching of ECC syndrome by
processing the data passed during Read/Write accesses.
All H/W based ECC schemes use GPMC controller to calculate ECC syndrome.
But each BCHx_ECC scheme has its own implemetation of post-processing and
fetching ECC syndrome from GPMC controller.
This patch updates OMAP_ECC_BCH8_CODE_HW ECC scheme in following way:
- merges multiple chip->calculate API for different ECC schemes
omap_calculate_ecc() + omap_calculate_ecc_bch() + omap_calculate_ecc_bch_sw()
==> omap_calculate_ecc()
- removes omap_ecc_disable() and instead uses it as inline.
Signed-off-by: Pekon Gupta <pekon@ti.com>
---
drivers/mtd/nand/omap_gpmc.c | 220 ++++++++++++-------------------------------
1 file changed, 59 insertions(+), 161 deletions(-)
diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c
index cf0da60..9bfa405 100644
--- a/drivers/mtd/nand/omap_gpmc.c
+++ b/drivers/mtd/nand/omap_gpmc.c
@@ -21,7 +21,10 @@
#define SECTOR_BYTES 512
#define ECCCLEAR (0x1 << 8)
#define ECCRESULTREG1 (0x1 << 0)
-
+#ifdef CONFIG_BCH
+static u8 bch8_polynomial[] = {0xef, 0x51, 0x2e, 0x09, 0xed, 0x93, 0x9a, 0xc2,
+ 0x97, 0x79, 0xe5, 0x24, 0xb5};
+#endif
static uint8_t cs;
static __maybe_unused struct nand_ecclayout omap_ecclayout;
@@ -143,42 +146,6 @@ static int __maybe_unused omap_correct_data(struct mtd_info *mtd, uint8_t *dat,
}
/*
- * omap_calculate_ecc - Generate non-inverted ECC bytes.
- *
- * Using noninverted ECC can be considered ugly since writing a blank
- * page ie. padding will clear the ECC bytes. This is no problem as
- * long nobody is trying to write data on the seemingly unused page.
- * Reading an erased page will produce an ECC mismatch between
- * generated and read ECC bytes that has to be dealt with separately.
- * E.g. if page is 0xFF (fresh erased), and if HW ECC engine within GPMC
- * is used, the result of read will be 0x0 while the ECC offsets of the
- * spare area will be 0xFF which will result in an ECC mismatch.
- * @mtd: MTD structure
- * @dat: unused
- * @ecc_code: ecc_code buffer
- */
-static int __maybe_unused omap_calculate_ecc(struct mtd_info *mtd,
- const uint8_t *dat, uint8_t *ecc_code)
-{
- u_int32_t val;
-
- /* Start Reading from HW ECC1_Result = 0x200 */
- val = readl(&gpmc_cfg->ecc1_result);
-
- ecc_code[0] = val & 0xFF;
- ecc_code[1] = (val >> 16) & 0xFF;
- ecc_code[2] = ((val >> 8) & 0x0F) | ((val >> 20) & 0xF0);
-
- /*
- * Stop reading anymore ECC vals and clear old results
- * enable will be called if more reads are required
- */
- writel(0x000, &gpmc_cfg->ecc_config);
-
- return 0;
-}
-
-/*
* Generic BCH interface
*/
struct nand_bch_priv {
@@ -194,12 +161,7 @@ struct nand_bch_priv {
#define ECC_BCH8 1
#define ECC_BCH16 2
-/* GPMC ecc engine settings */
-#define BCH_WRAPMODE_1 1 /* BCH wrap mode 1 */
-#define BCH_WRAPMODE_6 6 /* BCH wrap mode 6 */
-
/* BCH nibbles for diff bch levels */
-#define NAND_ECC_HW_BCH ((uint8_t)(NAND_ECC_HW_OOB_FIRST) + 1)
#define ECC_BCH4_NIBBLES 13
#define ECC_BCH8_NIBBLES 26
#define ECC_BCH16_NIBBLES 52
@@ -211,7 +173,6 @@ struct nand_bch_priv {
* When some users with other BCH strength will exists this have to change!
*/
static __maybe_unused struct nand_bch_priv bch_priv = {
- .mode = NAND_ECC_HW_BCH,
.type = ECC_BCH8,
.nibbles = ECC_BCH8_NIBBLES,
.control = NULL
@@ -284,54 +245,71 @@ static void omap_enable_hwecc(struct mtd_info *mtd, int32_t mode)
}
/*
- * omap_ecc_disable - Disable H/W ECC calculation
- *
- * @mtd: MTD device structure
- */
-static void __maybe_unused omap_ecc_disable(struct mtd_info *mtd)
-{
- writel((readl(&gpmc_cfg->ecc_config) & ~0x1), &gpmc_cfg->ecc_config);
-}
-
-/*
- * BCH support using ELM module
- */
-/*
- * omap_read_bch8_result - Read BCH result for BCH8 level
+ * omap_calculate_ecc - Read ECC result
+ * @mtd: MTD structure
+ * @dat: unused
+ * @ecc_code: ecc_code buffer
*
- * @mtd: MTD device structure
- * @big_endian: When set read register 3 first
- * @ecc_code: Read syndrome from BCH result registers
+ * Using noninverted ECC can be considered ugly since writing a blank
+ * page ie. padding will clear the ECC bytes. This is no problem as
+ * long nobody is trying to write data on the seemingly unused page.
+ * Reading an erased page will produce an ECC mismatch between
+ * generated and read ECC bytes that has to be dealt with separately.
+ * E.g. if page is 0xFF (fresh erased), and if HW ECC engine within GPMC
+ * is used, the result of read will be 0x0 while the ECC offsets of the
+ * spare area will be 0xFF which will result in an ECC mismatch.
*/
-static void omap_read_bch8_result(struct mtd_info *mtd, uint8_t big_endian,
+static int omap_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
uint8_t *ecc_code)
{
- uint32_t *ptr;
+ struct nand_chip *chip = mtd->priv;
+ struct nand_bch_priv *bch = chip->priv;
+ uint32_t *ptr, val = 0;
int8_t i = 0, j;
- if (big_endian) {
+ switch (bch->ecc_scheme) {
+ case OMAP_ECC_HAM1_CODE_HW:
+ val = readl(&gpmc_cfg->ecc1_result);
+ ecc_code[0] = val & 0xFF;
+ ecc_code[1] = (val >> 16) & 0xFF;
+ ecc_code[2] = ((val >> 8) & 0x0F) | ((val >> 20) & 0xF0);
+ break;
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+ case OMAP_ECC_BCH8_CODE_HW:
ptr = &gpmc_cfg->bch_result_0_3[0].bch_result_x[3];
- ecc_code[i++] = readl(ptr) & 0xFF;
+ val = readl(ptr);
+ ecc_code[i++] = (val >> 0) & 0xFF;
ptr--;
for (j = 0; j < 3; j++) {
- ecc_code[i++] = (readl(ptr) >> 24) & 0xFF;
- ecc_code[i++] = (readl(ptr) >> 16) & 0xFF;
- ecc_code[i++] = (readl(ptr) >> 8) & 0xFF;
- ecc_code[i++] = readl(ptr) & 0xFF;
+ val = readl(ptr);
+ ecc_code[i++] = (val >> 24) & 0xFF;
+ ecc_code[i++] = (val >> 16) & 0xFF;
+ ecc_code[i++] = (val >> 8) & 0xFF;
+ ecc_code[i++] = (val >> 0) & 0xFF;
ptr--;
}
- } else {
- ptr = &gpmc_cfg->bch_result_0_3[0].bch_result_x[0];
- for (j = 0; j < 3; j++) {
- ecc_code[i++] = readl(ptr) & 0xFF;
- ecc_code[i++] = (readl(ptr) >> 8) & 0xFF;
- ecc_code[i++] = (readl(ptr) >> 16) & 0xFF;
- ecc_code[i++] = (readl(ptr) >> 24) & 0xFF;
- ptr++;
- }
- ecc_code[i++] = readl(ptr) & 0xFF;
- ecc_code[i++] = 0; /* 14th byte is always zero */
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* ECC scheme specific syndrome customizations */
+ switch (bch->ecc_scheme) {
+ case OMAP_ECC_HAM1_CODE_HW:
+ break;
+ case OMAP_ECC_BCH8_CODE_HW_DETECTION_SW:
+#ifdef CONFIG_BCH
+ for (i = 0; i < chip->ecc.bytes; i++)
+ *(ecc_code + i) = *(ecc_code + i) ^
+ bch8_polynomial[i];
+#endif
+ break;
+ case OMAP_ECC_BCH8_CODE_HW:
+ ecc_code[chip->ecc.bytes - 1] = 0x00;
+ break;
+ default:
+ return -EINVAL;
}
+ return 0;
}
/*
@@ -370,35 +348,6 @@ static void omap_rotate_ecc_bch(struct mtd_info *mtd, uint8_t *calc_ecc,
}
/*
- * omap_calculate_ecc_bch - Read BCH ECC result
- *
- * @mtd: MTD structure
- * @dat: unused
- * @ecc_code: ecc_code buffer
- */
-static int omap_calculate_ecc_bch(struct mtd_info *mtd, const uint8_t *dat,
- uint8_t *ecc_code)
-{
- struct nand_chip *chip = mtd->priv;
- struct nand_bch_priv *bch = chip->priv;
- uint8_t big_endian = 1;
- int8_t ret = 0;
-
- if (bch->type == ECC_BCH8)
- omap_read_bch8_result(mtd, big_endian, ecc_code);
- else /* BCH4 and BCH16 currently not supported */
- ret = -1;
-
- /*
- * Stop reading anymore ECC vals and clear old results
- * enable will be called if more reads are required
- */
- omap_ecc_disable(mtd);
-
- return ret;
-}
-
-/*
* omap_fix_errors_bch - Correct bch error in the data
*
* @mtd: MTD device structure
@@ -553,57 +502,6 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip,
* OMAP3 BCH8 support (with BCH library)
*/
#ifdef CONFIG_BCH
-/*
- * omap_calculate_ecc_bch_sw - Read BCH ECC result
- *
- * @mtd: MTD device structure
- * @dat: The pointer to data on which ecc is computed (unused here)
- * @ecc: The ECC output buffer
- */
-static int omap_calculate_ecc_bch_sw(struct mtd_info *mtd, const uint8_t *dat,
- uint8_t *ecc)
-{
- int ret = 0;
- size_t i;
- unsigned long nsectors, val1, val2, val3, val4;
-
- nsectors = ((readl(&gpmc_cfg->ecc_config) >> 4) & 0x7) + 1;
-
- for (i = 0; i < nsectors; i++) {
- /* Read hw-computed remainder */
- val1 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[0]);
- val2 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[1]);
- val3 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[2]);
- val4 = readl(&gpmc_cfg->bch_result_0_3[i].bch_result_x[3]);
-
- /*
- * Add constant polynomial to remainder, in order to get an ecc
- * sequence of 0xFFs for a buffer filled with 0xFFs.
- */
- *ecc++ = 0xef ^ (val4 & 0xFF);
- *ecc++ = 0x51 ^ ((val3 >> 24) & 0xFF);
- *ecc++ = 0x2e ^ ((val3 >> 16) & 0xFF);
- *ecc++ = 0x09 ^ ((val3 >> 8) & 0xFF);
- *ecc++ = 0xed ^ (val3 & 0xFF);
- *ecc++ = 0x93 ^ ((val2 >> 24) & 0xFF);
- *ecc++ = 0x9a ^ ((val2 >> 16) & 0xFF);
- *ecc++ = 0xc2 ^ ((val2 >> 8) & 0xFF);
- *ecc++ = 0x97 ^ (val2 & 0xFF);
- *ecc++ = 0x79 ^ ((val1 >> 24) & 0xFF);
- *ecc++ = 0xe5 ^ ((val1 >> 16) & 0xFF);
- *ecc++ = 0x24 ^ ((val1 >> 8) & 0xFF);
- *ecc++ = 0xb5 ^ (val1 & 0xFF);
- }
-
- /*
- * Stop reading anymore ECC vals and clear old results
- * enable will be called if more reads are required
- */
- omap_ecc_disable(mtd);
-
- return ret;
-}
-
/**
* omap_correct_data_bch_sw - Decode received data and correct errors
* @mtd: MTD device structure
@@ -722,7 +620,7 @@ static int omap_select_ecc_scheme(struct nand_chip *nand, int ecc_scheme,
nand->ecc.bytes = 13;
nand->ecc.hwctl = omap_enable_hwecc;
nand->ecc.correct = omap_correct_data_bch_sw;
- nand->ecc.calculate = omap_calculate_ecc_bch_sw;
+ nand->ecc.calculate = omap_calculate_ecc;
/* BCH SW library is used for error detection */
bch_priv.control = init_bch(13, 8, 0x201b);
if (!bch_priv.control) {
@@ -749,7 +647,7 @@ static int omap_select_ecc_scheme(struct nand_chip *nand, int ecc_scheme,
nand->ecc.bytes = 14;
nand->ecc.hwctl = omap_enable_hwecc;
nand->ecc.correct = omap_correct_data_bch;
- nand->ecc.calculate = omap_calculate_ecc_bch;
+ nand->ecc.calculate = omap_calculate_ecc;
nand->ecc.read_page = omap_read_page_bch;
/* ELM is used for ECC error detection */
elm_init();
--
1.8.1
^ permalink raw reply related [flat|nested] 7+ messages in thread