[U-Boot] [PATH] 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_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
Signed-off-by: Nikolay Petukhov Nikolay.Petukhov@gmail.com
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

On Thu, Mar 18, 2010 at 11:55:29AM +0500, Nikolay Petukhov wrote:
- /* 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]
- /* 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?
- //struct atmel_nand_host *host = nand_chip->priv;
Just remove the line.
-Scott

2010/3/19 Scott Wood scottwood@freescale.com:
On Thu, Mar 18, 2010 at 11:55:29AM +0500, Nikolay Petukhov wrote:
- /* 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]
- /* 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/
- //struct atmel_nand_host *host = nand_chip->priv;
Just remove the line.
Done
-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 Nikolay.Petukhov@gmail.com
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

On Fri, Mar 19, 2010 at 10:49:27AM +0500, Nikolay Petukhov wrote:
2010/3/19 Scott Wood scottwood@freescale.com:
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/
- //struct atmel_nand_host *host = nand_chip->priv;
Just remove the line.
Done
-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 Nikolay.Petukhov@gmail.com
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

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

--- include/asm-arm/arch-at91/at91sam9260.h | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/include/asm-arm/arch-at91/at91sam9260.h b/include/asm-arm/arch-at91/at91sam9260.h index a60a081..3bc7167 100644 --- a/include/asm-arm/arch-at91/at91sam9260.h +++ b/include/asm-arm/arch-at91/at91sam9260.h @@ -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

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(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index d5eb54a..5f10a02 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -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 */

On Sun, Mar 28, 2010 at 02:42:25PM +0200, Alexander Holler wrote:
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(-)
Missing Signed-off-by line.
-Scott

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

Signed-off-by: Alexander Holler holler@ahsoftware.de --- include/asm-arm/arch-at91/at91sam9260.h | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/include/asm-arm/arch-at91/at91sam9260.h b/include/asm-arm/arch-at91/at91sam9260.h index a60a081..3bc7167 100644 --- a/include/asm-arm/arch-at91/at91sam9260.h +++ b/include/asm-arm/arch-at91/at91sam9260.h @@ -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

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.
Signed-off-by: Alexander Holler holler@ahsoftware.de --- drivers/mtd/nand/atmel_nand.c | 7 +++---- 1 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index d5eb54a..5f10a02 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -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 */

Alexander Holler wrote:
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
I've removed three unused variables too.
drivers/mtd/nand/atmel_nand.c | 7 +++---- 1 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index d5eb54a..5f10a02 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -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 */

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
participants (4)
-
Alexander Holler
-
Nikolay Petukhov
-
Scott Wood
-
Tom