[U-Boot] [PATCH] Nand driver for Nomadik SoC

From: Alessandro Rubini rubini@unipv.it
This driver implements the ECC algorithm described in the CPU data sheet and uses the OOB layout chosen in already-released development systems (shipped with a custom-made u-boot 1.3.1).
Signed-off-by: Alessandro Rubini rubini@unipv.it Acked-by: Andrea Gallo andrea.gallo@stnwireless.com --- cpu/arm926ejs/nomadik/Makefile | 6 +- cpu/arm926ejs/nomadik/nand.c | 240 ++++++++++++++++++++++++++++++++++++++++ include/configs/nmdk8815.h | 14 +-- 3 files changed, 250 insertions(+), 10 deletions(-) create mode 100644 cpu/arm926ejs/nomadik/nand.c
diff --git a/cpu/arm926ejs/nomadik/Makefile b/cpu/arm926ejs/nomadik/Makefile index e3bd2ee..9c73327 100644 --- a/cpu/arm926ejs/nomadik/Makefile +++ b/cpu/arm926ejs/nomadik/Makefile @@ -25,11 +25,13 @@ include $(TOPDIR)/config.mk
LIB = $(obj)lib$(SOC).a
-COBJS = timer.o SOBJS = reset.o
+COBJS-y = timer.o +COBJS-$(CONFIG_CMD_NAND) += nand.o + SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) -OBJS := $(addprefix $(obj),$(COBJS)) $(addprefix $(obj),$(SOBJS)) +OBJS := $(addprefix $(obj),$(COBJS-y)) $(addprefix $(obj),$(SOBJS))
all: $(obj).depend $(LIB)
diff --git a/cpu/arm926ejs/nomadik/nand.c b/cpu/arm926ejs/nomadik/nand.c new file mode 100644 index 0000000..91a0d0d --- /dev/null +++ b/cpu/arm926ejs/nomadik/nand.c @@ -0,0 +1,240 @@ +/* + * (C) Copyright 2007 STMicroelectronics, <www.st.com> + * (C) Copyright 2009 Alessandro Rubini rubini@unipv.it + * + * 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 <nand.h> +#include <asm/io.h> + +#ifdef __arm__ /* Assembly version of the parity-of-byte procedure */ + +static inline int parity(int b) /* b is really a byte; returns 0 or ~0 */ +{ + __asm__ __volatile__( + "eor %0, %0, %0, lsr #4\n\t" + "eor %0, %0, %0, lsr #2\n\t" + "eor %0, %0, %0, lsr #1\n\t" + "ands %0, %0, #1\n\t" + "subne %0, %0, #2\t" + : "=r" (b) : "0" (b)); + return b; +} + +#else /* Equivalent in C, which I used for testing the main procedure */ + +static inline int parity(int b) +{ + b = b ^ (b >> 4); + b = b ^ (b >> 2); + return (b ^ (b>>1)) & 1 + ? ~0 : 0; +} + +#endif + +/* + * This is the ECC routine used in hardware, according to the manual. + * HW claims to make the calculation but not the correction; so we must + * recalculate the bytes for a comparison. + */ +static int ecc512(unsigned char *data, unsigned char *ecc) +{ + int gpar = 0; + int i, val, par; + int pbits = 0; /* P8, P16, ... P2048 */ + int pprime = 0; /* P8', P16', ... P2048' */ + int lowbits; /* P1, P2, P4 and primes */ + + for (i=0; i<512; i++) { + par = parity( (val = data[i]) ); + gpar ^= val; + pbits ^= (i & par); + } + /* + * Ok, now gpar is global parity (xor of all bytes) + * pbits are all the parity bits (non-prime ones) + */ + par = parity(gpar); + pprime = pbits ^ par; + /* Put low bits in the right position for ecc[2] (bits 7..2) */ + lowbits = 0 + | ( parity(gpar & 0xf0) & 0x80 ) /* P4 */ + | ( parity(gpar & 0x0f) & 0x40 ) /* P4' */ + | ( parity(gpar & 0xcc) & 0x20 ) /* P2 */ + | ( parity(gpar & 0x33) & 0x10 ) /* P2' */ + | ( parity(gpar & 0xaa) & 0x08 ) /* P1 */ + | ( parity(gpar & 0x55) & 0x04 ); /* P1' */ + + ecc[2] = ~(lowbits | ((pbits & 0x100) >> 7) | ((pprime & 0x100) >> 8)); + /* now intermix bits for ecc[1] (P1024..P128') and ecc[0] (P64..P8') */ + ecc[1] = ~( (pbits & 0x80) >> 0 | ((pprime & 0x80) >> 1) + | ((pbits & 0x40) >> 1) | ((pprime & 0x40) >> 2) + | ((pbits & 0x20) >> 2) | ((pprime & 0x20) >> 3) + | ((pbits & 0x10) >> 3) | ((pprime & 0x10) >> 4)); + + ecc[0] = ~( (pbits & 0x8) << 4 | ((pprime & 0x8) << 3) + | ((pbits & 0x4) << 3) | ((pprime & 0x4) << 2) + | ((pbits & 0x2) << 2) | ((pprime & 0x2) << 1) + | ((pbits & 0x1) << 1) | ((pprime & 0x1) << 0)); + return 0; +} + +/* This is the method in the chip->ecc field */ +static int nomadik_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + return ecc512(dat, ecc_code); +} + +static int nomadik_ecc_correct(struct mtd_info *mtd, uint8_t *dat, + uint8_t *r_ecc, uint8_t *c_ecc) +{ + struct nand_chip *chip = mtd->priv; + uint32_t r, c, d, diff; /*read, calculated, xor of them */ + + if (!memcmp(r_ecc, c_ecc, chip->ecc.bytes)) + return 0; + + /* Reorder the bytes into ascending-order 24 bits -- see manual */ + r = r_ecc[2] << 22 | r_ecc[1] << 14 | r_ecc[0] << 6 | r_ecc[2] >> 2; + c = c_ecc[2] << 22 | c_ecc[1] << 14 | c_ecc[0] << 6 | c_ecc[2] >> 2; + diff = (r ^ c) & ((1<<24)-1); /* use 24 bits only */ + + /* If 12 bits are different, one per pair, it's correctable */ + if ( ((diff | (diff>>1)) & 0x555555) == 0x555555) { + int bit = ((diff & 2) >> 1) + | ((diff & 0x8) >> 2) | ((diff & 0x20) >> 3); + int byte; + + d = diff >> 6; /* remove bit-order info */ + byte = ((d & 2) >> 1) + | ((d & 0x8) >> 2) | ((d & 0x20) >> 3) + | ((d & 0x80) >> 4) | ((d & 0x200) >> 5) + | ((d & 0x800) >> 6) | ((d & 0x2000) >> 7) + | ((d & 0x8000) >> 8) | ((d & 0x20000) >> 9); + /* correct the single bit */ + dat[byte] ^= 1<<bit; + return 0; + } + /* If 1 bit only differs, it's one bit error in ECC, ignore */ + if ( (diff ^ (1 << (ffs(diff) -1))) == 0) + return 0; + /* Otherwise, uncorrectable */ + return -1; +} + +static void nomadik_ecc_hwctl(struct mtd_info *mtd, int mode) +{ /* mandatory in the structure but not used here */ } + + +/* This is the layout used by older installations, we keep compatible */ +struct nand_ecclayout nomadik_ecc_layout = { + .eccbytes = 3*4, + .eccpos = { /* each subpage has 16 bytes: pos 2,3,4 hosts ECC */ + 0x02, 0x03, 0x04, + 0x12, 0x13, 0x14, + 0x22, 0x23, 0x24, + 0x32, 0x33, 0x34}, + .oobfree = { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} }, +}; + +#define REG_FSMC_PCR0 (NOMADIK_FSMC_BASE + 0x40) +#define REG_FSMC_PMEM0 (NOMADIK_FSMC_BASE + 0x48) +#define REG_FSMC_PATT0 (NOMADIK_FSMC_BASE + 0x4c) +#define REG_FSMC_ECCR0 (NOMADIK_FSMC_BASE + 0x54) + +#define MASK_ALE (1 << 24) /* our ALE is AD21 */ +#define MASK_CLE (1 << 23) /* our CLE is AD22 */ + +/* This is copied from the AT91SAM9 devices (Stelian Pop, Lead Tech Design) */ +static void nomadik_nand_hwcontrol(struct mtd_info *mtd, + int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd->priv; + u32 pcr0 = readl(REG_FSMC_PCR0); + + if (ctrl & NAND_CTRL_CHANGE) { + ulong IO_ADDR_W = (ulong) this->IO_ADDR_W; + IO_ADDR_W &= ~(MASK_ALE | MASK_CLE); + + if (ctrl & NAND_CLE) + IO_ADDR_W |= MASK_CLE; + if (ctrl & NAND_ALE) + IO_ADDR_W |= MASK_ALE; + + if (ctrl & NAND_NCE) + writel(pcr0 | 0x4, REG_FSMC_PCR0); + else + writel(pcr0 & ~0x4, REG_FSMC_PCR0); + + this->IO_ADDR_W = (void *) IO_ADDR_W; + this->IO_ADDR_R = (void *) IO_ADDR_W; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +/* Returns 1 when ready; upper layers timeout at 20ms with timer routines */ +static int nomadik_nand_ready(struct mtd_info *mtd) +{ + return 1; /* The ready bit is handled in hardware */ +} + +/* Copy a buffer 32bits at a time: faster than defualt method which is 8bit */ +static void nomadik_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + u32 *p = (u32 *) buf; + len >>=2; + + writel(0, REG_FSMC_ECCR0); + for (i = 0; i < len; i++) + p[i] = readl(chip->IO_ADDR_R); +} + +int board_nand_init(struct nand_chip *chip) +{ + /* Set up the FSMC_PCR0 for nand access*/ + writel(0x0000004a, REG_FSMC_PCR0); + /* Set up FSMC_PMEM0, FSMC_PATT0 with timing data for access */ + writel(0x00020401, REG_FSMC_PMEM0); + writel(0x00020404, REG_FSMC_PATT0); + + chip->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING; + chip->cmd_ctrl = nomadik_nand_hwcontrol; + chip->dev_ready = nomadik_nand_ready; + /* The chip allows 32bit reads, so avoid the default 8bit copy */ + chip->read_buf = nomadik_nand_read_buf; + + /* ECC: follow the hardware-defined rulse, but do it in sw */ + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.bytes = 3; + chip->ecc.size = 512; + chip->ecc.layout = &nomadik_ecc_layout; + chip->ecc.calculate = nomadik_ecc_calculate; + chip->ecc.hwctl = nomadik_ecc_hwctl; + chip->ecc.correct = nomadik_ecc_correct; + + return 0; +} diff --git a/include/configs/nmdk8815.h b/include/configs/nmdk8815.h index 01de08f..7a503e0 100644 --- a/include/configs/nmdk8815.h +++ b/include/configs/nmdk8815.h @@ -37,10 +37,12 @@ #include <config_cmd_default.h> #define CONFIG_CMD_PING #define CONFIG_CMD_DHCP -/* At this point there is no flash driver, so remove some commands */ -#undef CONFIG_CMD_ENV +/* There is no NOR flash, so undefine these commands */ #undef CONFIG_CMD_FLASH #undef CONFIG_CMD_IMLS +/* There is NAND storage */ +#define CONFIG_CMD_NAND +#define CONFIG_CMD_JFFS2
/* user interface */ #define CONFIG_SYS_LONGHELP @@ -118,7 +120,7 @@ #define CONFIG_MTD_ONENAND_VERIFY_WRITE #define CONFIG_SYS_ONENAND_BASE 0x30000000 #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define CONFIG_SYS_NAND_BASE 0x40000000 +#define CONFIG_SYS_NAND_BASE 0x40000000 /* SMPS0n */
#define CONFIG_SYS_NO_FLASH
@@ -150,15 +152,11 @@ # define CONFIG_JFFS2_PART_OFFSET 0x00280000
# define CONFIG_ENV_IS_IN_NAND -# define CONFIG_ENV_SIZE 0x20000 /*128 Kb*/ +# define CONFIG_ENV_SIZE 0x20000 /* 128 Kb - one sector */ # define CONFIG_ENV_OFFSET (0x8000000 - CONFIG_ENV_SIZE)
#endif /* CONFIG_BOOT_ONENAND */
-/* Temporarily, until we have no driver, env is not in nand */ -#undef CONFIG_ENV_IS_IN_NAND -#define CONFIG_ENV_IS_NOWHERE - /* this is needed to make hello_world.c and other stuff happy */ #define CONFIG_SYS_MAX_FLASH_SECT 512 #define CONFIG_SYS_MAX_FLASH_BANKS 1

