* [PATCH] [NAND] This patch add support for HW ECC (HSIAO code) on AT91SAM9G20.
@ 2008-08-22 2:59 Hong Xu
2008-08-22 9:47 ` Marc Pignat
0 siblings, 1 reply; 3+ messages in thread
From: Hong Xu @ 2008-08-22 2:59 UTC (permalink / raw)
To: dwmw2, linux-arm-kernel, linux-mtd
Cc: linux, nicolas.ferre, Lin, JM, patrice.vilchez, dan.liang, linux
>From ce6b68cf009a94ab12a31d7f007003173b6cad2c Mon Sep 17 00:00:00 2001
From: Hong Xu <hong.xu@atmel.com>
Date: Fri, 22 Aug 2008 10:29:10 +0800
Subject: [PATCH] This patch add support for HW ECC on AT91SAM9G20.
AT91SAM9G20 ECC controller is capable of 1-bits error correction
and 2-bit random detection for every 256 bytes of data. And this
patch chooses a working mode which can be compatible with software
ECC.
Signed-off-by: Hong Xu <hong.xu@atmel.com>
---
drivers/mtd/nand/Kconfig | 16 ++++-
drivers/mtd/nand/atmel_nand.c | 147 ++++++++++++++++++++++++++++++++++---
drivers/mtd/nand/atmel_nand_ecc.h | 26 +++++++
3 files changed, 177 insertions(+), 12 deletions(-)
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 41f361c..045b702 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -310,6 +310,20 @@ config MTD_NAND_ATMEL_ECC_HW
If unsure, say Y
+config MTD_NAND_ATMEL_ECC_HW_HSIAO
+ bool "Hardware ECC HSIAO Code"
+ depends on ARCH_AT91SAM9G20
+ help
+ Use hardware ECC(HSIAO Code) instead of software ECC when the
+ chip supports it.
+
+ The hardware ECC controller is capable of single bit error
+ correction and 2-bit random detection per 256 bytes.
+
+ NB : hardware and software ECC schemes are compatible.
+
+ If unsure, say Y
+
config MTD_NAND_ATMEL_ECC_SOFT
bool "Software ECC"
help
@@ -329,8 +343,6 @@ config MTD_NAND_ATMEL_ECC_NONE
If unsure, say N
- endchoice
-
endchoice
config MTD_NAND_PXA3xx
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index 3387e0d..198ff4c 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -27,6 +27,7 @@
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <linux/gpio.h>
@@ -35,7 +36,8 @@
#include <mach/board.h>
#include <mach/cpu.h>
-#ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW
+#if defined(CONFIG_MTD_NAND_ATMEL_ECC_HW) || \
+ defined(CONFIG_MTD_NAND_ATMEL_ECC_HW_HSIAO)
#define hard_ecc 1
#else
#define hard_ecc 0
@@ -81,6 +83,41 @@ static struct nand_ecclayout atmel_oobinfo_small = {
},
};
+#ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW_HSIAO
+static struct nand_ecclayout nand_oob_16 = {
+ .eccbytes = 6,
+ .eccpos = {0, 1, 2, 3, 6, 7},
+ .oobfree = {
+ {.offset = 8,
+ . length = 8} }
+};
+
+static struct nand_ecclayout nand_oob_64 = {
+ .eccbytes = 24,
+ .eccpos = {
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63 },
+ .oobfree = {
+ {.offset = 2,
+ .length = 38} }
+};
+
+static struct nand_ecclayout nand_oob_128 = {
+ .eccbytes = 48,
+ .eccpos = {
+ 80, 81, 82, 83, 84, 85, 86, 87,
+ 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127},
+ .oobfree = {
+ {.offset = 2,
+ .length = 38} }
+};
+#endif
+
struct atmel_nand_host {
struct nand_chip nand_chip;
struct mtd_info mtd;
@@ -215,6 +252,38 @@ static int atmel_nand_read_oob_512(struct mtd_info *mtd,
return sndcmd;
}
+#ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW_HSIAO
+static unsigned int Hsiao2Hamming(unsigned int input)
+{
+ unsigned int output = 0;
+
+ output |= ((input & (1 << 2)) << 21);
+ output |= ((input & (1 << 14)) << 8);
+ output |= ((input & (1 << 1)) << 20);
+ output |= ((input & (1 << 13)) << 7);
+ output |= ((input & (1 << 0)) << 19);
+ output |= ((input & (1 << 12)) << 6);
+ output |= ((input & (1 << 7)) << 8);
+ output |= ((input & (1 << 19)) >> 5);
+ output |= ((input & (1 << 6)) << 7);
+ output |= ((input & (1 << 18)) >> 6);
+ output |= ((input & (1 << 5)) << 6);
+ output |= ((input & (1 << 17)) >> 7);
+ output |= ((input & (1 << 4)) << 5);
+ output |= ((input & (1 << 16)) >> 8);
+ output |= ((input & (1 << 11)) >> 4);
+ output |= ((input & (1 << 23)) >> 17);
+ output |= ((input & (1 << 10)) >> 5);
+ output |= ((input & (1 << 22)) >> 18);
+ output |= ((input & (1 << 9)) >> 6);
+ output |= ((input & (1 << 21)) >> 19);
+ output |= ((input & (1 << 8)) >> 7);
+ output |= ((input & (1 << 20)) >> 20);
+ output ^= 0xFFFFFF;
+
+ return output;
+}
+
/*
* Calculate HW ECC
*
@@ -229,6 +298,25 @@ static int atmel_nand_calculate(struct mtd_info *mtd,
{
struct nand_chip *nand_chip = mtd->priv;
struct atmel_nand_host *host = nand_chip->priv;
+ uint32_t hamming;
+
+ hamming = Hsiao2Hamming(ecc_readl(host->ecc, PR0));
+
+ ecc_writel(host->ecc, CR, 1);
+ ecc_code[0] = hamming & 0xFF;
+ ecc_code[1] = (hamming >> 8) & 0xFF;
+ ecc_code[2] = (hamming >> 16) & 0xFF;
+
+ return 0;
+}
+
+#else /* CONFIG_MTD_NAND_ATMEL_ECC_HW_HSIAO */
+
+static int atmel_nand_calculate(struct mtd_info *mtd,
+ const u_char *dat, unsigned char *ecc_code)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct atmel_nand_host *host = nand_chip->priv;
uint32_t *eccpos = nand_chip->ecc.layout->eccpos;
unsigned int ecc_value;
@@ -389,6 +477,7 @@ static int atmel_nand_correct(struct mtd_info
*mtd, u_char *dat,
dev_dbg(host->dev, "atmel_nand : error corrected\n");
return 1;
}
+#endif /* CONFIG_MTD_NAND_ATMEL_ECC_HW_HSIAO */
/*
* Enable HW ECC : unused on most chips
@@ -476,14 +565,22 @@ static int __init atmel_nand_probe(struct
platform_device *pdev)
res = -EIO;
goto err_ecc_ioremap;
}
- nand_chip->ecc.mode = NAND_ECC_HW_SYNDROME;
nand_chip->ecc.calculate = atmel_nand_calculate;
- nand_chip->ecc.correct = atmel_nand_correct;
nand_chip->ecc.hwctl = atmel_nand_hwctl;
+#ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW_HSIAO
+ nand_chip->ecc.mode = NAND_ECC_HW;
+ nand_chip->ecc.correct = nand_correct_data;
+ nand_chip->ecc.bytes = 3;
+ nand_chip->ecc.prepad = 0;
+ nand_chip->ecc.postpad = 0;
+#else
+ nand_chip->ecc.mode = NAND_ECC_HW_SYNDROME;
+ nand_chip->ecc.correct = atmel_nand_correct;
nand_chip->ecc.read_page = atmel_nand_read_page;
nand_chip->ecc.bytes = 4;
nand_chip->ecc.prepad = 0;
nand_chip->ecc.postpad = 0;
+#endif
}
nand_chip->chip_delay = 20; /* 20us command delay time */
@@ -542,17 +639,47 @@ static int __init atmel_nand_probe(struct
platform_device *pdev)
/* page size not handled by HW ECC */
/* switching back to soft ECC */
nand_chip->ecc.mode = NAND_ECC_SOFT;
- nand_chip->ecc.calculate = NULL;
- nand_chip->ecc.correct = NULL;
- nand_chip->ecc.hwctl = NULL;
- nand_chip->ecc.read_page = NULL;
- nand_chip->ecc.postpad = 0;
- nand_chip->ecc.prepad = 0;
- nand_chip->ecc.bytes = 0;
break;
}
}
+ if (nand_chip->ecc.mode == NAND_ECC_HW) {
+ nand_chip->ecc.size = 256;
+ switch (mtd->writesize) {
+ case 512:
+ nand_chip->ecc.layout = &nand_oob_16;
+ ecc_writel(host->ecc, MR,
+ ATMEL_ECC_PAGESIZE_528 | ATMEL_ECC_PER_256);
+ break;
+ case 2048:
+ nand_chip->ecc.layout = &nand_oob_64;
+ ecc_writel(host->ecc, MR,
+ ATMEL_ECC_PAGESIZE_2112 | ATMEL_ECC_PER_256);
+ break;
+ case 4096:
+ nand_chip->ecc.layout = &nand_oob_128;
+ ecc_writel(host->ecc, MR,
+ ATMEL_ECC_PAGESIZE_4224 | ATMEL_ECC_PER_256);
+ break;
+ default:
+ /* page size which is not supported by HW */
+ /* Switch back to software ECC */
+ nand_chip->ecc.mode = NAND_ECC_SOFT;
+ break;
+ }
+ }
+
+ if (nand_chip->ecc.mode == NAND_ECC_SOFT)
+ {
+ nand_chip->ecc.calculate = NULL;
+ nand_chip->ecc.correct = NULL;
+ nand_chip->ecc.hwctl = NULL;
+ nand_chip->ecc.read_page = NULL;
+ nand_chip->ecc.postpad = 0;
+ nand_chip->ecc.prepad = 0;
+ nand_chip->ecc.bytes = 0;
+ }
+
/* second phase scan */
if (nand_scan_tail(mtd)) {
res = -ENXIO;
diff --git a/drivers/mtd/nand/atmel_nand_ecc.h
b/drivers/mtd/nand/atmel_nand_ecc.h
index 1ee7f99..41e072e 100644
--- a/drivers/mtd/nand/atmel_nand_ecc.h
+++ b/drivers/mtd/nand/atmel_nand_ecc.h
@@ -26,6 +26,30 @@
#define ATMEL_ECC_ECCERR (1 << 1) /* ECC Single Bit Error */
#define ATMEL_ECC_MULERR (1 << 2) /* Multiple Errors */
+#ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW_HSIAO
+
+#define ATMEL_ECC_PER_256 (1 << 4)
+
+#define ATMEL_ECC_PR0 0x0c
+#define ATMEL_ECC_PR1 0x10
+#define ATMEL_ECC_SR2 0x14
+#define ATMEL_ECC_PR2 0x18
+#define ATMEL_ECC_PR3 0x1c
+#define ATMEL_ECC_PR4 0x20
+#define ATMEL_ECC_PR5 0x24
+#define ATMEL_ECC_PR6 0x28
+#define ATMEL_ECC_PR7 0x2c
+#define ATMEL_ECC_PR8 0x30
+#define ATMEL_ECC_PR9 0x34
+#define ATMEL_ECC_PR10 0x38
+#define ATMEL_ECC_PR11 0x3c
+#define ATMEL_ECC_PR12 0x40
+#define ATMEL_ECC_PR13 0x44
+#define ATMEL_ECC_PR14 0x48
+#define ATMEL_ECC_PR15 0x4c
+
+#else
+
#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 */
@@ -33,4 +57,6 @@
#define ATMEL_ECC_NPR 0x10 /* NParity register */
#define ATMEL_ECC_NPARITY (0xffff << 0) /* NParity */
+#endif /* CONFIG_MTD_NAND_ATMEL_ECC_HW_HSIAO */
+
#endif
--
1.5.2.2
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] [NAND] This patch add support for HW ECC (HSIAO code) on AT91SAM9G20.
2008-08-22 2:59 [PATCH] [NAND] This patch add support for HW ECC (HSIAO code) on AT91SAM9G20 Hong Xu
@ 2008-08-22 9:47 ` Marc Pignat
2008-08-25 5:53 ` Hong Xu
0 siblings, 1 reply; 3+ messages in thread
From: Marc Pignat @ 2008-08-22 9:47 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: Hong Xu, linux, Lin, JM, linux-mtd, dwmw2, linux
Hi all!
On Friday 22 August 2008, Hong Xu wrote:
> >From ce6b68cf009a94ab12a31d7f007003173b6cad2c Mon Sep 17 00:00:00 2001
> From: Hong Xu <hong.xu@atmel.com>
> Date: Fri, 22 Aug 2008 10:29:10 +0800
> Subject: [PATCH] This patch add support for HW ECC on AT91SAM9G20.
>
> AT91SAM9G20 ECC controller is capable of 1-bits error correction
> and 2-bit random detection for every 256 bytes of data. And this
> patch chooses a working mode which can be compatible with software
> ECC.
...
>
> +#ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW_HSIAO
> +static struct nand_ecclayout nand_oob_16 = {
> + .eccbytes = 6,
> + .eccpos = {0, 1, 2, 3, 6, 7},
> + .oobfree = {
> + {.offset = 8,
> + . length = 8} }
> +};
static const?
> +
> +static struct nand_ecclayout nand_oob_64 = {
> + .eccbytes = 24,
> + .eccpos = {
> + 40, 41, 42, 43, 44, 45, 46, 47,
> + 48, 49, 50, 51, 52, 53, 54, 55,
> + 56, 57, 58, 59, 60, 61, 62, 63 },
> + .oobfree = {
> + {.offset = 2,
> + .length = 38} }
> +};
> +
> +static struct nand_ecclayout nand_oob_128 = {
> + .eccbytes = 48,
> + .eccpos = {
> + 80, 81, 82, 83, 84, 85, 86, 87,
> + 88, 89, 90, 91, 92, 93, 94, 95,
> + 96, 97, 98, 99, 100, 101, 102, 103,
> + 104, 105, 106, 107, 108, 109, 110, 111,
> + 112, 113, 114, 115, 116, 117, 118, 119,
> + 120, 121, 122, 123, 124, 125, 126, 127},
> + .oobfree = {
> + {.offset = 2,
> + .length = 38} }
> +};
...
> * Enable HW ECC : unused on most chips
> @@ -476,14 +565,22 @@ static int __init atmel_nand_probe(struct
> platform_device *pdev)
> res = -EIO;
> goto err_ecc_ioremap;
> }
> - nand_chip->ecc.mode = NAND_ECC_HW_SYNDROME;
> nand_chip->ecc.calculate = atmel_nand_calculate;
> - nand_chip->ecc.correct = atmel_nand_correct;
> nand_chip->ecc.hwctl = atmel_nand_hwctl;
> +#ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW_HSIAO
> + nand_chip->ecc.mode = NAND_ECC_HW;
> + nand_chip->ecc.correct = nand_correct_data;
no nand_chip->ecc.read_page?
> + nand_chip->ecc.bytes = 3;
> + nand_chip->ecc.prepad = 0;
> + nand_chip->ecc.postpad = 0;
> +#else
> + nand_chip->ecc.mode = NAND_ECC_HW_SYNDROME;
> + nand_chip->ecc.correct = atmel_nand_correct;
> nand_chip->ecc.read_page = atmel_nand_read_page;
> nand_chip->ecc.bytes = 4;
> nand_chip->ecc.prepad = 0;
> nand_chip->ecc.postpad = 0;
...
> diff --git a/drivers/mtd/nand/atmel_nand_ecc.h
> b/drivers/mtd/nand/atmel_nand_ecc.h
> index 1ee7f99..41e072e 100644
> --- a/drivers/mtd/nand/atmel_nand_ecc.h
> +++ b/drivers/mtd/nand/atmel_nand_ecc.h
> @@ -26,6 +26,30 @@
> #define ATMEL_ECC_ECCERR (1 << 1) /* ECC Single Bit Error */
> #define ATMEL_ECC_MULERR (1 << 2) /* Multiple Errors */
>
> +#ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW_HSIAO
> +
> +#define ATMEL_ECC_PER_256 (1 << 4)
> +
> +#define ATMEL_ECC_PR0 0x0c
> +#define ATMEL_ECC_PR1 0x10
> +#define ATMEL_ECC_SR2 0x14
> +#define ATMEL_ECC_PR2 0x18
> +#define ATMEL_ECC_PR3 0x1c
> +#define ATMEL_ECC_PR4 0x20
> +#define ATMEL_ECC_PR5 0x24
> +#define ATMEL_ECC_PR6 0x28
> +#define ATMEL_ECC_PR7 0x2c
> +#define ATMEL_ECC_PR8 0x30
> +#define ATMEL_ECC_PR9 0x34
> +#define ATMEL_ECC_PR10 0x38
> +#define ATMEL_ECC_PR11 0x3c
> +#define ATMEL_ECC_PR12 0x40
> +#define ATMEL_ECC_PR13 0x44
> +#define ATMEL_ECC_PR14 0x48
> +#define ATMEL_ECC_PR15 0x4c
> +
> +#else
> +
> #define ATMEL_ECC_PR 0x0c /* Parity register */
Never used, perhaps a future use?
...
> #endif
Best regards
Marc
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] [NAND] This patch add support for HW ECC (HSIAO code) on AT91SAM9G20.
2008-08-22 9:47 ` Marc Pignat
@ 2008-08-25 5:53 ` Hong Xu
0 siblings, 0 replies; 3+ messages in thread
From: Hong Xu @ 2008-08-25 5:53 UTC (permalink / raw)
To: Marc Pignat; +Cc: linux-mtd, dwmw2, linux-arm-kernel, linux, linux
Hi Marc,
On Fri, Aug 22, 2008 at 17:47, Marc Pignat <marc.pignat@hevs.ch> wrote:
> Hi all!
>
> On Friday 22 August 2008, Hong Xu wrote:
>> >From ce6b68cf009a94ab12a31d7f007003173b6cad2c Mon Sep 17 00:00:00 2001
>> From: Hong Xu <hong.xu@atmel.com>
>> Date: Fri, 22 Aug 2008 10:29:10 +0800
>> Subject: [PATCH] This patch add support for HW ECC on AT91SAM9G20.
>>
>> AT91SAM9G20 ECC controller is capable of 1-bits error correction
>> and 2-bit random detection for every 256 bytes of data. And this
>> patch chooses a working mode which can be compatible with software
>> ECC.
>
> ...
>
>>
>> +#ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW_HSIAO
>> +static struct nand_ecclayout nand_oob_16 = {
>> + .eccbytes = 6,
>> + .eccpos = {0, 1, 2, 3, 6, 7},
>> + .oobfree = {
>> + {.offset = 8,
>> + . length = 8} }
>> +};
>
> static const?
To be strictly speaking, it's a static const. :-)
>
>> +
>> +static struct nand_ecclayout nand_oob_64 = {
>> + .eccbytes = 24,
>> + .eccpos = {
>> + 40, 41, 42, 43, 44, 45, 46, 47,
>> + 48, 49, 50, 51, 52, 53, 54, 55,
>> + 56, 57, 58, 59, 60, 61, 62, 63 },
>> + .oobfree = {
>> + {.offset = 2,
>> + .length = 38} }
>> +};
>> +> ...
>
>> diff --git a/drivers/mtd/nand/atmel_nand_ecc.h
>> b/drivers/mtd/nand/atmel_nand_ecc.h
>> index 1ee7f99..41e072e 100644
>> --- a/drivers/mtd/nand/atmel_nand_ecc.h
>> +++ b/drivers/mtd/nand/atmel_nand_ecc.h
>> @@ -26,6 +26,30 @@
>> #define ATMEL_ECC_ECCERR (1 << 1) /* ECC Single Bit Error */
>> #define ATMEL_ECC_MULERR (1 << 2) /* Multiple Errors */
>>
>> +#ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW_HSIAO
>> +
>> +#define ATMEL_ECC_PER_256 (1 << 4)
>> +
>> +#define ATMEL_ECC_PR10 0x38
>> +#define ATMEL_ECC_PR11 0x3c
>> +#define ATMEL_ECC_PR12 0x40
>> +#define ATMEL_ECC_PR13 0x44
>> +#define ATMEL_ECC_PR14 0x48
>> +#define ATMEL_ECC_PR15 0x4c
>> +
>> +#else
>> +
>> #define ATMEL_ECC_PR 0x0c /* Parity register */
>
> Never used, perhaps a future use?
No, it's used. For some AVR chips and At91SAM926x chips, 2 bytes of 4
ECC parity data are read from it.
>
> ...
>> #endif
>
> Best regards
>
> Marc
>
>
>
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2008-08-25 5:53 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-08-22 2:59 [PATCH] [NAND] This patch add support for HW ECC (HSIAO code) on AT91SAM9G20 Hong Xu
2008-08-22 9:47 ` Marc Pignat
2008-08-25 5:53 ` Hong Xu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox