
Unfortunately, except Nishanth's comments, I didn't get any further help (e.g. from TI) for this yet. So I started to look at this myself. Please forgive everything I missed as I'm no NAND expert. Maybe you like to explain some additional details regarding what I missed ;)
First version of updated NAND patch in attachment. This is RFC only. I'd like to fix anything still open. When this is done and when everybody is fine with this, I will send an updated v3 of whole OMAP3 patch set including the then final NAND patch (and the fixes for all other comments).
Scott Wood wrote:
On Fri, Oct 03, 2008 at 12:40:25PM +0200, dirk.behme@googlemail.com wrote:
+#include <common.h> +#include <asm/io.h> +#include <asm/arch/mem.h> +#include <linux/mtd/nand_ecc.h>
+#if defined(CONFIG_CMD_NAND)
+#include <nand.h>
Move the #ifdef to the Makefile.
Did this. Additionally, I moved OMAP3 NAND driver to drivers/mtd/nand/omap3.c.
+/*
- nand_read_buf16 - [DEFAULT] read chip data into buffer
- @mtd: MTD device structure
- @buf: buffer to store date
- @len: number of bytes to read
- Default read function for 16bit buswith
- */
+static void omap_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{
- int i;
- struct nand_chip *this = mtd->priv;
- u16 *p = (u16 *) buf;
- len >>= 1;
- for (i = 0; i < len; i++)
p[i] = readw(this->IO_ADDR_R);
+}
How does this differ from the default implementation?
It doesn't differ ;)
So I removed this and tried to use default nand_read_buf16() instead:
nand->read_buf = nand_read_buf16;
in board_nand_init(). But this doesn't compile as nand_read_buf16() is static in nand_base.c. How do I use it in OMAP3 NAND driver? Marked it as FIXME in patch.
+static void omap_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
int len)
+{
- int i;
- int j = 0;
- struct nand_chip *chip = mtd->priv;
- for (i = 0; i < len; i++) {
writeb(buf[i], chip->IO_ADDR_W);
for (j = 0; j < 10; j++) ;
- }
+}
+/*
- omap_nand_read_buf - read data from NAND controller into buffer
- @mtd: MTD device structure
- @buf: buffer to store date
- @len: number of bytes to read
- */
+static void omap_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{
- int i;
- int j = 0;
- struct nand_chip *chip = mtd->priv;
- for (i = 0; i < len; i++) {
buf[i] = readb(chip->IO_ADDR_R);
while (GPMC_BUF_EMPTY == (readl(GPMC_STATUS) & GPMC_BUF_FULL));
- }
+}
I'm a bit confused... with 16-bit NAND, you check GMPC_STATUS[BUF_FULL] when writing, but with 8-bit NAND, you check it when reading? And 8-bit writes have an arbitrary, cpu-speed-dependent delay, and 16-bit reads have no delay at all?
Removed 8-bit stuff completely as it isn't used.
+static void omap_hwecc_init(struct nand_chip *chip) +{
- unsigned long val = 0x0;
- /* Init ECC Control Register */
- /* Clear all ECC | Enable Reg1 */
- val = ((0x00000001 << 8) | 0x00000001);
- __raw_writel(val, GPMC_BASE + GPMC_ECC_CONTROL);
- __raw_writel(0x3fcff000, GPMC_BASE + GPMC_ECC_SIZE_CONFIG);
+}
Why raw?
Removed all _raw_ . At ARM, it seems that __raw_writex()/readx() are the same as writex()/readx().
+/*
- omap_correct_data - Compares the ecc read from nand spare area with
ECC registers values
and corrects one bit error if it has occured
- @mtd: MTD device structure
- @dat: page data
- @read_ecc: ecc read from nand flash
- @calc_ecc: ecc read from ECC registers
- */
+static int omap_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
+{
- return 0;
+}
This obviously is not correcting anything. If the errors are corrected automatically by the controller, say so in the comments.
I replaced this with the version from Nishanth's U-Boot v2:
http://git.denx.de/?p=u-boot/u-boot-v2.git;a=blob;f=drivers/nand/nand_omap_g...
+/*
- 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.
Is this a hardware limitation? If so, say so in the comment. If not, why do it this way?
Don't know.
All: Any help?
- @mtd: MTD structure
- @dat: unused
- @ecc_code: ecc_code buffer
- */
+static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
u_char *ecc_code)
+{
- unsigned long val = 0x0;
- unsigned long reg;
- /* Start Reading from HW ECC1_Result = 0x200 */
- reg = (unsigned long) (GPMC_BASE + GPMC_ECC1_RESULT);
- val = __raw_readl(reg);
- *ecc_code++ = ECC_P1_128_E(val);
- *ecc_code++ = ECC_P1_128_O(val);
- *ecc_code++ = ECC_P512_2048_E(val) | ECC_P512_2048_O(val) << 4;
These macros are used nowhere else; why obscure what it's doing by moving it to the top of the file?
I replaced this with the version from Nishanth's U-Boot v2 (see link above)
+static void omap_enable_hwecc(struct mtd_info *mtd, int mode) +{
- struct nand_chip *chip = mtd->priv;
- unsigned int val = __raw_readl(GPMC_BASE + GPMC_ECC_CONFIG);
- unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1;
- switch (mode) {
- case NAND_ECC_READ:
__raw_writel(0x101, GPMC_BASE + GPMC_ECC_CONTROL);
/* ECC col width | CS | ECC Enable */
val = (dev_width << 7) | (cs << 1) | (0x1);
break;
- case NAND_ECC_READSYN:
__raw_writel(0x100, GPMC_BASE + GPMC_ECC_CONTROL);
/* ECC col width | CS | ECC Enable */
val = (dev_width << 7) | (cs << 1) | (0x1);
break;
- case NAND_ECC_WRITE:
__raw_writel(0x101, GPMC_BASE + GPMC_ECC_CONTROL);
/* ECC col width | CS | ECC Enable */
val = (dev_width << 7) | (cs << 1) | (0x1);
break;
- default:
printf("Error: Unrecognized Mode[%d]!\n", mode);
break;
- }
- __raw_writel(val, GPMC_BASE + GPMC_ECC_CONFIG);
+}
Is it OK if config gets written before control, or if this whole thing gets done out of order with respect to other raw writes?
Hmm. I replaced this with the version from Nishanth's U-Boot v2 (see link above). If this isn't ok, can you explain a little more?
+static struct nand_ecclayout hw_nand_oob_64 = {
- .eccbytes = 12,
- .eccpos = {
2, 3, 4, 5,
6, 7, 8, 9,
10, 11, 12, 13},
- .oobfree = { {20, 50} } /* don't care */
Bytes 64-69 of a 64-byte OOB are free? What don't we care about?
Don't know (or understand?).
All: Any help?
- if (nand->options & NAND_BUSWIDTH_16) {
mtd->oobavail = mtd->oobsize - (nand->ecc.layout->eccbytes + 2);
if (nand->ecc.layout->eccbytes & 0x01)
mtd->oobavail--;
- } else
mtd->oobavail = mtd->oobsize - (nand->ecc.layout->eccbytes + 1);
+}
oobavail is calculated by the generic NAND code. You don't need to do it here.
Removed it.
+/*
- Board-specific NAND initialization. The following members of the
- argument are board-specific (per include/linux/mtd/nand_new.h):
- IO_ADDR_R?: address to read the 8 I/O lines of the flash device
- IO_ADDR_W?: address to write the 8 I/O lines of the flash device
- hwcontrol: hardwarespecific function for accesing control-lines
- dev_ready: hardwarespecific function for accesing device ready/busy line
- enable_hwecc?: function to enable (reset) hardware ecc generator. Must
- only be provided if a hardware ECC is available
- eccmode: mode of ecc, see defines
- chip_delay: chip dependent delay for transfering data from array to
- read regs (tR)
- options: various chip options. They can partly be set to inform
- nand_scan about special functionality. See the defines for further
- explanation
- Members with a "?" were not set in the merged testing-NAND branch,
- so they are not set here either.
IO_ADDR_R and IO_ADDR_W have a "?" but are set here. If you have a question about the API, ask it on the list, rather than encoding it in the source.
Seems that this was only a comment style issue (?). Cleaned up the comment. Better?
=================================================================== --- u-boot-arm.orig/drivers/mtd/nand/nand_base.c +++ u-boot-arm/drivers/mtd/nand/nand_base.c @@ -2730,6 +2730,151 @@ int nand_scan_tail(struct mtd_info *mtd) return 0; }
+#if defined(CONFIG_OMAP) && (defined(CONFIG_OMAP3_BEAGLE) \
- || defined(CONFIG_OMAP3_EVM) || defined(CONFIG_OVERO))
+void nand_switch_ecc(struct mtd_info *mtd)
NACK. First, explain why you need to be able to dynamically switch ECC modes.
Two topics here, changes in cmd_nand.c and nand_base.c.
cmd_nand.c:
We need to be able to switch ECC at runtime cause some images have to be written to NAND with HW ECC and some with SW ECC. This depends on what user (reader) of these parts expect.
OMAP3 has a boot ROM which is able to read a second level loader (called x-loader) from NAND and start/execute it. This 2nd level loader has to be written by U-Boot using HW ECC as ROM code does HW ECC to read the image. All other parts, e.g. Linux kernel, use SW ECC as default. For this we have to use SW ECC to write images, then.
Therefore we add an additional user command in cmd_nand.c to switch ECC depending on what user wants to write.
Then, if it is justified, implement it in a separate patch, without all the duplication of code, and without platform-specific #ifdefs.
nand_base.c:
The large nand_switch_ecc() in nand_base.c seems to be a merge error. Thanks for finding this. I *removed* it completely.
I.e. with patch in attachment we don't touch nand_base.c any more, but still have an #ifdeffed user command in cmd_nand.c. If you don't like this, please give a hint how to better do this.
Many thanks for your review and help
Dirk
Subject: [PATCH 08/13 v3] ARM: OMAP3: Add NAND support
From: Dirk Behme dirk.behme@gmail.com
Add NAND support
Signed-off-by: Dirk Behme dirk.behme@gmail.com
---
Changes in version v3:
- Fix/rewrite/update NAND driver and seperate it into an own patch as proposed by Scott Wood
common/cmd_nand.c | 29 +++ drivers/mtd/nand/Makefile | 1 drivers/mtd/nand/omap3.c | 370 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 400 insertions(+)
Index: u-boot-arm/drivers/mtd/nand/Makefile =================================================================== --- u-boot-arm.orig/drivers/mtd/nand/Makefile +++ u-boot-arm/drivers/mtd/nand/Makefile @@ -38,6 +38,7 @@ endif COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o +COBJS-$(CONFIG_NAND_OMAP3) += omap3.o endif
COBJS := $(COBJS-y) Index: u-boot-arm/drivers/mtd/nand/omap3.c =================================================================== --- /dev/null +++ u-boot-arm/drivers/mtd/nand/omap3.c @@ -0,0 +1,370 @@ +/* + * (C) Copyright 2004-2008 Texas Instruments, <www.ti.com> + * Rohit Choraria rohitkc@ti.com + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/mem.h> +#include <linux/mtd/nand_ecc.h> +#include <nand.h> + +unsigned char cs; +volatile unsigned long gpmc_cs_base_add; + +#define GPMC_BUF_EMPTY 0 +#define GPMC_BUF_FULL 1 + +/* + * omap_nand_hwcontrol - Set the address pointers corretly for the + * following address/data/command operation + */ +static void omap_nand_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + register struct nand_chip *this = mtd->priv; + + /* Point the IO_ADDR to DATA and ADDRESS registers instead + of chip address */ + switch (ctrl) { + case NAND_CTRL_CHANGE | NAND_CTRL_CLE: + this->IO_ADDR_W = (void *) gpmc_cs_base_add + GPMC_NAND_CMD; + this->IO_ADDR_R = (void *) gpmc_cs_base_add + GPMC_NAND_DAT; + break; + case NAND_CTRL_CHANGE | NAND_CTRL_ALE: + this->IO_ADDR_W = (void *) gpmc_cs_base_add + GPMC_NAND_ADR; + this->IO_ADDR_R = (void *) gpmc_cs_base_add + GPMC_NAND_DAT; + break; + case NAND_CTRL_CHANGE | NAND_NCE: + this->IO_ADDR_W = (void *) gpmc_cs_base_add + GPMC_NAND_DAT; + this->IO_ADDR_R = (void *) gpmc_cs_base_add + GPMC_NAND_DAT; + break; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +/* + * omap_nand_wait - called primarily after a program/erase operation + * so that we access NAND again only after the device + * is ready again. + * @mtd: MTD device structure + * @chip: nand_chip structure + */ +static int omap_nand_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + register struct nand_chip *this = mtd->priv; + int status = 0; + + this->IO_ADDR_W = (void *) gpmc_cs_base_add + GPMC_NAND_CMD; + this->IO_ADDR_R = (void *) gpmc_cs_base_add + GPMC_NAND_DAT; + /* Send the status command and loop until the device is free */ + while (!(status & 0x40)) { + writeb(NAND_CMD_STATUS & 0xFF, this->IO_ADDR_W); + status = readb(this->IO_ADDR_R); + } + return status; +} + +/* + * omap_nand_write_buf16 - [DEFAULT] write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + * + * Default write function for 16bit buswith + */ +static void omap_nand_write_buf16(struct mtd_info *mtd, const u_char *buf, + int len) +{ + int i; + struct nand_chip *this = mtd->priv; + u16 *p = (u16 *) buf; + len >>= 1; + + for (i = 0; i < len; i++) { + writew(p[i], this->IO_ADDR_W); + while (GPMC_BUF_EMPTY == (readl(GPMC_STATUS) & GPMC_BUF_FULL)); + } +} + +/* + * omap_hwecc_init - Initialize the Hardware ECC for NAND flash in + * GPMC controller + * @mtd: MTD device structure + * + */ +static void omap_hwecc_init(struct nand_chip *chip) +{ + unsigned long val = 0x0; + + /* Init ECC Control Register */ + /* Clear all ECC | Enable Reg1 */ + val = ((0x00000001 << 8) | 0x00000001); + writel(val, GPMC_BASE + GPMC_ECC_CONTROL); + writel(0x3fcff000, GPMC_BASE + GPMC_ECC_SIZE_CONFIG); +} + +/* + * gen_true_ecc - This function will generate true ECC value, which + * can be used when correcting data read from NAND flash memory core + * + * @ecc_buf: buffer to store ecc code + * + * @return: re-formatted ECC value + */ +static unsigned int gen_true_ecc(u8 *ecc_buf) +{ + return ecc_buf[0] | (ecc_buf[1] << 16) | ((ecc_buf[2] & 0xF0) << 20) | + ((ecc_buf[2] & 0x0F) << 8); +} + +/* + * omap_correct_data - Compares the ecc read from nand spare area with ECC + * registers values and corrects one bit error if it has occured + * Further details can be had from OMAP TRM and the following selected links: + * http://en.wikipedia.org/wiki/Hamming_code + * http://www.cs.utexas.edu/users/plaxton/c/337/05f/slides/ErrorCorrection-4.pd... + * + * @mtd: MTD device structure + * @dat: page data + * @read_ecc: ecc read from nand flash + * @calc_ecc: ecc read from ECC registers + * + * @return 0 if data is OK or corrected, else returns -1 + */ +static int omap_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + unsigned int orig_ecc, new_ecc, res, hm; + unsigned short parity_bits, byte; + unsigned char bit; + + /* Regenerate the orginal ECC */ + orig_ecc = gen_true_ecc(read_ecc); + new_ecc = gen_true_ecc(calc_ecc); + /* Get the XOR of real ecc */ + res = orig_ecc ^ new_ecc; + if (res) { + /* Get the hamming width */ + hm = hweight32(res); + /* Single bit errors can be corrected! */ + if (hm == 12) { + /* Correctable data! */ + parity_bits = res >> 16; + bit = (parity_bits & 0x7); + byte = (parity_bits >> 3) & 0x1FF; + /* Flip the bit to correct */ + dat[byte] ^= (0x1 << bit); + } else if (hm == 1) { + printf("Error: Ecc is wrong\n"); + /* ECC itself is corrupted */ + return 2; + } else { + printf("Error: Bad compare! failed\n"); + /* detected 2 bit error */ + return -1; + } + } + return 0; +} + +/* + * 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. + * @mtd: MTD structure + * @dat: unused + * @ecc_code: ecc_code buffer + */ +static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + unsigned long val = 0x0; + unsigned long reg; + + /* Start Reading from HW ECC1_Result = 0x200 */ + reg = (unsigned long) (GPMC_BASE + GPMC_ECC1_RESULT); + val = readl(reg); + + 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 */ + reg = (unsigned long) (GPMC_BASE + GPMC_ECC_CONFIG); + writel(0x000, reg); + + return 0; +} + +/* + * omap_enable_ecc - This function enables the hardware ecc functionality + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +static void omap_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct nand_chip *chip = mtd->priv; + unsigned int val, dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1; + + switch (mode) { + case NAND_ECC_READ: + case NAND_ECC_WRITE: + /* Clear the ecc result registers + * select ecc reg as 1 + */ + writel(0x101, GPMC_BASE + GPMC_ECC_CONTROL); + /* Size 0 = 0xFF, Size1 is 0xFF - both are 512 bytes + * tell all regs to generate size0 sized regs + * we just have a single ECC engine for all CS + */ + writel(0x3FCFF000, GPMC_BASE + GPMC_ECC_SIZE_CONFIG); + val = (dev_width << 7) | (cs << 1) | (0x1); + writel(val, GPMC_BASE + GPMC_ECC_CONFIG); + break; + default: + printf("Error: Unrecognized Mode[%d]!\n", mode); + break; + } +} + +static struct nand_ecclayout hw_nand_oob_64 = { + .eccbytes = 12, + .eccpos = { + 2, 3, 4, 5, + 6, 7, 8, 9, + 10, 11, 12, 13}, + .oobfree = { {20, 50} } /* don't care */ +}; + +static struct nand_ecclayout sw_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 = { {2, 38} } +}; + +void omap_nand_switch_ecc(struct mtd_info *mtd, int hardware) +{ + struct nand_chip *nand = mtd->priv; + + if (!hardware) { + nand->ecc.mode = NAND_ECC_SOFT; + nand->ecc.layout = &sw_nand_oob_64; + nand->ecc.size = 256; /* set default eccsize */ + nand->ecc.bytes = 3; + nand->ecc.steps = 8; + nand->ecc.hwctl = 0; + nand->ecc.calculate = nand_calculate_ecc; + nand->ecc.correct = nand_correct_data; + } else { + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &hw_nand_oob_64; + nand->ecc.size = 512; + nand->ecc.bytes = 3; + nand->ecc.steps = 4; + nand->ecc.hwctl = omap_enable_hwecc; + nand->ecc.correct = omap_correct_data; + nand->ecc.calculate = omap_calculate_ecc; + omap_hwecc_init(nand); + } +} + +/* + * Board-specific NAND initialization. The following members of the + * argument are board-specific: + * - IO_ADDR_R: address to read the 8 I/O lines of the flash device + * - IO_ADDR_W: address to write the 8 I/O lines of the flash device + * - cmd_ctrl: hardwarespecific function for accesing control-lines + * - waitfunc: hardwarespecific function for accesing device ready/busy line + * - ecc.hwctl: function to enable (reset) hardware ecc generator + * - ecc.mode: mode of ecc, see defines + * - chip_delay: chip dependent delay for transfering data from array to + * read regs (tR) + * - options: various chip options. They can partly be set to inform + * nand_scan about special functionality. See the defines for further + * explanation + */ +int board_nand_init(struct nand_chip *nand) +{ + int gpmc_config = 0; + cs = 0; + while (cs <= GPMC_MAX_CS) { + /* Each GPMC set for a single CS is at offset 0x30 */ + /* already remapped for us */ + gpmc_cs_base_add = (GPMC_CONFIG_CS0 + (cs * 0x30)); + /* xloader/Uboot would have written the NAND type for us + * NOTE: This is a temporary measure and cannot handle ONENAND. + * The proper way of doing this is to pass the setup of + * u-boot up to kernel using kernel params - something on + * the lines of machineID + */ + /* Check if NAND type is set */ + if ((readl(gpmc_cs_base_add + GPMC_CONFIG1) & 0xC00) == + 0x800) { + /* Found it!! */ + break; + } + cs++; + } + if (cs > GPMC_MAX_CS) { + printf("NAND: Unable to find NAND settings in " \ + "GPMC Configuration - quitting\n"); + } + + gpmc_config = readl(GPMC_CONFIG); + /* Disable Write protect */ + gpmc_config |= 0x10; + writel(gpmc_config, GPMC_CONFIG); + + nand->IO_ADDR_R = (int *) gpmc_cs_base_add + GPMC_NAND_DAT; + nand->IO_ADDR_W = (int *) gpmc_cs_base_add + GPMC_NAND_CMD; + + nand->cmd_ctrl = omap_nand_hwcontrol; + nand->options = NAND_NO_PADDING | NAND_CACHEPRG | NAND_NO_AUTOINCR | + NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR; + + // FIXME: How to get common nand_read_buf16 here? It's static... + //nand->read_buf = nand_read_buf16; + /* Use custom write buf due to additional delay */ + nand->write_buf = omap_nand_write_buf16; + nand->ecc.mode = NAND_ECC_SOFT; + /* if RDY/BSY line is connected to OMAP then use the omap ready + * function and the generic nand_wait function which reads the + * status register after monitoring the RDY/BSY line. Otherwise + * use a standard chip delay which is slightly more than tR + * (AC Timing) of the NAND device and read the status register + * until you get a failure or success + */ + nand->waitfunc = omap_nand_wait; + nand->chip_delay = 50; + + return 0; +} Index: u-boot-arm/common/cmd_nand.c =================================================================== --- u-boot-arm.orig/common/cmd_nand.c +++ u-boot-arm/common/cmd_nand.c @@ -38,6 +38,11 @@ int find_dev_and_part(const char *id, st u8 *part_num, struct part_info **part); #endif
+#if defined(CONFIG_OMAP) && (defined(CONFIG_OMAP3_BEAGLE)\ + || defined(CONFIG_OMAP3_EVM) || defined(CONFIG_OVERO)) +extern void omap_nand_switch_ecc(nand_info_t *nand, int hardware); +#endif + static int nand_dump(nand_info_t *nand, ulong off, int only_oob) { int i; @@ -234,6 +239,10 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, strncmp(cmd, "read", 4) != 0 && strncmp(cmd, "write", 5) != 0 && strcmp(cmd, "scrub") != 0 && strcmp(cmd, "markbad") != 0 && strcmp(cmd, "biterr") != 0 && +#if defined(CONFIG_OMAP) && (defined(CONFIG_OMAP3_BEAGLE)\ + || defined(CONFIG_OMAP3_EVM) || defined(CONFIG_OVERO)) + strncmp(cmd, "ecc", 3) != 0 && +#endif strcmp(cmd, "lock") != 0 && strcmp(cmd, "unlock") != 0 ) goto usage;
@@ -318,6 +327,22 @@ int do_nand(cmd_tbl_t * cmdtp, int flag,
}
+#if defined(CONFIG_OMAP) && (defined(CONFIG_OMAP3_BEAGLE) \ + || defined(CONFIG_OMAP3_EVM) || defined(CONFIG_OVERO)) + if (strncmp(cmd, "ecc", 3) == 0) { + if (argc < 2) + goto usage; + if (strncmp(argv[2], "hw", 2) == 0) + omap_nand_switch_ecc(nand, 1); + else if (strncmp(argv[2], "sw", 2) == 0) + omap_nand_switch_ecc(nand, 0); + else + goto usage; + + return 0; + } +#endif + if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) { int read;
@@ -483,6 +508,10 @@ U_BOOT_CMD(nand, 5, 1, do_nand, "nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n" "nand markbad off - mark bad block at offset (UNSAFE)\n" "nand biterr off - make a bit error at offset (UNSAFE)\n" +#if defined(CONFIG_OMAP) && (defined(CONFIG_OMAP3_BEAGLE) \ + || defined(CONFIG_OMAP3_EVM) || defined(CONFIG_OVERO)) + "nand ecc [hw/sw] - switch the ecc calculation algorithm \n" +#endif "nand lock [tight] [status]\n" " bring nand to lock state or display locked pages\n" "nand unlock [offset] [size] - unlock section\n");