MTD: atmel_nand: Update Kconfig to support PMECC
PATCH 1/2 MTD: atmel_nand: Update Kconfig to support PMECC
--- drivers/mtd/nand/Kconfig | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-)
drivers/mtd/nand/Kconfig +17 -0
@@ -365,6 +365,23 @@ config MTD_NAND_ATMEL_ECC_HW If unsure, say Y +config MTD_NAND_ATMEL_PMECC_HW + bool "Programmable Hardware ECC (BCH code)" + depends on ARCH_AT91SAM9X5 || ARCH_AT91SAM9N12 + help + Use Programmable Hardware ECC (PMECC) controller. + + The PMECC Controller is a programmable binary BCH (Bose, Chaudhuri + and Hocquenghem) encoder/decoder. This controller can be used to + generate redundancy information for both SLC and MLC NAND Flash + devices. + + NB : hardware and software ECC schemes are incompatible. + If you switch from one to another, you'll have to erase your + mtd partition. + + If unsure, say Y + config MTD_NAND_ATMEL_ECC_SOFT bool "Software ECC" help
Signature · 1 line
1.7.3.3
PATCH 2/2 MTD: atmel_nand: Update driver code to support Programmable HW ECC controller
The Programmable Hardware ECC (PMECC) controller is a programmable binary BCH(Bose, Chaudhuri and Hocquenghem) encoder and decoder. This controller can be used to support both SLC and MLC NAND Flash devices. It supports to generate ECC to correct 2, 4, 8, 12 or 24 bits of error per sector of data.
To use this driver, the user needs to pass in the correction capability and the sector size.
This driver has been tested on AT91SAM9X5-EK and AT91SAM9N12-EK with JFFS2,
YAFFS2 ,UBIFS and mtd-utils.
--- drivers/mtd/nand/atmel_nand.c | 235 ++++++++--- drivers/mtd/nand/atmel_nand_ecc.h | 78 ++++ drivers/mtd/nand/atmel_nand_pmecc.c | 745 +++++++++++++++++++++++++++++++++++ 3 files changed, 993 insertions(+), 65 deletions(-) create mode 100644 drivers/mtd/nand/atmel_nand_pmecc.c
drivers/mtd/nand/atmel_nand.c +170 -65
@@ -15,6 +15,8 @@* (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c) * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas * +* Add Programmable Hardware ECC support for various AT91 SoC +* (C) Copyright 2011 ATMEL, Hong Xu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as
@@ -38,7 +40,9 @@#include <mach/board.h> #include <mach/cpu.h> -#ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW +#if defined(CONFIG_MTD_NAND_ATMEL_ECC_HW ) +#define hard_ecc 1 +#elif defined(CONFIG_MTD_NAND_ATMEL_PMECC_HW )#define hard_ecc 1#else #define hard_ecc 0
@@ -53,6 +57,8 @@static int use_dma =1 ;module_param (use_dma, int,0 ); +#define NB_ERROR_MAX 25 +static int on_flash_bbt =0 ;module_param (on_flash_bbt, int,0 );
@@ -62,8 +68,38 @@ module_param(on_flash_bbt, int, 0);#define ecc_writel (add, reg, value) \ __raw_writel((value), add + ATMEL_ECC_##reg) -#include "atmel_nand_ecc.h" /* Hardware ECC registers */ +/* Register access macros for PMECC */ +#define pmecc_readl (addr, reg) \ + __raw_readl((addr) + ATMEL_PMECC_##reg) + +#define pmecc_writel (addr, reg, value) \ + __raw_writel((value), (addr) + ATMEL_PMECC_##reg) + +#define pmecc_readb_ecc (addr, sector, n) \ + __raw_readb((addr) + ATMEL_PMECC_ECCx + ((sector) * 0x40) + (n)) + +#define pmecc_readl_rem (addr, sector, n) \ + __raw_readl((addr) + ATMEL_PMECC_REMx + ((sector) * 0x40) + ((n) * 4)) + +#define pmerrloc_readl (addr, reg) \ + __raw_readl((addr) + ATMEL_PMERRLOC_##reg) + +#define pmerrloc_writel (addr, reg, value) \ + __raw_writel((value), (addr) + ATMEL_PMERRLOC_##reg) + +#define pmerrloc_writel_sigma (addr, n, value) \ + __raw_writel((value), (addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4)) + +#define pmerrloc_readl_sigma (addr, n) \ + __raw_readl((addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4)) + +#define pmerrloc_readl_el (addr, n) \ + __raw_readl((addr) + ATMEL_PMERRLOC_ELx + ((n) * 4)) +/* Include Hardware ECC registers */ +#include "atmel_nand_ecc.h" + +#if defined(CONFIG_MTD_NAND_ATMEL_ECC_HW )/* oob layout for large page size * bad block info is on bytes 0 and 1 * the bytes have to be consecutives to avoid
@@ -89,6 +125,7 @@ static struct nand_ecclayout atmel_oobinfo_small = { {6, 10} }, }; +#endif struct atmel_nand_host {struct nand_chip nand_chip;
@@ -101,11 +138,42 @@ struct atmel_nand_host {struct completion comp;struct dma_chan *dma_chan; + +#if defined(CONFIG_MTD_NAND_ATMEL_PMECC_HW ) +void __iomem *pmerrloc_base; +void __iomem *rom_base; +/* defines the error correcting capability */ +int tt; +/* The number of ecc bytes for one sector */ +int ecc_bytes_per_sector; +/* degree of the remainders, GF(2**mm) */ +int mm; +/* length of codeword, nn=2**mm -1 */ +int nn; +int sector_number; + +/* lookup table for alpha_to and index_of */ +int16_t *alpha_to; +int16_t *index_of; + +int16_t partial_syn[100 ]; +int16_t si[100 ]; +/* Sigma table */ +int16_t smu[NB_ERROR_MAX +2 ][2 *NB_ERROR_MAX +1 ]; +/* polynomal order */ +int16_t lmu[NB_ERROR_MAX +1 ]; +uint8_t ecc_table[42 *8 ]; +#endif }; +#if defined(CONFIG_MTD_NAND_ATMEL_PMECC_HW ) +#include "atmel_nand_pmecc.c" +#endif +static int cpu_has_dma (void ) { -return cpu_is_at91sam9rl () ||cpu_is_at91sam9g45 (); +return cpu_is_at91sam9rl () ||cpu_is_at91sam9g45 () + ||cpu_is_at91sam9x5 () ||cpu_is_at91sam9n12 (); }/*
@@ -228,7 +296,7 @@ err_dma:dma_unmap_single (dma_dev->dev, phys_addr, len, dir); err_buf:if (err !=0 ) -dev_warn (host->dev,"Fall back to CPU I/O\n" ); +dev_dbg (host->dev,"Fall back to CPU I/O\n" );return err; }
@@ -258,6 +326,7 @@ static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)memcpy_toio (chip->IO_ADDR_W, buf, len); } +#if defined(CONFIG_MTD_NAND_ATMEL_ECC_HW )/* * Calculate HW ECC *
@@ -444,6 +513,84 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) } } +static int __init atmel_nand_init_params (struct platform_device *pdev, +struct atmel_nand_host *host) +{ +struct resource *regs; +struct nand_chip *nand_chip; +struct mtd_info *mtd; + + nand_chip = &host->nand_chip; + mtd = &host->mtd; + + regs =platform_get_resource (pdev,IORESOURCE_MEM ,1 ); +if (!regs && hard_ecc) { +dev_err (host->dev,"atmel_nand: can't get I/O resource " +"regs\nFalling back on software ECC\n" ); + } + + nand_chip->ecc.mode =NAND_ECC_SOFT ;/* enable ECC */ +if (no_ecc) + nand_chip->ecc.mode =NAND_ECC_NONE ; +if (hard_ecc && regs) { + host->ecc =ioremap (regs->start,resource_size (regs)); +if (host->ecc ==NULL ) { +printk (KERN_ERR "atmel_nand: ioremap failed\n" ); + goto err_ecc_ioremap; + } + + nand_chip->ecc.mode =NAND_ECC_HW ; + nand_chip->ecc.calculate = atmel_nand_calculate; + nand_chip->ecc.correct = atmel_nand_correct; + nand_chip->ecc.hwctl = atmel_nand_hwctl; + nand_chip->ecc.read_page = atmel_nand_read_page; + nand_chip->ecc.bytes =4 ; + } + +if (nand_chip->ecc.mode ==NAND_ECC_HW ) { +/* ECC is calculated for the whole page (1 step) */ + nand_chip->ecc.size = mtd->writesize; + +/* set ECC page size and oob layout */ +switch (mtd->writesize) { +case 512 : + nand_chip->ecc.layout = &atmel_oobinfo_small; +ecc_writel (host->ecc,MR ,ATMEL_ECC_PAGESIZE_528 ); +break ; +case 1024 : + nand_chip->ecc.layout = &atmel_oobinfo_large; +ecc_writel (host->ecc,MR ,ATMEL_ECC_PAGESIZE_1056 ); +break ; +case 2048 : + nand_chip->ecc.layout = &atmel_oobinfo_large; +ecc_writel (host->ecc,MR ,ATMEL_ECC_PAGESIZE_2112 ); +break ; +case 4096 : + nand_chip->ecc.layout = &atmel_oobinfo_large; +ecc_writel (host->ecc,MR ,ATMEL_ECC_PAGESIZE_4224 ); +break ; +default : +/* 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 ; + } + } + +return 0 ; + +err_ecc_ioremap: +return -EIO ; +} +#endif +/* * Probe for the NAND device. */
@@ -452,9 +599,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)struct atmel_nand_host *host;struct mtd_info *mtd;struct nand_chip *nand_chip; -struct resource *regs;struct resource *mem; -int res; +int res =0 ; mem =platform_get_resource (pdev,IORESOURCE_MEM ,0 );if (!mem) {
@@ -495,29 +641,9 @@ static int __init atmel_nand_probe(struct platform_device *pdev)if (host->board->rdy_pin) nand_chip->dev_ready = atmel_nand_device_ready; - regs =platform_get_resource (pdev,IORESOURCE_MEM ,1 ); -if (!regs && hard_ecc) { -printk (KERN_ERR "atmel_nand: can't get I/O resource " -"regs\nFalling back on software ECC\n" ); - } - nand_chip->ecc.mode =NAND_ECC_SOFT ;/* enable ECC */ if (no_ecc) nand_chip->ecc.mode =NAND_ECC_NONE ; -if (hard_ecc && regs) { - host->ecc =ioremap (regs->start,resource_size (regs)); -if (host->ecc ==NULL ) { -printk (KERN_ERR "atmel_nand: ioremap failed\n" ); - res = -EIO ; - goto err_ecc_ioremap; - } - nand_chip->ecc.mode =NAND_ECC_HW ; - nand_chip->ecc.calculate = atmel_nand_calculate; - nand_chip->ecc.correct = atmel_nand_correct; - nand_chip->ecc.hwctl = atmel_nand_hwctl; - nand_chip->ecc.read_page = atmel_nand_read_page; - nand_chip->ecc.bytes =4 ; - } nand_chip->chip_delay =20 ;/* 20us command delay time */
@@ -569,42 +695,13 @@ static int __init atmel_nand_probe(struct platform_device *pdev) goto err_scan_ident; } -if (nand_chip->ecc.mode ==NAND_ECC_HW ) { -/* ECC is calculated for the whole page (1 step) */ - nand_chip->ecc.size = mtd->writesize; - -/* set ECC page size and oob layout */ -switch (mtd->writesize) { -case 512 : - nand_chip->ecc.layout = &atmel_oobinfo_small; -ecc_writel (host->ecc,MR ,ATMEL_ECC_PAGESIZE_528 ); -break ; -case 1024 : - nand_chip->ecc.layout = &atmel_oobinfo_large; -ecc_writel (host->ecc,MR ,ATMEL_ECC_PAGESIZE_1056 ); -break ; -case 2048 : - nand_chip->ecc.layout = &atmel_oobinfo_large; -ecc_writel (host->ecc,MR ,ATMEL_ECC_PAGESIZE_2112 ); -break ; -case 4096 : - nand_chip->ecc.layout = &atmel_oobinfo_large; -ecc_writel (host->ecc,MR ,ATMEL_ECC_PAGESIZE_4224 ); -break ; -default : -/* 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 defined(CONFIG_MTD_NAND_ATMEL_ECC_HW ) + res =atmel_nand_init_params (pdev, host); +#elif defined(CONFIG_MTD_NAND_ATMEL_PMECC_HW ) + res =atmel_pmecc_init_params (pdev, host); +#endif +if (res !=0 ) + goto err;/* second phase scan */ if (nand_scan_tail (mtd)) {
@@ -618,6 +715,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev)if (!res)return res; +err: err_scan_tail: err_scan_ident: err_no_card:
@@ -625,9 +723,6 @@ err_no_card:platform_set_drvdata (pdev,NULL );if (host->dma_chan)dma_release_channel (host->dma_chan); -if (host->ecc) -iounmap (host->ecc); -err_ecc_ioremap:iounmap (host->io_base); err_nand_ioremap:kfree (host);
@@ -646,6 +741,16 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)atmel_nand_disable (host); +#if defined(CONFIG_MTD_NAND_ATMEL_PMECC_HW ) +if (cpu_has_pmecc ()) +pmecc_writel (host->ecc,CTRL ,PMECC_CTRL_DISABLE ); +if (host->pmerrloc_base) { +pmerrloc_writel (host->pmerrloc_base,ELDIS ,PMERRLOC_DISABLE ); +iounmap (host->pmerrloc_base); + } +if (host->rom_base) +iounmap (host->rom_base); +#endif if (host->ecc)iounmap (host->ecc);
drivers/mtd/nand/atmel_nand_ecc.h +78 -0
82 lines not on this page · raw message
drivers/mtd/nand/atmel_nand_pmecc.c new file +745 -0
745 lines not on this page · raw message
Signature · 1 line
1.7.3.3
PATCH 1/2 MTD: atmel_nand: Update Kconfig to support PMECC
In reply to Hong XuQuoted 4 lines · Hong Xu
drivers/mtd/nand/Kconfig +4
-0+config MTD_NAND_ATMEL_PMECC_HW + bool "Programmable Hardware ECC (BCH code)" + depends on ARCH_AT91SAM9X5 || ARCH_AT91SAM9N12 + help
drivers/mtd/nand/Kconfig config MTD_NAND_ATMEL_ECC_HW · line 381
Quoted 10 lines
drivers/mtd/nand/Kconfig +10
-0+ Use Programmable Hardware ECC (PMECC) controller. + + The PMECC Controller is a programmable binary BCH (Bose, Chaudhuri + and Hocquenghem) encoder/decoder. This controller can be used to + generate redundancy information for both SLC and MLC NAND Flash + devices. + + NB : hardware and software ECC schemes are incompatible. + If you switch from one to another, you'll have to erase your + mtd partition.
Does it have to be a config option, can we avoid adding config options uless we really have to?
PATCH 1/2 MTD: atmel_nand: Update Kconfig to support PMECC
Hi David,
Quoted 10 lines · Artem Bityutskiy
Quoted 9 lines · Hong Xu
drivers/mtd/nand/Kconfig +9
-0+config MTD_NAND_ATMEL_PMECC_HW + bool "Programmable Hardware ECC (BCH code)" + depends on ARCH_AT91SAM9X5 || ARCH_AT91SAM9N12 + help + Use Programmable Hardware ECC (PMECC) controller. + + The PMECC Controller is a programmable binary BCH (Bose, Chaudhuri + and Hocquenghem) encoder/decoder. This controller can be used to + generate redundancy information for both SLC and MLC NAND Flash
Quoted 8 lines
Quoted 5 lines
+ devices.
+
+ NB : hardware and software ECC schemes are incompatible.
+ If you switch from one to another, you'll have to erase your
+ mtd partition.Does it have to be a config option, can we avoid adding config options uless we really have to?
Agree. The original MTD_NAND_ATMEL_ECC_HW can be reused. In driver, if the SoC has PMECC, it'll be used.
Thanks.
BR,
Eric
PATCH 1/2 MTD: atmel_nand: Update Kconfig to support PMECC
Hi Artem,
Quoted 1 line · Hong Xu
Hi David,
Sorry, this shall be Artem :-)
Quoted 21 lines · Artem Bityutskiy
Quoted 10 lines · Hong Xu
Quoted 9 lines
drivers/mtd/nand/Kconfig +9
-0+config MTD_NAND_ATMEL_PMECC_HW + bool "Programmable Hardware ECC (BCH code)" + depends on ARCH_AT91SAM9X5 || ARCH_AT91SAM9N12 + help + Use Programmable Hardware ECC (PMECC) controller. + + The PMECC Controller is a programmable binary BCH (Bose, Chaudhuri + and Hocquenghem) encoder/decoder. This controller can be used to + generate redundancy information for both SLC and MLC NAND FlashQuoted 8 lines
Quoted 5 lines
+ devices.
+
+ NB : hardware and software ECC schemes are incompatible.
+ If you switch from one to another, you'll have to erase your
+ mtd partition.Does it have to be a config option, can we avoid adding config options uless we really have to?
Quoted 2 lines
Agree. The original
MTD_NAND_ATMEL_ECC_HWcan be reused. In driver, if the SoC has PMECC, it'll be used.
PMECC is not compatible with the old HW ECC engine. It currently needs around 700 lines of C code to make it work. If we do not have a way to exclude the PMECC related code at compiling time, my quick testing shows that the driver code size will be doubled if some old SoCs do not support it. This sounds not good. What's your opinion?
Thanks.
BR,
Eric
Quoted 9 lines
Thanks.
BR,
Eric______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
PATCH 1/2 MTD: atmel_nand: Update Kconfig to support PMECC
Quoted 5 lines · Hong Xu
PMECC is not compatible with the old HW ECC engine. It currently needs around 700 lines of C code to make it work. If we do not have a way to exclude the PMECC related code at compiling time, my quick testing shows that the driver code size will be doubled if some old SoCs do not support it. This sounds not good. What's your opinion?
Well, depends on its size. Say, if it is about 8KiB - then doubling it is probably not a big deal.
Signature · 9 lines
Best Regards, Artem Bityutskiy -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 836 bytes Desc: This is a digitally signed message part URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20111217/14d24dc3/attachment.sig>
PATCH 1/2 MTD: atmel_nand: Update Kconfig to support PMECC
Quoted 10 lines · Artem Bityutskiy
Quoted 5 lines · Hong Xu
PMECC is not compatible with the old HW ECC engine. It currently needs around 700 lines of C code to make it work. If we do not have a way to exclude the PMECC related code at compiling time, my quick testing shows that the driver code size will be doubled if some old SoCs do not support it. This sounds not good. What's your opinion?
Well, depends on its size. Say, if it is about 8KiB - then doubling it is probably not a big deal.
Hmm.., not that big, less than 8KB. OK, I'll send out another patch removing the stuff modified in Kconfig.
Thanks!
BR,
Eric