* [U-Boot] [PATCH] Nand driver for Nomadik SoC
@ 2009-02-07 4:43 Alessandro Rubini
2009-02-07 15:04 ` [U-Boot] [PATCH v2] " Alessandro Rubini
0 siblings, 1 reply; 16+ messages in thread
From: Alessandro Rubini @ 2009-02-07 4:43 UTC (permalink / raw)
To: u-boot
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
--
1.6.0.2
^ permalink raw reply related [flat|nested] 16+ messages in thread* [U-Boot] [PATCH v2] Nand driver for Nomadik SoC
2009-02-07 4:43 [U-Boot] [PATCH] Nand driver for Nomadik SoC Alessandro Rubini
@ 2009-02-07 15:04 ` Alessandro Rubini
2009-02-07 21:08 ` Jean-Christophe PLAGNIOL-VILLARD
2009-02-07 23:19 ` [U-Boot] [PATCH v3] " Alessandro Rubini
0 siblings, 2 replies; 16+ messages in thread
From: Alessandro Rubini @ 2009-02-07 15:04 UTC (permalink / raw)
To: u-boot
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
--
1.6.0.2
^ permalink raw reply related [flat|nested] 16+ messages in thread* [U-Boot] [PATCH v2] Nand driver for Nomadik SoC
2009-02-07 15:04 ` [U-Boot] [PATCH v2] " Alessandro Rubini
@ 2009-02-07 21:08 ` Jean-Christophe PLAGNIOL-VILLARD
2009-02-07 23:19 ` [U-Boot] [PATCH v3] " Alessandro Rubini
1 sibling, 0 replies; 16+ messages in thread
From: Jean-Christophe PLAGNIOL-VILLARD @ 2009-02-07 21:08 UTC (permalink / raw)
To: u-boot
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.
^ permalink raw reply [flat|nested] 16+ messages in thread* [U-Boot] [PATCH v3] Nand driver for Nomadik SoC
2009-02-07 15:04 ` [U-Boot] [PATCH v2] " Alessandro Rubini
2009-02-07 21:08 ` Jean-Christophe PLAGNIOL-VILLARD
@ 2009-02-07 23:19 ` Alessandro Rubini
2009-02-09 7:34 ` Stefan Roese
2009-02-09 17:16 ` Scott Wood
1 sibling, 2 replies; 16+ messages in thread
From: Alessandro Rubini @ 2009-02-07 23:19 UTC (permalink / raw)
To: u-boot
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__ */
--
1.6.0.2
^ permalink raw reply related [flat|nested] 16+ messages in thread* [U-Boot] [PATCH v3] Nand driver for Nomadik SoC
2009-02-07 23:19 ` [U-Boot] [PATCH v3] " Alessandro Rubini
@ 2009-02-09 7:34 ` Stefan Roese
2009-02-09 8:40 ` Alessandro Rubini
2009-02-09 17:16 ` Scott Wood
1 sibling, 1 reply; 16+ messages in thread
From: Stefan Roese @ 2009-02-09 7:34 UTC (permalink / raw)
To: u-boot
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 at denx.de
=====================================================================
^ permalink raw reply [flat|nested] 16+ messages in thread
* [U-Boot] [PATCH v3] Nand driver for Nomadik SoC
2009-02-09 7:34 ` Stefan Roese
@ 2009-02-09 8:40 ` Alessandro Rubini
2009-02-09 8:48 ` Stefan Roese
0 siblings, 1 reply; 16+ messages in thread
From: Alessandro Rubini @ 2009-02-09 8:40 UTC (permalink / raw)
To: u-boot
>> 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
^ permalink raw reply [flat|nested] 16+ messages in thread
* [U-Boot] [PATCH v3] Nand driver for Nomadik SoC
2009-02-09 8:40 ` Alessandro Rubini
@ 2009-02-09 8:48 ` Stefan Roese
0 siblings, 0 replies; 16+ messages in thread
From: Stefan Roese @ 2009-02-09 8:48 UTC (permalink / raw)
To: u-boot
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 at denx.de
=====================================================================
^ permalink raw reply [flat|nested] 16+ messages in thread
* [U-Boot] [PATCH v3] Nand driver for Nomadik SoC
2009-02-07 23:19 ` [U-Boot] [PATCH v3] " Alessandro Rubini
2009-02-09 7:34 ` Stefan Roese
@ 2009-02-09 17:16 ` Scott Wood
2009-02-09 17:56 ` Alessandro Rubini
1 sibling, 1 reply; 16+ messages in thread
From: Scott Wood @ 2009-02-09 17:16 UTC (permalink / raw)
To: u-boot
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
^ permalink raw reply [flat|nested] 16+ messages in thread* [U-Boot] [PATCH v3] Nand driver for Nomadik SoC
2009-02-09 17:16 ` Scott Wood
@ 2009-02-09 17:56 ` Alessandro Rubini
2009-02-09 18:10 ` Scott Wood
0 siblings, 1 reply; 16+ messages in thread
From: Alessandro Rubini @ 2009-02-09 17:56 UTC (permalink / raw)
To: u-boot
> 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
^ permalink raw reply [flat|nested] 16+ messages in thread* [U-Boot] [PATCH v3] Nand driver for Nomadik SoC
2009-02-09 17:56 ` Alessandro Rubini
@ 2009-02-09 18:10 ` Scott Wood
0 siblings, 0 replies; 16+ messages in thread
From: Scott Wood @ 2009-02-09 18:10 UTC (permalink / raw)
To: u-boot
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
^ permalink raw reply [flat|nested] 16+ messages in thread
* [U-Boot] [PATCH 0/3] Drivers for nomadik 8815 board
@ 2009-02-09 14:53 Alessandro Rubini
2009-02-09 14:53 ` [U-Boot] [PATCH 1/3] Added nomadik.h header Alessandro Rubini
` (2 more replies)
0 siblings, 3 replies; 16+ messages in thread
From: Alessandro Rubini @ 2009-02-09 14:53 UTC (permalink / raw)
To: u-boot
I reformatted the patches as a patch series, splitting out creation
of the header file (I'm sure marking it as "nand driver" was not good)/
Second and third patch depend on first one but are independent.
Alessandro Rubini (4):
Added nomadik.h header
Nand driver for Nomadik SoC
Enable Ethernet for Nomadik 8815 Evaluation Kit
board/st/nmdk8815/nmdk8815.c | 9 ++-
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/nomadik.c | 221 ++++++++++++++++++++++++++++++++++++++++++
include/configs/nmdk8815.h | 49 +++-------
include/nomadik.h | 39 ++++++++
5 files changed, 283 insertions(+), 36 deletions(-)
create mode 100644 drivers/mtd/nand/nomadik.c
create mode 100644 include/nomadik.h
^ permalink raw reply [flat|nested] 16+ messages in thread* [U-Boot] [PATCH 1/3] Added nomadik.h header
2009-02-09 14:53 [U-Boot] [PATCH 0/3] Drivers for nomadik 8815 board Alessandro Rubini
@ 2009-02-09 14:53 ` Alessandro Rubini
2009-02-09 14:53 ` [U-Boot] [PATCH 2/3] Nand driver for Nomadik SoC Alessandro Rubini
2009-02-09 14:53 ` [U-Boot] [PATCH 3/3] Enable Ethernet for Nomadik 8815 Evaluation Kit Alessandro Rubini
2 siblings, 0 replies; 16+ messages in thread
From: Alessandro Rubini @ 2009-02-09 14:53 UTC (permalink / raw)
To: u-boot
From: Alessandro Rubini <rubini@unipv.it>
Signed-off-by: Alessandro Rubini <rubini@unipv.it>
Acked-by: Andrea Gallo <andrea.gallo@stnwireless.com>
---
include/configs/nmdk8815.h | 24 ++----------------------
include/nomadik.h | 39 +++++++++++++++++++++++++++++++++++++++
2 files changed, 41 insertions(+), 22 deletions(-)
create mode 100644 include/nomadik.h
diff --git a/include/configs/nmdk8815.h b/include/configs/nmdk8815.h
index 01de08f..a370f6c 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
@@ -163,26 +165,4 @@
#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__ */
--
1.6.0.2
^ permalink raw reply related [flat|nested] 16+ messages in thread* [U-Boot] [PATCH 2/3] Nand driver for Nomadik SoC
2009-02-09 14:53 [U-Boot] [PATCH 0/3] Drivers for nomadik 8815 board Alessandro Rubini
2009-02-09 14:53 ` [U-Boot] [PATCH 1/3] Added nomadik.h header Alessandro Rubini
@ 2009-02-09 14:53 ` Alessandro Rubini
2009-02-09 21:48 ` Alessandro Rubini
2009-02-09 14:53 ` [U-Boot] [PATCH 3/3] Enable Ethernet for Nomadik 8815 Evaluation Kit Alessandro Rubini
2 siblings, 1 reply; 16+ messages in thread
From: Alessandro Rubini @ 2009-02-09 14:53 UTC (permalink / raw)
To: u-boot
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>
---
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/nomadik.c | 221 ++++++++++++++++++++++++++++++++++++++++++++
include/configs/nmdk8815.h | 18 ++--
3 files changed, 230 insertions(+), 10 deletions(-)
create mode 100644 drivers/mtd/nand/nomadik.c
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 24edb27..5974d77 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -38,6 +38,7 @@ endif
COBJS-$(CONFIG_DRIVER_NAND_BFIN) += bfin_nand.o
COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o
COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o
+COBJS-$(CONFIG_NAND_NOMADIK) += nomadik.o
COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o
COBJS-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
endif
diff --git a/drivers/mtd/nand/nomadik.c b/drivers/mtd/nand/nomadik.c
new file mode 100644
index 0000000..08944d1
--- /dev/null
+++ b/drivers/mtd/nand/nomadik.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 a370f6c..0df989f 100644
--- a/include/configs/nmdk8815.h
+++ b/include/configs/nmdk8815.h
@@ -39,10 +39,14 @@
#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
+#define CONFIG_SYS_NO_FLASH
+/* There is NAND storage */
+#define CONFIG_NAND_NOMADIK
+#define CONFIG_CMD_NAND
+#define CONFIG_CMD_JFFS2
/* user interface */
#define CONFIG_SYS_LONGHELP
@@ -120,9 +124,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_NO_FLASH
+#define CONFIG_SYS_NAND_BASE 0x40000000 /* SMPS0n */
#ifdef CONFIG_BOOT_ONENAND
@@ -152,15 +154,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
--
1.6.0.2
^ permalink raw reply related [flat|nested] 16+ messages in thread* [U-Boot] [PATCH 2/3] Nand driver for Nomadik SoC
2009-02-09 14:53 ` [U-Boot] [PATCH 2/3] Nand driver for Nomadik SoC Alessandro Rubini
@ 2009-02-09 21:48 ` Alessandro Rubini
2009-02-09 22:28 ` Scott Wood
0 siblings, 1 reply; 16+ messages in thread
From: Alessandro Rubini @ 2009-02-09 21:48 UTC (permalink / raw)
To: u-boot
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 previous version: as suggested by Scott Wood. Moreover,
I removed the nomadik_nand_read_buf() procedure, as with the overhead
of ECC calculation copying 32 bits at a time wasn't much faster than
using the default method -- it was really faster before I added ECC code.
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/nomadik.c | 204 ++++++++++++++++++++++++++++++++++++++++++++
include/configs/nmdk8815.h | 18 ++--
3 files changed, 213 insertions(+), 10 deletions(-)
create mode 100644 drivers/mtd/nand/nomadik.c
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 24edb27..5974d77 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -38,6 +38,7 @@ endif
COBJS-$(CONFIG_DRIVER_NAND_BFIN) += bfin_nand.o
COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o
COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o
+COBJS-$(CONFIG_NAND_NOMADIK) += nomadik.o
COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o
COBJS-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o
endif
diff --git a/drivers/mtd/nand/nomadik.c b/drivers/mtd/nand/nomadik.c
new file mode 100644
index 0000000..74511be
--- /dev/null
+++ b/drivers/mtd/nand/nomadik.c
@@ -0,0 +1,204 @@
+/*
+ * (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) /* uses low 8 bits: returns 0 or all-1 */
+{
+ b = b ^ (b >> 4);
+ b = b ^ (b >> 2);
+ return (b ^ (b >> 1)) & 1
+ ? ~0 : 0;
+}
+
+/*
+ * This is the ECC routine used in hardware, according to the manual.
+ * HW claims to make the calculation but not the correction. However,
+ * I haven't managed to get the desired data out of it; so do it in sw.
+ * There is problably some errata involved, but currently miss the info.
+ */
+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},
+ /* let's keep bytes 5,6,7 for us, just in case we change ECC algo */
+ .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 */
+}
+
+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;
+
+ /* ECC: follow the hardware-defined rules, 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 a370f6c..0df989f 100644
--- a/include/configs/nmdk8815.h
+++ b/include/configs/nmdk8815.h
@@ -39,10 +39,14 @@
#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
+#define CONFIG_SYS_NO_FLASH
+/* There is NAND storage */
+#define CONFIG_NAND_NOMADIK
+#define CONFIG_CMD_NAND
+#define CONFIG_CMD_JFFS2
/* user interface */
#define CONFIG_SYS_LONGHELP
@@ -120,9 +124,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_NO_FLASH
+#define CONFIG_SYS_NAND_BASE 0x40000000 /* SMPS0n */
#ifdef CONFIG_BOOT_ONENAND
@@ -152,15 +154,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
--
1.6.0.2
^ permalink raw reply related [flat|nested] 16+ messages in thread* [U-Boot] [PATCH 2/3] Nand driver for Nomadik SoC
2009-02-09 21:48 ` Alessandro Rubini
@ 2009-02-09 22:28 ` Scott Wood
0 siblings, 0 replies; 16+ messages in thread
From: Scott Wood @ 2009-02-09 22:28 UTC (permalink / raw)
To: u-boot
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>
Acked-by: Scott Wood <scottwood@freescale.com>
-Scott
^ permalink raw reply [flat|nested] 16+ messages in thread
* [U-Boot] [PATCH 3/3] Enable Ethernet for Nomadik 8815 Evaluation Kit
2009-02-09 14:53 [U-Boot] [PATCH 0/3] Drivers for nomadik 8815 board Alessandro Rubini
2009-02-09 14:53 ` [U-Boot] [PATCH 1/3] Added nomadik.h header Alessandro Rubini
2009-02-09 14:53 ` [U-Boot] [PATCH 2/3] Nand driver for Nomadik SoC Alessandro Rubini
@ 2009-02-09 14:53 ` Alessandro Rubini
2 siblings, 0 replies; 16+ messages in thread
From: Alessandro Rubini @ 2009-02-09 14:53 UTC (permalink / raw)
To: u-boot
From: Alessandro Rubini <rubini@unipv.it>
This trivially enables Ethernet support in the debug board
by setting up the proper chip select.
Signed-off-by: Alessandro Rubini <rubini@unipv.it>
Acked-by: Andrea Gallo <andrea.gallo@stnwireless.com>
---
board/st/nmdk8815/nmdk8815.c | 5 ++++-
include/configs/nmdk8815.h | 3 +++
2 files changed, 7 insertions(+), 1 deletions(-)
diff --git a/board/st/nmdk8815/nmdk8815.c b/board/st/nmdk8815/nmdk8815.c
index c54eac1..edf4626 100644
--- a/board/st/nmdk8815/nmdk8815.c
+++ b/board/st/nmdk8815/nmdk8815.c
@@ -49,8 +49,11 @@ int board_init(void)
writel(0x00000000, NOMADIK_GPIO1_BASE + 0x28);
writel(readl(NOMADIK_SRC_BASE) | 0x8000, NOMADIK_SRC_BASE);
- icache_enable();
+ /* Set up SMCS1 for Ethernet: sram-like, enabled, timing values */
+ writel(0x0000305b, REG_FSMC_BCR1);
+ writel(0x00033f33, REG_FSMC_BTR1);
+ icache_enable();
return 0;
}
diff --git a/include/configs/nmdk8815.h b/include/configs/nmdk8815.h
index 0df989f..b37352e 100644
--- a/include/configs/nmdk8815.h
+++ b/include/configs/nmdk8815.h
@@ -37,8 +37,11 @@
/* commands */
#include <config_cmd_default.h>
+
+#define CONFIG_CMD_NET
#define CONFIG_CMD_PING
#define CONFIG_CMD_DHCP
+#define CONFIG_CMD_NFS
/* There is no NOR flash, so undefine these commands */
#undef CONFIG_CMD_FLASH
#undef CONFIG_CMD_IMLS
--
1.6.0.2
^ permalink raw reply related [flat|nested] 16+ messages in thread
end of thread, other threads:[~2009-02-09 22:28 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-02-07 4:43 [U-Boot] [PATCH] Nand driver for Nomadik SoC Alessandro Rubini
2009-02-07 15:04 ` [U-Boot] [PATCH v2] " Alessandro Rubini
2009-02-07 21:08 ` Jean-Christophe PLAGNIOL-VILLARD
2009-02-07 23:19 ` [U-Boot] [PATCH v3] " Alessandro Rubini
2009-02-09 7:34 ` Stefan Roese
2009-02-09 8:40 ` Alessandro Rubini
2009-02-09 8:48 ` Stefan Roese
2009-02-09 17:16 ` Scott Wood
2009-02-09 17:56 ` Alessandro Rubini
2009-02-09 18:10 ` Scott Wood
-- strict thread matches above, loose matches on Subject: below --
2009-02-09 14:53 [U-Boot] [PATCH 0/3] Drivers for nomadik 8815 board Alessandro Rubini
2009-02-09 14:53 ` [U-Boot] [PATCH 1/3] Added nomadik.h header Alessandro Rubini
2009-02-09 14:53 ` [U-Boot] [PATCH 2/3] Nand driver for Nomadik SoC Alessandro Rubini
2009-02-09 21:48 ` Alessandro Rubini
2009-02-09 22:28 ` Scott Wood
2009-02-09 14:53 ` [U-Boot] [PATCH 3/3] Enable Ethernet for Nomadik 8815 Evaluation Kit Alessandro Rubini
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox