All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alessandro Rubini <rubini-list@gnudd.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH 2/3] Nand driver for Nomadik SoC
Date: Mon, 9 Feb 2009 22:48:46 +0100	[thread overview]
Message-ID: <20090209214846.GA4867@mail.gnudd.com> (raw)
In-Reply-To: <d5b8be872e7fae665dd590cc1337df089cbc6ea2.1234189796.git.rubini@unipv.it>

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

  reply	other threads:[~2009-02-09 21:48 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
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
  -- strict thread matches above, loose matches on Subject: below --
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090209214846.GA4867@mail.gnudd.com \
    --to=rubini-list@gnudd.com \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.