Skip to contentSkip to conversation

at91: add hwecc method for nand

U-Boot New Nikolay Petukhov 4 participants 13 messages Mar 18, 2010 – Apr 4, 2010

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

Hi, all.

This is a patch to use the hardware ECC controller of the AT91SAM9260 for the AT91 nand. Taken from the kernel 2.6.33.

To use this it is necessary to add two definitions in config:
CONFIG_ATMEL_NAND_HWECC
CONFIG_SYS_NAND_ECC_BASE (AT91_ECC for the AT91SAM9260 and AT91_ECC0 for the AT91SAM9263 boards)

It has been tested only on AT91SAM9260-EK.
u-boot version : 2009.11
kernel version : 2.6.27(32)

Results of read bandwidth:
u-boot with SWECC: 1MB/s
u-boot with HWECC: 3MB/s
linux with HWECC: 6MB/s

diff -uprN u-boot-2009.11/drivers/mtd/nand/atmel_nand.c u-boot-2009.11-at91-nand-hwecc/drivers/mtd/nand/atmel_nand.c
--- u-boot-2009.11/drivers/mtd/nand/atmel_nand.c 2009-12-16
03:20:54.000000000 +0500
+++ u-boot-2009.11-at91-nand-hwecc/drivers/mtd/nand/atmel_nand.c 2010-03-18
11:15:01.000000000 +0500
@@ -31,6 +31,210 @@

#include <nand.h>