From: Alessandro Rubini rubini@unipv.it
This driver implements the ECC algorithm described in the CPU data sheet and uses the OOB layout chosen in already-released development systems (shipped with a custom-made u-boot 1.3.1).
Signed-off-by: Alessandro Rubini rubini@unipv.it Acked-by: Andrea Gallo andrea.gallo@stnwireless.com ---
About V2: I noticed I forgot to run through checkpatch, I fixed all whitespace warnings.I left 2 of them (space after open parens) as I think the bit fiddling is more readable this way.
cpu/arm926ejs/nomadik/Makefile | 6 +- cpu/arm926ejs/nomadik/nand.c | 240 ++++++++++++++++++++++++++++++++++++++++ include/configs/nmdk8815.h | 14 +-- 3 files changed, 250 insertions(+), 10 deletions(-) create mode 100644 cpu/arm926ejs/nomadik/nand.c
diff --git a/cpu/arm926ejs/nomadik/Makefile b/cpu/arm926ejs/nomadik/Makefile index e3bd2ee..9c73327 100644 --- a/cpu/arm926ejs/nomadik/Makefile +++ b/cpu/arm926ejs/nomadik/Makefile @@ -25,11 +25,13 @@ include $(TOPDIR)/config.mk
LIB = $(obj)lib$(SOC).a
-COBJS = timer.o SOBJS = reset.o
+COBJS-y = timer.o +COBJS-$(CONFIG_CMD_NAND) += nand.o + SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) -OBJS := $(addprefix $(obj),$(COBJS)) $(addprefix $(obj),$(SOBJS)) +OBJS := $(addprefix $(obj),$(COBJS-y)) $(addprefix $(obj),$(SOBJS))
all: $(obj).depend $(LIB)
diff --git a/cpu/arm926ejs/nomadik/nand.c b/cpu/arm926ejs/nomadik/nand.c new file mode 100644 index 0000000..22ee591 --- /dev/null +++ b/cpu/arm926ejs/nomadik/nand.c @@ -0,0 +1,240 @@ +/* + * (C) Copyright 2007 STMicroelectronics, <www.st.com> + * (C) Copyright 2009 Alessandro Rubini rubini@unipv.it + * + * 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 <nand.h> +#include <asm/io.h> + +#ifdef __arm__ /* Assembly version of the parity-of-byte procedure */ + +static inline int parity(int b) /* b is really a byte; returns 0 or ~0 */ +{ + __asm__ __volatile__( + "eor %0, %0, %0, lsr #4\n\t" + "eor %0, %0, %0, lsr #2\n\t" + "eor %0, %0, %0, lsr #1\n\t" + "ands %0, %0, #1\n\t" + "subne %0, %0, #2\t" + : "=r" (b) : "0" (b)); + return b; +} + +#else /* Equivalent in C, which I used for testing the main procedure */ + +static inline int parity(int b) +{ + b = b ^ (b >> 4); + b = b ^ (b >> 2); + return (b ^ (b>>1)) & 1 + ? ~0 : 0; +} + +#endif + +/* + * This is the ECC routine used in hardware, according to the manual. + * HW claims to make the calculation but not the correction; so we must + * recalculate the bytes for a comparison. + */ +static int ecc512(unsigned char *data, unsigned char *ecc) +{ + int gpar = 0; + int i, val, par; + int pbits = 0; /* P8, P16, ... P2048 */ + int pprime = 0; /* P8', P16', ... P2048' */ + int lowbits; /* P1, P2, P4 and primes */ + + for (i = 0; i < 512; i++) { + par = parity((val = data[i])); + gpar ^= val; + pbits ^= (i & par); + } + /* + * Ok, now gpar is global parity (xor of all bytes) + * pbits are all the parity bits (non-prime ones) + */ + par = parity(gpar); + pprime = pbits ^ par; + /* Put low bits in the right position for ecc[2] (bits 7..2) */ + lowbits = 0 + | (parity(gpar & 0xf0) & 0x80) /* P4 */ + | (parity(gpar & 0x0f) & 0x40) /* P4' */ + | (parity(gpar & 0xcc) & 0x20) /* P2 */ + | (parity(gpar & 0x33) & 0x10) /* P2' */ + | (parity(gpar & 0xaa) & 0x08) /* P1 */ + | (parity(gpar & 0x55) & 0x04); /* P1' */ + + ecc[2] = ~(lowbits | ((pbits & 0x100) >> 7) | ((pprime & 0x100) >> 8)); + /* now intermix bits for ecc[1] (P1024..P128') and ecc[0] (P64..P8') */ + ecc[1] = ~( (pbits & 0x80) >> 0 | ((pprime & 0x80) >> 1) + | ((pbits & 0x40) >> 1) | ((pprime & 0x40) >> 2) + | ((pbits & 0x20) >> 2) | ((pprime & 0x20) >> 3) + | ((pbits & 0x10) >> 3) | ((pprime & 0x10) >> 4)); + + ecc[0] = ~( (pbits & 0x8) << 4 | ((pprime & 0x8) << 3) + | ((pbits & 0x4) << 3) | ((pprime & 0x4) << 2) + | ((pbits & 0x2) << 2) | ((pprime & 0x2) << 1) + | ((pbits & 0x1) << 1) | ((pprime & 0x1) << 0)); + return 0; +} + +/* This is the method in the chip->ecc field */ +static int nomadik_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + return ecc512(dat, ecc_code); +} + +static int nomadik_ecc_correct(struct mtd_info *mtd, uint8_t *dat, + uint8_t *r_ecc, uint8_t *c_ecc) +{ + struct nand_chip *chip = mtd->priv; + uint32_t r, c, d, diff; /*read, calculated, xor of them */ + + if (!memcmp(r_ecc, c_ecc, chip->ecc.bytes)) + return 0; + + /* Reorder the bytes into ascending-order 24 bits -- see manual */ + r = r_ecc[2] << 22 | r_ecc[1] << 14 | r_ecc[0] << 6 | r_ecc[2] >> 2; + c = c_ecc[2] << 22 | c_ecc[1] << 14 | c_ecc[0] << 6 | c_ecc[2] >> 2; + diff = (r ^ c) & ((1<<24)-1); /* use 24 bits only */ + + /* If 12 bits are different, one per pair, it's correctable */ + if (((diff | (diff>>1)) & 0x555555) == 0x555555) { + int bit = ((diff & 2) >> 1) + | ((diff & 0x8) >> 2) | ((diff & 0x20) >> 3); + int byte; + + d = diff >> 6; /* remove bit-order info */ + byte = ((d & 2) >> 1) + | ((d & 0x8) >> 2) | ((d & 0x20) >> 3) + | ((d & 0x80) >> 4) | ((d & 0x200) >> 5) + | ((d & 0x800) >> 6) | ((d & 0x2000) >> 7) + | ((d & 0x8000) >> 8) | ((d & 0x20000) >> 9); + /* correct the single bit */ + dat[byte] ^= 1<<bit; + return 0; + } + /* If 1 bit only differs, it's one bit error in ECC, ignore */ + if ((diff ^ (1 << (ffs(diff) - 1))) == 0) + return 0; + /* Otherwise, uncorrectable */ + return -1; +} + +static void nomadik_ecc_hwctl(struct mtd_info *mtd, int mode) +{ /* mandatory in the structure but not used here */ } + + +/* This is the layout used by older installations, we keep compatible */ +struct nand_ecclayout nomadik_ecc_layout = { + .eccbytes = 3*4, + .eccpos = { /* each subpage has 16 bytes: pos 2,3,4 hosts ECC */ + 0x02, 0x03, 0x04, + 0x12, 0x13, 0x14, + 0x22, 0x23, 0x24, + 0x32, 0x33, 0x34}, + .oobfree = { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} }, +}; + +#define REG_FSMC_PCR0 (NOMADIK_FSMC_BASE + 0x40) +#define REG_FSMC_PMEM0 (NOMADIK_FSMC_BASE + 0x48) +#define REG_FSMC_PATT0 (NOMADIK_FSMC_BASE + 0x4c) +#define REG_FSMC_ECCR0 (NOMADIK_FSMC_BASE + 0x54) + +#define MASK_ALE (1 << 24) /* our ALE is AD21 */ +#define MASK_CLE (1 << 23) /* our CLE is AD22 */ + +/* This is copied from the AT91SAM9 devices (Stelian Pop, Lead Tech Design) */ +static void nomadik_nand_hwcontrol(struct mtd_info *mtd, + int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd->priv; + u32 pcr0 = readl(REG_FSMC_PCR0); + + if (ctrl & NAND_CTRL_CHANGE) { + ulong IO_ADDR_W = (ulong) this->IO_ADDR_W; + IO_ADDR_W &= ~(MASK_ALE | MASK_CLE); + + if (ctrl & NAND_CLE) + IO_ADDR_W |= MASK_CLE; + if (ctrl & NAND_ALE) + IO_ADDR_W |= MASK_ALE; + + if (ctrl & NAND_NCE) + writel(pcr0 | 0x4, REG_FSMC_PCR0); + else + writel(pcr0 & ~0x4, REG_FSMC_PCR0); + + this->IO_ADDR_W = (void *) IO_ADDR_W; + this->IO_ADDR_R = (void *) IO_ADDR_W; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +/* Returns 1 when ready; upper layers timeout at 20ms with timer routines */ +static int nomadik_nand_ready(struct mtd_info *mtd) +{ + return 1; /* The ready bit is handled in hardware */ +} + +/* Copy a buffer 32bits at a time: faster than defualt method which is 8bit */ +static void nomadik_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + u32 *p = (u32 *) buf; + len >>= 2; + + writel(0, REG_FSMC_ECCR0); + for (i = 0; i < len; i++) + p[i] = readl(chip->IO_ADDR_R); +} + +int board_nand_init(struct nand_chip *chip) +{ + /* Set up the FSMC_PCR0 for nand access*/ + writel(0x0000004a, REG_FSMC_PCR0); + /* Set up FSMC_PMEM0, FSMC_PATT0 with timing data for access */ + writel(0x00020401, REG_FSMC_PMEM0); + writel(0x00020404, REG_FSMC_PATT0); + + chip->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING; + chip->cmd_ctrl = nomadik_nand_hwcontrol; + chip->dev_ready = nomadik_nand_ready; + /* The chip allows 32bit reads, so avoid the default 8bit copy */ + chip->read_buf = nomadik_nand_read_buf; + + /* ECC: follow the hardware-defined rulse, but do it in sw */ + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.bytes = 3; + chip->ecc.size = 512; + chip->ecc.layout = &nomadik_ecc_layout; + chip->ecc.calculate = nomadik_ecc_calculate; + chip->ecc.hwctl = nomadik_ecc_hwctl; + chip->ecc.correct = nomadik_ecc_correct; + + return 0; +} diff --git a/include/configs/nmdk8815.h b/include/configs/nmdk8815.h index 01de08f..7a503e0 100644 --- a/include/configs/nmdk8815.h +++ b/include/configs/nmdk8815.h @@ -37,10 +37,12 @@ #include <config_cmd_default.h> #define CONFIG_CMD_PING #define CONFIG_CMD_DHCP -/* At this point there is no flash driver, so remove some commands */ -#undef CONFIG_CMD_ENV +/* There is no NOR flash, so undefine these commands */ #undef CONFIG_CMD_FLASH #undef CONFIG_CMD_IMLS +/* There is NAND storage */ +#define CONFIG_CMD_NAND +#define CONFIG_CMD_JFFS2
/* user interface */ #define CONFIG_SYS_LONGHELP @@ -118,7 +120,7 @@ #define CONFIG_MTD_ONENAND_VERIFY_WRITE #define CONFIG_SYS_ONENAND_BASE 0x30000000 #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define CONFIG_SYS_NAND_BASE 0x40000000 +#define CONFIG_SYS_NAND_BASE 0x40000000 /* SMPS0n */
#define CONFIG_SYS_NO_FLASH
@@ -150,15 +152,11 @@ # define CONFIG_JFFS2_PART_OFFSET 0x00280000
# define CONFIG_ENV_IS_IN_NAND -# define CONFIG_ENV_SIZE 0x20000 /*128 Kb*/ +# define CONFIG_ENV_SIZE 0x20000 /* 128 Kb - one sector */ # define CONFIG_ENV_OFFSET (0x8000000 - CONFIG_ENV_SIZE)
#endif /* CONFIG_BOOT_ONENAND */
-/* Temporarily, until we have no driver, env is not in nand */ -#undef CONFIG_ENV_IS_IN_NAND -#define CONFIG_ENV_IS_NOWHERE - /* this is needed to make hello_world.c and other stuff happy */ #define CONFIG_SYS_MAX_FLASH_SECT 512 #define CONFIG_SYS_MAX_FLASH_BANKS 1

