[U-Boot] [PATCH v2 0/8] NAND support for AM33XX

These series add support for NAND on AM33XX. AM33XX has the same GPMC controller as OMAP3 so the first part of the series just add required defines/initialization to enable the existing omap_gpmc driver to work on AM33XX. The rest of the series adds support for BCH8 error correction code. We use GPMC to generate codes/syndromes and ELM to find the errors.
Changes in v2: -fix nand mux settings (profiles 2&3 don't have NAND) - rebased on current master - clean up mem.c (remove unused stuff that was copied from OMAP3) - nand headers: remove unneeded stuff - rebased onto master - minor config style fix (wrt nand) - fix wrong braces in Makefile
Ilya Yanok (6): OMAP: include sys_proto.h from boot-common am335x_evm: add nand pinmux definition am33xx: NAND support am335x_evm: enable NAND support am33xx_spl_bch: simple SPL nand loader for AM33XX am335x_evm: enable SPL NAND support
Mansoor Ahamed (2): am33xx: add ELM support omap_gpmc: BCH8 support (ELM based)
arch/arm/cpu/armv7/am33xx/Makefile | 2 + arch/arm/cpu/armv7/am33xx/board.c | 1 + arch/arm/cpu/armv7/am33xx/clock.c | 10 + arch/arm/cpu/armv7/am33xx/elm.c | 212 ++++++++++++++ arch/arm/cpu/armv7/am33xx/mem.c | 101 +++++++ arch/arm/cpu/armv7/omap-common/boot-common.c | 1 + arch/arm/include/asm/arch-am33xx/cpu.h | 53 ++++ arch/arm/include/asm/arch-am33xx/elm.h | 93 ++++++ arch/arm/include/asm/arch-am33xx/hardware.h | 3 + arch/arm/include/asm/arch-am33xx/mem.h | 83 ++++++ arch/arm/include/asm/arch-am33xx/omap_gpmc.h | 120 ++++++++ arch/arm/include/asm/arch-am33xx/sys_proto.h | 3 + board/ti/am335x/board.c | 2 + board/ti/am335x/mux.c | 22 ++ drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/am335x_spl_bch.c | 238 +++++++++++++++ drivers/mtd/nand/omap_gpmc.c | 403 +++++++++++++++++++++++++- include/configs/am335x_evm.h | 46 +++ 18 files changed, 1393 insertions(+), 1 deletion(-) create mode 100644 arch/arm/cpu/armv7/am33xx/elm.c create mode 100644 arch/arm/cpu/armv7/am33xx/mem.c create mode 100644 arch/arm/include/asm/arch-am33xx/elm.h create mode 100644 arch/arm/include/asm/arch-am33xx/mem.h create mode 100644 arch/arm/include/asm/arch-am33xx/omap_gpmc.h create mode 100644 drivers/mtd/nand/am335x_spl_bch.c

Include asm/arch/sys_proto.h for gpmc_init prototype. Without this we get a warning while building for AM335x.
Signed-off-by: Ilya Yanok ilya.yanok@cogentembedded.com ---
arch/arm/cpu/armv7/omap-common/boot-common.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/arch/arm/cpu/armv7/omap-common/boot-common.c b/arch/arm/cpu/armv7/omap-common/boot-common.c index 0f19141..2b584e0 100644 --- a/arch/arm/cpu/armv7/omap-common/boot-common.c +++ b/arch/arm/cpu/armv7/omap-common/boot-common.c @@ -21,6 +21,7 @@ #include <asm/omap_common.h> #include <asm/arch/omap.h> #include <asm/arch/mmc_host_def.h> +#include <asm/arch/sys_proto.h>
/* * This is used to verify if the configuration header

Add NAND pins mux settings for AM335X devices. Enable NAND pins for AM335X EVM board.
Signed-off-by: Ilya Yanok ilya.yanok@cogentembedded.com
--- Changes in v2: -fix nand mux settings (profiles 2&3 don't have NAND)
board/ti/am335x/mux.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+)
diff --git a/board/ti/am335x/mux.c b/board/ti/am335x/mux.c index 8437ef5..0283708 100644 --- a/board/ti/am335x/mux.c +++ b/board/ti/am335x/mux.c @@ -171,6 +171,25 @@ static struct module_pin_mux mii1_pin_mux[] = { {-1}, };
+static struct module_pin_mux nand_pin_mux[] = { + {OFFSET(gpmc_ad0), (MODE(0) | PULLUP_EN | RXACTIVE)}, /* NAND AD0 */ + {OFFSET(gpmc_ad1), (MODE(0) | PULLUP_EN | RXACTIVE)}, /* NAND AD1 */ + {OFFSET(gpmc_ad2), (MODE(0) | PULLUP_EN | RXACTIVE)}, /* NAND AD2 */ + {OFFSET(gpmc_ad3), (MODE(0) | PULLUP_EN | RXACTIVE)}, /* NAND AD3 */ + {OFFSET(gpmc_ad4), (MODE(0) | PULLUP_EN | RXACTIVE)}, /* NAND AD4 */ + {OFFSET(gpmc_ad5), (MODE(0) | PULLUP_EN | RXACTIVE)}, /* NAND AD5 */ + {OFFSET(gpmc_ad6), (MODE(0) | PULLUP_EN | RXACTIVE)}, /* NAND AD6 */ + {OFFSET(gpmc_ad7), (MODE(0) | PULLUP_EN | RXACTIVE)}, /* NAND AD7 */ + {OFFSET(gpmc_wait0), (MODE(0) | RXACTIVE | PULLUP_EN)}, /* NAND WAIT */ + {OFFSET(gpmc_wpn), (MODE(7) | PULLUP_EN | RXACTIVE)}, /* NAND_WPN */ + {OFFSET(gpmc_csn0), (MODE(0) | PULLUDEN)}, /* NAND_CS0 */ + {OFFSET(gpmc_advn_ale), (MODE(0) | PULLUDEN)}, /* NAND_ADV_ALE */ + {OFFSET(gpmc_oen_ren), (MODE(0) | PULLUDEN)}, /* NAND_OE */ + {OFFSET(gpmc_wen), (MODE(0) | PULLUDEN)}, /* NAND_WEN */ + {OFFSET(gpmc_be0n_cle), (MODE(0) | PULLUDEN)}, /* NAND_BE_CLE */ + {-1}, +}; + void enable_uart0_pin_mux(void) { configure_module_pin_mux(uart0_pin_mux); @@ -257,6 +276,9 @@ void enable_board_pin_mux(struct am335x_baseboard_id *header) /* In profile #2 i2c1 and spi0 conflict. */ if (profile & ~PROFILE_2) configure_module_pin_mux(i2c1_pin_mux); + /* Profiles 2 & 3 don't have NAND */ + if (profile & ~(PROFILE_2 | PROFILE_3)) + configure_module_pin_mux(nand_pin_mux); else if (profile == PROFILE_2) { configure_module_pin_mux(mmc1_pin_mux); configure_module_pin_mux(spi0_pin_mux);

TI AM33XX has the same GPMC controller as OMAP3 so we could just use the existing omap_gpmc driver. This patch adds adds required definitions/intialization.
Signed-off-by: Ilya Yanok ilya.yanok@cogentembedded.com
--- Changes in v2: - rebased on current master - clean up mem.c (remove unused stuff that was copied from OMAP3) - nand headers: remove unneeded stuff
arch/arm/cpu/armv7/am33xx/Makefile | 1 + arch/arm/cpu/armv7/am33xx/board.c | 1 + arch/arm/cpu/armv7/am33xx/clock.c | 5 ++ arch/arm/cpu/armv7/am33xx/mem.c | 101 ++++++++++++++++++++++ arch/arm/include/asm/arch-am33xx/cpu.h | 53 ++++++++++++ arch/arm/include/asm/arch-am33xx/hardware.h | 3 + arch/arm/include/asm/arch-am33xx/mem.h | 83 ++++++++++++++++++ arch/arm/include/asm/arch-am33xx/omap_gpmc.h | 120 ++++++++++++++++++++++++++ arch/arm/include/asm/arch-am33xx/sys_proto.h | 3 + 9 files changed, 370 insertions(+) create mode 100644 arch/arm/cpu/armv7/am33xx/mem.c create mode 100644 arch/arm/include/asm/arch-am33xx/mem.h create mode 100644 arch/arm/include/asm/arch-am33xx/omap_gpmc.h
diff --git a/arch/arm/cpu/armv7/am33xx/Makefile b/arch/arm/cpu/armv7/am33xx/Makefile index 74875b3..f565357 100644 --- a/arch/arm/cpu/armv7/am33xx/Makefile +++ b/arch/arm/cpu/armv7/am33xx/Makefile @@ -18,6 +18,7 @@ LIB = $(obj)lib$(SOC).o
COBJS += clock.o COBJS += sys_info.o +COBJS += mem.o COBJS += ddr.o COBJS += emif4.o COBJS += board.o diff --git a/arch/arm/cpu/armv7/am33xx/board.c b/arch/arm/cpu/armv7/am33xx/board.c index e4c123c..c756c09 100644 --- a/arch/arm/cpu/armv7/am33xx/board.c +++ b/arch/arm/cpu/armv7/am33xx/board.c @@ -25,6 +25,7 @@ #include <asm/arch/ddr_defs.h> #include <asm/arch/clock.h> #include <asm/arch/gpio.h> +#include <asm/arch/mem.h> #include <asm/arch/mmc_host_def.h> #include <asm/arch/sys_proto.h> #include <asm/io.h> diff --git a/arch/arm/cpu/armv7/am33xx/clock.c b/arch/arm/cpu/armv7/am33xx/clock.c index bc2abb6..6eb7d9f 100644 --- a/arch/arm/cpu/armv7/am33xx/clock.c +++ b/arch/arm/cpu/armv7/am33xx/clock.c @@ -150,6 +150,11 @@ static void enable_per_clocks(void) ; #endif /* CONFIG_SERIAL6 */
+ /* GPMC */ + writel(PRCM_MOD_EN, &cmper->gpmcclkctrl); + while (readl(&cmper->gpmcclkctrl) != PRCM_MOD_EN) + ; + /* MMC0*/ writel(PRCM_MOD_EN, &cmper->mmc0clkctrl); while (readl(&cmper->mmc0clkctrl) != PRCM_MOD_EN) diff --git a/arch/arm/cpu/armv7/am33xx/mem.c b/arch/arm/cpu/armv7/am33xx/mem.c new file mode 100644 index 0000000..b8f54ab --- /dev/null +++ b/arch/arm/cpu/armv7/am33xx/mem.c @@ -0,0 +1,101 @@ +/* + * (C) Copyright 2010 + * Texas Instruments, <www.ti.com> + * + * Author : + * Mansoor Ahamed mansoor.ahamed@ti.com + * + * Initial Code from: + * Manikandan Pillai mani.pillai@ti.com + * Richard Woodruff r-woodruff2@ti.com + * Syed Mohammed Khasim khasim@ti.com + * + * 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/cpu.h> +#include <asm/arch/mem.h> +#include <asm/arch/sys_proto.h> +#include <command.h> + +struct gpmc *gpmc_cfg; + +#if defined(CONFIG_CMD_NAND) +static const u32 gpmc_m_nand[GPMC_MAX_REG] = { + M_NAND_GPMC_CONFIG1, + M_NAND_GPMC_CONFIG2, + M_NAND_GPMC_CONFIG3, + M_NAND_GPMC_CONFIG4, + M_NAND_GPMC_CONFIG5, + M_NAND_GPMC_CONFIG6, 0 +}; +#endif + + +void enable_gpmc_cs_config(const u32 *gpmc_config, struct gpmc_cs *cs, u32 base, + u32 size) +{ + writel(0, &cs->config7); + sdelay(1000); + /* Delay for settling */ + writel(gpmc_config[0], &cs->config1); + writel(gpmc_config[1], &cs->config2); + writel(gpmc_config[2], &cs->config3); + writel(gpmc_config[3], &cs->config4); + writel(gpmc_config[4], &cs->config5); + writel(gpmc_config[5], &cs->config6); + /* Enable the config */ + writel((((size & 0xF) << 8) | ((base >> 24) & 0x3F) | + (1 << 6)), &cs->config7); + sdelay(2000); +} + +/***************************************************** + * gpmc_init(): init gpmc bus + * Init GPMC for x16, MuxMode (SDRAM in x32). + * This code can only be executed from SRAM or SDRAM. + *****************************************************/ +void gpmc_init(void) +{ + /* putting a blanket check on GPMC based on ZeBu for now */ + gpmc_cfg = (struct gpmc *)GPMC_BASE; + +#ifdef CONFIG_CMD_NAND + const u32 *gpmc_config = NULL; + u32 base = 0; + u32 size = 0; +#endif + /* global settings */ + writel(0x00000008, &gpmc_cfg->sysconfig); + writel(0x00000100, &gpmc_cfg->irqstatus); + writel(0x00000200, &gpmc_cfg->irqenable); + writel(0x00000012, &gpmc_cfg->config); + /* + * Disable the GPMC0 config set by ROM code + */ + writel(0, &gpmc_cfg->cs[0].config7); + sdelay(1000); + +#ifdef CONFIG_CMD_NAND + gpmc_config = gpmc_m_nand; + + base = PISMO1_NAND_BASE; + size = PISMO1_NAND_SIZE; + enable_gpmc_cs_config(gpmc_config, &gpmc_cfg->cs[0], base, size); +#endif +} diff --git a/arch/arm/include/asm/arch-am33xx/cpu.h b/arch/arm/include/asm/arch-am33xx/cpu.h index 819fd2f..b91441f 100644 --- a/arch/arm/include/asm/arch-am33xx/cpu.h +++ b/arch/arm/include/asm/arch-am33xx/cpu.h @@ -60,6 +60,59 @@
#ifndef __KERNEL_STRICT_NAMES #ifndef __ASSEMBLY__ +struct gpmc_cs { + u32 config1; /* 0x00 */ + u32 config2; /* 0x04 */ + u32 config3; /* 0x08 */ + u32 config4; /* 0x0C */ + u32 config5; /* 0x10 */ + u32 config6; /* 0x14 */ + u32 config7; /* 0x18 */ + u32 nand_cmd; /* 0x1C */ + u32 nand_adr; /* 0x20 */ + u32 nand_dat; /* 0x24 */ + u8 res[8]; /* blow up to 0x30 byte */ +}; + +struct bch_res_0_3 { + u32 bch_result_x[4]; +}; + +struct gpmc { + u8 res1[0x10]; + u32 sysconfig; /* 0x10 */ + u8 res2[0x4]; + u32 irqstatus; /* 0x18 */ + u32 irqenable; /* 0x1C */ + u8 res3[0x20]; + u32 timeout_control; /* 0x40 */ + u8 res4[0xC]; + u32 config; /* 0x50 */ + u32 status; /* 0x54 */ + u8 res5[0x8]; /* 0x58 */ + struct gpmc_cs cs[8]; /* 0x60, 0x90, .. */ + u8 res6[0x14]; /* 0x1E0 */ + u32 ecc_config; /* 0x1F4 */ + u32 ecc_control; /* 0x1F8 */ + u32 ecc_size_config; /* 0x1FC */ + u32 ecc1_result; /* 0x200 */ + u32 ecc2_result; /* 0x204 */ + u32 ecc3_result; /* 0x208 */ + u32 ecc4_result; /* 0x20C */ + u32 ecc5_result; /* 0x210 */ + u32 ecc6_result; /* 0x214 */ + u32 ecc7_result; /* 0x218 */ + u32 ecc8_result; /* 0x21C */ + u32 ecc9_result; /* 0x220 */ + u8 res7[12]; /* 0x224 */ + u32 testmomde_ctrl; /* 0x230 */ + u8 res8[12]; /* 0x234 */ + struct bch_res_0_3 bch_result_0_3[2]; /* 0x240 */ +}; + +/* Used for board specific gpmc initialization */ +extern struct gpmc *gpmc_cfg; + /* Encapsulating core pll registers */ struct cm_wkuppll { unsigned int wkclkstctrl; /* offset 0x00 */ diff --git a/arch/arm/include/asm/arch-am33xx/hardware.h b/arch/arm/include/asm/arch-am33xx/hardware.h index 5bd4bc8..b3922d9 100644 --- a/arch/arm/include/asm/arch-am33xx/hardware.h +++ b/arch/arm/include/asm/arch-am33xx/hardware.h @@ -80,6 +80,9 @@ #define DDRPHY_0_CONFIG_BASE (CTRL_BASE + 0x1400) #define DDRPHY_CONFIG_BASE DDRPHY_0_CONFIG_BASE
+/* GPMC Base address */ +#define GPMC_BASE 0x50000000 + /* CPSW Config space */ #define AM335X_CPSW_BASE 0x4A100000 #define AM335X_CPSW_MDIO_BASE 0x4A101000 diff --git a/arch/arm/include/asm/arch-am33xx/mem.h b/arch/arm/include/asm/arch-am33xx/mem.h new file mode 100644 index 0000000..c3bf74e --- /dev/null +++ b/arch/arm/include/asm/arch-am33xx/mem.h @@ -0,0 +1,83 @@ +/* + * (C) Copyright 2006-2008 + * Texas Instruments, <www.ti.com> + * + * Author + * Mansoor Ahamed mansoor.ahamed@ti.com + * + * Initial Code from: + * Richard Woodruff r-woodruff2@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 _MEM_H_ +#define _MEM_H_ + +/* + * GPMC settings - + * Definitions is as per the following format + * #define <PART>_GPMC_CONFIG<x> <value> + * Where: + * PART is the part name e.g. STNOR - Intel Strata Flash + * x is GPMC config registers from 1 to 6 (there will be 6 macros) + * Value is corresponding value + * + * For every valid PRCM configuration there should be only one definition of + * the same. if values are independent of the board, this definition will be + * present in this file if values are dependent on the board, then this should + * go into corresponding mem-boardName.h file + * + * Currently valid part Names are (PART): + * M_NAND - Micron NAND + */ +#define GPMC_SIZE_256M 0x0 +#define GPMC_SIZE_128M 0x8 +#define GPMC_SIZE_64M 0xC +#define GPMC_SIZE_32M 0xE +#define GPMC_SIZE_16M 0xF + +#define M_NAND_GPMC_CONFIG1 0x00000800 +#define M_NAND_GPMC_CONFIG2 0x001e1e00 +#define M_NAND_GPMC_CONFIG3 0x001e1e00 +#define M_NAND_GPMC_CONFIG4 0x16051807 +#define M_NAND_GPMC_CONFIG5 0x00151e1e +#define M_NAND_GPMC_CONFIG6 0x16000f80 +#define M_NAND_GPMC_CONFIG7 0x00000008 + +/* max number of GPMC Chip Selects */ +#define GPMC_MAX_CS 8 +/* max number of GPMC regs */ +#define GPMC_MAX_REG 7 + +#define PISMO1_NOR 1 +#define PISMO1_NAND 2 +#define PISMO2_CS0 3 +#define PISMO2_CS1 4 +#define PISMO1_ONENAND 5 +#define DBG_MPDB 6 +#define PISMO2_NAND_CS0 7 +#define PISMO2_NAND_CS1 8 + +/* make it readable for the gpmc_init */ +#define PISMO1_NOR_BASE FLASH_BASE +#define PISMO1_NAND_BASE CONFIG_SYS_NAND_BASE +#define PISMO1_NAND_SIZE GPMC_SIZE_256M + +#endif /* endif _MEM_H_ */ diff --git a/arch/arm/include/asm/arch-am33xx/omap_gpmc.h b/arch/arm/include/asm/arch-am33xx/omap_gpmc.h new file mode 100644 index 0000000..572f9d0 --- /dev/null +++ b/arch/arm/include/asm/arch-am33xx/omap_gpmc.h @@ -0,0 +1,120 @@ +/* + * (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 = 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 = 5,\ + .length = 11 } } \ +} +#endif + +#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 } } \ +} + +#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/arch/arm/include/asm/arch-am33xx/sys_proto.h b/arch/arm/include/asm/arch-am33xx/sys_proto.h index 9cf35e0..588d8de 100644 --- a/arch/arm/include/asm/arch-am33xx/sys_proto.h +++ b/arch/arm/include/asm/arch-am33xx/sys_proto.h @@ -33,4 +33,7 @@ u32 get_device_type(void); void setup_clocks_for_console(void); void ddr_pll_config(unsigned int ddrpll_M);
+void sdelay(unsigned long); +void gpmc_init(void); +void omap_nand_switch_ecc(int); #endif

"Ilya" == Ilya Yanok ilya.yanok@cogentembedded.com writes:
Ilya> TI AM33XX has the same GPMC controller as OMAP3 so we could just use the Ilya> existing omap_gpmc driver. This patch adds adds required Ilya> definitions/intialization.
Ilya> Signed-off-by: Ilya Yanok ilya.yanok@cogentembedded.com
..
Ilya> +void enable_gpmc_cs_config(const u32 *gpmc_config, struct gpmc_cs *cs, u32 base, Ilya> + u32 size) Ilya> +{ Ilya> + writel(0, &cs->config7); Ilya> + sdelay(1000); Ilya> + /* Delay for settling */
That comment should go above the delay.
Ilya> + writel(gpmc_config[0], &cs->config1); Ilya> + writel(gpmc_config[1], &cs->config2); Ilya> + writel(gpmc_config[2], &cs->config3); Ilya> + writel(gpmc_config[3], &cs->config4); Ilya> + writel(gpmc_config[4], &cs->config5); Ilya> + writel(gpmc_config[5], &cs->config6); Ilya> + /* Enable the config */ Ilya> + writel((((size & 0xF) << 8) | ((base >> 24) & 0x3F) | Ilya> + (1 << 6)), &cs->config7); Ilya> + sdelay(2000);
Any reason you now wait double as long?
Ilya> +} Ilya> + Ilya> +/***************************************************** Ilya> + * gpmc_init(): init gpmc bus Ilya> + * Init GPMC for x16, MuxMode (SDRAM in x32). Ilya> + * This code can only be executed from SRAM or SDRAM. Ilya> + *****************************************************/ Ilya> +void gpmc_init(void) Ilya> +{ Ilya> + /* putting a blanket check on GPMC based on ZeBu for now */ Ilya> + gpmc_cfg = (struct gpmc *)GPMC_BASE; Ilya> + Ilya> +#ifdef CONFIG_CMD_NAND Ilya> + const u32 *gpmc_config = NULL; Ilya> + u32 base = 0; Ilya> + u32 size = 0; Ilya> +#endif Ilya> + /* global settings */ Ilya> + writel(0x00000008, &gpmc_cfg->sysconfig); Ilya> + writel(0x00000100, &gpmc_cfg->irqstatus); Ilya> + writel(0x00000200, &gpmc_cfg->irqenable); Ilya> + writel(0x00000012, &gpmc_cfg->config); Ilya> + /* Ilya> + * Disable the GPMC0 config set by ROM code Ilya> + */ Ilya> + writel(0, &gpmc_cfg->cs[0].config7); Ilya> + sdelay(1000);
Why? You already do this in enable_gpmc_cs_config().
Ilya> + Ilya> +#ifdef CONFIG_CMD_NAND Ilya> + gpmc_config = gpmc_m_nand; Ilya> + Ilya> + base = PISMO1_NAND_BASE; Ilya> + size = PISMO1_NAND_SIZE; Ilya> + enable_gpmc_cs_config(gpmc_config, &gpmc_cfg->cs[0], base, size); Ilya> +#endif Ilya> +}
Ilya> +++ b/arch/arm/include/asm/arch-am33xx/mem.h Ilya> @@ -0,0 +1,83 @@ Ilya> +/* Ilya> + * (C) Copyright 2006-2008 Ilya> + * Texas Instruments, <www.ti.com> Ilya> + * Ilya> + * Author Ilya> + * Mansoor Ahamed mansoor.ahamed@ti.com Ilya> + * Ilya> + * Initial Code from: Ilya> + * Richard Woodruff r-woodruff2@ti.com Ilya> + * Ilya> + * See file CREDITS for list of people who contributed to this Ilya> + * project. Ilya> + * Ilya> + * This program is free software; you can redistribute it and/or Ilya> + * modify it under the terms of the GNU General Public License as Ilya> + * published by the Free Software Foundation; either version 2 of Ilya> + * the License, or (at your option) any later version. Ilya> + * Ilya> + * This program is distributed in the hope that it will be useful, Ilya> + * but WITHOUT ANY WARRANTY; without even the implied warranty of Ilya> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Ilya> + * GNU General Public License for more details. Ilya> + * Ilya> + * You should have received a copy of the GNU General Public License Ilya> + * along with this program; if not, write to the Free Software Ilya> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, Ilya> + * MA 02111-1307 USA Ilya> + */ Ilya> + Ilya> +#ifndef _MEM_H_ Ilya> +#define _MEM_H_ Ilya> + Ilya> +/* Ilya> + * GPMC settings - Ilya> + * Definitions is as per the following format Ilya> + * #define <PART>_GPMC_CONFIG<x> <value> Ilya> + * Where: Ilya> + * PART is the part name e.g. STNOR - Intel Strata Flash Ilya> + * x is GPMC config registers from 1 to 6 (there will be 6 macros) Ilya> + * Value is corresponding value Ilya> + * Ilya> + * For every valid PRCM configuration there should be only one definition of Ilya> + * the same. if values are independent of the board, this definition will be Ilya> + * present in this file if values are dependent on the board, then this should Ilya> + * go into corresponding mem-boardName.h file Ilya> + * Ilya> + * Currently valid part Names are (PART): Ilya> + * M_NAND - Micron NAND Ilya> + */ Ilya> +#define GPMC_SIZE_256M 0x0 Ilya> +#define GPMC_SIZE_128M 0x8 Ilya> +#define GPMC_SIZE_64M 0xC Ilya> +#define GPMC_SIZE_32M 0xE Ilya> +#define GPMC_SIZE_16M 0xF Ilya> + Ilya> +#define M_NAND_GPMC_CONFIG1 0x00000800 Ilya> +#define M_NAND_GPMC_CONFIG2 0x001e1e00 Ilya> +#define M_NAND_GPMC_CONFIG3 0x001e1e00 Ilya> +#define M_NAND_GPMC_CONFIG4 0x16051807 Ilya> +#define M_NAND_GPMC_CONFIG5 0x00151e1e Ilya> +#define M_NAND_GPMC_CONFIG6 0x16000f80 Ilya> +#define M_NAND_GPMC_CONFIG7 0x00000008
For what Micron part is this exactly? How about using the actual part number in the define like we recently did for the DDR configuration?
Ilya> +++ b/arch/arm/include/asm/arch-am33xx/omap_gpmc.h Ilya> @@ -0,0 +1,120 @@ Ilya> +/* Ilya> + * (C) Copyright 2004-2008 Texas Instruments, <www.ti.com> Ilya> + * Rohit Choraria rohitkc@ti.com Ilya> + * Ilya> + * See file CREDITS for list of people who contributed to this Ilya> + * project. Ilya> + * Ilya> + * This program is free software; you can redistribute it and/or Ilya> + * modify it under the terms of the GNU General Public License as Ilya> + * published by the Free Software Foundation; either version 2 of Ilya> + * the License, or (at your option) any later version. Ilya> + * Ilya> + * This program is distributed in the hope that it will be useful, Ilya> + * but WITHOUT ANY WARRANTY; without even the implied warranty of Ilya> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Ilya> + * GNU General Public License for more details. Ilya> + * Ilya> + * You should have received a copy of the GNU General Public License Ilya> + * along with this program; if not, write to the Free Software Ilya> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, Ilya> + * MA 02111-1307 USA Ilya> + */ Ilya> +#ifndef __ASM_ARCH_OMAP_GPMC_H Ilya> +#define __ASM_ARCH_OMAP_GPMC_H Ilya> + Ilya> +#define GPMC_BUF_EMPTY 0 Ilya> +#define GPMC_BUF_FULL 1 Ilya> + Ilya> +#define ECCCLEAR (0x1 << 8) Ilya> +#define ECCRESULTREG1 (0x1 << 0) Ilya> +#define ECCSIZE512BYTE 0xFF Ilya> +#define ECCSIZE1 (ECCSIZE512BYTE << 22) Ilya> +#define ECCSIZE0 (ECCSIZE512BYTE << 12) Ilya> +#define ECCSIZE0SEL (0x000 << 0) Ilya> + Ilya> +/* Generic ECC Layouts */ Ilya> +/* Large Page x8 NAND device Layout */ Ilya> +#ifdef GPMC_NAND_ECC_LP_x8_LAYOUT Ilya> +#define GPMC_NAND_HW_ECC_LAYOUT {\ Ilya> + .eccbytes = 12,\ Ilya> + .eccpos = {1, 2, 3, 4, 5, 6, 7, 8,\ Ilya> + 9, 10, 11, 12},\ Ilya> + .oobfree = {\ Ilya> + {.offset = 13,\ Ilya> + .length = 51 } } \ Ilya> +} Ilya> +#endif Ilya> + Ilya> +/* Large Page x16 NAND device Layout */ Ilya> +#ifdef GPMC_NAND_ECC_LP_x16_LAYOUT Ilya> +#define GPMC_NAND_HW_ECC_LAYOUT {\ Ilya> + .eccbytes = 12,\ Ilya> + .eccpos = {2, 3, 4, 5, 6, 7, 8, 9,\ Ilya> + 10, 11, 12, 13},\ Ilya> + .oobfree = {\ Ilya> + {.offset = 14,\ Ilya> + .length = 50 } } \ Ilya> +} Ilya> +#endif Ilya> + Ilya> +/* Small Page x8 NAND device Layout */ Ilya> +#ifdef GPMC_NAND_ECC_SP_x8_LAYOUT Ilya> +#define GPMC_NAND_HW_ECC_LAYOUT {\ Ilya> + .eccbytes = 3,\ Ilya> + .eccpos = {1, 2, 3},\ Ilya> + .oobfree = {\ Ilya> + {.offset = 4,\ Ilya> + .length = 12 } } \ Ilya> +} Ilya> +#endif Ilya> + Ilya> +/* Small Page x16 NAND device Layout */ Ilya> +#ifdef GPMC_NAND_ECC_SP_x16_LAYOUT Ilya> +#define GPMC_NAND_HW_ECC_LAYOUT {\ Ilya> + .eccbytes = 3,\ Ilya> + .eccpos = {2, 3, 4},\ Ilya> + .oobfree = {\ Ilya> + {.offset = 5,\ Ilya> + .length = 11 } } \ Ilya> +} Ilya> +#endif Ilya> + Ilya> +#define GPMC_NAND_HW_BCH4_ECC_LAYOUT {\ Ilya> + .eccbytes = 32,\ Ilya> + .eccpos = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\ Ilya> + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,\ Ilya> + 28, 29, 30, 31, 32, 33},\ Ilya> + .oobfree = {\ Ilya> + {.offset = 34,\ Ilya> + .length = 30 } } \ Ilya> +} Ilya> + Ilya> +#define GPMC_NAND_HW_BCH8_ECC_LAYOUT {\ Ilya> + .eccbytes = 56,\ Ilya> + .eccpos = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\ Ilya> + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,\ Ilya> + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,\ Ilya> + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,\ Ilya> + 52, 53, 54, 55, 56, 57},\ Ilya> + .oobfree = {\ Ilya> + {.offset = 58,\ Ilya> + .length = 6 } } \ Ilya> +} Ilya> + Ilya> +#define GPMC_NAND_HW_BCH16_ECC_LAYOUT {\ Ilya> + .eccbytes = 104,\ Ilya> + .eccpos = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\ Ilya> + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,\ Ilya> + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,\ Ilya> + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,\ Ilya> + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,\ Ilya> + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75,\ Ilya> + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,\ Ilya> + 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,\ Ilya> + 100, 101, 102, 103, 104, 105},\ Ilya> + .oobfree = {\ Ilya> + {.offset = 106,\ Ilya> + .length = 8 } } \ Ilya> +} Ilya> +#endif /* __ASM_ARCH_OMAP_GPMC_H */
Do the non-BCH layouts make sense for am33xx? I've noticed that the TRM shows the BCH8 format as 13 bytes/256 tightly packed instead of 14, but that's wrong. I'll report it to the doc people.

Hi Peter,
On Thu, Nov 8, 2012 at 10:33 AM, Peter Korsgaard jacmet@sunsite.dk wrote:
Ilya> +void enable_gpmc_cs_config(const u32 *gpmc_config, struct gpmc_cs *cs, u32 base, Ilya> + u32 size) Ilya> +{ Ilya> + writel(0, &cs->config7); Ilya> + sdelay(1000); Ilya> + /* Delay for settling */
That comment should go above the delay.
Ok, will fix.
Ilya> + writel(gpmc_config[0], &cs->config1); Ilya> + writel(gpmc_config[1], &cs->config2); Ilya> + writel(gpmc_config[2], &cs->config3); Ilya> + writel(gpmc_config[3], &cs->config4); Ilya> + writel(gpmc_config[4], &cs->config5); Ilya> + writel(gpmc_config[5], &cs->config6); Ilya> + /* Enable the config */ Ilya> + writel((((size & 0xF) << 8) | ((base >> 24) & 0x3F) | Ilya> + (1 << 6)), &cs->config7); Ilya> + sdelay(2000);
Any reason you now wait double as long?
No idea ;) To tell the truth I've taken all this code from omap3 as is.
Ilya> +} Ilya> + Ilya> +/***************************************************** Ilya> + * gpmc_init(): init gpmc bus Ilya> + * Init GPMC for x16, MuxMode (SDRAM in x32). Ilya> + * This code can only be executed from SRAM or SDRAM. Ilya> + *****************************************************/ Ilya> +void gpmc_init(void) Ilya> +{ Ilya> + /* putting a blanket check on GPMC based on ZeBu for now */ Ilya> + gpmc_cfg = (struct gpmc *)GPMC_BASE; Ilya> + Ilya> +#ifdef CONFIG_CMD_NAND Ilya> + const u32 *gpmc_config = NULL; Ilya> + u32 base = 0; Ilya> + u32 size = 0; Ilya> +#endif Ilya> + /* global settings */ Ilya> + writel(0x00000008, &gpmc_cfg->sysconfig); Ilya> + writel(0x00000100, &gpmc_cfg->irqstatus); Ilya> + writel(0x00000200, &gpmc_cfg->irqenable); Ilya> + writel(0x00000012, &gpmc_cfg->config); Ilya> + /* Ilya> + * Disable the GPMC0 config set by ROM code Ilya> + */ Ilya> + writel(0, &gpmc_cfg->cs[0].config7); Ilya> + sdelay(1000);
Why? You already do this in enable_gpmc_cs_config().
Hm... Again, I'm not the one who written this code, I just stole it ;) but probably the idea was to disable the config even in case of CONFIG_CMD_NAND undefined...
Ilya> + Ilya> +#ifdef CONFIG_CMD_NAND Ilya> + gpmc_config = gpmc_m_nand; Ilya> + Ilya> + base = PISMO1_NAND_BASE; Ilya> + size = PISMO1_NAND_SIZE; Ilya> + enable_gpmc_cs_config(gpmc_config, &gpmc_cfg->cs[0], base, size); Ilya> +#endif Ilya> +}
Ilya> +#define M_NAND_GPMC_CONFIG1 0x00000800 Ilya> +#define M_NAND_GPMC_CONFIG2 0x001e1e00 Ilya> +#define M_NAND_GPMC_CONFIG3 0x001e1e00 Ilya> +#define M_NAND_GPMC_CONFIG4 0x16051807 Ilya> +#define M_NAND_GPMC_CONFIG5 0x00151e1e Ilya> +#define M_NAND_GPMC_CONFIG6 0x16000f80 Ilya> +#define M_NAND_GPMC_CONFIG7 0x00000008
For what Micron part is this exactly? How about using the actual part number in the define like we recently did for the DDR configuration?
Ok, I'll try to figure that out from schematics.
Do the non-BCH layouts make sense for am33xx? I've noticed that the TRM
shows the BCH8 format as 13 bytes/256 tightly packed instead of 14, but that's wrong. I'll report it to the doc people.
Hm. Non-BCH layouts was here because I initially planned to support nandecc command to switch between supported ECC schemas... But as Tom requested to remove it I guess we don't need these layouts any more... Well, my idea was to make separate patches (one to just support existing omap_gpmc driver on AM33xx and one to add BCH8 support) and this kinda needs non-BCH layouts too... but I guess I should just squash the patches into one.
Thanks for the review.
Regards, Ilya.

"Ilya" == Ilya Yanok ilya.yanok@cogentembedded.com writes:
Hi,
Peter> Why? You already do this in enable_gpmc_cs_config().
Ilya> Hm... Again, I'm not the one who written this code, I just stole Ilya> it ;) but probably the idea was to disable the config even in Ilya> case of CONFIG_CMD_NAND undefined...
Why would you want to do that? I don't see a reason for touching the GPMC if you're not going to use it.
Ilya> Ilya> +#define M_NAND_GPMC_CONFIG1 0x00000800 Ilya> Ilya> +#define M_NAND_GPMC_CONFIG2 0x001e1e00 Ilya> Ilya> +#define M_NAND_GPMC_CONFIG3 0x001e1e00 Ilya> Ilya> +#define M_NAND_GPMC_CONFIG4 0x16051807 Ilya> Ilya> +#define M_NAND_GPMC_CONFIG5 0x00151e1e Ilya> Ilya> +#define M_NAND_GPMC_CONFIG6 0x16000f80 Ilya> Ilya> +#define M_NAND_GPMC_CONFIG7 0x00000008
Peter> For what Micron part is this exactly? How about using the actual part Peter> number in the define like we recently did for the DDR configuration?
Ilya> Ok, I'll try to figure that out from schematics.
Great, thanks. The choice of GPMC configuration values should also be moved to board code, similar to how I did it for DDR.
Peter> Do the non-BCH layouts make sense for am33xx? I've noticed Peter> that the TRM shows the BCH8 format as 13 bytes/256 tightly Peter> packed instead of 14, but that's wrong. I'll report it to the Peter> doc people.
Ilya> Hm. Non-BCH layouts was here because I initially planned to Ilya> support nandecc command to switch between supported ECC Ilya> schemas... But as Tom requested to remove it I guess we don't Ilya> need these layouts any more... Well, my idea was to make Ilya> separate patches (one to just support existing omap_gpmc driver Ilya> on AM33xx and one to add BCH8 support) and this kinda needs Ilya> non-BCH layouts too... but I guess I should just squash the Ilya> patches into one.
I don't think non-BCH makes much sense on am33xx considering the ROM uses/requires BCH8.
Ilya> Thanks for the review.
You're welcome.

On Thu, Nov 15, 2012 at 11:26:59PM +0100, Peter Korsgaard wrote:
Ilya> Hm. Non-BCH layouts was here because I initially planned to Ilya> support nandecc command to switch between supported ECC Ilya> schemas... But as Tom requested to remove it I guess we don't Ilya> need these layouts any more... Well, my idea was to make Ilya> separate patches (one to just support existing omap_gpmc driver Ilya> on AM33xx and one to add BCH8 support) and this kinda needs Ilya> non-BCH layouts too... but I guess I should just squash the Ilya> patches into one.
I don't think non-BCH makes much sense on am33xx considering the ROM uses/requires BCH8.
Long term (not today tho) we need to support BCH16 as well since the ROM requires BCH16 on some parts and BCH8 on others. We can cross that bridge when we come to it, however.

Enable NAND support for AM335X boards.
Signed-off-by: Ilya Yanok ilya.yanok@cogentembedded.com
--- Changes in v2: - rebased onto master - minor config style fix (wrt nand)
board/ti/am335x/board.c | 2 ++ include/configs/am335x_evm.h | 12 ++++++++++++ 2 files changed, 14 insertions(+)
diff --git a/board/ti/am335x/board.c b/board/ti/am335x/board.c index b56a801..6908378 100644 --- a/board/ti/am335x/board.c +++ b/board/ti/am335x/board.c @@ -318,6 +318,8 @@ int board_init(void)
gd->bd->bi_boot_params = PHYS_DRAM_1 + 0x100;
+ gpmc_init(); + return 0; }
diff --git a/include/configs/am335x_evm.h b/include/configs/am335x_evm.h index b6e48f8..ded1cab 100644 --- a/include/configs/am335x_evm.h +++ b/include/configs/am335x_evm.h @@ -274,4 +274,16 @@ #define CONFIG_PHYLIB #define CONFIG_PHY_SMSC
+#define CONFIG_NAND +/* NAND support */ +#ifdef CONFIG_NAND +#define CONFIG_CMD_NAND +#define CONFIG_NAND_OMAP_GPMC +#define GPMC_NAND_ECC_LP_x16_LAYOUT 1 +#define CONFIG_SYS_NAND_BASE (0x08000000) /* physical address */ + /* to access nand at */ + /* CS0 */ +#define CONFIG_SYS_MAX_NAND_DEVICE 1 /* Max number of NAND */ +#endif /* devices */ + #endif /* ! __CONFIG_AM335X_EVM_H */

From: Mansoor Ahamed mansoor.ahamed@ti.com
AM33XX has Error Location Module (ELM) that can be used in conjuction with GPMC controller to implement BCH codes fully in hardware. This code is mostly taken from arago tree.
Signed-off-by: Mansoor Ahamed mansoor.ahamed@ti.com Signed-off-by: Ilya Yanok ilya.yanok@cogentembedded.com
--- Changes in v2: - fix wrong braces in Makefile
arch/arm/cpu/armv7/am33xx/Makefile | 1 + arch/arm/cpu/armv7/am33xx/clock.c | 5 + arch/arm/cpu/armv7/am33xx/elm.c | 212 ++++++++++++++++++++++++++++++++ arch/arm/include/asm/arch-am33xx/elm.h | 93 ++++++++++++++ 4 files changed, 311 insertions(+) create mode 100644 arch/arm/cpu/armv7/am33xx/elm.c create mode 100644 arch/arm/include/asm/arch-am33xx/elm.h
diff --git a/arch/arm/cpu/armv7/am33xx/Makefile b/arch/arm/cpu/armv7/am33xx/Makefile index f565357..70c443e 100644 --- a/arch/arm/cpu/armv7/am33xx/Makefile +++ b/arch/arm/cpu/armv7/am33xx/Makefile @@ -23,6 +23,7 @@ COBJS += ddr.o COBJS += emif4.o COBJS += board.o COBJS += mux.o +COBJS-$(CONFIG_NAND_OMAP_GPMC) += elm.o
SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(COBJS) $(COBJS-y) $(SOBJS)) diff --git a/arch/arm/cpu/armv7/am33xx/clock.c b/arch/arm/cpu/armv7/am33xx/clock.c index 6eb7d9f..2b7c910 100644 --- a/arch/arm/cpu/armv7/am33xx/clock.c +++ b/arch/arm/cpu/armv7/am33xx/clock.c @@ -155,6 +155,11 @@ static void enable_per_clocks(void) while (readl(&cmper->gpmcclkctrl) != PRCM_MOD_EN) ;
+ /* ELM */ + writel(PRCM_MOD_EN, &cmper->elmclkctrl); + while (readl(&cmper->elmclkctrl) != PRCM_MOD_EN) + ; + /* MMC0*/ writel(PRCM_MOD_EN, &cmper->mmc0clkctrl); while (readl(&cmper->mmc0clkctrl) != PRCM_MOD_EN) diff --git a/arch/arm/cpu/armv7/am33xx/elm.c b/arch/arm/cpu/armv7/am33xx/elm.c new file mode 100644 index 0000000..9eed23d --- /dev/null +++ b/arch/arm/cpu/armv7/am33xx/elm.c @@ -0,0 +1,212 @@ +/* + * (C) Copyright 2010-2011 Texas Instruments, <www.ti.com> + * Mansoor Ahamed mansoor.ahamed@ti.com + * + * BCH Error Location Module (ELM) support. + * + * NOTE: + * 1. Supports only continuous mode. Dont see need for page mode in uboot + * 2. Supports only syndrome polynomial 0. i.e. poly local variable is + * always set to ELM_DEFAULT_POLY. Dont see need for other polynomial + * sets in uboot + * + * 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/cpu.h> +#include <asm/arch/omap_gpmc.h> +#include <asm/arch/elm.h> + +#define ELM_DEFAULT_POLY (0) + +struct elm *elm_cfg; + +/** + * elm_load_syndromes - Load BCH syndromes based on nibble selection + * @syndrome: BCH syndrome + * @nibbles: + * @poly: Syndrome Polynomial set to use + * + * Load BCH syndromes based on nibble selection + */ +static void elm_load_syndromes(u8 *syndrome, u32 nibbles, u8 poly) +{ + u32 *ptr; + u32 val; + + /* reg 0 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[0]; + val = syndrome[0] | (syndrome[1] << 8) | (syndrome[2] << 16) | + (syndrome[3] << 24); + writel(val, ptr); + /* reg 1 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[1]; + val = syndrome[4] | (syndrome[5] << 8) | (syndrome[6] << 16) | + (syndrome[7] << 24); + writel(val, ptr); + + /* BCH 8-bit with 26 nibbles (4*8=32) */ + if (nibbles > 13) { + /* reg 2 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[2]; + val = syndrome[8] | (syndrome[9] << 8) | (syndrome[10] << 16) | + (syndrome[11] << 24); + writel(val, ptr); + /* reg 3 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[3]; + val = syndrome[12] | (syndrome[13] << 8) | + (syndrome[14] << 16) | (syndrome[15] << 24); + writel(val, ptr); + } + + /* BCH 16-bit with 52 nibbles (7*8=56) */ + if (nibbles > 26) { + /* reg 4 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[4]; + val = syndrome[16] | (syndrome[17] << 8) | + (syndrome[18] << 16) | (syndrome[19] << 24); + writel(val, ptr); + + /* reg 5 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[5]; + val = syndrome[20] | (syndrome[21] << 8) | + (syndrome[22] << 16) | (syndrome[23] << 24); + writel(val, ptr); + + /* reg 6 */ + ptr = &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]; + val = syndrome[24] | (syndrome[25] << 8) | + (syndrome[26] << 16) | (syndrome[27] << 24); + writel(val, ptr); + } +} + +/** + * elm_check_errors - Check for BCH errors and return error locations + * @syndrome: BCH syndrome + * @nibbles: + * @error_count: Returns number of errrors in the syndrome + * @error_locations: Returns error locations (in decimal) in this array + * + * Check the provided syndrome for BCH errors and return error count + * and locations in the array passed. Returns -1 if error is not correctable, + * else returns 0 + */ +int elm_check_error(u8 *syndrome, u32 nibbles, u32 *error_count, + u32 *error_locations) +{ + u8 poly = ELM_DEFAULT_POLY; + s8 i; + u32 location_status; + + elm_load_syndromes(syndrome, nibbles, poly); + + /* start processing */ + writel((readl(&elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]) + | ELM_SYNDROME_FRAGMENT_6_SYNDROME_VALID), + &elm_cfg->syndrome_fragments[poly].syndrome_fragment_x[6]); + + /* wait for processing to complete */ + while ((readl(&elm_cfg->irqstatus) & (0x1 << poly)) != 0x1) + ; + /* clear status */ + writel((readl(&elm_cfg->irqstatus) | (0x1 << poly)), + &elm_cfg->irqstatus); + + /* check if correctable */ + location_status = readl(&elm_cfg->error_location[poly].location_status); + if (!(location_status & ELM_LOCATION_STATUS_ECC_CORRECTABLE_MASK)) + return -1; + + /* get error count */ + *error_count = readl(&elm_cfg->error_location[poly].location_status) & + ELM_LOCATION_STATUS_ECC_NB_ERRORS_MASK; + + for (i = 0; i < *error_count; i++) { + error_locations[i] = + readl(&elm_cfg->error_location[poly].error_location_x[i]); + } + + return 0; +} + + +/** + * elm_config - Configure ELM module + * @level: 4 / 8 / 16 bit BCH + * + * Configure ELM module based on BCH level. + * Set mode as continuous mode. + * Currently we are using only syndrome 0 and syndromes 1 to 6 are not used. + * Also, the mode is set only for syndrome 0 + */ +int elm_config(enum bch_level level) +{ + u32 val; + u8 poly = ELM_DEFAULT_POLY; + u32 buffer_size = 0x7FF; + + /* config size and level */ + val = (u32)(level) & ELM_LOCATION_CONFIG_ECC_BCH_LEVEL_MASK; + val |= ((buffer_size << ELM_LOCATION_CONFIG_ECC_SIZE_POS) & + ELM_LOCATION_CONFIG_ECC_SIZE_MASK); + writel(val, &elm_cfg->location_config); + + /* config continous mode */ + /* enable interrupt generation for syndrome polynomial set */ + writel((readl(&elm_cfg->irqenable) | (0x1 << poly)), + &elm_cfg->irqenable); + /* set continuous mode for the syndrome polynomial set */ + writel((readl(&elm_cfg->page_ctrl) & ~(0x1 << poly)), + &elm_cfg->page_ctrl); + + return 0; +} + +/** + * elm_reset - Do a soft reset of ELM + * + * Perform a soft reset of ELM and return after reset is done. + */ +void elm_reset(void) +{ + /* initiate reset */ + writel((readl(&elm_cfg->sysconfig) | ELM_SYSCONFIG_SOFTRESET), + &elm_cfg->sysconfig); + + /* wait for reset complete and normal operation */ + while ((readl(&elm_cfg->sysstatus) & ELM_SYSSTATUS_RESETDONE) != + ELM_SYSSTATUS_RESETDONE) + ; +} + +/** + * elm_init - Initialize ELM module + * + * Initialize ELM support. Currently it does only base address init + * and ELM reset. + */ +void elm_init(void) +{ + elm_cfg = (struct elm *)ELM_BASE; + elm_reset(); +} diff --git a/arch/arm/include/asm/arch-am33xx/elm.h b/arch/arm/include/asm/arch-am33xx/elm.h new file mode 100644 index 0000000..e80f7d4 --- /dev/null +++ b/arch/arm/include/asm/arch-am33xx/elm.h @@ -0,0 +1,93 @@ +/* + * (C) Copyright 2010-2011 Texas Instruments, <www.ti.com> + * Mansoor Ahamed mansoor.ahamed@ti.com + * + * Derived from work done by Rohit Choraria rohitkc@ti.com for omap3 + * + * 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_ELM_H +#define __ASM_ARCH_ELM_H +/* + * ELM Module Registers + */ + +/* ELM registers bit fields */ +#define ELM_SYSCONFIG_SOFTRESET_MASK (0x2) +#define ELM_SYSCONFIG_SOFTRESET (0x2) +#define ELM_SYSSTATUS_RESETDONE_MASK (0x1) +#define ELM_SYSSTATUS_RESETDONE (0x1) +#define ELM_LOCATION_CONFIG_ECC_BCH_LEVEL_MASK (0x3) +#define ELM_LOCATION_CONFIG_ECC_SIZE_MASK (0x7FF0000) +#define ELM_LOCATION_CONFIG_ECC_SIZE_POS (16) +#define ELM_SYNDROME_FRAGMENT_6_SYNDROME_VALID (0x00010000) +#define ELM_LOCATION_STATUS_ECC_CORRECTABLE_MASK (0x100) +#define ELM_LOCATION_STATUS_ECC_NB_ERRORS_MASK (0x1F) + +#ifndef __ASSEMBLY__ + +enum bch_level { + BCH_4_BIT = 0, + BCH_8_BIT, + BCH_16_BIT +}; + + +/* BCH syndrome registers */ +struct syndrome { + u32 syndrome_fragment_x[7]; /* 0x400, 0x404.... 0x418 */ + u8 res1[36]; /* 0x41c */ +}; + +/* BCH error status & location register */ +struct location { + u32 location_status; /* 0x800 */ + u8 res1[124]; /* 0x804 */ + u32 error_location_x[16]; /* 0x880.... */ + u8 res2[64]; /* 0x8c0 */ +}; + +/* BCH ELM register map - do not try to allocate memmory for this structure. + * We have used plenty of reserved variables to fill the slots in the ELM + * register memory map. + * Directly initialize the struct pointer to ELM base address. + */ +struct elm { + u32 rev; /* 0x000 */ + u8 res1[12]; /* 0x004 */ + u32 sysconfig; /* 0x010 */ + u32 sysstatus; /* 0x014 */ + u32 irqstatus; /* 0x018 */ + u32 irqenable; /* 0x01c */ + u32 location_config; /* 0x020 */ + u8 res2[92]; /* 0x024 */ + u32 page_ctrl; /* 0x080 */ + u8 res3[892]; /* 0x084 */ + struct syndrome syndrome_fragments[8]; /* 0x400 */ + u8 res4[512]; /* 0x600 */ + struct location error_location[8]; /* 0x800 */ +}; + +int elm_check_error(u8 *syndrome, u32 nibbles, u32 *error_count, + u32 *error_locations); +int elm_config(enum bch_level level); +void elm_reset(void); +void elm_init(void); +#endif /* __ASSEMBLY__ */ +#endif /* __ASM_ARCH_ELM_H */