+#ifdef CONFIG_ATMEL_NAND_HWECC
+
+/* Register access macros */
+#define ecc_readl(add, reg) \
+ readl(AT91_BASE_SYS + add + ATMEL_ECC_##reg)
+#define ecc_writel(add, reg, value) \
+ writel((value), AT91_BASE_SYS + add + ATMEL_ECC_##reg)
+
+#include "atmel_nand_ecc.h" /* Hardware ECC registers */
+
+/* oob layout for large page size
+ * bad block info is on bytes 0 and 1
+ * the bytes have to be consecutives to avoid
+ * several NAND_CMD_RNDOUT during read
+ */
+static struct nand_ecclayout atmel_oobinfo_large = {
+ .eccbytes = 4,
+ .eccpos = {60, 61, 62, 63},
+ .oobfree = {
+ {2, 58}
+ },
+};
+
+/* oob layout for small page size
+ * bad block info is on bytes 4 and 5
+ * the bytes have to be consecutives to avoid
+ * several NAND_CMD_RNDOUT during read
+ */
+static struct nand_ecclayout atmel_oobinfo_small = {
+ .eccbytes = 4,
+ .eccpos = {0, 1, 2, 3},
+ .oobfree = {
+ {6, 10}
+ },
+};
+
+/*
+ * Calculate HW ECC
+ *
+ * function called after a write
+ *

+ * mtd:        MTD block structure
+ * dat:        raw data (unused)
+ * ecc_code:   buffer for ECC

+ */
+static int atmel_nand_calculate(struct mtd_info *mtd,
+ const u_char *dat, unsigned char *ecc_code)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ unsigned int ecc_value;
+
+ /* get the first 2 ECC bytes */
+ ecc_value = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR);
+
+ ecc_code[0] = ecc_value & 0xFF;
+ ecc_code[1] = (ecc_value >> 8) & 0xFF;
+
+ /* get the last 2 ECC bytes */
+ ecc_value = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, NPR) & ATMEL_ECC_NPARITY;
+
+ ecc_code[2] = ecc_value & 0xFF;
+ ecc_code[3] = (ecc_value >> 8) & 0xFF;
+
+ return 0;
+}
+
+/*
+ * HW ECC read page function
+ *

+ * mtd:        mtd info structure
+ * chip:       nand chip info structure
+ * buf:        buffer to store read data

+ */
+static int atmel_nand_read_page(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int page)
+{
+ int eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint8_t *p = buf;
+ uint8_t *oob = chip->oob_poi;
+ uint8_t *ecc_pos;
+ int stat;
+
+ /* read the page */
+ chip->read_buf(mtd, p, eccsize);
+
+ /* move to ECC position if needed */
+ if (eccpos[0] != 0) {
+ /* This only works on large pages
+ * because the ECC controller waits for
+ * NAND_CMD_RNDOUTSTART after the
+ * NAND_CMD_RNDOUT.
+ * anyway, for small pages, the eccpos[0] == 0
+ */
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
+ mtd->writesize + eccpos[0], -1);
+ }
+
+ /* the ECC controller needs to read the ECC just after the data */
+ ecc_pos = oob + eccpos[0];
+ chip->read_buf(mtd, ecc_pos, eccbytes);
+
+ /* check if there's an error */
+ stat = chip->ecc.correct(mtd, p, oob, NULL);
+
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+
+ /* get back to oob start (end of page) */
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
+
+ /* read the oob */
+ chip->read_buf(mtd, oob, mtd->oobsize);
+
+ return 0;
+}
+
+/*
+ * HW ECC Correction
+ *
+ * function called after a read
+ *

+ * mtd:        MTD block structure
+ * dat:        raw data read from the chip
+ * read_ecc:   ECC from the chip (unused)
+ * isnull:     unused

+ *
+ * Detect and correct a 1 bit error for a page
+ */
+static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,
+ u_char *read_ecc, u_char *isnull)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ //struct atmel_nand_host *host = nand_chip->priv;
+ unsigned int ecc_status, ecc_parity, ecc_mode;
+ unsigned int ecc_word, ecc_bit;
+
+ /* get the status from the Status Register */
+ ecc_status = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, SR);
+
+ /* if there's no error */
+ if (likely(!(ecc_status & ATMEL_ECC_RECERR)))
+ return 0;
+
+ /* get error bit offset (4 bits) */
+ ecc_bit = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR) & ATMEL_ECC_BITADDR;
+ /* get word address (12 bits) */
+ ecc_word = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR) & ATMEL_ECC_WORDADDR;
+ ecc_word >>= 4;
+
+ /* if there are multiple errors */
+ if (ecc_status & ATMEL_ECC_MULERR) {
+ /* check if it is a freshly erased block
+ * (filled with 0xff) */
+ if ((ecc_bit == ATMEL_ECC_BITADDR)
+ && (ecc_word == (ATMEL_ECC_WORDADDR >> 4))) {
+ /* the block has just been erased, return OK */
+ return 0;
+ }
+ /* it doesn't seems to be a freshly
+ * erased block.
+ * We can't correct so many errors */
+ printk(KERN_WARNING "atmel_nand : multiple errors detected."
+ " Unable to correct.\n");
+ return -EIO;
+ }
+
+ /* if there's a single bit error : we can correct it */
+ if (ecc_status & ATMEL_ECC_ECCERR) {
+ /* there's nothing much to do here.
+ * the bit error is on the ECC itself.
+ */
+ printk(KERN_WARNING "atmel_nand : one bit error on ECC code."
+ " Nothing to correct\n");
+ return 0;
+ }
+
+ printk(KERN_WARNING "atmel_nand : one bit error on data."
+ " (word offset in the page :"
+ " 0x%x bit offset : 0x%x)\n",
+ ecc_word, ecc_bit);
+ /* correct the error */
+ if (nand_chip->options & NAND_BUSWIDTH_16) {
+ /* 16 bits words */
+ ((unsigned short *) dat)[ecc_word] ^= (1 << ecc_bit);
+ } else {
+ /* 8 bits words */
+ dat[ecc_word] ^= (1 << ecc_bit);
+ }
+ printk(KERN_WARNING "atmel_nand : error corrected\n");
+ return 1;
+}
+
+/*
+ * Enable HW ECC : unused on most chips
+ */
+static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) +{
+}
+#endif
+
static void at91_nand_hwcontrol(struct mtd_info *mtd,
int cmd, unsigned int ctrl)
{
@@ -64,6 +268,11 @@ static int at91_nand_ready(struct mtd_in

int board_nand_init(struct nand_chip *nand)
{
+#ifdef CONFIG_ATMEL_NAND_HWECC
+ static int chip_nr = 0;
+ struct mtd_info *mtd;
+#endif
+
nand->ecc.mode = NAND_ECC_SOFT;
#ifdef CONFIG_SYS_NAND_DBW_16
nand->options = NAND_BUSWIDTH_16;
@@ -74,5 +283,62 @@ int board_nand_init(struct nand_chip *na
#endif
nand->chip_delay = 20;

+#ifdef CONFIG_ATMEL_NAND_HWECC
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.calculate = atmel_nand_calculate;
+ nand->ecc.correct = atmel_nand_correct;
+ nand->ecc.hwctl = atmel_nand_hwctl;
+ nand->ecc.read_page = atmel_nand_read_page;
+ nand->ecc.bytes = 4;
+#endif
+
+#ifdef CONFIG_ATMEL_NAND_HWECC
+ mtd = &nand_info[chip_nr++];
+ mtd->priv = nand;
+
+ /* Detect NAND chips */
+ if (nand_scan_ident(mtd, 1)) {
+ printk(KERN_WARNING "NAND Flash not found !\n");
+ return -ENXIO;
+ }
+
+ if (nand->ecc.mode == NAND_ECC_HW) {
+ /* ECC is calculated for the whole page (1 step) */
+ nand->ecc.size = mtd->writesize;
+
+ /* set ECC page size and oob layout */
+ switch (mtd->writesize) {
+ case 512:
+ nand->ecc.layout = &atmel_oobinfo_small;
+ ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_528);
+ break;
+ case 1024:
+ nand->ecc.layout = &atmel_oobinfo_large;
+ ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_1056);
+ break;
+ case 2048:
+ nand->ecc.layout = &atmel_oobinfo_large;
+ ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_2112);
+ break;
+ case 4096:
+ nand->ecc.layout = &atmel_oobinfo_large;
+ ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_4224);
+ break;
+ default:
+ /* page size not handled by HW ECC */
+ /* switching back to soft ECC */
+ nand->ecc.mode = NAND_ECC_SOFT;
+ nand->ecc.calculate = NULL;
+ nand->ecc.correct = NULL;
+ nand->ecc.hwctl = NULL;
+ nand->ecc.read_page = NULL;
+ nand->ecc.postpad = 0;
+ nand->ecc.prepad = 0;
+ nand->ecc.bytes = 0;
+ break;
+ }
+ }
+#endif
+
return 0;
}
diff -uprN u-boot-2009.11/drivers/mtd/nand/atmel_nand_ecc.h u-boot-2009.11-at91-nand-hwecc/drivers/mtd/nand/atmel_nand_ecc.h
--- u-boot-2009.11/drivers/mtd/nand/atmel_nand_ecc.h 1970-01-01
05:00:00.000000000 +0500
+++ u-boot-2009.11-at91-nand-hwecc/drivers/mtd/nand/atmel_nand_ecc.h 2010-03-18
11:15:01.000000000 +0500
@@ -0,0 +1,36 @@
+/*
+ * Error Corrected Code Controller (ECC) - System peripherals regsters.
+ * Based on AT91SAM9260 datasheet revision B.
+ *
+ * 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.
+ */
+
+#ifndef ATMEL_NAND_ECC_H
+#define ATMEL_NAND_ECC_H
+
+#define ATMEL_ECC_CR 0x00 /* Control register */
+#define ATMEL_ECC_RST (1 << 0) /* Reset parity */
+
+#define ATMEL_ECC_MR 0x04 /* Mode register */
+#define ATMEL_ECC_PAGESIZE (3 << 0) /* Page Size */
+#define ATMEL_ECC_PAGESIZE_528 (0)
+#define ATMEL_ECC_PAGESIZE_1056 (1)
+#define ATMEL_ECC_PAGESIZE_2112 (2)
+#define ATMEL_ECC_PAGESIZE_4224 (3)
+
+#define ATMEL_ECC_SR 0x08 /* Status register */
+#define ATMEL_ECC_RECERR (1 << 0) /* Recoverable Error */
+#define ATMEL_ECC_ECCERR (1 << 1) /* ECC Single Bit Error */
+#define ATMEL_ECC_MULERR (1 << 2) /* Multiple Errors */
+
+#define ATMEL_ECC_PR 0x0c /* Parity register */
+#define ATMEL_ECC_BITADDR (0xf << 0) /* Bit Error Address */
+#define ATMEL_ECC_WORDADDR (0xfff << 4) /* Word Error Address */
+
+#define ATMEL_ECC_NPR 0x10 /* NParity register */
+#define ATMEL_ECC_NPARITY (0xffff << 0) /* NParity */
+
+#endif

--
Nikolay

#1 lore raw
Quoted 11 lines · Nikolay Petukhov
(quoted diff) +11 -0
+	/* move to ECC position if needed */
+	if (eccpos[0] != 0) {
+		/* This only works on large pages
+		 * because the ECC controller waits for
+		 * NAND_CMD_RNDOUTSTART after the
+		 * NAND_CMD_RNDOUT.
+		 * anyway, for small pages, the eccpos[0] == 0
+		 */
+		chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
+				mtd->writesize + eccpos[0], -1);
+	}

[snip]

Review

Quoted 2 lines
(quoted diff) +2 -0
+	/* get back to oob start (end of page) */
+	chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);

Is the second RNDOUT OK on small page NAND?

Review

Quoted 1 line
(quoted diff) +1 -0
+	//struct atmel_nand_host *host = nand_chip->priv;

Just remove the line.

-Scott

#2 lore raw

2010/3/19 Scott Wood <[email protected]>:

Quoted 12 lines · Nikolay Petukhov
Quoted 11 lines

+ ? ? /* move to ECC position if needed */
+ ? ? if (eccpos[0] != 0) {
+ ? ? ? ? ? ? /* This only works on large pages
+ ? ? ? ? ? ? ?* because the ECC controller waits for
+ ? ? ? ? ? ? ?* NAND_CMD_RNDOUTSTART after the
+ ? ? ? ? ? ? ?* NAND_CMD_RNDOUT.
+ ? ? ? ? ? ? ?* anyway, for small pages, the eccpos[0] == 0
+ ? ? ? ? ? ? ?*/
+ ? ? ? ? ? ? chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
+ ? ? ? ? ? ? ? ? ? ? ? ? ? ? mtd->writesize + eccpos[0], -1);
+ ? ? }

Quoted 6 lines

[snip]

Quoted 2 lines

+ ? ? /* get back to oob start (end of page) */
+ ? ? chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);

Is the second RNDOUT OK on small page NAND?

I have NAND chip with 2K page size.
NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)

Here, the positive results of operation of the at91_nand with small pages http://patchwork.ozlabs.org/patch/4049/

Quoted 3 lines
Quoted 1 line

+ ? ? //struct atmel_nand_host *host = nand_chip->priv;

Just remove the line.

Done

Quoted 3 lines

-Scott

This is a patch to use the hardware ECC controller of the AT91SAM9260 for the AT91 nand. Taken from the kernel 2.6.33.

diff -uprN u-boot-2009.11/drivers/mtd/nand/atmel_nand.c u-boot-2009.11-at91-nand-hwecc/drivers/mtd/nand/atmel_nand.c
--- u-boot-2009.11/drivers/mtd/nand/atmel_nand.c 2009-12-16
03:20:54.000000000 +0500
+++ u-boot-2009.11-at91-nand-hwecc/drivers/mtd/nand/atmel_nand.c 2010-03-19
10:34:28.000000000 +0500
@@ -31,6 +31,209 @@

#include <nand.h>

+#ifdef CONFIG_ATMEL_NAND_HWECC
+
+/* Register access macros */
+#define ecc_readl(add, reg) \
+ readl(AT91_BASE_SYS + add + ATMEL_ECC_##reg)
+#define ecc_writel(add, reg, value) \
+ writel((value), AT91_BASE_SYS + add + ATMEL_ECC_##reg)
+
+#include "atmel_nand_ecc.h" /* Hardware ECC registers */
+
+/* oob layout for large page size
+ * bad block info is on bytes 0 and 1
+ * the bytes have to be consecutives to avoid
+ * several NAND_CMD_RNDOUT during read
+ */
+static struct nand_ecclayout atmel_oobinfo_large = {
+ .eccbytes = 4,
+ .eccpos = {60, 61, 62, 63},
+ .oobfree = {
+ {2, 58}
+ },
+};
+
+/* oob layout for small page size
+ * bad block info is on bytes 4 and 5
+ * the bytes have to be consecutives to avoid
+ * several NAND_CMD_RNDOUT during read
+ */
+static struct nand_ecclayout atmel_oobinfo_small = {
+ .eccbytes = 4,
+ .eccpos = {0, 1, 2, 3},
+ .oobfree = {
+ {6, 10}
+ },
+};
+
+/*
+ * Calculate HW ECC
+ *
+ * function called after a write
+ *

+ * mtd:        MTD block structure
+ * dat:        raw data (unused)
+ * ecc_code:   buffer for ECC

+ */
+static int atmel_nand_calculate(struct mtd_info *mtd,
+ const u_char *dat, unsigned char *ecc_code)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ unsigned int ecc_value;
+
+ /* get the first 2 ECC bytes */
+ ecc_value = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR);
+
+ ecc_code[0] = ecc_value & 0xFF;
+ ecc_code[1] = (ecc_value >> 8) & 0xFF;
+
+ /* get the last 2 ECC bytes */
+ ecc_value = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, NPR) & ATMEL_ECC_NPARITY;
+
+ ecc_code[2] = ecc_value & 0xFF;
+ ecc_code[3] = (ecc_value >> 8) & 0xFF;
+
+ return 0;
+}
+
+/*
+ * HW ECC read page function
+ *

+ * mtd:        mtd info structure
+ * chip:       nand chip info structure
+ * buf:        buffer to store read data

+ */
+static int atmel_nand_read_page(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int page)
+{
+ int eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint8_t *p = buf;
+ uint8_t *oob = chip->oob_poi;
+ uint8_t *ecc_pos;
+ int stat;
+
+ /* read the page */
+ chip->read_buf(mtd, p, eccsize);
+
+ /* move to ECC position if needed */
+ if (eccpos[0] != 0) {
+ /* This only works on large pages
+ * because the ECC controller waits for
+ * NAND_CMD_RNDOUTSTART after the
+ * NAND_CMD_RNDOUT.
+ * anyway, for small pages, the eccpos[0] == 0
+ */
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
+ mtd->writesize + eccpos[0], -1);
+ }
+
+ /* the ECC controller needs to read the ECC just after the data */
+ ecc_pos = oob + eccpos[0];
+ chip->read_buf(mtd, ecc_pos, eccbytes);
+
+ /* check if there's an error */
+ stat = chip->ecc.correct(mtd, p, oob, NULL);
+
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+
+ /* get back to oob start (end of page) */
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
+
+ /* read the oob */
+ chip->read_buf(mtd, oob, mtd->oobsize);
+
+ return 0;
+}
+
+/*
+ * HW ECC Correction
+ *
+ * function called after a read
+ *

+ * mtd:        MTD block structure
+ * dat:        raw data read from the chip
+ * read_ecc:   ECC from the chip (unused)
+ * isnull:     unused

+ *
+ * Detect and correct a 1 bit error for a page
+ */
+static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,
+ u_char *read_ecc, u_char *isnull)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ unsigned int ecc_status, ecc_parity, ecc_mode;
+ unsigned int ecc_word, ecc_bit;
+
+ /* get the status from the Status Register */
+ ecc_status = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, SR);
+
+ /* if there's no error */
+ if (likely(!(ecc_status & ATMEL_ECC_RECERR)))
+ return 0;
+
+ /* get error bit offset (4 bits) */
+ ecc_bit = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR) & ATMEL_ECC_BITADDR;
+ /* get word address (12 bits) */
+ ecc_word = ecc_readl(CONFIG_SYS_NAND_ECC_BASE, PR) & ATMEL_ECC_WORDADDR;
+ ecc_word >>= 4;
+
+ /* if there are multiple errors */
+ if (ecc_status & ATMEL_ECC_MULERR) {
+ /* check if it is a freshly erased block
+ * (filled with 0xff) */
+ if ((ecc_bit == ATMEL_ECC_BITADDR)
+ && (ecc_word == (ATMEL_ECC_WORDADDR >> 4))) {
+ /* the block has just been erased, return OK */
+ return 0;
+ }
+ /* it doesn't seems to be a freshly
+ * erased block.
+ * We can't correct so many errors */
+ printk(KERN_WARNING "atmel_nand : multiple errors detected."
+ " Unable to correct.\n");
+ return -EIO;
+ }
+
+ /* if there's a single bit error : we can correct it */
+ if (ecc_status & ATMEL_ECC_ECCERR) {
+ /* there's nothing much to do here.
+ * the bit error is on the ECC itself.
+ */
+ printk(KERN_WARNING "atmel_nand : one bit error on ECC code."
+ " Nothing to correct\n");
+ return 0;
+ }
+
+ printk(KERN_WARNING "atmel_nand : one bit error on data."
+ " (word offset in the page :"
+ " 0x%x bit offset : 0x%x)\n",
+ ecc_word, ecc_bit);
+ /* correct the error */
+ if (nand_chip->options & NAND_BUSWIDTH_16) {
+ /* 16 bits words */
+ ((unsigned short *) dat)[ecc_word] ^= (1 << ecc_bit);
+ } else {
+ /* 8 bits words */
+ dat[ecc_word] ^= (1 << ecc_bit);
+ }
+ printk(KERN_WARNING "atmel_nand : error corrected\n");
+ return 1;
+}
+
+/*
+ * Enable HW ECC : unused on most chips
+ */
+static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) +{
+}
+#endif
+
static void at91_nand_hwcontrol(struct mtd_info *mtd,
int cmd, unsigned int ctrl)
{
@@ -64,6 +267,11 @@ static int at91_nand_ready(struct mtd_in

int board_nand_init(struct nand_chip *nand)
{
+#ifdef CONFIG_ATMEL_NAND_HWECC
+ static int chip_nr = 0;
+ struct mtd_info *mtd;
+#endif
+
nand->ecc.mode = NAND_ECC_SOFT;
#ifdef CONFIG_SYS_NAND_DBW_16
nand->options = NAND_BUSWIDTH_16;
@@ -74,5 +282,62 @@ int board_nand_init(struct nand_chip *na
#endif
nand->chip_delay = 20;

+#ifdef CONFIG_ATMEL_NAND_HWECC
+ nand->ecc.mode = NAND_ECC_HW;
+ nand->ecc.calculate = atmel_nand_calculate;
+ nand->ecc.correct = atmel_nand_correct;
+ nand->ecc.hwctl = atmel_nand_hwctl;
+ nand->ecc.read_page = atmel_nand_read_page;
+ nand->ecc.bytes = 4;
+#endif
+
+#ifdef CONFIG_ATMEL_NAND_HWECC
+ mtd = &nand_info[chip_nr++];
+ mtd->priv = nand;
+
+ /* Detect NAND chips */
+ if (nand_scan_ident(mtd, 1)) {
+ printk(KERN_WARNING "NAND Flash not found !\n");
+ return -ENXIO;
+ }
+
+ if (nand->ecc.mode == NAND_ECC_HW) {
+ /* ECC is calculated for the whole page (1 step) */
+ nand->ecc.size = mtd->writesize;
+
+ /* set ECC page size and oob layout */
+ switch (mtd->writesize) {
+ case 512:
+ nand->ecc.layout = &atmel_oobinfo_small;
+ ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_528);
+ break;
+ case 1024:
+ nand->ecc.layout = &atmel_oobinfo_large;
+ ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_1056);
+ break;
+ case 2048:
+ nand->ecc.layout = &atmel_oobinfo_large;
+ ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_2112);
+ break;
+ case 4096:
+ nand->ecc.layout = &atmel_oobinfo_large;
+ ecc_writel(CONFIG_SYS_NAND_ECC_BASE, MR, ATMEL_ECC_PAGESIZE_4224);
+ break;
+ default:
+ /* page size not handled by HW ECC */
+ /* switching back to soft ECC */
+ nand->ecc.mode = NAND_ECC_SOFT;
+ nand->ecc.calculate = NULL;
+ nand->ecc.correct = NULL;
+ nand->ecc.hwctl = NULL;
+ nand->ecc.read_page = NULL;
+ nand->ecc.postpad = 0;
+ nand->ecc.prepad = 0;
+ nand->ecc.bytes = 0;
+ break;
+ }
+ }
+#endif
+
return 0;
}
diff -uprN u-boot-2009.11/drivers/mtd/nand/atmel_nand_ecc.h u-boot-2009.11-at91-nand-hwecc/drivers/mtd/nand/atmel_nand_ecc.h
--- u-boot-2009.11/drivers/mtd/nand/atmel_nand_ecc.h 1970-01-01
05:00:00.000000000 +0500
+++ u-boot-2009.11-at91-nand-hwecc/drivers/mtd/nand/atmel_nand_ecc.h 2010-03-19
10:33:54.000000000 +0500
@@ -0,0 +1,36 @@
+/*
+ * Error Corrected Code Controller (ECC) - System peripherals regsters.
+ * Based on AT91SAM9260 datasheet revision B.
+ *
+ * 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.
+ */
+
+#ifndef ATMEL_NAND_ECC_H
+#define ATMEL_NAND_ECC_H
+
+#define ATMEL_ECC_CR 0x00 /* Control register */
+#define ATMEL_ECC_RST (1 << 0) /* Reset parity */
+
+#define ATMEL_ECC_MR 0x04 /* Mode register */
+#define ATMEL_ECC_PAGESIZE (3 << 0) /* Page Size */
+#define ATMEL_ECC_PAGESIZE_528 (0)
+#define ATMEL_ECC_PAGESIZE_1056 (1)
+#define ATMEL_ECC_PAGESIZE_2112 (2)
+#define ATMEL_ECC_PAGESIZE_4224 (3)
+
+#define ATMEL_ECC_SR 0x08 /* Status register */
+#define ATMEL_ECC_RECERR (1 << 0) /* Recoverable Error */
+#define ATMEL_ECC_ECCERR (1 << 1) /* ECC Single Bit Error */
+#define ATMEL_ECC_MULERR (1 << 2) /* Multiple Errors */
+
+#define ATMEL_ECC_PR 0x0c /* Parity register */
+#define ATMEL_ECC_BITADDR (0xf << 0) /* Bit Error Address */
+#define ATMEL_ECC_WORDADDR (0xfff << 4) /* Word Error Address */
+
+#define ATMEL_ECC_NPR 0x10 /* NParity register */
+#define ATMEL_ECC_NPARITY (0xffff << 0) /* NParity */
+
+#endif