On 16:04 Sat 07 Feb , Alessandro Rubini wrote:
From: Alessandro Rubini rubini@unipv.it
This driver implements the ECC algorithm described in the CPU data sheet and uses the OOB layout chosen in already-released development systems (shipped with a custom-made u-boot 1.3.1).
Signed-off-by: Alessandro Rubini rubini@unipv.it Acked-by: Andrea Gallo andrea.gallo@stnwireless.com
About V2: I noticed I forgot to run through checkpatch, I fixed all whitespace warnings.I left 2 of them (space after open parens) as I think the bit fiddling is more readable this way.
cpu/arm926ejs/nomadik/Makefile | 6 +- cpu/arm926ejs/nomadik/nand.c | 240 ++++++++++++++++++++++++++++++++++++++++ include/configs/nmdk8815.h | 14 +-- 3 files changed, 250 insertions(+), 10 deletions(-) create mode 100644 cpu/arm926ejs/nomadik/nand.c
diff --git a/cpu/arm926ejs/nomadik/Makefile b/cpu/arm926ejs/nomadik/Makefile index e3bd2ee..9c73327 100644 --- a/cpu/arm926ejs/nomadik/Makefile +++ b/cpu/arm926ejs/nomadik/Makefile @@ -25,11 +25,13 @@ include $(TOPDIR)/config.mk
LIB = $(obj)lib$(SOC).a
-COBJS = timer.o SOBJS = reset.o
+COBJS-y = timer.o +COBJS-$(CONFIG_CMD_NAND) += nand.o
SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) -OBJS := $(addprefix $(obj),$(COBJS)) $(addprefix $(obj),$(SOBJS)) +OBJS := $(addprefix $(obj),$(COBJS-y)) $(addprefix $(obj),$(SOBJS))
please use this
SRCS := $(SOBJS:.o=.S) $(COBJS-y:.o=.c) OBJS := $(addprefix $(obj),$(COBJS-y)) SOBJS := $(addprefix $(obj),$(SOBJS))
all: $(obj).depend $(LIB)
diff --git a/cpu/arm926ejs/nomadik/nand.c b/cpu/arm926ejs/nomadik/nand.c new file mode 100644 index 0000000..22ee591 --- /dev/null +++ b/cpu/arm926ejs/nomadik/nand.c @@ -0,0 +1,240 @@ +/*
- (C) Copyright 2007 STMicroelectronics, <www.st.com>
- (C) Copyright 2009 Alessandro Rubini rubini@unipv.it
- 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 <nand.h> +#include <asm/io.h>
+#ifdef __arm__ /* Assembly version of the parity-of-byte procedure */
we should need only one
+static inline int parity(int b) /* b is really a byte; returns 0 or ~0 */ +{
- __asm__ __volatile__(
"eor %0, %0, %0, lsr #4\n\t"
"eor %0, %0, %0, lsr #2\n\t"
"eor %0, %0, %0, lsr #1\n\t"
"ands %0, %0, #1\n\t"
"subne %0, %0, #2\t"
: "=r" (b) : "0" (b));
- return b;
+}
+#else /* Equivalent in C, which I used for testing the main procedure */
+static inline int parity(int b) +{
- b = b ^ (b >> 4);
- b = b ^ (b >> 2);
- return (b ^ (b>>1)) & 1
? ~0 : 0;
+}
+#endif
+/*
- This is the ECC routine used in hardware, according to the manual.
- HW claims to make the calculation but not the correction; so we must
- recalculate the bytes for a comparison.
- */
+static int ecc512(unsigned char *data, unsigned char *ecc) +{
- int gpar = 0;
- int i, val, par;
- int pbits = 0; /* P8, P16, ... P2048 */
- int pprime = 0; /* P8', P16', ... P2048' */
- int lowbits; /* P1, P2, P4 and primes */
- for (i = 0; i < 512; i++) {
par = parity((val = data[i]));
gpar ^= val;
pbits ^= (i & par);
- }
- /*
* Ok, now gpar is global parity (xor of all bytes)
* pbits are all the parity bits (non-prime ones)
*/
- par = parity(gpar);
- pprime = pbits ^ par;
- /* Put low bits in the right position for ecc[2] (bits 7..2) */
- lowbits = 0
| (parity(gpar & 0xf0) & 0x80) /* P4 */
^^ whitespace please fix
| (parity(gpar & 0x0f) & 0x40) /* P4' */
| (parity(gpar & 0xcc) & 0x20) /* P2 */
^^ whitespace please fix
| (parity(gpar & 0x33) & 0x10) /* P2' */
| (parity(gpar & 0xaa) & 0x08) /* P1 */
^^ whitespace please fix
| (parity(gpar & 0x55) & 0x04); /* P1' */
- ecc[2] = ~(lowbits | ((pbits & 0x100) >> 7) | ((pprime & 0x100) >> 8));
- /* now intermix bits for ecc[1] (P1024..P128') and ecc[0] (P64..P8') */
- ecc[1] = ~( (pbits & 0x80) >> 0 | ((pprime & 0x80) >> 1)
| ((pbits & 0x40) >> 1) | ((pprime & 0x40) >> 2)
| ((pbits & 0x20) >> 2) | ((pprime & 0x20) >> 3)
| ((pbits & 0x10) >> 3) | ((pprime & 0x10) >> 4));
- ecc[0] = ~( (pbits & 0x8) << 4 | ((pprime & 0x8) << 3)
| ((pbits & 0x4) << 3) | ((pprime & 0x4) << 2)
| ((pbits & 0x2) << 2) | ((pprime & 0x2) << 1)
| ((pbits & 0x1) << 1) | ((pprime & 0x1) << 0));
- return 0;
+}
+/* This is the method in the chip->ecc field */ +static int nomadik_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat,
uint8_t *ecc_code)
+{
- return ecc512(dat, ecc_code);
+}
+static int nomadik_ecc_correct(struct mtd_info *mtd, uint8_t *dat,
^^ whitespace please fix
uint8_t *r_ecc, uint8_t *c_ecc)
+{
- struct nand_chip *chip = mtd->priv;
- uint32_t r, c, d, diff; /*read, calculated, xor of them */
- if (!memcmp(r_ecc, c_ecc, chip->ecc.bytes))
return 0;
- /* Reorder the bytes into ascending-order 24 bits -- see manual */
- r = r_ecc[2] << 22 | r_ecc[1] << 14 | r_ecc[0] << 6 | r_ecc[2] >> 2;
- c = c_ecc[2] << 22 | c_ecc[1] << 14 | c_ecc[0] << 6 | c_ecc[2] >> 2;
- diff = (r ^ c) & ((1<<24)-1); /* use 24 bits only */
- /* If 12 bits are different, one per pair, it's correctable */
- if (((diff | (diff>>1)) & 0x555555) == 0x555555) {
int bit = ((diff & 2) >> 1)
| ((diff & 0x8) >> 2) | ((diff & 0x20) >> 3);
int byte;
d = diff >> 6; /* remove bit-order info */
byte = ((d & 2) >> 1)
| ((d & 0x8) >> 2) | ((d & 0x20) >> 3)
| ((d & 0x80) >> 4) | ((d & 0x200) >> 5)
| ((d & 0x800) >> 6) | ((d & 0x2000) >> 7)
| ((d & 0x8000) >> 8) | ((d & 0x20000) >> 9);
/* correct the single bit */
dat[byte] ^= 1<<bit;
return 0;
- }
- /* If 1 bit only differs, it's one bit error in ECC, ignore */
- if ((diff ^ (1 << (ffs(diff) - 1))) == 0)
return 0;
- /* Otherwise, uncorrectable */
- return -1;
+}
+static void nomadik_ecc_hwctl(struct mtd_info *mtd, int mode) +{ /* mandatory in the structure but not used here */ }
+/* This is the layout used by older installations, we keep compatible */ +struct nand_ecclayout nomadik_ecc_layout = {
- .eccbytes = 3*4,
please add a space before and after '*'
- .eccpos = { /* each subpage has 16 bytes: pos 2,3,4 hosts ECC */
0x02, 0x03, 0x04,
0x12, 0x13, 0x14,
0x22, 0x23, 0x24,
0x32, 0x33, 0x34},
- .oobfree = { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} },
+};
+#define REG_FSMC_PCR0 (NOMADIK_FSMC_BASE + 0x40) +#define REG_FSMC_PMEM0 (NOMADIK_FSMC_BASE + 0x48) +#define REG_FSMC_PATT0 (NOMADIK_FSMC_BASE + 0x4c) +#define REG_FSMC_ECCR0 (NOMADIK_FSMC_BASE + 0x54)
please move this to a header
+#define MASK_ALE (1 << 24) /* our ALE is AD21 */ +#define MASK_CLE (1 << 23) /* our CLE is AD22 */
+/* This is copied from the AT91SAM9 devices (Stelian Pop, Lead Tech Design) */ +static void nomadik_nand_hwcontrol(struct mtd_info *mtd,
int cmd, unsigned int ctrl)
+{
- struct nand_chip *this = mtd->priv;
- u32 pcr0 = readl(REG_FSMC_PCR0);
- if (ctrl & NAND_CTRL_CHANGE) {
ulong IO_ADDR_W = (ulong) this->IO_ADDR_W;
IO_ADDR_W &= ~(MASK_ALE | MASK_CLE);
if (ctrl & NAND_CLE)
IO_ADDR_W |= MASK_CLE;
if (ctrl & NAND_ALE)
IO_ADDR_W |= MASK_ALE;
if (ctrl & NAND_NCE)
writel(pcr0 | 0x4, REG_FSMC_PCR0);
^^ whitespace please fix
else
writel(pcr0 & ~0x4, REG_FSMC_PCR0);
this->IO_ADDR_W = (void *) IO_ADDR_W;
this->IO_ADDR_R = (void *) IO_ADDR_W;
- }
- if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
+}
+/* Returns 1 when ready; upper layers timeout at 20ms with timer routines */ +static int nomadik_nand_ready(struct mtd_info *mtd) +{
- return 1; /* The ready bit is handled in hardware */
+}
+/* Copy a buffer 32bits at a time: faster than defualt method which is 8bit */ +static void nomadik_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{
- int i;
- struct nand_chip *chip = mtd->priv;
- u32 *p = (u32 *) buf;
please add an empty line
- len >>= 2;
- writel(0, REG_FSMC_ECCR0);
- for (i = 0; i < len; i++)
p[i] = readl(chip->IO_ADDR_R);
+}
+int board_nand_init(struct nand_chip *chip) +{
- /* Set up the FSMC_PCR0 for nand access*/
- writel(0x0000004a, REG_FSMC_PCR0);
- /* Set up FSMC_PMEM0, FSMC_PATT0 with timing data for access */
- writel(0x00020401, REG_FSMC_PMEM0);
- writel(0x00020404, REG_FSMC_PATT0);
- chip->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING;
- chip->cmd_ctrl = nomadik_nand_hwcontrol;
- chip->dev_ready = nomadik_nand_ready;
- /* The chip allows 32bit reads, so avoid the default 8bit copy */
- chip->read_buf = nomadik_nand_read_buf;
- /* ECC: follow the hardware-defined rulse, but do it in sw */
- chip->ecc.mode = NAND_ECC_HW;
- chip->ecc.bytes = 3;
- chip->ecc.size = 512;
- chip->ecc.layout = &nomadik_ecc_layout;
- chip->ecc.calculate = nomadik_ecc_calculate;
- chip->ecc.hwctl = nomadik_ecc_hwctl;
- chip->ecc.correct = nomadik_ecc_correct;
- return 0;
+}
Best Regards, J.

