at91: add hwecc method for nand
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_HWECCCONFIG_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
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
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_RNDOUTSTARTafter 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
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_nandwith 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.
Signed-off-by Nikolay Petukhov <[email protected]>
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
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
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
@@ -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
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
@@ -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
PATCH 2/2 Fix compile errors for atmel_nand with hw-ecc and new SoC access
Quoted 4 lines · Alexander Holler
atmel_nand.cwith HW-ECC doesn't compile with the new SoC access. UsingCONFIG_AT91_LEGACYto 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_nandone has to useCONFIG_SYS_NAND_ECC_BASEAT91_ECC0_BASE(instead ofAT91_ECC0) for an AT91SAM9263 orAT91_ECC_BASEfor 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
PATCH 2/2 Fix compile errors for atmel_nand with hw-ecc and new SoC access
If someone else wonders why an author should sign his own patches, a good explanation can be found here: http://kerneltrap.org/files/Jeremy/DCO.txt ;)
Regards,
Alexander
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
@@ -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
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
@@ -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
Tom
NAKPATCH 2/2 Fix compile errors for atmel_nand with hw-ecc and new SoC access
In reply to Alexander HollerQuoted 8 lines · Alexander Holler
atmel_nand.cwith HW-ECC doesn't compile with the new SoC access. UsingCONFIG_AT91_LEGACYto circumvent the compile errors only leaves the driver in a state where it doesn't find the NAND.To use HW-ECC with
atmel_nandone has to useCONFIG_SYS_NAND_ECC_BASEAT91_ECC0_BASE(instead ofAT91_ECC0) for an AT91SAM9263 orAT91_ECC_BASEfor 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@@ -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 */
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