Signature · 1 line
Nikolay
#3 lore raw
Quoted 18 lines · Nikolay Petukhov

2010/3/19 Scott Wood <[email protected]>:

Quoted 2 lines

Is the second RNDOUT OK on small page NAND?

I have NAND chip with 2K page size.
NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)

Here, the positive results of operation of the at91_nand with small pages http://patchwork.ozlabs.org/patch/4049/

Quoted 3 lines
Quoted 1 line

+ ? ? //struct atmel_nand_host *host = nand_chip->priv;

Just remove the line.

Done

Quoted 1 line
Quoted 7 lines
Quoted 2 lines

-Scott

This is a patch to use the hardware ECC controller of the AT91SAM9260 for the AT91 nand. Taken from the kernel 2.6.33.

Applied to u-boot-nand-flash next, but it took some manual editing of the e-mail to apply. Please don't let quoted printable be used when sending a patch (git seems to kind of support it, but I had to unwrap a couple lines manually, and if "git am" fails, the standard patch utility cannot be used), and make sure any comments which aren't to become part of the changelog go below a --- line.

It's usually simpler to just send the new patch in a separate e-mail rather than combined with a discussion reply.

-Scott

#4 lore raw

cover atmel_nand with HW-ECC

Hello,

I've cherry-picked the patch for atmel_nand to enable HW-ECC on the master and had to apply some changes. Afterwards it seems to work on an AT91SAM9263.