From: Alessandro Rubini rubini@unipv.it
This driver implements the ECC algorithm described in the CPU data sheet and uses the OOB layout chosen in already-released development systems (shipped with a custom-made u-boot 1.3.1).
Signed-off-by: Alessandro Rubini rubini@unipv.it Acked-by: Andrea Gallo andrea.gallo@stnwireless.com ---
Changes from V2: added nomadik.h, moved all register addresses there, fixed what J.C. suggested.
cpu/arm926ejs/nomadik/Makefile | 9 +- cpu/arm926ejs/nomadik/nand.c | 221 ++++++++++++++++++++++++++++++++++++++++ include/configs/nmdk8815.h | 38 ++------ include/nomadik.h | 39 +++++++ 4 files changed, 274 insertions(+), 33 deletions(-) create mode 100644 cpu/arm926ejs/nomadik/nand.c create mode 100644 include/nomadik.h
diff --git a/cpu/arm926ejs/nomadik/Makefile b/cpu/arm926ejs/nomadik/Makefile index e3bd2ee..c39162b 100644 --- a/cpu/arm926ejs/nomadik/Makefile +++ b/cpu/arm926ejs/nomadik/Makefile @@ -25,11 +25,14 @@ include $(TOPDIR)/config.mk
LIB = $(obj)lib$(SOC).a
-COBJS = timer.o SOBJS = reset.o
-SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) -OBJS := $(addprefix $(obj),$(COBJS)) $(addprefix $(obj),$(SOBJS)) +COBJS-y = timer.o +COBJS-$(CONFIG_CMD_NAND) += nand.o + +SRCS := $(SOBJS:.o=.S) $(COBJS-y:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS-y)) +SOBJS := $(addprefix $(obj),$(SOBJS))
all: $(obj).depend $(LIB)
diff --git a/cpu/arm926ejs/nomadik/nand.c b/cpu/arm926ejs/nomadik/nand.c new file mode 100644 index 0000000..08944d1 --- /dev/null +++ b/cpu/arm926ejs/nomadik/nand.c @@ -0,0 +1,221 @@ +/* + * (C) Copyright 2007 STMicroelectronics, <www.st.com> + * (C) Copyright 2009 Alessandro Rubini rubini@unipv.it + * + * 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 <nand.h> +#include <asm/io.h> + +static inline int parity(int b) /* b is really a byte; returns 0 or ~0 */ +{ + __asm__ __volatile__( + "eor %0, %0, %0, lsr #4\n\t" + "eor %0, %0, %0, lsr #2\n\t" + "eor %0, %0, %0, lsr #1\n\t" + "ands %0, %0, #1\n\t" + "subne %0, %0, #2\t" + : "=r" (b) : "0" (b)); + return b; +} + +/* + * This is the ECC routine used in hardware, according to the manual. + * HW claims to make the calculation but not the correction; so we must + * recalculate the bytes for a comparison. + */ +static int ecc512(unsigned char *data, unsigned char *ecc) +{ + int gpar = 0; + int i, val, par; + int pbits = 0; /* P8, P16, ... P2048 */ + int pprime = 0; /* P8', P16', ... P2048' */ + int lowbits; /* P1, P2, P4 and primes */ + + for (i = 0; i < 512; i++) { + par = parity((val = data[i])); + gpar ^= val; + pbits ^= (i & par); + } + /* + * Ok, now gpar is global parity (xor of all bytes) + * pbits are all the parity bits (non-prime ones) + */ + par = parity(gpar); + pprime = pbits ^ par; + /* Put low bits in the right position for ecc[2] (bits 7..2) */ + lowbits = 0 + | (parity(gpar & 0xf0) & 0x80) /* P4 */ + | (parity(gpar & 0x0f) & 0x40) /* P4' */ + | (parity(gpar & 0xcc) & 0x20) /* P2 */ + | (parity(gpar & 0x33) & 0x10) /* P2' */ + | (parity(gpar & 0xaa) & 0x08) /* P1 */ + | (parity(gpar & 0x55) & 0x04); /* P1' */ + + ecc[2] = ~(lowbits | ((pbits & 0x100) >> 7) | ((pprime & 0x100) >> 8)); + /* now intermix bits for ecc[1] (P1024..P128') and ecc[0] (P64..P8') */ + ecc[1] = ~( (pbits & 0x80) >> 0 | ((pprime & 0x80) >> 1) + | ((pbits & 0x40) >> 1) | ((pprime & 0x40) >> 2) + | ((pbits & 0x20) >> 2) | ((pprime & 0x20) >> 3) + | ((pbits & 0x10) >> 3) | ((pprime & 0x10) >> 4)); + + ecc[0] = ~( (pbits & 0x8) << 4 | ((pprime & 0x8) << 3) + | ((pbits & 0x4) << 3) | ((pprime & 0x4) << 2) + | ((pbits & 0x2) << 2) | ((pprime & 0x2) << 1) + | ((pbits & 0x1) << 1) | ((pprime & 0x1) << 0)); + return 0; +} + +/* This is the method in the chip->ecc field */ +static int nomadik_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + return ecc512(dat, ecc_code); +} + +static int nomadik_ecc_correct(struct mtd_info *mtd, uint8_t *dat, + uint8_t *r_ecc, uint8_t *c_ecc) +{ + struct nand_chip *chip = mtd->priv; + uint32_t r, c, d, diff; /*read, calculated, xor of them */ + + if (!memcmp(r_ecc, c_ecc, chip->ecc.bytes)) + return 0; + + /* Reorder the bytes into ascending-order 24 bits -- see manual */ + r = r_ecc[2] << 22 | r_ecc[1] << 14 | r_ecc[0] << 6 | r_ecc[2] >> 2; + c = c_ecc[2] << 22 | c_ecc[1] << 14 | c_ecc[0] << 6 | c_ecc[2] >> 2; + diff = (r ^ c) & ((1<<24)-1); /* use 24 bits only */ + + /* If 12 bits are different, one per pair, it's correctable */ + if (((diff | (diff>>1)) & 0x555555) == 0x555555) { + int bit = ((diff & 2) >> 1) + | ((diff & 0x8) >> 2) | ((diff & 0x20) >> 3); + int byte; + + d = diff >> 6; /* remove bit-order info */ + byte = ((d & 2) >> 1) + | ((d & 0x8) >> 2) | ((d & 0x20) >> 3) + | ((d & 0x80) >> 4) | ((d & 0x200) >> 5) + | ((d & 0x800) >> 6) | ((d & 0x2000) >> 7) + | ((d & 0x8000) >> 8) | ((d & 0x20000) >> 9); + /* correct the single bit */ + dat[byte] ^= 1<<bit; + return 0; + } + /* If 1 bit only differs, it's one bit error in ECC, ignore */ + if ((diff ^ (1 << (ffs(diff) - 1))) == 0) + return 0; + /* Otherwise, uncorrectable */ + return -1; +} + +static void nomadik_ecc_hwctl(struct mtd_info *mtd, int mode) +{ /* mandatory in the structure but not used here */ } + + +/* This is the layout used by older installations, we keep compatible */ +struct nand_ecclayout nomadik_ecc_layout = { + .eccbytes = 3 * 4, + .eccpos = { /* each subpage has 16 bytes: pos 2,3,4 hosts ECC */ + 0x02, 0x03, 0x04, + 0x12, 0x13, 0x14, + 0x22, 0x23, 0x24, + 0x32, 0x33, 0x34}, + .oobfree = { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} }, +}; + +#define MASK_ALE (1 << 24) /* our ALE is AD21 */ +#define MASK_CLE (1 << 23) /* our CLE is AD22 */ + +/* This is copied from the AT91SAM9 devices (Stelian Pop, Lead Tech Design) */ +static void nomadik_nand_hwcontrol(struct mtd_info *mtd, + int cmd, unsigned int ctrl) +{ + struct nand_chip *this = mtd->priv; + u32 pcr0 = readl(REG_FSMC_PCR0); + + if (ctrl & NAND_CTRL_CHANGE) { + ulong IO_ADDR_W = (ulong) this->IO_ADDR_W; + IO_ADDR_W &= ~(MASK_ALE | MASK_CLE); + + if (ctrl & NAND_CLE) + IO_ADDR_W |= MASK_CLE; + if (ctrl & NAND_ALE) + IO_ADDR_W |= MASK_ALE; + + if (ctrl & NAND_NCE) + writel(pcr0 | 0x4, REG_FSMC_PCR0); + else + writel(pcr0 & ~0x4, REG_FSMC_PCR0); + + this->IO_ADDR_W = (void *) IO_ADDR_W; + this->IO_ADDR_R = (void *) IO_ADDR_W; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +/* Returns 1 when ready; upper layers timeout at 20ms with timer routines */ +static int nomadik_nand_ready(struct mtd_info *mtd) +{ + return 1; /* The ready bit is handled in hardware */ +} + +/* Copy a buffer 32bits at a time: faster than defualt method which is 8bit */ +static void nomadik_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *chip = mtd->priv; + u32 *p = (u32 *) buf; + + len >>= 2; + writel(0, REG_FSMC_ECCR0); + for (i = 0; i < len; i++) + p[i] = readl(chip->IO_ADDR_R); +} + +int board_nand_init(struct nand_chip *chip) +{ + /* Set up the FSMC_PCR0 for nand access*/ + writel(0x0000004a, REG_FSMC_PCR0); + /* Set up FSMC_PMEM0, FSMC_PATT0 with timing data for access */ + writel(0x00020401, REG_FSMC_PMEM0); + writel(0x00020404, REG_FSMC_PATT0); + + chip->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING; + chip->cmd_ctrl = nomadik_nand_hwcontrol; + chip->dev_ready = nomadik_nand_ready; + /* The chip allows 32bit reads, so avoid the default 8bit copy */ + chip->read_buf = nomadik_nand_read_buf; + + /* ECC: follow the hardware-defined rulse, but do it in sw */ + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.bytes = 3; + chip->ecc.size = 512; + chip->ecc.layout = &nomadik_ecc_layout; + chip->ecc.calculate = nomadik_ecc_calculate; + chip->ecc.hwctl = nomadik_ecc_hwctl; + chip->ecc.correct = nomadik_ecc_correct; + + return 0; +} diff --git a/include/configs/nmdk8815.h b/include/configs/nmdk8815.h index 01de08f..75311a5 100644 --- a/include/configs/nmdk8815.h +++ b/include/configs/nmdk8815.h @@ -25,6 +25,8 @@ #ifndef __CONFIG_H #define __CONFIG_H
+#include <nomadik.h> + #define CONFIG_ARM926EJS #define CONFIG_NOMADIK #define CONFIG_NOMADIK_8815 @@ -37,10 +39,12 @@ #include <config_cmd_default.h> #define CONFIG_CMD_PING #define CONFIG_CMD_DHCP -/* At this point there is no flash driver, so remove some commands */ -#undef CONFIG_CMD_ENV +/* There is no NOR flash, so undefine these commands */ #undef CONFIG_CMD_FLASH #undef CONFIG_CMD_IMLS +/* There is NAND storage */ +#define CONFIG_CMD_NAND +#define CONFIG_CMD_JFFS2
/* user interface */ #define CONFIG_SYS_LONGHELP @@ -118,7 +122,7 @@ #define CONFIG_MTD_ONENAND_VERIFY_WRITE #define CONFIG_SYS_ONENAND_BASE 0x30000000 #define CONFIG_SYS_MAX_NAND_DEVICE 1 -#define CONFIG_SYS_NAND_BASE 0x40000000 +#define CONFIG_SYS_NAND_BASE 0x40000000 /* SMPS0n */
#define CONFIG_SYS_NO_FLASH
@@ -150,39 +154,13 @@ # define CONFIG_JFFS2_PART_OFFSET 0x00280000
# define CONFIG_ENV_IS_IN_NAND -# define CONFIG_ENV_SIZE 0x20000 /*128 Kb*/ +# define CONFIG_ENV_SIZE 0x20000 /* 128 Kb - one sector */ # define CONFIG_ENV_OFFSET (0x8000000 - CONFIG_ENV_SIZE)
#endif /* CONFIG_BOOT_ONENAND */
-/* Temporarily, until we have no driver, env is not in nand */ -#undef CONFIG_ENV_IS_IN_NAND -#define CONFIG_ENV_IS_NOWHERE - /* this is needed to make hello_world.c and other stuff happy */ #define CONFIG_SYS_MAX_FLASH_SECT 512 #define CONFIG_SYS_MAX_FLASH_BANKS 1
-/* base addresses of our peripherals */ -#define NOMADIK_SRC_BASE 0x101E0000 /* System and Reset Cnt */ -#define NOMADIK_PMU_BASE 0x101E9000 /* Power Management Unit */ -#define NOMADIK_MPMC_BASE 0x10110000 /* SDRAM Controller */ -#define NOMADIK_FSMC_BASE 0x10100000 /* FSMC Controller */ -#define NOMADIK_1NAND_BASE 0x30000000 -#define NOMADIK_GPIO0_BASE 0x101E4000 -#define NOMADIK_GPIO1_BASE 0x101E5000 -#define NOMADIK_GPIO2_BASE 0x101E6000 -#define NOMADIK_GPIO3_BASE 0x101E7000 -#define NOMADIK_CPLD_BASE 0x36000000 -#define NOMADIK_UART0_BASE 0x101FD000 -#define NOMADIK_UART1_BASE 0x101FB000 -#define NOMADIK_UART2_BASE 0x101F2000 - -#define NOMADIK_I2C1_BASE 0x101F7000 /* I2C1 interface */ -#define NOMADIK_I2C0_BASE 0x101F8000 /* I2C0 interface */ - -#define NOMADIK_RTC_BASE 0x101E8000 -#define NOMADIK_ETH0_BASE 0x36800300 -#define NOMADIK_CPLD_UART_BASE 0x36480000 - #endif /* __CONFIG_H */ diff --git a/include/nomadik.h b/include/nomadik.h new file mode 100644 index 0000000..d9405fd --- /dev/null +++ b/include/nomadik.h @@ -0,0 +1,39 @@ +/* Collection of constants used to access Nomadik registers */ + +#ifndef __NOMADIK_H__ +#define __NOMADIK_H__ + +/* Base addresses of our peripherals */ +#define NOMADIK_SRC_BASE 0x101E0000 /* System and Reset Cnt */ +#define NOMADIK_PMU_BASE 0x101E9000 /* Power Management Unit */ +#define NOMADIK_MPMC_BASE 0x10110000 /* SDRAM Controller */ +#define NOMADIK_FSMC_BASE 0x10100000 /* FSMC Controller */ +#define NOMADIK_1NAND_BASE 0x30000000 +#define NOMADIK_GPIO0_BASE 0x101E4000 +#define NOMADIK_GPIO1_BASE 0x101E5000 +#define NOMADIK_GPIO2_BASE 0x101E6000 +#define NOMADIK_GPIO3_BASE 0x101E7000 +#define NOMADIK_CPLD_BASE 0x36000000 +#define NOMADIK_UART0_BASE 0x101FD000 +#define NOMADIK_UART1_BASE 0x101FB000 +#define NOMADIK_UART2_BASE 0x101F2000 + +#define NOMADIK_I2C1_BASE 0x101F7000 /* I2C1 interface */ +#define NOMADIK_I2C0_BASE 0x101F8000 /* I2C0 interface */ + +#define NOMADIK_RTC_BASE 0x101E8000 +#define NOMADIK_ETH0_BASE 0x36800300 +#define NOMADIK_CPLD_UART_BASE 0x36480000 + +/* Chip select registers ("Flexible Static Memory Controller") */ + +#define REG_FSMC_BCR0 (NOMADIK_FSMC_BASE + 0x00) +#define REG_FSMC_BTR0 (NOMADIK_FSMC_BASE + 0x04) +#define REG_FSMC_BCR1 (NOMADIK_FSMC_BASE + 0x08) +#define REG_FSMC_BTR1 (NOMADIK_FSMC_BASE + 0x0c) +#define REG_FSMC_PCR0 (NOMADIK_FSMC_BASE + 0x40) +#define REG_FSMC_PMEM0 (NOMADIK_FSMC_BASE + 0x48) +#define REG_FSMC_PATT0 (NOMADIK_FSMC_BASE + 0x4c) +#define REG_FSMC_ECCR0 (NOMADIK_FSMC_BASE + 0x54) + +#endif /* __NOMADIK_H__ */