From: Mansoor Ahamed mansoor.ahamed@ti.com
This patch adds support for BCH8 error correction code to omap_gpmc driver. We use GPMC to generate codes/syndromes but we need ELM to find error locations from given syndrome.
Signed-off-by: Mansoor Ahamed mansoor.ahamed@ti.com [ilya: merge it with omap_gpmc driver, some fixes and cleanup] Signed-off-by: Ilya Yanok ilya.yanok@cogentembedded.com ---
drivers/mtd/nand/omap_gpmc.c | 403 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 402 insertions(+), 1 deletion(-)
diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c index f1469d1..cee394e 100644 --- a/drivers/mtd/nand/omap_gpmc.c +++ b/drivers/mtd/nand/omap_gpmc.c @@ -29,6 +29,9 @@ #include <linux/mtd/nand_ecc.h> #include <linux/compiler.h> #include <nand.h> +#ifdef CONFIG_AM33XX +#include <asm/arch/elm.h> +#endif
static uint8_t cs; static __maybe_unused struct nand_ecclayout hw_nand_oob = @@ -234,6 +237,370 @@ static void __maybe_unused omap_enable_hwecc(struct mtd_info *mtd, int32_t mode) } }
+/* + * BCH8 support (needs ELM and thus AM33xx-only) + */ +#ifdef CONFIG_AM33XX +struct nand_bch_priv { + uint8_t mode; + uint8_t type; + uint8_t nibbles; +}; + +/* bch types */ +#define ECC_BCH4 0 +#define ECC_BCH8 1 +#define ECC_BCH16 2 + +/* 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 + +static struct nand_ecclayout hw_bch8_nand_oob = GPMC_NAND_HW_BCH8_ECC_LAYOUT; + +static struct nand_bch_priv bch_priv = { + .mode = NAND_ECC_HW_BCH, + .type = ECC_BCH8, + .nibbles = ECC_BCH8_NIBBLES +}; + +/* + * omap_read_bch8_result - Read BCH result for BCH8 level + * + * @mtd: MTD device structure + * @big_endian: When set read register 3 first + * @ecc_code: Read syndrome from BCH result registers + */ +static void omap_read_bch8_result(struct mtd_info *mtd, uint8_t big_endian, + uint8_t *ecc_code) +{ + uint32_t *ptr; + int8_t i = 0, j; + + if (big_endian) { + ptr = &gpmc_cfg->bch_result_0_3[0].bch_result_x[3]; + ecc_code[i++] = readl(ptr) & 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; + 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 */ + } +} + +/* + * omap_ecc_disable - Disable H/W ECC calculation + * + * @mtd: MTD device structure + * + */ +static void omap_ecc_disable(struct mtd_info *mtd) +{ + writel((readl(&gpmc_cfg->ecc_config) & ~0x1), + &gpmc_cfg->ecc_config); +} + +/* + * omap_rotate_ecc_bch - Rotate the syndrome bytes + * + * @mtd: MTD device structure + * @calc_ecc: ECC read from ECC registers + * @syndrome: Rotated syndrome will be retuned in this array + * + */ +static void omap_rotate_ecc_bch(struct mtd_info *mtd, uint8_t *calc_ecc, + uint8_t *syndrome) +{ + struct nand_chip *chip = mtd->priv; + struct nand_bch_priv *bch = chip->priv; + uint8_t n_bytes = 0; + int8_t i, j; + + switch (bch->type) { + case ECC_BCH4: + n_bytes = 8; + break; + + case ECC_BCH16: + n_bytes = 28; + break; + + case ECC_BCH8: + default: + n_bytes = 13; + break; + } + + for (i = 0, j = (n_bytes-1); i < n_bytes; i++, j--) + syndrome[i] = calc_ecc[j]; +} + +/* + * 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 + * @data: Data read from flash + * @error_count:Number of errors in data + * @error_loc: Locations of errors in the data + * + */ +static void omap_fix_errors_bch(struct mtd_info *mtd, uint8_t *data, + uint32_t error_count, uint32_t *error_loc) +{ + struct nand_chip *chip = mtd->priv; + struct nand_bch_priv *bch = chip->priv; + uint8_t count = 0; + uint32_t error_byte_pos; + uint32_t error_bit_mask; + uint32_t last_bit = (bch->nibbles * 4) - 1; + + /* Flip all bits as specified by the error location array. */ + /* FOR( each found error location flip the bit ) */ + for (count = 0; count < error_count; count++) { + if (error_loc[count] > last_bit) { + /* Remove the ECC spare bits from correction. */ + error_loc[count] -= (last_bit + 1); + /* Offset bit in data region */ + error_byte_pos = ((512 * 8) - + (error_loc[count]) - 1) / 8; + /* Error Bit mask */ + error_bit_mask = 0x1 << (error_loc[count] % 8); + /* Toggle the error bit to make the correction. */ + data[error_byte_pos] ^= error_bit_mask; + } + } +} + +/* + * omap_correct_data_bch - 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 (ignored) + * @calc_ecc: ecc read from ECC registers + * + * @return 0 if data is OK or corrected, else returns -1 + */ +static int omap_correct_data_bch(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + struct nand_chip *chip = mtd->priv; + struct nand_bch_priv *bch = chip->priv; + uint8_t syndrome[28]; + uint32_t error_count = 0; + uint32_t error_loc[8]; + uint32_t i, ecc_flag; + + ecc_flag = 0; + for (i = 0; i < chip->ecc.bytes; i++) + if (read_ecc[i] != 0xff) + ecc_flag = 1; + + if (!ecc_flag) + return 0; + + elm_reset(); + elm_config((enum bch_level)(bch->type)); + + /* + * while reading ECC result we read it in big endian. + * Hence while loading to ELM we have rotate to get the right endian. + */ + omap_rotate_ecc_bch(mtd, calc_ecc, syndrome); + + /* use elm module to check for errors */ + if (elm_check_error(syndrome, bch->nibbles, &error_count, + error_loc) != 0) { + printf("ECC: uncorrectable.\n"); + return -1; + } + + /* correct bch error */ + if (error_count > 0) + omap_fix_errors_bch(mtd, dat, error_count, error_loc); + + return 0; +} +/* + * omap_hwecc_init_bch - Initialize the BCH Hardware ECC for NAND flash in + * GPMC controller + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +static void omap_hwecc_init_bch(struct nand_chip *chip, int32_t mode) +{ + uint32_t val, dev_width = (chip->options & NAND_BUSWIDTH_16) >> 1; + uint32_t unused_length = 0; + struct nand_bch_priv *bch = chip->priv; + + 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; + } + + /* Clear the ecc result registers, select ecc reg as 1 */ + writel(ECCCLEAR | ECCRESULTREG1, &gpmc_cfg->ecc_control); + + switch (mode) { + case NAND_ECC_WRITE: + /* 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; + } + /* ecc size configuration */ + writel(val, &gpmc_cfg->ecc_size_config); + /* by default 512bytes sector page is selected */ + /* set bch mode */ + val = (1 << 16); + /* bch4 / bch8 / bch16 */ + val |= (bch->type << 12); + /* set wrap mode to 1 */ + val |= (1 << 8); + val |= (dev_width << 7); + val |= (cs << 1); + writel(val, &gpmc_cfg->ecc_config); +} + +/* + * omap_enable_ecc_bch- This function enables the bch h/w ecc functionality + * @mtd: MTD device structure + * @mode: Read/Write mode + * + */ +static void omap_enable_ecc_bch(struct mtd_info *mtd, int32_t mode) +{ + struct nand_chip *chip = mtd->priv; + + omap_hwecc_init_bch(chip, mode); + /* enable ecc */ + writel((readl(&gpmc_cfg->ecc_config) | 0x1), &gpmc_cfg->ecc_config); +} + +/** + * omap_read_page_bch - hardware ecc based page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @page: page number to read + * + */ +static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *oob = chip->oob_poi; + uint32_t data_pos; + uint32_t oob_pos; + + data_pos = 0; + /* oob area start */ + oob_pos = (eccsize * eccsteps) + chip->ecc.layout->eccpos[0]; + oob += chip->ecc.layout->eccpos[0]; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize, + oob += eccbytes) { + chip->ecc.hwctl(mtd, NAND_ECC_READ); + /* read data */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_pos, page); + chip->read_buf(mtd, p, eccsize); + + /* read respective ecc from oob area */ + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_pos, page); + chip->read_buf(mtd, oob, eccbytes); + /* read syndrome */ + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + data_pos += eccsize; + oob_pos += eccbytes; + } + + for (i = 0; i < chip->ecc.total; i++) + ecc_code[i] = chip->oob_poi[eccpos[i]]; + + eccsteps = chip->ecc.steps; + p = buf; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + int stat; + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} +#endif /* CONFIG_AM33XX */ + #ifndef CONFIG_SPL_BUILD /* * omap_nand_switch_ecc - switch the ECC operation b/w h/w ecc and s/w ecc. @@ -269,7 +636,7 @@ void omap_nand_switch_ecc(int32_t hardware) nand->ecc.calculate = NULL;
/* Setup the ecc configurations again */ - if (hardware) { + if (hardware == 1) { nand->ecc.mode = NAND_ECC_HW; nand->ecc.layout = &hw_nand_oob; nand->ecc.size = 512; @@ -279,6 +646,19 @@ void omap_nand_switch_ecc(int32_t hardware) nand->ecc.calculate = omap_calculate_ecc; omap_hwecc_init(nand); printf("HW ECC selected\n"); +#ifdef CONFIG_AM33XX + } else if (hardware == 2) { + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = &hw_bch8_nand_oob; + nand->ecc.size = 512; + nand->ecc.bytes = 14; + nand->ecc.read_page = omap_read_page_bch; + 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("HW BCH8 selected\n"); +#endif } else { nand->ecc.mode = NAND_ECC_SOFT; /* Use mtd default settings */ @@ -350,7 +730,27 @@ int board_nand_init(struct nand_chip *nand) nand->options |= NAND_BUSWIDTH_16;
nand->chip_delay = 100; + +#ifdef CONFIG_AM33XX + /* required in case of BCH */ + elm_init(); + + /* BCH info that will be correct for SPL or overridden otherwise. */ + nand->priv = &bch_priv; +#endif + /* Default ECC mode */ +#ifdef CONFIG_AM33XX + 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.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; + 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 @@ -363,6 +763,7 @@ int board_nand_init(struct nand_chip *nand) nand->ecc.calculate = omap_calculate_ecc; omap_hwecc_init(nand); #endif +#endif
#ifdef CONFIG_SPL_BUILD if (nand->options & NAND_BUSWIDTH_16)

Dear Ilya Yanok,
On 07.11.2012 00:06, Ilya Yanok wrote:
From: Mansoor Ahamed mansoor.ahamed@ti.com
This patch adds support for BCH8 error correction code to omap_gpmc driver. We use GPMC to generate codes/syndromes but we need ELM to find error locations from given syndrome.
first of all, I wonder why this is so different than the kernel implementation for BCH. I mean the API (and content) of this and commit 8d602cf50d3bba864bc1438f486b626df69c87b3 mainline linux seems to differ. The main question coming to mind is: Is the resulting OOB layout compatible then?
Best regards
Andreas Bießmann

On Thu, Nov 15, 2012 at 02:25:23PM +0100, Andreas Bießmann wrote:
Dear Ilya Yanok,
On 07.11.2012 00:06, Ilya Yanok wrote:
From: Mansoor Ahamed mansoor.ahamed@ti.com
This patch adds support for BCH8 error correction code to omap_gpmc driver. We use GPMC to generate codes/syndromes but we need ELM to find error locations from given syndrome.
first of all, I wonder why this is so different than the kernel implementation for BCH. I mean the API (and content) of this and commit 8d602cf50d3bba864bc1438f486b626df69c87b3 mainline linux seems to differ. The main question coming to mind is: Is the resulting OOB layout compatible then?
I think this has been mostly addressed now, but for clarity: - We do NOT want to have > 1 layout used per NAND chip unless we must (historically we did because we had ROM that couldn't use >1bit ECC). - We DO want to utilize the HW as this is the only easy way to get a match with the BCH constants the ROM uses. - There are corresponding kernel patches already posted and working their way along.

On 21.11.12 18:04, Tom Rini wrote:
On Thu, Nov 15, 2012 at 02:25:23PM +0100, Andreas Bießmann wrote:
Dear Ilya Yanok,
On 07.11.2012 00:06, Ilya Yanok wrote:
From: Mansoor Ahamed mansoor.ahamed@ti.com
This patch adds support for BCH8 error correction code to omap_gpmc driver. We use GPMC to generate codes/syndromes but we need ELM to find error locations from given syndrome.
first of all, I wonder why this is so different than the kernel implementation for BCH. I mean the API (and content) of this and commit 8d602cf50d3bba864bc1438f486b626df69c87b3 mainline linux seems to differ. The main question coming to mind is: Is the resulting OOB layout compatible then?
I think this has been mostly addressed now, but for clarity:
- We do NOT want to have > 1 layout used per NAND chip unless we must (historically we did because we had ROM that couldn't use >1bit ECC).
- We DO want to utilize the HW as this is the only easy way to get a match with the BCH constants the ROM uses.
- There are corresponding kernel patches already posted and working their way along.
I'm fine with all these three points. My question came up when I looked into this deeply the very first time cause BCH4/8 support was missing for OMAP35xx/AM37xx devices. I do have now working support for these (hw assisted BCH but sw correction) like the kernel does. It needs some final polishing however I will send it these days as RFC, would be great to get some feedback.
Best regards
Andreas Bießmann

AM33XX with BCH8 can't work with nand_spl_simple correctly because custom read_page implementation is required for proper syndrome generation.
This simple driver mostly duplicates nand_spl_simple but has nand_read_page changed to suit our needs.
Signed-off-by: Ilya Yanok ilya.yanok@cogentembedded.com ---
drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/am335x_spl_bch.c | 238 +++++++++++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+) create mode 100644 drivers/mtd/nand/am335x_spl_bch.c
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index beb99ca..5322f3a 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -30,6 +30,7 @@ ifdef CONFIG_SPL_BUILD ifdef CONFIG_SPL_NAND_SIMPLE COBJS-y += nand_spl_simple.o endif +COBJS-$(CONFIG_SPL_NAND_AM33XX_BCH) += am335x_spl_bch.o ifdef CONFIG_SPL_NAND_LOAD COBJS-y += nand_spl_load.o endif diff --git a/drivers/mtd/nand/am335x_spl_bch.c b/drivers/mtd/nand/am335x_spl_bch.c new file mode 100644 index 0000000..b84528b --- /dev/null +++ b/drivers/mtd/nand/am335x_spl_bch.c @@ -0,0 +1,238 @@ +/* + * (C) Copyright 2012 + * Konstantin Kozhevnikov, Cogent Embedded + * + * based on nand_spl_simple code + * + * (C) Copyright 2006-2008 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * 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. + */ + +#include <common.h> +#include <nand.h> +#include <asm/io.h> +#include <linux/mtd/nand_ecc.h> + +static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS; +static nand_info_t mtd; +static struct nand_chip nand_chip; + +#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ + CONFIG_SYS_NAND_ECCSIZE) +#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES) + + +/* + * NAND command for large page NAND devices (2k) + */ +static int nand_command(int block, int page, uint32_t offs, + u8 cmd) +{ + struct nand_chip *this = mtd.priv; + int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; + void (*hwctrl)(struct mtd_info *mtd, int cmd, + unsigned int ctrl) = this->cmd_ctrl; + + while (!this->dev_ready(&mtd)) + ; + + /* Emulate NAND_CMD_READOOB */ + if (cmd == NAND_CMD_READOOB) { + offs += CONFIG_SYS_NAND_PAGE_SIZE; + cmd = NAND_CMD_READ0; + } + + /* Begin command latch cycle */ + hwctrl(&mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + + if (cmd == NAND_CMD_RESET) { + hwctrl(&mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + while (!this->dev_ready(&mtd)) + ; + return 0; + } + + /* Shift the offset from byte addressing to word addressing. */ + if (this->options & NAND_BUSWIDTH_16) + offs >>= 1; + + /* Set ALE and clear CLE to start address cycle */ + /* Column address */ + hwctrl(&mtd, offs & 0xff, + NAND_CTRL_ALE | NAND_CTRL_CHANGE); /* A[7:0] */ + hwctrl(&mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE); /* A[11:9] */ + /* Row address */ + hwctrl(&mtd, (page_addr & 0xff), NAND_CTRL_ALE); /* A[19:12] */ + hwctrl(&mtd, ((page_addr >> 8) & 0xff), + NAND_CTRL_ALE); /* A[27:20] */ +#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE + /* One more address cycle for devices > 128MiB */ + hwctrl(&mtd, (page_addr >> 16) & 0x0f, + NAND_CTRL_ALE); /* A[31:28] */ +#endif + hwctrl(&mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + if (cmd == NAND_CMD_READ0) { + /* Latch in address */ + hwctrl(&mtd, NAND_CMD_READSTART, + NAND_CTRL_CLE | NAND_CTRL_CHANGE); + hwctrl(&mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + + /* + * Wait a while for the data to be ready + */ + while (!this->dev_ready(&mtd)) + ; + } else if (cmd == NAND_CMD_RNDOUT) { + hwctrl(&mtd, NAND_CMD_RNDOUTSTART, NAND_CTRL_CLE | + NAND_CTRL_CHANGE); + hwctrl(&mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + } + + return 0; +} + +static int nand_is_bad_block(int block) +{ + struct nand_chip *this = mtd.priv; + + nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS, + NAND_CMD_READOOB); + + /* + * Read one byte (or two if it's a 16 bit chip). + */ + if (this->options & NAND_BUSWIDTH_16) { + if (readw(this->IO_ADDR_R) != 0xffff) + return 1; + } else { + if (readb(this->IO_ADDR_R) != 0xff) + return 1; + } + + return 0; +} + +static int nand_read_page(int block, int page, void *dst) +{ + struct nand_chip *this = mtd.priv; + u_char ecc_calc[ECCTOTAL]; + u_char ecc_code[ECCTOTAL]; + u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; + int i; + int eccsize = CONFIG_SYS_NAND_ECCSIZE; + int eccbytes = CONFIG_SYS_NAND_ECCBYTES; + int eccsteps = ECCSTEPS; + uint8_t *p = dst; + uint32_t data_pos = 0; + uint8_t *oob = &oob_data[0] + nand_ecc_pos[0]; + uint32_t oob_pos = eccsize * eccsteps + nand_ecc_pos[0]; + + nand_command(block, page, 0, NAND_CMD_READ0); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + this->ecc.hwctl(&mtd, NAND_ECC_READ); + nand_command(block, page, data_pos, NAND_CMD_RNDOUT); + + this->read_buf(&mtd, p, eccsize); + + nand_command(block, page, oob_pos, NAND_CMD_RNDOUT); + + this->read_buf(&mtd, oob, eccbytes); + this->ecc.calculate(&mtd, p, &ecc_calc[i]); + + data_pos += eccsize; + oob_pos += eccbytes; + oob += eccbytes; + } + + /* Pick the ECC bytes out of the oob data */ + for (i = 0; i < ECCTOTAL; i++) + ecc_code[i] = oob_data[nand_ecc_pos[i]]; + + eccsteps = ECCSTEPS; + p = dst; + + for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + /* No chance to do something with the possible error message + * from correct_data(). We just hope that all possible errors + * are corrected by this routine. + */ + this->ecc.correct(&mtd, p, &ecc_code[i], &ecc_calc[i]); + } + + return 0; +} + +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) +{ + unsigned int block, lastblock; + unsigned int page; + + /* + * offs has to be aligned to a page address! + */ + block = offs / CONFIG_SYS_NAND_BLOCK_SIZE; + lastblock = (offs + size - 1) / CONFIG_SYS_NAND_BLOCK_SIZE; + page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE; + + while (block <= lastblock) { + if (!nand_is_bad_block(block)) { + /* + * Skip bad blocks + */ + while (page < CONFIG_SYS_NAND_PAGE_COUNT) { + nand_read_page(block, page, dst); + dst += CONFIG_SYS_NAND_PAGE_SIZE; + page++; + } + + page = 0; + } else { + lastblock++; + } + + block++; + } + + return 0; +} + +/* nand_init() - initialize data to make nand usable by SPL */ +void nand_init(void) +{ + /* + * Init board specific nand support + */ + mtd.priv = &nand_chip; + nand_chip.IO_ADDR_R = nand_chip.IO_ADDR_W = + (void __iomem *)CONFIG_SYS_NAND_BASE; + board_nand_init(&nand_chip); + + if (nand_chip.select_chip) + nand_chip.select_chip(&mtd, 0); + + /* NAND chip may require reset after power-on */ + nand_command(0, 0, 0, NAND_CMD_RESET); +} + +/* Unselect after operation */ +void nand_deselect(void) +{ + if (nand_chip.select_chip) + nand_chip.select_chip(&mtd, -1); +}

Enable booting from NAND support from AM335x boards as well as environment in NAND.
Signed-off-by: Ilya Yanok ilya.yanok@cogentembedded.com
---
include/configs/am335x_evm.h | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-)
diff --git a/include/configs/am335x_evm.h b/include/configs/am335x_evm.h index ded1cab..6abe544 100644 --- a/include/configs/am335x_evm.h +++ b/include/configs/am335x_evm.h @@ -240,6 +240,35 @@ #define CONFIG_SYS_SPI_U_BOOT_SIZE 0x40000 #define CONFIG_SPL_LDSCRIPT "$(CPUDIR)/omap-common/u-boot-spl.lds"
+#define CONFIG_SPL_BOARD_INIT +#define CONFIG_SPL_NAND_AM33XX_BCH +#define CONFIG_SPL_NAND_SUPPORT +#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_PAGE_SIZE 2048 +#define CONFIG_SYS_NAND_OOBSIZE 64 +#define CONFIG_SYS_NAND_BLOCK_SIZE (128*1024) +#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, \ + 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, } + +#define CONFIG_SYS_NAND_ECCSIZE 512 +#define CONFIG_SYS_NAND_ECCBYTES 14 + +#define CONFIG_SYS_NAND_ECCSTEPS 4 +#define CONFIG_SYS_NAND_ECCTOTAL (CONFIG_SYS_NAND_ECCBYTES * \ + CONFIG_SYS_NAND_ECCSTEPS) + +#define CONFIG_SYS_NAND_U_BOOT_START CONFIG_SYS_TEXT_BASE + +#define CONFIG_SYS_NAND_U_BOOT_OFFS 0x80000 + /* * 1MB into the SDRAM to allow for SPL's bss at the beginning of SDRAM * 64 bytes before this address should be set aside for u-boot.img's @@ -283,7 +312,12 @@ #define CONFIG_SYS_NAND_BASE (0x08000000) /* physical address */ /* to access nand at */ /* CS0 */ -#define CONFIG_SYS_MAX_NAND_DEVICE 1 /* Max number of NAND */ -#endif /* devices */ +#define CONFIG_SYS_MAX_NAND_DEVICE 1 /* Max number of NAND + devices */ +#undef CONFIG_ENV_IS_NOWHERE +#define CONFIG_ENV_IS_IN_NAND +#define CONFIG_ENV_OFFSET 0x260000 /* environment starts here */ +#define CONFIG_SYS_ENV_SECT_SIZE (128 << 10) /* 128 KiB */ +#endif
#endif /* ! __CONFIG_AM335X_EVM_H */

On Wed, Nov 07, 2012 at 12:06:27AM +0100, Ilya Yanok wrote:
These series add support for NAND on AM33XX. AM33XX has the same GPMC controller as OMAP3 so the first part of the series just add required defines/initialization to enable the existing omap_gpmc driver to work on AM33XX. The rest of the series adds support for BCH8 error correction code. We use GPMC to generate codes/syndromes and ELM to find the errors.
Changes in v2: -fix nand mux settings (profiles 2&3 don't have NAND)
- rebased on current master
- clean up mem.c (remove unused stuff that was copied from OMAP3)
- nand headers: remove unneeded stuff
- rebased onto master
- minor config style fix (wrt nand)
- fix wrong braces in Makefile
Ilya Yanok (6): OMAP: include sys_proto.h from boot-common am335x_evm: add nand pinmux definition am33xx: NAND support am335x_evm: enable NAND support am33xx_spl_bch: simple SPL nand loader for AM33XX am335x_evm: enable SPL NAND support
Mansoor Ahamed (2): am33xx: add ELM support omap_gpmc: BCH8 support (ELM based)
arch/arm/cpu/armv7/am33xx/Makefile | 2 + arch/arm/cpu/armv7/am33xx/board.c | 1 + arch/arm/cpu/armv7/am33xx/clock.c | 10 + arch/arm/cpu/armv7/am33xx/elm.c | 212 ++++++++++++++ arch/arm/cpu/armv7/am33xx/mem.c | 101 +++++++ arch/arm/cpu/armv7/omap-common/boot-common.c | 1 + arch/arm/include/asm/arch-am33xx/cpu.h | 53 ++++ arch/arm/include/asm/arch-am33xx/elm.h | 93 ++++++ arch/arm/include/asm/arch-am33xx/hardware.h | 3 + arch/arm/include/asm/arch-am33xx/mem.h | 83 ++++++ arch/arm/include/asm/arch-am33xx/omap_gpmc.h | 120 ++++++++ arch/arm/include/asm/arch-am33xx/sys_proto.h | 3 + board/ti/am335x/board.c | 2 + board/ti/am335x/mux.c | 22 ++ drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/am335x_spl_bch.c | 238 +++++++++++++++ drivers/mtd/nand/omap_gpmc.c | 403 +++++++++++++++++++++++++- include/configs/am335x_evm.h | 46 +++ 18 files changed, 1393 insertions(+), 1 deletion(-) create mode 100644 arch/arm/cpu/armv7/am33xx/elm.c create mode 100644 arch/arm/cpu/armv7/am33xx/mem.c create mode 100644 arch/arm/include/asm/arch-am33xx/elm.h create mode 100644 arch/arm/include/asm/arch-am33xx/mem.h create mode 100644 arch/arm/include/asm/arch-am33xx/omap_gpmc.h create mode 100644 drivers/mtd/nand/am335x_spl_bch.c
For the series, applied to u-boot-ti/master, thanks!
participants (4)
-
Andreas Bießmann
-
Ilya Yanok
-
Peter Korsgaard
-
Tom Rini