Thanks for that functionality.

Regards,

Alexander Holler

#5 lore raw

PATCH 1/2 Add AT91_ECC_BASE to at91sam9260.h to use atmel_nand with HW-ECC

---
 include/asm-arm/arch-at91/at91sam9260.h |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
include/asm-arm/arch-at91/at91sam9260.h +1 -0
index a60a081..3bc7167 100644
@@ -50,6 +50,7 @@
 #define AT91SAM9260_ID_IRQ2	31	/* Advanced Interrupt Controller (IRQ2) */
 
 #define AT91_EMAC_BASE		0xfffc4000
+#define AT91_ECC_BASE	        0xffffe800
 #define AT91_SDRAMC_BASE	0xffffea00
 #define AT91_SMC_BASE		0xffffec00
 #define AT91_MATRIX_BASE	0xffffee00
Signature · 1 line
1.6.2.5
#6 lore raw

PATCH 2/2 Fix compile errors for atmel_nand with hw-ecc and new SoC access

atmel_nand.c with HW-ECC doesn't compile with the new SoC access. Using CONFIG_AT91_LEGACY to circumvent the compile errors only leaves the driver in a state where it doesn't find the NAND.

To use HW-ECC with atmel_nand one has to use CONFIG_SYS_NAND_ECC_BASE AT91_ECC0_BASE (instead of AT91_ECC0) for an AT91SAM9263 or AT91_ECC_BASE for an AT91SAM9260.

