[U-Boot] [PATCH 08/13 v4] ARM: OMAP3: Add NAND support

Subject: [PATCH 08/13 v4] ARM: OMAP3: Add NAND support
From: Dirk Behme dirk.behme@gmail.com
Add NAND support
Signed-off-by: Dirk Behme dirk.behme@gmail.com Signed-off-by: Nishanth Menon nm@ti.com
---
Changes in version v4:
- Incorporate further review results from Scott Wood. - Further sets of cleanup, making the logic generic, rename of files, better handling of ecc switch by Nishanth Menon.
Changes in version v3:
- Fix/update NAND driver and seperate it into an own patch as proposed by Scott Wood
drivers/mtd/nand/Makefile | 1 drivers/mtd/nand/omap_gpmc.c | 337 +++++++++++++++++++++++++++++++++ include/asm-arm/arch-omap3/omap_gpmc.h | 84 ++++++++ 3 files changed, 422 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_OMAP_GPMC) += omap_gpmc.o endif
COBJS := $(COBJS-y) Index: u-boot-arm/drivers/mtd/nand/omap_gpmc.c =================================================================== --- /dev/null +++ u-boot-arm/drivers/mtd/nand/omap_gpmc.c @@ -0,0 +1,337 @@ +/* + * (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/errno.h> +#include <asm/arch/mem.h> +#include <asm/arch/omap_gpmc.h> +#include <linux/mtd/nand_ecc.h> +#include <nand.h> + +static unsigned char cs; +static void __iomem *gpmc_cs_base_add; +static struct nand_ecclayout hw_nand_oob = GPMC_NAND_HW_ECC_LAYOUT; + +/* + * 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 = gpmc_cs_base_add + GPMC_NAND_CMD; + break; + case NAND_CTRL_CHANGE | NAND_CTRL_ALE: + this->IO_ADDR_W = gpmc_cs_base_add + GPMC_NAND_ADR; + break; + case NAND_CTRL_CHANGE | NAND_NCE: + this->IO_ADDR_W = gpmc_cs_base_add + GPMC_NAND_DAT; + break; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +/* + * 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) +{ + /* Init ECC Control Register */ + /* Clear all ECC | Enable Reg1 */ + writel(ECCCLEAR | ECCRESULTREG1, GPMC_BASE + GPMC_ECC_CONTROL); + writel(ECCSIZE1 | ECCSIZE0 | ECCSIZE0SEL, + 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 { + /* + * hm distance != parity pairs OR one, could mean 2 bit + * error OR potentially be on a blank page.. + * orig_ecc: contains spare area data from nand flash. + * new_ecc: generated ecc while reading data area. + * Note: if the ecc = 0, all data bits from which it was + * generated are 0xFF. + * The 3 byte(24 bits) ecc is generated per 512byte + * chunk of a page. If orig_ecc(from spare area) + * is 0xFF && new_ecc(computed now from data area)=0x0, + * this means that data area is 0xFF and spare area is + * 0xFF. A sure sign of a erased page! + */ + if ((orig_ecc == 0x0FFF0FFF) && (new_ecc == 0x00000000)) + return 0; + 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. + * 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 omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + unsigned long val; + + /* Start Reading from HW ECC1_Result = 0x200 */ + val = readl(GPMC_BASE + GPMC_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_BASE + GPMC_ECC_CONFIG); + + 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(ECCCLEAR | ECCRESULTREG1, 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(ECCSIZE1 | ECCSIZE0 | ECCSIZE0SEL, + 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; + } +} + +/** + * omap_nand_switch_ecc - switch the ECC operation b/w h/w ecc and s/w ecc. + * The default is to come up on s/w ecc + * + * @hardware - 1 -switch to h/w ecc, 0 - s/w ecc + * + */ +void omap_nand_switch_ecc(int hardware) +{ + struct nand_chip *nand; + struct mtd_info *mtd; + + 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; + } + + mtd = &nand_info[nand_curr_device]; + nand = mtd->priv; + + /* clean up allocated buffers */ + nand_release(mtd); + /* mark me unscanned */ + nand->options &= ~NAND_BBT_SCANNED; + + /* Setup the ecc configurations again */ + if (!hardware) { + nand->ecc.mode = NAND_ECC_SOFT; + /* Use mtd default settings */ + nand->ecc.layout = NULL; + } else { + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &hw_nand_oob; + nand->ecc.size = 512; + nand->ecc.bytes = 3; + nand->ecc.steps = hw_nand_oob.eccbytes / nand->ecc.bytes; + nand->ecc.hwctl = omap_enable_hwecc; + nand->ecc.correct = omap_correct_data; + nand->ecc.calculate = omap_calculate_ecc; + omap_hwecc_init(nand); + } + + /* Update NAND handling after ECC mode switch */ + nand_scan_tail(mtd); +} + +/* + * 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; + + /* xloader/Uboot's gpmc configuration would have configured GPMC for + * nand type of memory. The following logic scans and latches on to the + * first CS with NAND type memory. + * TBD: need to make this logic generic to handle multiple CS NAND + * devices. + */ + while (cs < GPMC_MAX_CS) { + /* Each GPMC set for a single CS is at offset 0x30 + * - already remapped for us + */ + gpmc_cs_base_add = (void __iomem *)(GPMC_CONFIG_CS0 + + (cs * 0x30)); + /* 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"); + return -ENODEV; + } + + gpmc_config = readl(GPMC_CONFIG); + /* Disable Write protect */ + gpmc_config |= 0x10; + writel(gpmc_config, GPMC_CONFIG); + + nand->IO_ADDR_R = gpmc_cs_base_add + GPMC_NAND_DAT; + nand->IO_ADDR_W = gpmc_cs_base_add + GPMC_NAND_CMD; + + nand->cmd_ctrl = omap_nand_hwcontrol; + nand->options = NAND_NO_PADDING | NAND_CACHEPRG | NAND_NO_AUTOINCR | + NAND_NO_AUTOINCR; + /* If we are 16 bit dev, our gpmc config tells us that */ + if ((readl(gpmc_cs_base_add) & 0x3000) == 0x1000) + nand->options |= NAND_BUSWIDTH_16; + + nand->chip_delay = 100; + /* Default ECC mode */ + nand->ecc.mode = NAND_ECC_SOFT; + + return 0; +} Index: u-boot-arm/include/asm-arm/arch-omap3/omap_gpmc.h =================================================================== --- /dev/null +++ u-boot-arm/include/asm-arm/arch-omap3/omap_gpmc.h @@ -0,0 +1,84 @@ +/* + * (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 + */ +#ifndef __ASM_ARCH_OMAP_GPMC_H +#define __ASM_ARCH_OMAP_GPMC_H + +#define GPMC_BUF_EMPTY 0 +#define GPMC_BUF_FULL 1 + +#define ECCCLEAR (0x1 << 8) +#define ECCRESULTREG1 (0x1 << 0) +#define ECCSIZE512BYTE 0xFF +#define ECCSIZE1 (ECCSIZE512BYTE << 22) +#define ECCSIZE0 (ECCSIZE512BYTE << 12) +#define ECCSIZE0SEL (0x000 << 0) + +/* Generic ECC Layouts */ +/* Large Page x8 NAND device Layout */ +#ifdef GPMC_NAND_ECC_LP_x8_LAYOUT +#define GPMC_NAND_HW_ECC_LAYOUT {\ + .eccbytes = 12,\ + .eccpos = {1, 2, 3, 4, 5, 6, 7, 8,\ + 9, 10, 11, 12},\ + .oobfree = {\ + {.offset = 13,\ + .length = 51 } } \ +} +#endif + +/* Large Page x16 NAND device Layout */ +#ifdef GPMC_NAND_ECC_LP_x16_LAYOUT +#define GPMC_NAND_HW_ECC_LAYOUT {\ + .eccbytes = 12,\ + .eccpos = {2, 3, 4, 5, 6, 7, 8, 9,\ + 10, 11, 12, 13},\ + .oobfree = {\ + {.offset = 14,\ + .length = 50 } } \ +} +#endif + +/* Small Page x8 NAND device Layout */ +#ifdef GPMC_NAND_ECC_SP_x8_LAYOUT +#define GPMC_NAND_HW_ECC_LAYOUT {\ + .eccbytes = 3,\ + .eccpos = {1, 2, 3},\ + .oobfree = {\ + {.offset = 4,\ + .length = 13 } } \ +} +#endif + +/* Small Page x16 NAND device Layout */ +#ifdef GPMC_NAND_ECC_SP_x16_LAYOUT +#define GPMC_NAND_HW_ECC_LAYOUT {\ + .eccbytes = 3,\ + .eccpos = {2, 3, 4},\ + .oobfree = {\ + {.offset = 4,\ + .length = 12 } } \ +} +#endif + +#endif /* __ASM_ARCH_OMAP_GPMC_H */ +

On 21:28 Fri 17 Oct , dirk.behme@googlemail.com wrote:
Subject: [PATCH 08/13 v4] ARM: OMAP3: Add NAND support
From: Dirk Behme dirk.behme@gmail.com
Add NAND support
Signed-off-by: Dirk Behme dirk.behme@gmail.com Signed-off-by: Nishanth Menon nm@ti.com
Changes in version v4:
- Incorporate further review results from Scott Wood.
- Further sets of cleanup, making the logic generic, rename of files, better handling of ecc switch by Nishanth Menon.
Changes in version v3:
- Fix/update NAND driver and seperate it into an own patch as proposed by Scott Wood
drivers/mtd/nand/Makefile | 1 drivers/mtd/nand/omap_gpmc.c | 337 +++++++++++++++++++++++++++++++++ include/asm-arm/arch-omap3/omap_gpmc.h | 84 ++++++++ 3 files changed, 422 insertions(+)
Scott could you ack this one please?
Best Regards, J.

On Fri, Oct 17, 2008 at 09:28:50PM +0200, dirk.behme@googlemail.com wrote:
- 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;
- }
It's much more readable if you align the continuation lines with the beginning of the condition, rather than with the if-body.
- mtd = &nand_info[nand_curr_device];
- nand = mtd->priv;
- /* clean up allocated buffers */
- nand_release(mtd);
- /* mark me unscanned */
- nand->options &= ~NAND_BBT_SCANNED;
Better to set it back to the original value explicitly, in case other internal NAND flags crop up.
- /* Setup the ecc configurations again */
- if (!hardware) {
nand->ecc.mode = NAND_ECC_SOFT;
/* Use mtd default settings */
nand->ecc.layout = NULL;
- } else {
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.layout = &hw_nand_oob;
nand->ecc.size = 512;
nand->ecc.bytes = 3;
nand->ecc.steps = hw_nand_oob.eccbytes / nand->ecc.bytes;
No need to set ecc.steps; nand_scan_tail() will do it.
+/* Small Page x8 NAND device Layout */ +#ifdef GPMC_NAND_ECC_SP_x8_LAYOUT +#define GPMC_NAND_HW_ECC_LAYOUT {\
- .eccbytes = 3,\
- .eccpos = {1, 2, 3},\
- .oobfree = {\
{.offset = 4,\
.length = 13 } } \
+}
.length = 12
+#endif
+/* Small Page x16 NAND device Layout */ +#ifdef GPMC_NAND_ECC_SP_x16_LAYOUT +#define GPMC_NAND_HW_ECC_LAYOUT {\
- .eccbytes = 3,\
- .eccpos = {2, 3, 4},\
- .oobfree = {\
{.offset = 4,\
.length = 12 } } \
+}
.offset = 5 .length = 11
Otherwise ACK, if the ARM maintainers are OK with passing integers as addresses to writel().
-Scott
participants (3)
-
dirk.behmeï¼ googlemail.com
-
Jean-Christophe PLAGNIOL-VILLARD
-
Scott Wood