On Sunday 08 February 2009, Alessandro Rubini wrote:
From: Alessandro Rubini rubini@unipv.it
This driver implements the ECC algorithm described in the CPU data sheet and uses the OOB layout chosen in already-released development systems (shipped with a custom-made u-boot 1.3.1).
Signed-off-by: Alessandro Rubini rubini@unipv.it Acked-by: Andrea Gallo andrea.gallo@stnwireless.com
Changes from V2: added nomadik.h, moved all register addresses there, fixed what J.C. suggested.
cpu/arm926ejs/nomadik/Makefile | 9 +- cpu/arm926ejs/nomadik/nand.c | 221++++++++++++++++++++++++++++++++++++++++
This driver should go into drivers/mtd/nand instead.
Best regards, Stefan
===================================================================== DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office@denx.de =====================================================================

cpu/arm926ejs/nomadik/nand.c | 221++++++++++++++++++++++++++++++++++++++++
This driver should go into drivers/mtd/nand instead.
I've seen a lot of nand.c in board directories and three of them in cpu directories (cpu/arm920t/s3c24x0/nand.c cpu/arm926ejs/nomadik/nand.c cpu/arm926ejs/davinci/nand.c), while drivers/mtd/nand looked like generic code to me with a few exceptions.
If you confirm that all SoC-specific code is expected to go there over time, I'll move it and resubmit.
/alessandro