I've removed three unused variables too.

---
 drivers/mtd/nand/atmel_nand.c |    7 +++----
 1 files changed, 3 insertions(+), 4 deletions(-)
drivers/mtd/nand/atmel_nand.c +3 -4
index d5eb54a..5f10a02 100644
@@ -35,9 +35,9 @@
 
 /* Register access macros */
 #define ecc_readl(add, reg)				\
-	readl(AT91_BASE_SYS + add + ATMEL_ECC_##reg)
+	readl(add + ATMEL_ECC_##reg)
 #define ecc_writel(add, reg, value)			\
-	writel((value), AT91_BASE_SYS + add + ATMEL_ECC_##reg)
+	writel((value), add + ATMEL_ECC_##reg)
 
 #include "atmel_nand_ecc.h"	/* Hardware ECC registers */
 
@@ -79,7 +79,6 @@ static struct nand_ecclayout atmel_oobinfo_small = {
 static int atmel_nand_calculate(struct mtd_info *mtd,
 		const u_char *dat, unsigned char *ecc_code)
 {
-	struct nand_chip *nand_chip = mtd->priv;
 	unsigned int ecc_value;
 
 	/* get the first 2 ECC bytes */
@@ -167,7 +166,7 @@ static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,
 		u_char *read_ecc, u_char *isnull)
 {
 	struct nand_chip *nand_chip = mtd->priv;
-	unsigned int ecc_status, ecc_parity, ecc_mode;
+	unsigned int ecc_status;
 	unsigned int ecc_word, ecc_bit;
 
 	/* get the status from the Status Register */
Signature · 1 line
1.6.2.5
#7 lore raw

PATCH 2/2 Fix compile errors for atmel_nand with hw-ecc and new SoC access

Quoted 4 lines · Alexander Holler

atmel_nand.c with HW-ECC doesn't compile with the new SoC access. Using CONFIG_AT91_LEGACY to circumvent the compile errors only leaves the driver in a state where it doesn't find the NAND.

Quoted 8 lines

To use HW-ECC with atmel_nand one has to use CONFIG_SYS_NAND_ECC_BASE AT91_ECC0_BASE (instead of AT91_ECC0) for an AT91SAM9263 or AT91_ECC_BASE for an AT91SAM9260.

I've removed three unused variables too.

---
 drivers/mtd/nand/atmel_nand.c |    7 +++----
 1 files changed, 3 insertions(+), 4 deletions(-)

Missing Signed-off-by line.

-Scott

#9 lore raw

PATCH 1/2 Add AT91_ECC_BASE to at91sam9260.h to use atmel_nand with HW-ECC

---
 include/asm-arm/arch-at91/at91sam9260.h |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)
include/asm-arm/arch-at91/at91sam9260.h +1 -0
index a60a081..3bc7167 100644
@@ -50,6 +50,7 @@
 #define AT91SAM9260_ID_IRQ2	31	/* Advanced Interrupt Controller (IRQ2) */
 
 #define AT91_EMAC_BASE		0xfffc4000
+#define AT91_ECC_BASE	        0xffffe800
 #define AT91_SDRAMC_BASE	0xffffea00
 #define AT91_SMC_BASE		0xffffec00
 #define AT91_MATRIX_BASE	0xffffee00
Signature · 1 line
1.6.2.5
#10 lore raw

PATCH 2/2 Fix compile errors for atmel_nand with hw-ecc and new SoC access

atmel_nand.c with HW-ECC doesn't compile with the new SoC access. Using CONFIG_AT91_LEGACY to circumvent the compile errors only leaves the driver in a state where it doesn't find the NAND.

To use HW-ECC with atmel_nand one has to use CONFIG_SYS_NAND_ECC_BASE AT91_ECC0_BASE (instead of AT91_ECC0) for an AT91SAM9263 or AT91_ECC_BASE for an AT91SAM9260.

I've removed three unused variables too.

---
 drivers/mtd/nand/atmel_nand.c |    7 +++----
 1 files changed, 3 insertions(+), 4 deletions(-)
drivers/mtd/nand/atmel_nand.c +3 -4
index d5eb54a..5f10a02 100644
@@ -35,9 +35,9 @@
 
 /* Register access macros */
 #define ecc_readl(add, reg)				\
-	readl(AT91_BASE_SYS + add + ATMEL_ECC_##reg)
+	readl(add + ATMEL_ECC_##reg)
 #define ecc_writel(add, reg, value)			\
-	writel((value), AT91_BASE_SYS + add + ATMEL_ECC_##reg)
+	writel((value), add + ATMEL_ECC_##reg)
 
 #include "atmel_nand_ecc.h"	/* Hardware ECC registers */
 
@@ -79,7 +79,6 @@ static struct nand_ecclayout atmel_oobinfo_small = {
 static int atmel_nand_calculate(struct mtd_info *mtd,
 		const u_char *dat, unsigned char *ecc_code)
 {
-	struct nand_chip *nand_chip = mtd->priv;
 	unsigned int ecc_value;
 
 	/* get the first 2 ECC bytes */
@@ -167,7 +166,7 @@ static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,
 		u_char *read_ecc, u_char *isnull)
 {
 	struct nand_chip *nand_chip = mtd->priv;
-	unsigned int ecc_status, ecc_parity, ecc_mode;
+	unsigned int ecc_status;
 	unsigned int ecc_word, ecc_bit;
 
 	/* get the status from the Status Register */
Signature · 1 line
1.6.2.5
NAK
#11 lore raw

PATCH 2/2 Fix compile errors for atmel_nand with hw-ecc and new SoC access

In reply to Alexander Holler
Quoted 8 lines · Alexander Holler

atmel_nand.c with HW-ECC doesn't compile with the new SoC access. Using CONFIG_AT91_LEGACY to circumvent the compile errors only leaves the driver in a state where it doesn't find the NAND.

To use HW-ECC with atmel_nand one has to use CONFIG_SYS_NAND_ECC_BASE AT91_ECC0_BASE (instead of AT91_ECC0) for an AT91SAM9263 or AT91_ECC_BASE for an AT91SAM9260.

Nak
The new Soc access is the preferred method.
The fix should use the Soc access, not the legacy method.

Tom

Quoted 38 lines

I've removed three unused variables too.

---
 drivers/mtd/nand/atmel_nand.c |    7 +++----
 1 files changed, 3 insertions(+), 4 deletions(-)
drivers/mtd/nand/atmel_nand.c +3 -4
index d5eb54a..5f10a02 100644
@@ -35,9 +35,9 @@
 
 /* Register access macros */
 #define ecc_readl(add, reg)				\
-	readl(AT91_BASE_SYS + add + ATMEL_ECC_##reg)
+	readl(add + ATMEL_ECC_##reg)
 #define ecc_writel(add, reg, value)			\
-	writel((value), AT91_BASE_SYS + add + ATMEL_ECC_##reg)
+	writel((value), add + ATMEL_ECC_##reg)
 
 #include "atmel_nand_ecc.h"	/* Hardware ECC registers */
 
@@ -79,7 +79,6 @@ static struct nand_ecclayout atmel_oobinfo_small = {
 static int atmel_nand_calculate(struct mtd_info *mtd,
 		const u_char *dat, unsigned char *ecc_code)
 {
-	struct nand_chip *nand_chip = mtd->priv;
 	unsigned int ecc_value;
 
 	/* get the first 2 ECC bytes */
@@ -167,7 +166,7 @@ static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat,
 		u_char *read_ecc, u_char *isnull)
 {
 	struct nand_chip *nand_chip = mtd->priv;
-	unsigned int ecc_status, ecc_parity, ecc_mode;
+	unsigned int ecc_status;
 	unsigned int ecc_word, ecc_bit;
 
 	/* get the status from the Status Register */
#12 lore raw

PATCH 2/2 Fix compile errors for atmel_nand with hw-ecc and new SoC access

Hi,

please just forget my patches.

I don't feel the need to waste your and my time in posting examples of my obivous incompetence and I'm long out of school and don't want to take lessons about exploring how other people want their names of variables.

Happy easter,

Alexander