On Monday 09 February 2009, Alessandro Rubini wrote:
cpu/arm926ejs/nomadik/nand.c | 221++++++++++++++++++++++++++++++++++++++++
This driver should go into drivers/mtd/nand instead.
I've seen a lot of nand.c in board directories and three of them in cpu directories (cpu/arm920t/s3c24x0/nand.c cpu/arm926ejs/nomadik/nand.c cpu/arm926ejs/davinci/nand.c), while drivers/mtd/nand looked like generic code to me with a few exceptions.
Those are older drivers that were added before we restructured the drivers directory (matching the Linux one). Those drivers should be moved to the drivers/mtd/nand/ directory over time as well.
If you confirm that all SoC-specific code is expected to go there over time, I'll move it and resubmit.
Yes, I confirm. I added Scott Wood the NAND custodian to CC. You should CC him on all NAND related patches.
Best regards, Stefan
===================================================================== DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office@denx.de =====================================================================

On Sun, Feb 08, 2009 at 12:19:56AM +0100, Alessandro Rubini wrote:
+static inline int parity(int b) /* b is really a byte; returns 0 or ~0 */
If it's really a byte, then why not tell the compiler this with uint8_t?
+{
- __asm__ __volatile__(
"eor %0, %0, %0, lsr #4\n\t"
"eor %0, %0, %0, lsr #2\n\t"
"eor %0, %0, %0, lsr #1\n\t"
"ands %0, %0, #1\n\t"
"subne %0, %0, #2\t"
: "=r" (b) : "0" (b));
- return b;
+}
Why is this volatile? The underscores are unnecessary, BTW.
Have you verified that this is noticeably better than C code?
+/*
- This is the ECC routine used in hardware, according to the manual.
- HW claims to make the calculation but not the correction; so we must
- recalculate the bytes for a comparison.
- */
Why must you recalculate? What does the hardware do with the ECC it calculates?
- diff = (r ^ c) & ((1<<24)-1); /* use 24 bits only */
Put spaces around binary operators.
+/* This is the layout used by older installations, we keep compatible */ +struct nand_ecclayout nomadik_ecc_layout = {
- .eccbytes = 3 * 4,
- .eccpos = { /* each subpage has 16 bytes: pos 2,3,4 hosts ECC */
0x02, 0x03, 0x04,
0x12, 0x13, 0x14,
0x22, 0x23, 0x24,
0x32, 0x33, 0x34},
- .oobfree = { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} },
+};
Any particular reason why bytes 0x05-0x07, 0x10-0x11, 0x15-0x17, etc. aren't marked free?
+/* Copy a buffer 32bits at a time: faster than defualt method which is 8bit */ +static void nomadik_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{
- int i;
- struct nand_chip *chip = mtd->priv;
- u32 *p = (u32 *) buf;
- len >>= 2;
- writel(0, REG_FSMC_ECCR0);
- for (i = 0; i < len; i++)
p[i] = readl(chip->IO_ADDR_R);
+}
What if "len" isn't a multiple of 4?
+int board_nand_init(struct nand_chip *chip) +{
- /* Set up the FSMC_PCR0 for nand access*/
- writel(0x0000004a, REG_FSMC_PCR0);
- /* Set up FSMC_PMEM0, FSMC_PATT0 with timing data for access */
- writel(0x00020401, REG_FSMC_PMEM0);
- writel(0x00020404, REG_FSMC_PATT0);
- chip->options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING;
- chip->cmd_ctrl = nomadik_nand_hwcontrol;
- chip->dev_ready = nomadik_nand_ready;
- /* The chip allows 32bit reads, so avoid the default 8bit copy */
- chip->read_buf = nomadik_nand_read_buf;
- /* ECC: follow the hardware-defined rulse, but do it in sw */
"rules"
-#define CONFIG_SYS_NAND_BASE 0x40000000 +#define CONFIG_SYS_NAND_BASE 0x40000000 /* SMPS0n */
What is "SMPS0n"?
-Scott

From: Scott Wood scottwood@freescale.com
Unfortunately freescale.com i.e. mail.global.frontbridge.com i.e. microsoft has blacklisted me. I'm trying to do what they say but I fear you won't get direct email.
+static inline int parity(int b) /* b is really a byte; returns 0 or ~0 */
If it's really a byte, then why not tell the compiler this with uint8_t?
Because otherwise it will add instructions to mask the value.
- __asm__ __volatile__(
Why is this volatile? The underscores are unnecessary, BTW.
Both for my own pedantry.
Have you verified that this is noticeably better than C code?
Well... it looked like I only checked without -O. I rechecked and the result is the same. Ok, will switch to the C version.
+/*
- This is the ECC routine used in hardware, according to the manual.
- HW claims to make the calculation but not the correction; so we must
- recalculate the bytes for a comparison.
- */
Why must you recalculate? What does the hardware do with the ECC it calculates?
It only makes it available. You must recalculate and compare. However, I haven't been able to make the hardware work (nor original vendor code did actually use the hardware). I'm waiting for an errata sheet or direct clarification. Meanwhile this code is working and it's the best I can do (I can't use ECC_SOFT as the ECC layout would be different from shipped devices).
- .oobfree = { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} },
+};
Any particular reason why bytes 0x05-0x07, 0x10-0x11, 0x15-0x17, etc. aren't marked free?
Since most other ECC routines use 2..7 I chose to leave open the possibility to switch over from 2..4. Is that wrong?
- len >>= 2;
What if "len" isn't a multiple of 4?
I thought it never is. This always reads either 512 or 64 bytes. Aligned, too.
-#define CONFIG_SYS_NAND_BASE 0x40000000 +#define CONFIG_SYS_NAND_BASE 0x40000000 /* SMPS0n */
What is "SMPS0n"?
It's the chip select. Usually who would use this code will have the manuals, so all the strange names can be looked up. Specifically, it's never spelled out. It's something like "Static Memory Pccard/nand Select". Kind of black magic, like most vendor names (all vendors).
thanks /alessandro

Alessandro Rubini wrote:
From: Scott Wood scottwood@freescale.com
Unfortunately freescale.com i.e. mail.global.frontbridge.com i.e. microsoft has blacklisted me. I'm trying to do what they say but I fear you won't get direct email.
Hmm, I've e-mailed the postmaster inquiring as to why. Is there a specific IP address or range that is being blocked? Is there a rejection message?
+static inline int parity(int b) /* b is really a byte; returns 0 or ~0 */
If it's really a byte, then why not tell the compiler this with uint8_t?
Because otherwise it will add instructions to mask the value.
OK.
- __asm__ __volatile__(
Why is this volatile? The underscores are unnecessary, BTW.
Both for my own pedantry.
volatile should be left off of pure calculations; you're just removing optimization opportunity.
And I think the underscores are ugly. :-)
Have you verified that this is noticeably better than C code?
Well... it looked like I only checked without -O. I rechecked and the result is the same. Ok, will switch to the C version.
OK, good.
+/*
- This is the ECC routine used in hardware, according to the manual.
- HW claims to make the calculation but not the correction; so we must
- recalculate the bytes for a comparison.
- */
Why must you recalculate? What does the hardware do with the ECC it calculates?
It only makes it available. You must recalculate and compare.
Makes what available? If it makes the calculated ECC available, you should only need to do the code in ecc_correct, not ecc512.
However, I haven't been able to make the hardware work (nor original vendor code did actually use the hardware).
OK, that seems to be the more relevant reason. :-)
Include a comment to that effect.
- .oobfree = { {0x08, 0x08}, {0x18, 0x08}, {0x28, 0x08}, {0x38, 0x08} },
+};
Any particular reason why bytes 0x05-0x07, 0x10-0x11, 0x15-0x17, etc. aren't marked free?
Since most other ECC routines use 2..7 I chose to leave open the possibility to switch over from 2..4. Is that wrong?
It's not wrong as long as the free bytes you do claim meet all expected needs; I was just curious.
- len >>= 2;
What if "len" isn't a multiple of 4?
I thought it never is. This always reads either 512 or 64 bytes. Aligned, too.
Suppose it only wants to read a few bytes of OOB.
-#define CONFIG_SYS_NAND_BASE 0x40000000 +#define CONFIG_SYS_NAND_BASE 0x40000000 /* SMPS0n */
What is "SMPS0n"?
It's the chip select.
OK. Just making sure it wasn't something left in by accident.
-Scott
participants (4)
-
Alessandro Rubini
-
Jean-Christophe PLAGNIOL-VILLARD
-
Scott Wood
-
Stefan Roese