* [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver
@ 2012-06-25 10:07 Josh Wu
2012-06-25 10:07 ` [PATCH v11 1/4] MTD: at91: extract hw ecc initialization to one function Josh Wu
` (6 more replies)
0 siblings, 7 replies; 22+ messages in thread
From: Josh Wu @ 2012-06-25 10:07 UTC (permalink / raw)
To: linux-arm-kernel
Those patches is based on v3.5-rc4
Changes since v10,
add one more patch in this patch set, which add 'int' return value for write_page()/write_page_raw() functions in structure of nand_ecc_ctrl.
Instead of calling BUG(), atmel_nand_pmecc_write_page() will return -EIO when time out to read the pmecc status register.
increase the time-out duration to 100ms, which has more toleration.
add oob_required argument for pmecc read/write functions to align with v3.5-rc4.
Changes since v9,
use jiffies for timeout test to read PMECC status register.
modified according to J.C and Artem's comments.
add rom lookup table offset as DT variables.
refine coding style and error handling.
Changes since v8,
use _relaxed read/write in most place. use writel in operations of Control Register since it needs memory barrier.
allocate the data for PMECC computation.
add pmecc prefix for related variable/functions.
modify code according to J.C's suggestion.
Josh Wu (4):
MTD: at91: extract hw ecc initialization to one function
MTD: at91: add dt parameters for Atmel PMECC
MTD: nand: add return value for write_page()/write_page_raw() functions in structure of nand_ecc_ctrl.
MTD: at91: atmel_nand: Update driver to support Programmable Multibit ECC controller
.../devicetree/bindings/mtd/atmel-nand.txt | 9 +
drivers/mtd/nand/atmel_nand.c | 914 ++++++++++++++++++--
drivers/mtd/nand/atmel_nand_ecc.h | 114 ++-
drivers/mtd/nand/bcm_umi_bch.c | 6 +-
drivers/mtd/nand/bf5xx_nand.c | 6 +-
drivers/mtd/nand/cafe_nand.c | 11 +-
drivers/mtd/nand/denali.c | 12 +-
drivers/mtd/nand/docg4.c | 8 +-
drivers/mtd/nand/fsl_elbc_nand.c | 4 +-
drivers/mtd/nand/fsl_ifc_nand.c | 4 +-
drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 6 +-
drivers/mtd/nand/nand_base.c | 27 +-
drivers/mtd/nand/pxa3xx_nand.c | 4 +-
drivers/mtd/nand/sh_flctl.c | 4 +-
include/linux/mtd/nand.h | 4 +-
15 files changed, 1039 insertions(+), 94 deletions(-)
--
1.7.9.5
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v11 1/4] MTD: at91: extract hw ecc initialization to one function
2012-06-25 10:07 [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver Josh Wu
@ 2012-06-25 10:07 ` Josh Wu
2012-06-27 9:26 ` Artem Bityutskiy
2012-06-25 10:07 ` [PATCH v11 2/4] MTD: at91: add dt parameters for Atmel PMECC Josh Wu
` (5 subsequent siblings)
6 siblings, 1 reply; 22+ messages in thread
From: Josh Wu @ 2012-06-25 10:07 UTC (permalink / raw)
To: linux-arm-kernel
This patch moves hw ecc initialization code to one function.
Signed-off-by: Hong Xu <hong.xu@atmel.com>
Signed-off-by: Josh Wu <josh.wu@atmel.com>
---
drivers/mtd/nand/atmel_nand.c | 127 +++++++++++++++++++++--------------------
1 file changed, 66 insertions(+), 61 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index 97ac671..7a41a04 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -527,6 +527,66 @@ static int __devinit atmel_of_init_port(struct atmel_nand_host *host,
}
#endif
+static int __init atmel_hw_nand_init_params(struct platform_device *pdev,
+ struct atmel_nand_host *host)
+{
+ struct mtd_info *mtd = &host->mtd;
+ struct nand_chip *nand_chip = &host->nand_chip;
+ struct resource *regs;
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!regs) {
+ dev_err(host->dev,
+ "Can't get I/O resource regs, use software ECC\n");
+ nand_chip->ecc.mode = NAND_ECC_SOFT;
+ return 0;
+ }
+
+ host->ecc = ioremap(regs->start, resource_size(regs));
+ if (host->ecc == NULL) {
+ dev_err(host->dev, "ioremap failed\n");
+ return -EIO;
+ }
+
+ /* 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;
+ return 0;
+ }
+
+ /* set up for HW ECC */
+ 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->ecc.strength = 1;
+
+ return 0;
+}
+
/*
* Probe for the NAND device.
*/
@@ -535,7 +595,6 @@ 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;
struct mtd_part_parser_data ppdata = {};
int res;
@@ -587,29 +646,6 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->dev_ready = atmel_nand_device_ready;
nand_chip->ecc.mode = host->board.ecc_mode;
-
- regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!regs && nand_chip->ecc.mode == NAND_ECC_HW) {
- 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;
- }
-
- if (nand_chip->ecc.mode == NAND_ECC_HW) {
- 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.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->ecc.strength = 1;
- }
-
nand_chip->chip_delay = 20; /* 20us command delay time */
if (host->board.bus_width_16) /* 16-bit bus width */
@@ -661,40 +697,9 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
}
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;
- }
+ res = atmel_hw_nand_init_params(pdev, host);
+ if (res != 0)
+ goto err_hw_ecc;
}
/* second phase scan */
@@ -711,15 +716,15 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
return res;
err_scan_tail:
+ if (host->ecc)
+ iounmap(host->ecc);
+err_hw_ecc:
err_scan_ident:
err_no_card:
atmel_nand_disable(host);
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);
--
1.7.9.5
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v11 2/4] MTD: at91: add dt parameters for Atmel PMECC
2012-06-25 10:07 [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver Josh Wu
2012-06-25 10:07 ` [PATCH v11 1/4] MTD: at91: extract hw ecc initialization to one function Josh Wu
@ 2012-06-25 10:07 ` Josh Wu
2012-06-25 10:35 ` Richard Genoud
2012-06-25 10:07 ` [PATCH v11 3/4] MTD: nand: add return value for write_page()/write_page_raw() functions in structure of nand_ecc_ctrl Josh Wu
` (4 subsequent siblings)
6 siblings, 1 reply; 22+ messages in thread
From: Josh Wu @ 2012-06-25 10:07 UTC (permalink / raw)
To: linux-arm-kernel
Add DT support for PMECC parameters.
Signed-off-by: Hong Xu <hong.xu@atmel.com>
Signed-off-by: Josh Wu <josh.wu@atmel.com>
---
.../devicetree/bindings/mtd/atmel-nand.txt | 9 ++++
drivers/mtd/nand/atmel_nand.c | 52 +++++++++++++++++++-
2 files changed, 60 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/mtd/atmel-nand.txt b/Documentation/devicetree/bindings/mtd/atmel-nand.txt
index a200695..ed6927a 100644
--- a/Documentation/devicetree/bindings/mtd/atmel-nand.txt
+++ b/Documentation/devicetree/bindings/mtd/atmel-nand.txt
@@ -16,6 +16,15 @@ Optional properties:
- nand-ecc-mode : String, operation mode of the NAND ecc mode, soft by default.
Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first",
"soft_bch".
+- atmel,has-pmecc : boolean to enable Programmable Multibit ECC hardware.
+ Only supported by at91sam9x5 or later sam9 product.
+- atmel,pmecc-cap : error correct capability for Programmable Multibit ECC
+ Controller. Supported values are: 2, 4, 8, 12, 24.
+- atmel,pmecc-sector-size : sector size for ECC computation. Supported values
+ are: 512, 1024.
+- atmel,pmecc-lookup-table-offset : includes two offsets of lookup table in ROM
+ for different sector size. First one is for sector size 512, the next is for
+ sector size 1024.
- nand-bus-width : 8 or 16 bus width if not present 8
- nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index 7a41a04..b97ad9f 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -93,6 +93,11 @@ struct atmel_nand_host {
struct completion comp;
struct dma_chan *dma_chan;
+
+ bool has_pmecc;
+ u8 pmecc_corr_cap;
+ u16 pmecc_sector_size;
+ u32 pmecc_lookup_table_offset;
};
static int cpu_has_dma(void)
@@ -481,7 +486,8 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
static int __devinit atmel_of_init_port(struct atmel_nand_host *host,
struct device_node *np)
{
- u32 val;
+ u32 val, table_offset;
+ u32 offset[2];
int ecc_mode;
struct atmel_nand_data *board = &host->board;
enum of_gpio_flags flags;
@@ -517,6 +523,50 @@ static int __devinit atmel_of_init_port(struct atmel_nand_host *host,
board->enable_pin = of_get_gpio(np, 1);
board->det_pin = of_get_gpio(np, 2);
+ host->has_pmecc = of_property_read_bool(np, "atmel,has-pmecc");
+
+ if (!(board->ecc_mode == NAND_ECC_HW) || !host->has_pmecc)
+ return 0; /* Not using PMECC */
+
+ /* use PMECC, get correction capability, sector size and lookup
+ * table offset.
+ */
+ if (of_property_read_u32(np, "atmel,pmecc-cap", &val) != 0) {
+ dev_err(host->dev, "Cannot decide PMECC Capability\n");
+ return -EINVAL;
+ } else if ((val != 2) && (val != 4) && (val != 8) && (val != 12) &&
+ (val != 24)) {
+ dev_err(host->dev,
+ "Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n",
+ val);
+ return -EINVAL;
+ }
+ host->pmecc_corr_cap = (u8)val;
+
+ if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) != 0) {
+ dev_err(host->dev, "Cannot decide PMECC Sector Size\n");
+ return -EINVAL;
+ } else if ((val != 512) && (val != 1024)) {
+ dev_err(host->dev,
+ "Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n",
+ val);
+ return -EINVAL;
+ }
+ host->pmecc_sector_size = (u16)val;
+
+ if (of_property_read_u32_array(np, "atmel,pmecc-lookup-table-offset",
+ offset, 2) != 0) {
+ dev_err(host->dev, "Cannot get PMECC lookup table offset\n");
+ return -EINVAL;
+ }
+ table_offset = host->pmecc_sector_size == 512 ? offset[0] : offset[1];
+
+ if (!table_offset) {
+ dev_err(host->dev, "Invalid PMECC lookup table offset\n");
+ return -EINVAL;
+ }
+ host->pmecc_lookup_table_offset = table_offset;
+
return 0;
}
#else
--
1.7.9.5
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v11 3/4] MTD: nand: add return value for write_page()/write_page_raw() functions in structure of nand_ecc_ctrl.
2012-06-25 10:07 [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver Josh Wu
2012-06-25 10:07 ` [PATCH v11 1/4] MTD: at91: extract hw ecc initialization to one function Josh Wu
2012-06-25 10:07 ` [PATCH v11 2/4] MTD: at91: add dt parameters for Atmel PMECC Josh Wu
@ 2012-06-25 10:07 ` Josh Wu
2012-06-27 9:26 ` Artem Bityutskiy
2012-06-25 10:07 ` [PATCH v11 4/4] MTD: at91: atmel_nand: Update driver to support Programmable Multibit ECC controller Josh Wu
` (3 subsequent siblings)
6 siblings, 1 reply; 22+ messages in thread
From: Josh Wu @ 2012-06-25 10:07 UTC (permalink / raw)
To: linux-arm-kernel
There is an implemention of hardware ECC write page function which may return an
error indication.
For instance, using Atmel HW PMECC to write one page into a nand flash, the hardware
engine will compute the BCH ecc code for this page. so we need read a the
status register to theck whether the ecc code is generated.
But we cannot assume the status register always can be ready, for example,
incorrect hardware configuration or hardware issue, in such case we need
write_page() to return a error code.
Since the definition of 'write_page' function in struct nand_ecc_ctrl is 'void'.
So this patch will:
1. add return 'int' value for 'write_page' function.
2. to be consitent, add return 'int' value for 'write_page_raw' fuctions too.
3. add code to test the return value, and if negative, indicate an
error happend when write page with ECC.
4. fix the compile warning in all impacted nand flash driver.
Note: I couldn't compile-test all of these easily, as some had ARCH dependencies.
Signed-off-by: Josh Wu <josh.wu@atmel.com>
---
changes:
remove code to print error message base on suggestion of Brian Norris.
drivers/mtd/nand/bcm_umi_bch.c | 6 ++++--
drivers/mtd/nand/bf5xx_nand.c | 6 ++++--
drivers/mtd/nand/cafe_nand.c | 11 ++++++++---
drivers/mtd/nand/denali.c | 12 +++++++-----
drivers/mtd/nand/docg4.c | 8 +++++---
drivers/mtd/nand/fsl_elbc_nand.c | 4 +++-
drivers/mtd/nand/fsl_ifc_nand.c | 4 +++-
drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 6 ++++--
drivers/mtd/nand/nand_base.c | 27 +++++++++++++++++++--------
drivers/mtd/nand/pxa3xx_nand.c | 4 +++-
drivers/mtd/nand/sh_flctl.c | 4 +++-
include/linux/mtd/nand.h | 4 ++--
12 files changed, 65 insertions(+), 31 deletions(-)
diff --git a/drivers/mtd/nand/bcm_umi_bch.c b/drivers/mtd/nand/bcm_umi_bch.c
index 5914bb3..c8799a0 100644
--- a/drivers/mtd/nand/bcm_umi_bch.c
+++ b/drivers/mtd/nand/bcm_umi_bch.c
@@ -23,7 +23,7 @@
/* ---- Private Function Prototypes -------------------------------------- */
static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, uint8_t *buf, int oob_required, int page);
-static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
+static int bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf, int oob_required);
/* ---- Private Variables ------------------------------------------------ */
@@ -194,7 +194,7 @@ static int bcm_umi_bch_read_page_hwecc(struct mtd_info *mtd,
* @oob_required: must write chip->oob_poi to OOB
*
***************************************************************************/
-static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
+static int bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf, int oob_required)
{
int sectorIdx = 0;
@@ -214,4 +214,6 @@ static void bcm_umi_bch_write_page_hwecc(struct mtd_info *mtd,
}
bcm_umi_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
}
diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c
index 3f1c185..ab0caa7 100644
--- a/drivers/mtd/nand/bf5xx_nand.c
+++ b/drivers/mtd/nand/bf5xx_nand.c
@@ -566,11 +566,13 @@ static int bf5xx_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip
return 0;
}
-static void bf5xx_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int oob_required)
+static int bf5xx_nand_write_page_raw(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf, int oob_required)
{
bf5xx_nand_write_buf(mtd, buf, mtd->writesize);
bf5xx_nand_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
}
/*
diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c
index 41371ba..738c970 100644
--- a/drivers/mtd/nand/cafe_nand.c
+++ b/drivers/mtd/nand/cafe_nand.c
@@ -520,7 +520,7 @@ static struct nand_bbt_descr cafe_bbt_mirror_descr_512 = {
};
-static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
+static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
@@ -531,6 +531,8 @@ static void cafe_nand_write_page_lowlevel(struct mtd_info *mtd,
/* Set up ECC autogeneration */
cafe->ctl2 |= (1<<30);
+
+ return 0;
}
static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
@@ -542,9 +544,12 @@ static int cafe_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
if (unlikely(raw))
- chip->ecc.write_page_raw(mtd, chip, buf, oob_required);
+ status = chip->ecc.write_page_raw(mtd, chip, buf, oob_required);
else
- chip->ecc.write_page(mtd, chip, buf, oob_required);
+ status = chip->ecc.write_page(mtd, chip, buf, oob_required);
+
+ if (status < 0)
+ return status;
/*
* Cached progamming disabled for now, Not sure if its worth the
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index 0650aaf..e706a23 100644
--- a/drivers/mtd/nand/denali.c
+++ b/drivers/mtd/nand/denali.c
@@ -1028,7 +1028,7 @@ static void denali_setup_dma(struct denali_nand_info *denali, int op)
/* writes a page. user specifies type, and this function handles the
* configuration details. */
-static void write_page(struct mtd_info *mtd, struct nand_chip *chip,
+static int write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, bool raw_xfer)
{
struct denali_nand_info *denali = mtd_to_denali(mtd);
@@ -1078,6 +1078,8 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *chip,
denali_enable_dma(denali, false);
dma_sync_single_for_cpu(denali->dev, addr, size, DMA_TO_DEVICE);
+
+ return 0;
}
/* NAND core entry points */
@@ -1086,24 +1088,24 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *chip,
* writing a page with ECC or without is similar, all the work is done
* by write_page above.
* */
-static void denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+static int denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
/* for regular page writes, we let HW handle all the ECC
* data written to the device. */
- write_page(mtd, chip, buf, false);
+ return write_page(mtd, chip, buf, false);
}
/* This is the callback that the NAND core calls to write a page without ECC.
* raw access is similar to ECC page writes, so all the work is done in the
* write_page() function above.
*/
-static void denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
/* for raw page writes, we want to disable ECC and simply write
whatever data is in the buffer. */
- write_page(mtd, chip, buf, true);
+ return write_page(mtd, chip, buf, true);
}
static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
diff --git a/drivers/mtd/nand/docg4.c b/drivers/mtd/nand/docg4.c
index a225e49..0f2ffd7 100644
--- a/drivers/mtd/nand/docg4.c
+++ b/drivers/mtd/nand/docg4.c
@@ -898,7 +898,7 @@ static void docg4_erase_block(struct mtd_info *mtd, int page)
write_nop(docptr);
}
-static void write_page(struct mtd_info *mtd, struct nand_chip *nand,
+static int write_page(struct mtd_info *mtd, struct nand_chip *nand,
const uint8_t *buf, bool use_ecc)
{
struct docg4_priv *doc = nand->priv;
@@ -950,15 +950,17 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *nand,
write_nop(docptr);
writew(0, docptr + DOC_DATAEND);
write_nop(docptr);
+
+ return 0;
}
-static void docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand,
+static int docg4_write_page_raw(struct mtd_info *mtd, struct nand_chip *nand,
const uint8_t *buf, int oob_required)
{
return write_page(mtd, nand, buf, false);
}
-static void docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand,
+static int docg4_write_page(struct mtd_info *mtd, struct nand_chip *nand,
const uint8_t *buf, int oob_required)
{
return write_page(mtd, nand, buf, true);
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c
index 7842938..35d731d 100644
--- a/drivers/mtd/nand/fsl_elbc_nand.c
+++ b/drivers/mtd/nand/fsl_elbc_nand.c
@@ -766,11 +766,13 @@ static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
/* ECC will be calculated automatically, and errors will be detected in
* waitfunc.
*/
-static void fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
fsl_elbc_write_buf(mtd, buf, mtd->writesize);
fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
}
static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
diff --git a/drivers/mtd/nand/fsl_ifc_nand.c b/drivers/mtd/nand/fsl_ifc_nand.c
index 9602c1b..47ba534 100644
--- a/drivers/mtd/nand/fsl_ifc_nand.c
+++ b/drivers/mtd/nand/fsl_ifc_nand.c
@@ -721,11 +721,13 @@ static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
/* ECC will be calculated automatically, and errors will be detected in
* waitfunc.
*/
-static void fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+static int fsl_ifc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
fsl_ifc_write_buf(mtd, buf, mtd->writesize);
fsl_ifc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
}
static int fsl_ifc_chip_init_tail(struct mtd_info *mtd)
diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
index a05b7b4..3bda330 100644
--- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
+++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
@@ -930,7 +930,7 @@ exit_nfc:
return ret;
}
-static void gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
struct gpmi_nand_data *this = chip->priv;
@@ -972,7 +972,7 @@ static void gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip,
&payload_virt, &payload_phys);
if (ret) {
pr_err("Inadequate payload DMA buffer\n");
- return;
+ return 0;
}
ret = send_page_prepare(this,
@@ -1002,6 +1002,8 @@ exit_auxiliary:
nfc_geo->payload_size,
payload_virt, payload_phys);
}
+
+ return 0;
}
/*
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index d47586c..b461ff6 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1927,12 +1927,14 @@ out:
*
* Not for syndrome calculating ECC controllers, which use a special oob layout.
*/
-static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+static int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
chip->write_buf(mtd, buf, mtd->writesize);
if (oob_required)
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
}
/**
@@ -1944,7 +1946,7 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
*
* We need a special oob layout and handling even when ECC isn't checked.
*/
-static void nand_write_page_raw_syndrome(struct mtd_info *mtd,
+static int nand_write_page_raw_syndrome(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
@@ -1974,6 +1976,8 @@ static void nand_write_page_raw_syndrome(struct mtd_info *mtd,
size = mtd->oobsize - (oob - chip->oob_poi);
if (size)
chip->write_buf(mtd, oob, size);
+
+ return 0;
}
/**
* nand_write_page_swecc - [REPLACEABLE] software ECC based page write function
@@ -1982,7 +1986,7 @@ static void nand_write_page_raw_syndrome(struct mtd_info *mtd,
* @buf: data buffer
* @oob_required: must write chip->oob_poi to OOB
*/
-static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
+static int nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
int i, eccsize = chip->ecc.size;
@@ -1999,7 +2003,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
for (i = 0; i < chip->ecc.total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
- chip->ecc.write_page_raw(mtd, chip, buf, 1);
+ return chip->ecc.write_page_raw(mtd, chip, buf, 1);
}
/**
@@ -2009,7 +2013,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
* @buf: data buffer
* @oob_required: must write chip->oob_poi to OOB
*/
-static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
int i, eccsize = chip->ecc.size;
@@ -2029,6 +2033,8 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
chip->oob_poi[eccpos[i]] = ecc_calc[i];
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
}
/**
@@ -2041,7 +2047,7 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
* The hw generator calculates the error syndrome automatically. Therefore we
* need a special oob layout and handling.
*/
-static void nand_write_page_syndrome(struct mtd_info *mtd,
+static int nand_write_page_syndrome(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
@@ -2075,6 +2081,8 @@ static void nand_write_page_syndrome(struct mtd_info *mtd,
i = mtd->oobsize - (oob - chip->oob_poi);
if (i)
chip->write_buf(mtd, oob, i);
+
+ return 0;
}
/**
@@ -2096,9 +2104,12 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
if (unlikely(raw))
- chip->ecc.write_page_raw(mtd, chip, buf, oob_required);
+ status = chip->ecc.write_page_raw(mtd, chip, buf, oob_required);
else
- chip->ecc.write_page(mtd, chip, buf, oob_required);
+ status = chip->ecc.write_page(mtd, chip, buf, oob_required);
+
+ if (status < 0)
+ return status;
/*
* Cached progamming disabled for now. Not sure if it's worth the
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 252aaef..86640f7 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -681,11 +681,13 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
info->state = STATE_IDLE;
}
-static void pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
+static int pxa3xx_nand_write_page_hwecc(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf, int oob_required)
{
chip->write_buf(mtd, buf, mtd->writesize);
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
}
static int pxa3xx_nand_read_page_hwecc(struct mtd_info *mtd,
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c
index aa9b8a5..0700ad9 100644
--- a/drivers/mtd/nand/sh_flctl.c
+++ b/drivers/mtd/nand/sh_flctl.c
@@ -365,7 +365,7 @@ static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
return 0;
}
-static void flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+static int flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required)
{
int i, eccsize = chip->ecc.size;
@@ -375,6 +375,8 @@ static void flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
chip->write_buf(mtd, p, eccsize);
+
+ return 0;
}
static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr)
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 57977c6..4cb6c7fe 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -361,13 +361,13 @@ struct nand_ecc_ctrl {
uint8_t *calc_ecc);
int (*read_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page);
- void (*write_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
+ int (*write_page_raw)(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required);
int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int oob_required, int page);
int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
uint32_t offs, uint32_t len, uint8_t *buf);
- void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
+ int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf, int oob_required);
int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
int page);
--
1.7.9.5
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v11 4/4] MTD: at91: atmel_nand: Update driver to support Programmable Multibit ECC controller
2012-06-25 10:07 [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver Josh Wu
` (2 preceding siblings ...)
2012-06-25 10:07 ` [PATCH v11 3/4] MTD: nand: add return value for write_page()/write_page_raw() functions in structure of nand_ecc_ctrl Josh Wu
@ 2012-06-25 10:07 ` Josh Wu
2012-06-25 10:27 ` Richard Genoud
2012-06-26 14:15 ` Richard Genoud
2012-06-27 3:45 ` [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver Artem Bityutskiy
` (2 subsequent siblings)
6 siblings, 2 replies; 22+ messages in thread
From: Josh Wu @ 2012-06-25 10:07 UTC (permalink / raw)
To: linux-arm-kernel
The Programmable Multibit 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,
the sector size and ROM lookup table offsets.
This driver has been tested on AT91SAM9X5-EK and AT91SAM9N12-EK with JFFS2,
YAFFS2, UBIFS and mtd-utils.
Signed-off-by: Hong Xu <hong.xu@atmel.com>
Signed-off-by: Josh Wu <josh.wu@atmel.com>
---
changes:
1. atmel_nand_pmecc_write_page() will return -EIO when time out to read the pmecc status register.
2. increase the time-out duration to 100ms, which has more toleration.
3. add oob_required argument for pmecc read/write functions to align with v3.5-rc4.
drivers/mtd/nand/atmel_nand.c | 737 ++++++++++++++++++++++++++++++++++++-
drivers/mtd/nand/atmel_nand_ecc.h | 114 +++++-
2 files changed, 849 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index b97ad9f..8c0f9e33 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -15,6 +15,8 @@
* (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
* (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
*
+ * Add Programmable Multibit ECC support for various AT91 SoC
+ * (C) Copyright 2012 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
@@ -98,8 +100,31 @@ struct atmel_nand_host {
u8 pmecc_corr_cap;
u16 pmecc_sector_size;
u32 pmecc_lookup_table_offset;
+
+ int pmecc_bytes_per_sector;
+ int pmecc_sector_number;
+ int pmecc_degree; /* Degree of remainders */
+ int pmecc_cw_len; /* Length of codeword */
+
+ void __iomem *pmerrloc_base;
+ void __iomem *pmecc_rom_base;
+
+ /* lookup table for alpha_to and index_of */
+ void __iomem *pmecc_alpha_to;
+ void __iomem *pmecc_index_of;
+
+ /* data for pmecc computation */
+ int16_t *pmecc_partial_syn;
+ int16_t *pmecc_si;
+ int16_t *pmecc_smu; /* Sigma table */
+ int16_t *pmecc_lmu; /* polynomal order */
+ int *pmecc_mu;
+ int *pmecc_dmu;
+ int *pmecc_delta;
};
+static struct nand_ecclayout atmel_pmecc_oobinfo;
+
static int cpu_has_dma(void)
{
return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
@@ -293,6 +318,693 @@ static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
}
/*
+ * Return number of ecc bytes per sector according to sector size and
+ * correction capability
+ *
+ * Following table shows what at91 PMECC supported:
+ * Correction Capability Sector_512_bytes Sector_1024_bytes
+ * ===================== ================ =================
+ * 2-bits 4-bytes 4-bytes
+ * 4-bits 7-bytes 7-bytes
+ * 8-bits 13-bytes 14-bytes
+ * 12-bits 20-bytes 21-bytes
+ * 24-bits 39-bytes 42-bytes
+ */
+static int __devinit pmecc_get_ecc_bytes(int cap, int sector_size)
+{
+ int m = 12 + sector_size / 512;
+ return (m * cap + 7) / 8;
+}
+
+static void __devinit pmecc_config_ecc_layout(struct nand_ecclayout *layout,
+ int oobsize, int ecc_len)
+{
+ int i;
+
+ layout->eccbytes = ecc_len;
+
+ /* ECC will occupy the last ecc_len bytes continuously */
+ for (i = 0; i < ecc_len; i++)
+ layout->eccpos[i] = oobsize - ecc_len + i;
+
+ layout->oobfree[0].offset = 2;
+ layout->oobfree[0].length =
+ oobsize - ecc_len - layout->oobfree[0].offset;
+}
+
+static void __devinit __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host)
+{
+ int table_size;
+
+ table_size = host->pmecc_sector_size == 512 ?
+ PMECC_LOOKUP_TABLE_SIZE_512 : PMECC_LOOKUP_TABLE_SIZE_1024;
+
+ return host->pmecc_rom_base + host->pmecc_lookup_table_offset +
+ table_size * sizeof(int16_t);
+}
+
+static void pmecc_data_free(struct atmel_nand_host *host)
+{
+ kfree(host->pmecc_partial_syn);
+ kfree(host->pmecc_si);
+ kfree(host->pmecc_lmu);
+ kfree(host->pmecc_smu);
+ kfree(host->pmecc_mu);
+ kfree(host->pmecc_dmu);
+ kfree(host->pmecc_delta);
+}
+
+static int __devinit pmecc_data_alloc(struct atmel_nand_host *host)
+{
+ const int cap = host->pmecc_corr_cap;
+
+ host->pmecc_partial_syn = kzalloc((2 * cap + 1) * sizeof(int16_t),
+ GFP_KERNEL);
+ host->pmecc_si = kzalloc((2 * cap + 1) * sizeof(int16_t), GFP_KERNEL);
+ host->pmecc_lmu = kzalloc((cap + 1) * sizeof(int16_t), GFP_KERNEL);
+ host->pmecc_smu = kzalloc((cap + 2) * (2 * cap + 1) * sizeof(int16_t),
+ GFP_KERNEL);
+ host->pmecc_mu = kzalloc((cap + 1) * sizeof(int), GFP_KERNEL);
+ host->pmecc_dmu = kzalloc((cap + 1) * sizeof(int), GFP_KERNEL);
+ host->pmecc_delta = kzalloc((cap + 1) * sizeof(int), GFP_KERNEL);
+
+ if (host->pmecc_partial_syn &&
+ host->pmecc_si &&
+ host->pmecc_lmu &&
+ host->pmecc_smu &&
+ host->pmecc_mu &&
+ host->pmecc_dmu &&
+ host->pmecc_delta)
+ return 0;
+
+ /* error happened */
+ pmecc_data_free(host);
+ return -ENOMEM;
+}
+
+static void pmecc_gen_syndrome(struct mtd_info *mtd, int sector)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct atmel_nand_host *host = nand_chip->priv;
+ int i;
+ uint32_t value;
+
+ /* Fill odd syndromes */
+ for (i = 0; i < host->pmecc_corr_cap; i++) {
+ value = pmecc_readl_rem_relaxed(host->ecc, sector, i / 2);
+ if (i & 1)
+ value >>= 16;
+ value &= 0xffff;
+ host->pmecc_partial_syn[(2 * i) + 1] = (int16_t)value;
+ }
+}
+
+static void pmecc_substitute(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct atmel_nand_host *host = nand_chip->priv;
+ int16_t __iomem *alpha_to = host->pmecc_alpha_to;
+ int16_t __iomem *index_of = host->pmecc_index_of;
+ int16_t *partial_syn = host->pmecc_partial_syn;
+ const int cap = host->pmecc_corr_cap;
+ int16_t *si;
+ int i, j;
+
+ /* si[] is a table that holds the current syndrome value,
+ * an element of that table belongs to the field
+ */
+ si = host->pmecc_si;
+
+ memset(&si[1], 0, sizeof(int16_t) * (2 * cap - 1));
+
+ /* Computation 2t syndromes based on S(x) */
+ /* Odd syndromes */
+ for (i = 1; i < 2 * cap; i += 2) {
+ for (j = 0; j < host->pmecc_degree; j++) {
+ if (partial_syn[i] & ((unsigned short)0x1 << j))
+ si[i] = readw_relaxed(alpha_to + i * j) ^ si[i];
+ }
+ }
+ /* Even syndrome = (Odd syndrome) ** 2 */
+ for (i = 2, j = 1; j <= cap; i = ++j << 1) {
+ if (si[j] == 0) {
+ si[i] = 0;
+ } else {
+ int16_t tmp;
+
+ tmp = readw_relaxed(index_of + si[j]);
+ tmp = (tmp * 2) % host->pmecc_cw_len;
+ si[i] = readw_relaxed(alpha_to + tmp);
+ }
+ }
+
+ return;
+}
+
+static void pmecc_get_sigma(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct atmel_nand_host *host = nand_chip->priv;
+
+ int16_t *lmu = host->pmecc_lmu;
+ int16_t *si = host->pmecc_si;
+ int *mu = host->pmecc_mu;
+ int *dmu = host->pmecc_dmu; /* Discrepancy */
+ int *delta = host->pmecc_delta; /* Delta order */
+ int cw_len = host->pmecc_cw_len;
+ const int16_t cap = host->pmecc_corr_cap;
+ int16_t __iomem *index_of = host->pmecc_index_of;
+ int16_t __iomem *alpha_to = host->pmecc_alpha_to;
+ int i, j, k;
+ uint32_t dmu_0_count, tmp;
+ int16_t (*smu)[2 * cap + 1];
+
+ /* index of largest delta */
+ int ro;
+ int largest;
+ int diff;
+
+ dmu_0_count = 0;
+ smu = (int16_t(*)[2 * cap + 1])host->pmecc_smu;
+
+ /* First Row */
+
+ /* Mu */
+ mu[0] = -1;
+
+ memset(&smu[0][0], 0,
+ sizeof(int16_t) * (2 * cap + 1));
+ smu[0][0] = 1;
+
+ /* discrepancy set to 1 */
+ dmu[0] = 1;
+ /* polynom order set to 0 */
+ lmu[0] = 0;
+ delta[0] = (mu[0] * 2 - lmu[0]) >> 1;
+
+ /* Second Row */
+
+ /* Mu */
+ mu[1] = 0;
+ /* Sigma(x) set to 1 */
+ memset(&smu[1][0], 0,
+ sizeof(int16_t) * (2 * cap + 1));
+ smu[1][0] = 1;
+
+ /* discrepancy set to S1 */
+ dmu[1] = si[1];
+
+ /* polynom order set to 0 */
+ lmu[1] = 0;
+
+ delta[1] = (mu[1] * 2 - lmu[1]) >> 1;
+
+ /* Init the Sigma(x) last row */
+ memset(&smu[cap + 1][0], 0,
+ sizeof(int16_t) * (2 * cap + 1));
+
+ for (i = 1; i <= cap; i++) {
+ mu[i + 1] = i << 1;
+ /* Begin Computing Sigma (Mu+1) and L(mu) */
+ /* check if discrepancy is set to 0 */
+ if (dmu[i] == 0) {
+ dmu_0_count++;
+
+ tmp = ((cap - (lmu[i] >> 1) - 1) / 2);
+ if ((cap - (lmu[i] >> 1) - 1) & 0x1)
+ tmp += 2;
+ else
+ tmp += 1;
+
+ if (dmu_0_count == tmp) {
+ for (j = 0; j <= (lmu[i] >> 1) + 1; j++)
+ smu[cap + 1][j] = smu[i][j];
+ lmu[cap + 1] = lmu[i];
+ return;
+ }
+
+ /* copy polynom */
+ for (j = 0; j <= lmu[i] >> 1; j++)
+ smu[i + 1][j] = smu[i][j];
+
+ /* copy previous polynom order to the next */
+ lmu[i + 1] = lmu[i];
+ } else {
+ ro = 0;
+ largest = -1;
+ /* find largest delta with dmu != 0 */
+ for (j = 0; j < i; j++) {
+ if ((dmu[j]) && (delta[j] > largest)) {
+ largest = delta[j];
+ ro = j;
+ }
+ }
+
+ /* compute difference */
+ diff = (mu[i] - mu[ro]);
+
+ /* Compute degree of the new smu polynomial */
+ if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff))
+ lmu[i + 1] = lmu[i];
+ else
+ lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2;
+
+ /* Init smu[i+1] with 0 */
+ for (k = 0; k < (2 * cap + 1); k++)
+ smu[i + 1][k] = 0;
+
+ /* Compute smu[i+1] */
+ for (k = 0; k <= lmu[ro] >> 1; k++) {
+ int16_t a, b, c;
+
+ if (!(smu[ro][k] && dmu[i]))
+ continue;
+ a = readw_relaxed(index_of + dmu[i]);
+ b = readw_relaxed(index_of + dmu[ro]);
+ c = readw_relaxed(index_of + smu[ro][k]);
+ tmp = a + (cw_len - b) + c;
+ a = readw_relaxed(alpha_to + tmp % cw_len);
+ smu[i + 1][k + diff] = a;
+ }
+
+ for (k = 0; k <= lmu[i] >> 1; k++)
+ smu[i + 1][k] ^= smu[i][k];
+ }
+
+ /* End Computing Sigma (Mu+1) and L(mu) */
+ /* In either case compute delta */
+ delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1;
+
+ /* Do not compute discrepancy for the last iteration */
+ if (i >= cap)
+ continue;
+
+ for (k = 0; k <= (lmu[i + 1] >> 1); k++) {
+ tmp = 2 * (i - 1);
+ if (k == 0) {
+ dmu[i + 1] = si[tmp + 3];
+ } else if (smu[i + 1][k] && si[tmp + 3 - k]) {
+ int16_t a, b, c;
+ a = readw_relaxed(index_of + smu[i + 1][k]);
+ b = si[2 * (i - 1) + 3 - k];
+ c = readw_relaxed(index_of + b);
+ tmp = a + c;
+ tmp %= cw_len;
+ dmu[i + 1] = readw_relaxed(alpha_to + tmp) ^
+ dmu[i + 1];
+ }
+ }
+ }
+
+ return;
+}
+
+static int pmecc_err_location(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct atmel_nand_host *host = nand_chip->priv;
+ unsigned long end_time;
+ const int cap = host->pmecc_corr_cap;
+ int sector_size = host->pmecc_sector_size;
+ int err_nbr = 0; /* number of error */
+ int roots_nbr; /* number of roots */
+ int i;
+ uint32_t val;
+ int16_t (*smu)[2 * cap + 1];
+
+ smu = (int16_t(*)[2 * cap + 1])host->pmecc_smu;
+
+ pmerrloc_writel(host->pmerrloc_base, ELDIS, PMERRLOC_DISABLE);
+
+ for (i = 0; i <= host->pmecc_lmu[cap + 1] >> 1; i++) {
+ pmerrloc_writel_sigma_relaxed(host->pmerrloc_base, i,
+ smu[cap + 1][i]);
+ err_nbr++;
+ }
+
+ val = (err_nbr - 1) << 16;
+ if (sector_size == 1024)
+ val |= 1;
+
+ pmerrloc_writel(host->pmerrloc_base, ELCFG, val);
+ pmerrloc_writel(host->pmerrloc_base, ELEN,
+ sector_size * 8 + host->pmecc_degree * cap);
+
+ end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS);
+ while (!(pmerrloc_readl_relaxed(host->pmerrloc_base, ELISR)
+ & PMERRLOC_CALC_DONE)) {
+ if (unlikely(time_after(jiffies, end_time))) {
+ dev_err(host->dev, "PMECC: Timeout to calculate error location.\n");
+ return -1;
+ }
+ cpu_relax();
+ }
+
+ roots_nbr = (pmerrloc_readl_relaxed(host->pmerrloc_base, ELISR)
+ & PMERRLOC_ERR_NUM_MASK) >> 8;
+ /* Number of roots == degree of smu hence <= cap */
+ if (roots_nbr == host->pmecc_lmu[cap + 1] >> 1)
+ return err_nbr - 1;
+
+ /* Number of roots does not match the degree of smu
+ * unable to correct error */
+ return -1;
+}
+
+static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf,
+ int extra_bytes, int err_nbr)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct atmel_nand_host *host = nand_chip->priv;
+ int i = 0;
+ int byte_pos, bit_pos, sector_size, ecc_size;
+ uint32_t tmp;
+
+ sector_size = host->pmecc_sector_size;
+ ecc_size = nand_chip->ecc.bytes;
+
+ while (err_nbr) {
+ tmp = pmerrloc_readl_el_relaxed(host->pmerrloc_base, i) - 1;
+ byte_pos = tmp / 8;
+ bit_pos = tmp % 8;
+ dev_info(host->dev, "PMECC correction, byte_pos: %d bit_pos: %d\n",
+ byte_pos, bit_pos);
+
+ if (byte_pos < (sector_size + extra_bytes)) {
+ tmp = sector_size +
+ pmecc_readl_relaxed(host->ecc, SADDR);
+
+ if (byte_pos < tmp)
+ *(buf + byte_pos) ^= (1 << bit_pos);
+ else
+ *(buf + byte_pos + ecc_size) ^= (1 << bit_pos);
+ }
+
+ i++;
+ err_nbr--;
+ }
+
+ return;
+}
+
+static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf,
+ u8 *ecc)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct atmel_nand_host *host = nand_chip->priv;
+ int i, err_nbr, eccbytes;
+ uint8_t *buf_pos;
+
+ eccbytes = nand_chip->ecc.bytes;
+ for (i = 0; i < eccbytes; i++)
+ if (ecc[i] != 0xff)
+ goto normal_check;
+ /* Erased page, return OK */
+ return 0;
+
+normal_check:
+ for (i = 0; i < host->pmecc_sector_number; i++) {
+ err_nbr = 0;
+ if (pmecc_stat & 0x1) {
+ buf_pos = buf + i * host->pmecc_sector_size;
+
+ pmecc_gen_syndrome(mtd, i);
+ pmecc_substitute(mtd);
+ pmecc_get_sigma(mtd);
+
+ err_nbr = pmecc_err_location(mtd);
+ if (err_nbr == -1) {
+ dev_err(host->dev, "PMECC: Too many errors\n");
+ mtd->ecc_stats.failed++;
+ return -EIO;
+ } else {
+ pmecc_correct_data(mtd, buf_pos, 0, err_nbr);
+ mtd->ecc_stats.corrected += err_nbr;
+ }
+ }
+ pmecc_stat >>= 1;
+ }
+
+ return 0;
+}
+
+static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
+{
+ struct atmel_nand_host *host = chip->priv;
+ int eccsize = chip->ecc.size;
+ uint8_t *oob = chip->oob_poi;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint32_t stat;
+ unsigned long end_time;
+
+ pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST);
+ pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
+ pmecc_writel(host->ecc, CFG, (pmecc_readl_relaxed(host->ecc, CFG)
+ & ~PMECC_CFG_WRITE_OP) | PMECC_CFG_AUTO_ENABLE);
+
+ pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE);
+ pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DATA);
+
+ chip->read_buf(mtd, buf, eccsize);
+ chip->read_buf(mtd, oob, mtd->oobsize);
+
+ end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS);
+ while ((pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY)) {
+ if (unlikely(time_after(jiffies, end_time))) {
+ dev_err(host->dev, "PMECC: Timeout to get error status.\n");
+ return -EIO;
+ }
+ cpu_relax();
+ }
+
+ stat = pmecc_readl_relaxed(host->ecc, ISR);
+ if (stat != 0)
+ if (pmecc_correction(mtd, stat, buf, &oob[eccpos[0]]) != 0)
+ return -EIO;
+
+ return 0;
+}
+
+static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
+ struct nand_chip *chip, const uint8_t *buf, int oob_required)
+{
+ struct atmel_nand_host *host = chip->priv;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ int i, j;
+ unsigned long end_time;
+
+ pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST);
+ pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
+
+ pmecc_writel(host->ecc, CFG, (pmecc_readl_relaxed(host->ecc, CFG) |
+ PMECC_CFG_WRITE_OP) & ~PMECC_CFG_AUTO_ENABLE);
+
+ pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE);
+ pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DATA);
+
+ chip->write_buf(mtd, (u8 *)buf, mtd->writesize);
+
+ end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS);
+ while ((pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY)) {
+ if (unlikely(time_after(jiffies, end_time))) {
+ dev_err(host->dev, "PMECC: Timeout to get ECC value.\n");
+ return -EIO;
+ }
+ cpu_relax();
+ }
+
+ for (i = 0; i < host->pmecc_sector_number; i++) {
+ for (j = 0; j < host->pmecc_bytes_per_sector; j++) {
+ int pos;
+
+ pos = i * host->pmecc_bytes_per_sector + j;
+ chip->oob_poi[eccpos[pos]] =
+ pmecc_readb_ecc_relaxed(host->ecc, i, j);
+ }
+ }
+ chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+ return 0;
+}
+
+static void atmel_pmecc_core_init(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct atmel_nand_host *host = nand_chip->priv;
+ uint32_t val = 0;
+ struct nand_ecclayout *ecc_layout;
+
+ pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST);
+ pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
+
+ switch (host->pmecc_corr_cap) {
+ case 2:
+ val = PMECC_CFG_BCH_ERR2;
+ break;
+ case 4:
+ val = PMECC_CFG_BCH_ERR4;
+ break;
+ case 8:
+ val = PMECC_CFG_BCH_ERR8;
+ break;
+ case 12:
+ val = PMECC_CFG_BCH_ERR12;
+ break;
+ case 24:
+ val = PMECC_CFG_BCH_ERR24;
+ break;
+ }
+
+ if (host->pmecc_sector_size == 512)
+ val |= PMECC_CFG_SECTOR512;
+ else if (host->pmecc_sector_size == 1024)
+ val |= PMECC_CFG_SECTOR1024;
+
+ switch (host->pmecc_sector_number) {
+ case 1:
+ val |= PMECC_CFG_PAGE_1SECTOR;
+ break;
+ case 2:
+ val |= PMECC_CFG_PAGE_2SECTORS;
+ break;
+ case 4:
+ val |= PMECC_CFG_PAGE_4SECTORS;
+ break;
+ case 8:
+ val |= PMECC_CFG_PAGE_8SECTORS;
+ break;
+ }
+
+ val |= (PMECC_CFG_READ_OP | PMECC_CFG_SPARE_DISABLE
+ | PMECC_CFG_AUTO_DISABLE);
+ pmecc_writel(host->ecc, CFG, val);
+
+ ecc_layout = nand_chip->ecc.layout;
+ pmecc_writel(host->ecc, SAREA, mtd->oobsize - 1);
+ pmecc_writel(host->ecc, SADDR, ecc_layout->eccpos[0]);
+ pmecc_writel(host->ecc, EADDR,
+ ecc_layout->eccpos[ecc_layout->eccbytes - 1]);
+ /* See datasheet about PMECC Clock Control Register */
+ pmecc_writel(host->ecc, CLK, 2);
+ pmecc_writel(host->ecc, IDR, 0xff);
+ pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE);
+}
+
+static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev,
+ struct atmel_nand_host *host)
+{
+ struct mtd_info *mtd = &host->mtd;
+ struct nand_chip *nand_chip = &host->nand_chip;
+ struct resource *regs, *regs_pmerr, *regs_rom;
+ int cap, sector_size, err_no;
+
+ cap = host->pmecc_corr_cap;
+ sector_size = host->pmecc_sector_size;
+ dev_info(host->dev, "Initialize PMECC params, cap: %d, sector: %d\n",
+ cap, sector_size);
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!regs) {
+ dev_warn(host->dev,
+ "Can't get I/O resource regs, rolling back on software ECC\n");
+ nand_chip->ecc.mode = NAND_ECC_SOFT;
+ return 0;
+ }
+
+ host->ecc = ioremap(regs->start, resource_size(regs));
+ if (host->ecc == NULL) {
+ dev_err(host->dev, "ioremap failed\n");
+ err_no = -EIO;
+ goto err_pmecc_ioremap;
+ }
+
+ regs_pmerr = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ regs_rom = platform_get_resource(pdev, IORESOURCE_MEM, 3);
+ if (regs_pmerr && regs_rom) {
+ host->pmerrloc_base = ioremap(regs_pmerr->start,
+ resource_size(regs_pmerr));
+ host->pmecc_rom_base = ioremap(regs_rom->start,
+ resource_size(regs_rom));
+
+ if (!host->pmerrloc_base || !host->pmecc_rom_base) {
+ dev_err(host->dev,
+ "Can not get I/O resource for PMECC controller!\n");
+ err_no = -EIO;
+ goto err_pmloc_ioremap;
+ }
+ }
+
+ /* 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 2048:
+ host->pmecc_degree = PMECC_GF_DIMENSION_13;
+ host->pmecc_cw_len = (1 << host->pmecc_degree) - 1;
+ host->pmecc_sector_number = mtd->writesize / sector_size;
+ host->pmecc_bytes_per_sector = pmecc_get_ecc_bytes(
+ cap, sector_size);
+ host->pmecc_alpha_to = pmecc_get_alpha_to(host);
+ host->pmecc_index_of = host->pmecc_rom_base +
+ host->pmecc_lookup_table_offset;
+
+ nand_chip->ecc.steps = 1;
+ nand_chip->ecc.strength = cap;
+ nand_chip->ecc.bytes = host->pmecc_bytes_per_sector *
+ host->pmecc_sector_number;
+ if (nand_chip->ecc.bytes > mtd->oobsize - 2) {
+ dev_err(host->dev, "No room for ECC bytes\n");
+ err_no = -EINVAL;
+ goto err_no_ecc_room;
+ }
+ pmecc_config_ecc_layout(&atmel_pmecc_oobinfo,
+ mtd->oobsize,
+ nand_chip->ecc.bytes);
+ nand_chip->ecc.layout = &atmel_pmecc_oobinfo;
+ break;
+ case 512:
+ case 1024:
+ case 4096:
+ /* TODO */
+ dev_warn(host->dev,
+ "Unsupported page size for PMECC, use Software ECC\n");
+ default:
+ /* page size not handled by HW ECC */
+ /* switching back to soft ECC */
+ nand_chip->ecc.mode = NAND_ECC_SOFT;
+ return 0;
+ }
+
+ /* Allocate data for PMECC computation */
+ err_no = pmecc_data_alloc(host);
+ if (err_no) {
+ dev_err(host->dev,
+ "Cannot allocate memory for PMECC computation!\n");
+ goto err_pmecc_data_alloc;
+ }
+
+ nand_chip->ecc.read_page = atmel_nand_pmecc_read_page;
+ nand_chip->ecc.write_page = atmel_nand_pmecc_write_page;
+
+ atmel_pmecc_core_init(mtd);
+
+ return 0;
+
+err_pmecc_data_alloc:
+err_no_ecc_room:
+err_pmloc_ioremap:
+ iounmap(host->ecc);
+ if (host->pmerrloc_base)
+ iounmap(host->pmerrloc_base);
+ if (host->pmecc_rom_base)
+ iounmap(host->pmecc_rom_base);
+err_pmecc_ioremap:
+ return err_no;
+}
+
+/*
* Calculate HW ECC
*
* function called after a write
@@ -747,7 +1459,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
}
if (nand_chip->ecc.mode == NAND_ECC_HW) {
- res = atmel_hw_nand_init_params(pdev, host);
+ if (host->has_pmecc)
+ res = atmel_pmecc_nand_init_params(pdev, host);
+ else
+ res = atmel_hw_nand_init_params(pdev, host);
+
if (res != 0)
goto err_hw_ecc;
}
@@ -766,8 +1482,16 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
return res;
err_scan_tail:
+ if (host->has_pmecc && host->nand_chip.ecc.mode == NAND_ECC_HW) {
+ pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
+ pmecc_data_free(host);
+ }
if (host->ecc)
iounmap(host->ecc);
+ if (host->pmerrloc_base)
+ iounmap(host->pmerrloc_base);
+ if (host->pmecc_rom_base)
+ iounmap(host->pmecc_rom_base);
err_hw_ecc:
err_scan_ident:
err_no_card:
@@ -793,8 +1517,19 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
atmel_nand_disable(host);
+ if (host->has_pmecc && host->nand_chip.ecc.mode == NAND_ECC_HW) {
+ pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
+ pmerrloc_writel(host->pmerrloc_base, ELDIS,
+ PMERRLOC_DISABLE);
+ pmecc_data_free(host);
+ }
+
if (host->ecc)
iounmap(host->ecc);
+ if (host->pmecc_rom_base)
+ iounmap(host->pmecc_rom_base);
+ if (host->pmerrloc_base)
+ iounmap(host->pmerrloc_base);
if (host->dma_chan)
dma_release_channel(host->dma_chan);
diff --git a/drivers/mtd/nand/atmel_nand_ecc.h b/drivers/mtd/nand/atmel_nand_ecc.h
index 578c776..8a1e9a6 100644
--- a/drivers/mtd/nand/atmel_nand_ecc.h
+++ b/drivers/mtd/nand/atmel_nand_ecc.h
@@ -3,7 +3,7 @@
* Based on AT91SAM9260 datasheet revision B.
*
* Copyright (C) 2007 Andrew Victor
- * Copyright (C) 2007 Atmel Corporation.
+ * Copyright (C) 2007 - 2012 Atmel Corporation.
*
* 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
@@ -36,4 +36,116 @@
#define ATMEL_ECC_NPR 0x10 /* NParity register */
#define ATMEL_ECC_NPARITY (0xffff << 0) /* NParity */
+/* PMECC Register Definitions */
+#define ATMEL_PMECC_CFG 0x000 /* Configuration Register */
+#define PMECC_CFG_BCH_ERR2 (0 << 0)
+#define PMECC_CFG_BCH_ERR4 (1 << 0)
+#define PMECC_CFG_BCH_ERR8 (2 << 0)
+#define PMECC_CFG_BCH_ERR12 (3 << 0)
+#define PMECC_CFG_BCH_ERR24 (4 << 0)
+
+#define PMECC_CFG_SECTOR512 (0 << 4)
+#define PMECC_CFG_SECTOR1024 (1 << 4)
+
+#define PMECC_CFG_PAGE_1SECTOR (0 << 8)
+#define PMECC_CFG_PAGE_2SECTORS (1 << 8)
+#define PMECC_CFG_PAGE_4SECTORS (2 << 8)
+#define PMECC_CFG_PAGE_8SECTORS (3 << 8)
+
+#define PMECC_CFG_READ_OP (0 << 12)
+#define PMECC_CFG_WRITE_OP (1 << 12)
+
+#define PMECC_CFG_SPARE_ENABLE (1 << 16)
+#define PMECC_CFG_SPARE_DISABLE (0 << 16)
+
+#define PMECC_CFG_AUTO_ENABLE (1 << 20)
+#define PMECC_CFG_AUTO_DISABLE (0 << 20)
+
+#define ATMEL_PMECC_SAREA 0x004 /* Spare area size */
+#define ATMEL_PMECC_SADDR 0x008 /* PMECC starting address */
+#define ATMEL_PMECC_EADDR 0x00c /* PMECC ending address */
+#define ATMEL_PMECC_CLK 0x010 /* PMECC clock control */
+#define PMECC_CLK_133MHZ (2 << 0)
+
+#define ATMEL_PMECC_CTRL 0x014 /* PMECC control register */
+#define PMECC_CTRL_RST (1 << 0)
+#define PMECC_CTRL_DATA (1 << 1)
+#define PMECC_CTRL_USER (1 << 2)
+#define PMECC_CTRL_ENABLE (1 << 4)
+#define PMECC_CTRL_DISABLE (1 << 5)
+
+#define ATMEL_PMECC_SR 0x018 /* PMECC status register */
+#define PMECC_SR_BUSY (1 << 0)
+#define PMECC_SR_ENABLE (1 << 4)
+
+#define ATMEL_PMECC_IER 0x01c /* PMECC interrupt enable */
+#define PMECC_IER_ENABLE (1 << 0)
+#define ATMEL_PMECC_IDR 0x020 /* PMECC interrupt disable */
+#define PMECC_IER_DISABLE (1 << 0)
+#define ATMEL_PMECC_IMR 0x024 /* PMECC interrupt mask */
+#define PMECC_IER_MASK (1 << 0)
+#define ATMEL_PMECC_ISR 0x028 /* PMECC interrupt status */
+#define ATMEL_PMECC_ECCx 0x040 /* PMECC ECC x */
+#define ATMEL_PMECC_REMx 0x240 /* PMECC REM x */
+
+/* PMERRLOC Register Definitions */
+#define ATMEL_PMERRLOC_ELCFG 0x000 /* Error location config */
+#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0)
+#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0)
+#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16)
+
+#define ATMEL_PMERRLOC_ELPRIM 0x004 /* Error location primitive */
+#define ATMEL_PMERRLOC_ELEN 0x008 /* Error location enable */
+#define ATMEL_PMERRLOC_ELDIS 0x00c /* Error location disable */
+#define PMERRLOC_DISABLE (1 << 0)
+
+#define ATMEL_PMERRLOC_ELSR 0x010 /* Error location status */
+#define PMERRLOC_ELSR_BUSY (1 << 0)
+#define ATMEL_PMERRLOC_ELIER 0x014 /* Error location int enable */
+#define ATMEL_PMERRLOC_ELIDR 0x018 /* Error location int disable */
+#define ATMEL_PMERRLOC_ELIMR 0x01c /* Error location int mask */
+#define ATMEL_PMERRLOC_ELISR 0x020 /* Error location int status */
+#define PMERRLOC_ERR_NUM_MASK (0x1f << 8)
+#define PMERRLOC_CALC_DONE (1 << 0)
+#define ATMEL_PMERRLOC_SIGMAx 0x028 /* Error location SIGMA x */
+#define ATMEL_PMERRLOC_ELx 0x08c /* Error location x */
+
+/* Register access macros for PMECC */
+#define pmecc_readl_relaxed(addr, reg) \
+ readl_relaxed((addr) + ATMEL_PMECC_##reg)
+
+#define pmecc_writel(addr, reg, value) \
+ writel((value), (addr) + ATMEL_PMECC_##reg)
+
+#define pmecc_readb_ecc_relaxed(addr, sector, n) \
+ readb_relaxed((addr) + ATMEL_PMECC_ECCx + ((sector) * 0x40) + (n))
+
+#define pmecc_readl_rem_relaxed(addr, sector, n) \
+ readl_relaxed((addr) + ATMEL_PMECC_REMx + ((sector) * 0x40) + ((n) * 4))
+
+#define pmerrloc_readl_relaxed(addr, reg) \
+ readl_relaxed((addr) + ATMEL_PMERRLOC_##reg)
+
+#define pmerrloc_writel(addr, reg, value) \
+ writel((value), (addr) + ATMEL_PMERRLOC_##reg)
+
+#define pmerrloc_writel_sigma_relaxed(addr, n, value) \
+ writel_relaxed((value), (addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4))
+
+#define pmerrloc_readl_sigma_relaxed(addr, n) \
+ readl_relaxed((addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4))
+
+#define pmerrloc_readl_el_relaxed(addr, n) \
+ readl_relaxed((addr) + ATMEL_PMERRLOC_ELx + ((n) * 4))
+
+/* Galois field dimension */
+#define PMECC_GF_DIMENSION_13 13
+#define PMECC_GF_DIMENSION_14 14
+
+#define PMECC_LOOKUP_TABLE_SIZE_512 0x2000
+#define PMECC_LOOKUP_TABLE_SIZE_1024 0x4000
+
+/* Time out value for reading PMECC status register */
+#define PMECC_MAX_TIMEOUT_MS 100
+
#endif
--
1.7.9.5
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v11 4/4] MTD: at91: atmel_nand: Update driver to support Programmable Multibit ECC controller
2012-06-25 10:07 ` [PATCH v11 4/4] MTD: at91: atmel_nand: Update driver to support Programmable Multibit ECC controller Josh Wu
@ 2012-06-25 10:27 ` Richard Genoud
2012-06-26 6:03 ` Josh Wu
2012-06-26 14:15 ` Richard Genoud
1 sibling, 1 reply; 22+ messages in thread
From: Richard Genoud @ 2012-06-25 10:27 UTC (permalink / raw)
To: linux-arm-kernel
2012/6/25 Josh Wu <josh.wu@atmel.com>:
> The Programmable Multibit 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,
> the sector size and ROM lookup table offsets.
>
> This driver has been tested on AT91SAM9X5-EK and AT91SAM9N12-EK with JFFS2,
> YAFFS2, UBIFS and mtd-utils.
>
> Signed-off-by: Hong Xu <hong.xu@atmel.com>
> Signed-off-by: Josh Wu <josh.wu@atmel.com>
> ---
> changes:
> ? 1. atmel_nand_pmecc_write_page() will return -EIO when time out to read the pmecc status register.
> ? 2. increase the time-out duration to 100ms, which has more toleration.
> ? 3. add oob_required argument for pmecc read/write functions to align with v3.5-rc4.
>
> ?drivers/mtd/nand/atmel_nand.c ? ? | ?737 ++++++++++++++++++++++++++++++++++++-
> ?drivers/mtd/nand/atmel_nand_ecc.h | ?114 +++++-
> ?2 files changed, 849 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
> index b97ad9f..8c0f9e33 100644
> --- a/drivers/mtd/nand/atmel_nand.c
> +++ b/drivers/mtd/nand/atmel_nand.c
> @@ -15,6 +15,8 @@
> ?* ? ? ? ? ? ? ? ? ? ? (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
> ?* ? ? (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
> ?*
> + * ?Add Programmable Multibit ECC support for various AT91 SoC
> + * ? ? (C) Copyright 2012 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
> @@ -98,8 +100,31 @@ struct atmel_nand_host {
> ? ? ? ?u8 ? ? ? ? ? ? ? ? ? ? ?pmecc_corr_cap;
> ? ? ? ?u16 ? ? ? ? ? ? ? ? ? ? pmecc_sector_size;
> ? ? ? ?u32 ? ? ? ? ? ? ? ? ? ? pmecc_lookup_table_offset;
> +
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? pmecc_bytes_per_sector;
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? pmecc_sector_number;
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? pmecc_degree; ? /* Degree of remainders */
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? pmecc_cw_len; ? /* Length of codeword */
> +
> + ? ? ? void __iomem ? ? ? ? ? ?*pmerrloc_base;
> + ? ? ? void __iomem ? ? ? ? ? ?*pmecc_rom_base;
> +
> + ? ? ? /* lookup table for alpha_to and index_of */
> + ? ? ? void __iomem ? ? ? ? ? ?*pmecc_alpha_to;
> + ? ? ? void __iomem ? ? ? ? ? ?*pmecc_index_of;
> +
> + ? ? ? /* data for pmecc computation */
> + ? ? ? int16_t ? ? ? ? ? ? ? ? *pmecc_partial_syn;
> + ? ? ? int16_t ? ? ? ? ? ? ? ? *pmecc_si;
> + ? ? ? int16_t ? ? ? ? ? ? ? ? *pmecc_smu; ? ? /* Sigma table */
> + ? ? ? int16_t ? ? ? ? ? ? ? ? *pmecc_lmu; ? ? /* polynomal order */
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? *pmecc_mu;
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? *pmecc_dmu;
> + ? ? ? int ? ? ? ? ? ? ? ? ? ? *pmecc_delta;
> ?};
>
> +static struct nand_ecclayout atmel_pmecc_oobinfo;
> +
> ?static int cpu_has_dma(void)
> ?{
> ? ? ? ?return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
> @@ -293,6 +318,693 @@ static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> ?}
>
> ?/*
> + * Return number of ecc bytes per sector according to sector size and
> + * correction capability
> + *
> + * Following table shows what at91 PMECC supported:
> + * Correction Capability ? ? ? Sector_512_bytes ? ? ? ?Sector_1024_bytes
> + * ===================== ? ? ? ================ ? ? ? ?=================
> + * ? ? ? ? ? ? ? ?2-bits ? ? ? ? ? ? ? ? 4-bytes ? ? ? ? ? ? ? ? ?4-bytes
> + * ? ? ? ? ? ? ? ?4-bits ? ? ? ? ? ? ? ? 7-bytes ? ? ? ? ? ? ? ? ?7-bytes
> + * ? ? ? ? ? ? ? ?8-bits ? ? ? ? ? ? ? ?13-bytes ? ? ? ? ? ? ? ? 14-bytes
> + * ? ? ? ? ? ? ? 12-bits ? ? ? ? ? ? ? ?20-bytes ? ? ? ? ? ? ? ? 21-bytes
> + * ? ? ? ? ? ? ? 24-bits ? ? ? ? ? ? ? ?39-bytes ? ? ? ? ? ? ? ? 42-bytes
> + */
> +static int __devinit pmecc_get_ecc_bytes(int cap, int sector_size)
> +{
> + ? ? ? int m = 12 + sector_size / 512;
> + ? ? ? return (m * cap + 7) / 8;
> +}
> +
> +static void __devinit pmecc_config_ecc_layout(struct nand_ecclayout *layout,
> + ? ? ? int oobsize, int ecc_len)
> +{
> + ? ? ? int i;
> +
> + ? ? ? layout->eccbytes = ecc_len;
> +
> + ? ? ? /* ECC will occupy the last ecc_len bytes continuously */
> + ? ? ? for (i = 0; i < ecc_len; i++)
> + ? ? ? ? ? ? ? layout->eccpos[i] = oobsize - ecc_len + i;
> +
> + ? ? ? layout->oobfree[0].offset = 2;
> + ? ? ? layout->oobfree[0].length =
> + ? ? ? ? ? ? ? oobsize - ecc_len - layout->oobfree[0].offset;
> +}
> +
> +static void __devinit __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host)
> +{
> + ? ? ? int table_size;
> +
> + ? ? ? table_size = host->pmecc_sector_size == 512 ?
> + ? ? ? ? ? ? ? PMECC_LOOKUP_TABLE_SIZE_512 : PMECC_LOOKUP_TABLE_SIZE_1024;
> +
> + ? ? ? return host->pmecc_rom_base + host->pmecc_lookup_table_offset +
> + ? ? ? ? ? ? ? ? ? ? ? table_size * sizeof(int16_t);
> +}
> +
> +static void pmecc_data_free(struct atmel_nand_host *host)
> +{
> + ? ? ? kfree(host->pmecc_partial_syn);
> + ? ? ? kfree(host->pmecc_si);
> + ? ? ? kfree(host->pmecc_lmu);
> + ? ? ? kfree(host->pmecc_smu);
> + ? ? ? kfree(host->pmecc_mu);
> + ? ? ? kfree(host->pmecc_dmu);
> + ? ? ? kfree(host->pmecc_delta);
> +}
> +
> +static int __devinit pmecc_data_alloc(struct atmel_nand_host *host)
> +{
> + ? ? ? const int cap = host->pmecc_corr_cap;
> +
> + ? ? ? host->pmecc_partial_syn = kzalloc((2 * cap + 1) * sizeof(int16_t),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? GFP_KERNEL);
> + ? ? ? host->pmecc_si = kzalloc((2 * cap + 1) * sizeof(int16_t), GFP_KERNEL);
> + ? ? ? host->pmecc_lmu = kzalloc((cap + 1) * sizeof(int16_t), GFP_KERNEL);
> + ? ? ? host->pmecc_smu = kzalloc((cap + 2) * (2 * cap + 1) * sizeof(int16_t),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? GFP_KERNEL);
> + ? ? ? host->pmecc_mu = kzalloc((cap + 1) * sizeof(int), GFP_KERNEL);
> + ? ? ? host->pmecc_dmu = kzalloc((cap + 1) * sizeof(int), GFP_KERNEL);
> + ? ? ? host->pmecc_delta = kzalloc((cap + 1) * sizeof(int), GFP_KERNEL);
> +
> + ? ? ? if (host->pmecc_partial_syn &&
> + ? ? ? ? ? ? ? ? ? ? ? host->pmecc_si &&
> + ? ? ? ? ? ? ? ? ? ? ? host->pmecc_lmu &&
> + ? ? ? ? ? ? ? ? ? ? ? host->pmecc_smu &&
> + ? ? ? ? ? ? ? ? ? ? ? host->pmecc_mu &&
> + ? ? ? ? ? ? ? ? ? ? ? host->pmecc_dmu &&
> + ? ? ? ? ? ? ? ? ? ? ? host->pmecc_delta)
> + ? ? ? ? ? ? ? return 0;
> +
> + ? ? ? /* error happened */
> + ? ? ? pmecc_data_free(host);
> + ? ? ? return -ENOMEM;
> +}
> +
> +static void pmecc_gen_syndrome(struct mtd_info *mtd, int sector)
> +{
> + ? ? ? struct nand_chip *nand_chip = mtd->priv;
> + ? ? ? struct atmel_nand_host *host = nand_chip->priv;
> + ? ? ? int i;
> + ? ? ? uint32_t value;
> +
> + ? ? ? /* Fill odd syndromes */
> + ? ? ? for (i = 0; i < host->pmecc_corr_cap; i++) {
> + ? ? ? ? ? ? ? value = pmecc_readl_rem_relaxed(host->ecc, sector, i / 2);
> + ? ? ? ? ? ? ? if (i & 1)
> + ? ? ? ? ? ? ? ? ? ? ? value >>= 16;
> + ? ? ? ? ? ? ? value &= 0xffff;
> + ? ? ? ? ? ? ? host->pmecc_partial_syn[(2 * i) + 1] = (int16_t)value;
> + ? ? ? }
> +}
> +
> +static void pmecc_substitute(struct mtd_info *mtd)
> +{
> + ? ? ? struct nand_chip *nand_chip = mtd->priv;
> + ? ? ? struct atmel_nand_host *host = nand_chip->priv;
> + ? ? ? int16_t __iomem *alpha_to = host->pmecc_alpha_to;
> + ? ? ? int16_t __iomem *index_of = host->pmecc_index_of;
> + ? ? ? int16_t *partial_syn = host->pmecc_partial_syn;
> + ? ? ? const int cap = host->pmecc_corr_cap;
> + ? ? ? int16_t *si;
> + ? ? ? int i, j;
> +
> + ? ? ? /* si[] is a table that holds the current syndrome value,
> + ? ? ? ?* an element of that table belongs to the field
> + ? ? ? ?*/
> + ? ? ? si = host->pmecc_si;
> +
> + ? ? ? memset(&si[1], 0, sizeof(int16_t) * (2 * cap - 1));
> +
> + ? ? ? /* Computation 2t syndromes based on S(x) */
> + ? ? ? /* Odd syndromes */
> + ? ? ? for (i = 1; i < 2 * cap; i += 2) {
> + ? ? ? ? ? ? ? for (j = 0; j < host->pmecc_degree; j++) {
> + ? ? ? ? ? ? ? ? ? ? ? if (partial_syn[i] & ((unsigned short)0x1 << j))
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? si[i] = readw_relaxed(alpha_to + i * j) ^ si[i];
> + ? ? ? ? ? ? ? }
> + ? ? ? }
> + ? ? ? /* Even syndrome = (Odd syndrome) ** 2 */
> + ? ? ? for (i = 2, j = 1; j <= cap; i = ++j << 1) {
> + ? ? ? ? ? ? ? if (si[j] == 0) {
> + ? ? ? ? ? ? ? ? ? ? ? si[i] = 0;
> + ? ? ? ? ? ? ? } else {
> + ? ? ? ? ? ? ? ? ? ? ? int16_t tmp;
> +
> + ? ? ? ? ? ? ? ? ? ? ? tmp = readw_relaxed(index_of + si[j]);
> + ? ? ? ? ? ? ? ? ? ? ? tmp = (tmp * 2) % host->pmecc_cw_len;
> + ? ? ? ? ? ? ? ? ? ? ? si[i] = readw_relaxed(alpha_to + tmp);
> + ? ? ? ? ? ? ? }
> + ? ? ? }
> +
> + ? ? ? return;
> +}
> +
> +static void pmecc_get_sigma(struct mtd_info *mtd)
> +{
> + ? ? ? struct nand_chip *nand_chip = mtd->priv;
> + ? ? ? struct atmel_nand_host *host = nand_chip->priv;
> +
> + ? ? ? int16_t *lmu = host->pmecc_lmu;
> + ? ? ? int16_t *si = host->pmecc_si;
> + ? ? ? int *mu = host->pmecc_mu;
> + ? ? ? int *dmu = host->pmecc_dmu; ? ? /* Discrepancy */
> + ? ? ? int *delta = host->pmecc_delta; /* Delta order */
> + ? ? ? int cw_len = host->pmecc_cw_len;
> + ? ? ? const int16_t cap = host->pmecc_corr_cap;
> + ? ? ? int16_t __iomem *index_of = host->pmecc_index_of;
> + ? ? ? int16_t __iomem *alpha_to = host->pmecc_alpha_to;
> + ? ? ? int i, j, k;
> + ? ? ? uint32_t dmu_0_count, tmp;
> + ? ? ? int16_t (*smu)[2 * cap + 1];
> +
> + ? ? ? /* index of largest delta */
> + ? ? ? int ro;
> + ? ? ? int largest;
> + ? ? ? int diff;
> +
> + ? ? ? dmu_0_count = 0;
> + ? ? ? smu = (int16_t(*)[2 * cap + 1])host->pmecc_smu;
> +
> + ? ? ? /* First Row */
> +
> + ? ? ? /* Mu */
> + ? ? ? mu[0] = -1;
> +
> + ? ? ? memset(&smu[0][0], 0,
> + ? ? ? ? ? ? ? sizeof(int16_t) * (2 * cap + 1));
> + ? ? ? smu[0][0] = 1;
> +
> + ? ? ? /* discrepancy set to 1 */
> + ? ? ? dmu[0] = 1;
> + ? ? ? /* polynom order set to 0 */
> + ? ? ? lmu[0] = 0;
> + ? ? ? delta[0] = (mu[0] * 2 - lmu[0]) >> 1;
> +
> + ? ? ? /* Second Row */
> +
> + ? ? ? /* Mu */
> + ? ? ? mu[1] = 0;
> + ? ? ? /* Sigma(x) set to 1 */
> + ? ? ? memset(&smu[1][0], 0,
> + ? ? ? ? ? ? ? sizeof(int16_t) * (2 * cap + 1));
> + ? ? ? smu[1][0] = 1;
> +
> + ? ? ? /* discrepancy set to S1 */
> + ? ? ? dmu[1] = si[1];
> +
> + ? ? ? /* polynom order set to 0 */
> + ? ? ? lmu[1] = 0;
> +
> + ? ? ? delta[1] = (mu[1] * 2 - lmu[1]) >> 1;
> +
> + ? ? ? /* Init the Sigma(x) last row */
> + ? ? ? memset(&smu[cap + 1][0], 0,
> + ? ? ? ? ? ? ? sizeof(int16_t) * (2 * cap + 1));
> +
> + ? ? ? for (i = 1; i <= cap; i++) {
> + ? ? ? ? ? ? ? mu[i + 1] = i << 1;
> + ? ? ? ? ? ? ? /* Begin Computing Sigma (Mu+1) and L(mu) */
> + ? ? ? ? ? ? ? /* check if discrepancy is set to 0 */
> + ? ? ? ? ? ? ? if (dmu[i] == 0) {
> + ? ? ? ? ? ? ? ? ? ? ? dmu_0_count++;
> +
> + ? ? ? ? ? ? ? ? ? ? ? tmp = ((cap - (lmu[i] >> 1) - 1) / 2);
> + ? ? ? ? ? ? ? ? ? ? ? if ((cap - (lmu[i] >> 1) - 1) & 0x1)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? tmp += 2;
> + ? ? ? ? ? ? ? ? ? ? ? else
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? tmp += 1;
> +
> + ? ? ? ? ? ? ? ? ? ? ? if (dmu_0_count == tmp) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? for (j = 0; j <= (lmu[i] >> 1) + 1; j++)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? smu[cap + 1][j] = smu[i][j];
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? lmu[cap + 1] = lmu[i];
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return;
> + ? ? ? ? ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? ? ? ? ? /* copy polynom */
> + ? ? ? ? ? ? ? ? ? ? ? for (j = 0; j <= lmu[i] >> 1; j++)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? smu[i + 1][j] = smu[i][j];
> +
> + ? ? ? ? ? ? ? ? ? ? ? /* copy previous polynom order to the next */
> + ? ? ? ? ? ? ? ? ? ? ? lmu[i + 1] = lmu[i];
> + ? ? ? ? ? ? ? } else {
> + ? ? ? ? ? ? ? ? ? ? ? ro = 0;
> + ? ? ? ? ? ? ? ? ? ? ? largest = -1;
> + ? ? ? ? ? ? ? ? ? ? ? /* find largest delta with dmu != 0 */
> + ? ? ? ? ? ? ? ? ? ? ? for (j = 0; j < i; j++) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if ((dmu[j]) && (delta[j] > largest)) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? largest = delta[j];
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ro = j;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? ? ? ? ? /* compute difference */
> + ? ? ? ? ? ? ? ? ? ? ? diff = (mu[i] - mu[ro]);
> +
> + ? ? ? ? ? ? ? ? ? ? ? /* Compute degree of the new smu polynomial */
> + ? ? ? ? ? ? ? ? ? ? ? if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff))
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? lmu[i + 1] = lmu[i];
> + ? ? ? ? ? ? ? ? ? ? ? else
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2;
> +
> + ? ? ? ? ? ? ? ? ? ? ? /* Init smu[i+1] with 0 */
> + ? ? ? ? ? ? ? ? ? ? ? for (k = 0; k < (2 * cap + 1); k++)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? smu[i + 1][k] = 0;
> +
> + ? ? ? ? ? ? ? ? ? ? ? /* Compute smu[i+1] */
> + ? ? ? ? ? ? ? ? ? ? ? for (k = 0; k <= lmu[ro] >> 1; k++) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int16_t a, b, c;
> +
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (!(smu[ro][k] && dmu[i]))
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? continue;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? a = readw_relaxed(index_of + dmu[i]);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? b = readw_relaxed(index_of + dmu[ro]);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? c = readw_relaxed(index_of + smu[ro][k]);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? tmp = a + (cw_len - b) + c;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? a = readw_relaxed(alpha_to + tmp % cw_len);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? smu[i + 1][k + diff] = a;
> + ? ? ? ? ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? ? ? ? ? for (k = 0; k <= lmu[i] >> 1; k++)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? smu[i + 1][k] ^= smu[i][k];
> + ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? /* End Computing Sigma (Mu+1) and L(mu) */
> + ? ? ? ? ? ? ? /* In either case compute delta */
> + ? ? ? ? ? ? ? delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1;
> +
> + ? ? ? ? ? ? ? /* Do not compute discrepancy for the last iteration */
> + ? ? ? ? ? ? ? if (i >= cap)
> + ? ? ? ? ? ? ? ? ? ? ? continue;
> +
> + ? ? ? ? ? ? ? for (k = 0; k <= (lmu[i + 1] >> 1); k++) {
> + ? ? ? ? ? ? ? ? ? ? ? tmp = 2 * (i - 1);
> + ? ? ? ? ? ? ? ? ? ? ? if (k == 0) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dmu[i + 1] = si[tmp + 3];
> + ? ? ? ? ? ? ? ? ? ? ? } else if (smu[i + 1][k] && si[tmp + 3 - k]) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int16_t a, b, c;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? a = readw_relaxed(index_of + smu[i + 1][k]);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? b = si[2 * (i - 1) + 3 - k];
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? c = readw_relaxed(index_of + b);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? tmp = a + c;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? tmp %= cw_len;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dmu[i + 1] = readw_relaxed(alpha_to + tmp) ^
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dmu[i + 1];
> + ? ? ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? }
> + ? ? ? }
> +
> + ? ? ? return;
> +}
> +
> +static int pmecc_err_location(struct mtd_info *mtd)
> +{
> + ? ? ? struct nand_chip *nand_chip = mtd->priv;
> + ? ? ? struct atmel_nand_host *host = nand_chip->priv;
> + ? ? ? unsigned long end_time;
> + ? ? ? const int cap = host->pmecc_corr_cap;
> + ? ? ? int sector_size = host->pmecc_sector_size;
> + ? ? ? int err_nbr = 0; ? ? ? ?/* number of error */
> + ? ? ? int roots_nbr; ? ? ? ? ?/* number of roots */
> + ? ? ? int i;
> + ? ? ? uint32_t val;
> + ? ? ? int16_t (*smu)[2 * cap + 1];
> +
> + ? ? ? smu = (int16_t(*)[2 * cap + 1])host->pmecc_smu;
> +
> + ? ? ? pmerrloc_writel(host->pmerrloc_base, ELDIS, PMERRLOC_DISABLE);
> +
> + ? ? ? for (i = 0; i <= host->pmecc_lmu[cap + 1] >> 1; i++) {
> + ? ? ? ? ? ? ? pmerrloc_writel_sigma_relaxed(host->pmerrloc_base, i,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? smu[cap + 1][i]);
> + ? ? ? ? ? ? ? err_nbr++;
> + ? ? ? }
> +
> + ? ? ? val = (err_nbr - 1) << 16;
> + ? ? ? if (sector_size == 1024)
> + ? ? ? ? ? ? ? val |= 1;
> +
> + ? ? ? pmerrloc_writel(host->pmerrloc_base, ELCFG, val);
> + ? ? ? pmerrloc_writel(host->pmerrloc_base, ELEN,
> + ? ? ? ? ? ? ? ? ? ? ? sector_size * 8 + host->pmecc_degree * cap);
> +
> + ? ? ? end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS);
> + ? ? ? while (!(pmerrloc_readl_relaxed(host->pmerrloc_base, ELISR)
> + ? ? ? ? ? ? ? ?& PMERRLOC_CALC_DONE)) {
> + ? ? ? ? ? ? ? if (unlikely(time_after(jiffies, end_time))) {
> + ? ? ? ? ? ? ? ? ? ? ? dev_err(host->dev, "PMECC: Timeout to calculate error location.\n");
> + ? ? ? ? ? ? ? ? ? ? ? return -1;
> + ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? cpu_relax();
> + ? ? ? }
> +
> + ? ? ? roots_nbr = (pmerrloc_readl_relaxed(host->pmerrloc_base, ELISR)
> + ? ? ? ? ? ? ? & PMERRLOC_ERR_NUM_MASK) >> 8;
> + ? ? ? /* Number of roots == degree of smu hence <= cap */
> + ? ? ? if (roots_nbr == host->pmecc_lmu[cap + 1] >> 1)
> + ? ? ? ? ? ? ? return err_nbr - 1;
> +
> + ? ? ? /* Number of roots does not match the degree of smu
> + ? ? ? ?* unable to correct error */
> + ? ? ? return -1;
> +}
> +
> +static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf,
> + ? ? ? ? ? ? ? int extra_bytes, int err_nbr)
> +{
> + ? ? ? struct nand_chip *nand_chip = mtd->priv;
> + ? ? ? struct atmel_nand_host *host = nand_chip->priv;
> + ? ? ? int i = 0;
> + ? ? ? int byte_pos, bit_pos, sector_size, ecc_size;
> + ? ? ? uint32_t tmp;
> +
> + ? ? ? sector_size = host->pmecc_sector_size;
> + ? ? ? ecc_size = nand_chip->ecc.bytes;
> +
> + ? ? ? while (err_nbr) {
> + ? ? ? ? ? ? ? tmp = pmerrloc_readl_el_relaxed(host->pmerrloc_base, i) - 1;
> + ? ? ? ? ? ? ? byte_pos = tmp / 8;
> + ? ? ? ? ? ? ? bit_pos ?= tmp % 8;
> + ? ? ? ? ? ? ? dev_info(host->dev, "PMECC correction, byte_pos: %d bit_pos: %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? byte_pos, bit_pos);
> +
> + ? ? ? ? ? ? ? if (byte_pos < (sector_size + extra_bytes)) {
> + ? ? ? ? ? ? ? ? ? ? ? tmp = sector_size +
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pmecc_readl_relaxed(host->ecc, SADDR);
> +
> + ? ? ? ? ? ? ? ? ? ? ? if (byte_pos < tmp)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? *(buf + byte_pos) ^= (1 << bit_pos);
> + ? ? ? ? ? ? ? ? ? ? ? else
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? *(buf + byte_pos + ecc_size) ^= (1 << bit_pos);
> + ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? i++;
> + ? ? ? ? ? ? ? err_nbr--;
> + ? ? ? }
> +
> + ? ? ? return;
> +}
> +
> +static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf,
> + ? ? ? u8 *ecc)
> +{
> + ? ? ? struct nand_chip *nand_chip = mtd->priv;
> + ? ? ? struct atmel_nand_host *host = nand_chip->priv;
> + ? ? ? int i, err_nbr, eccbytes;
> + ? ? ? uint8_t *buf_pos;
> +
> + ? ? ? eccbytes = nand_chip->ecc.bytes;
> + ? ? ? for (i = 0; i < eccbytes; i++)
> + ? ? ? ? ? ? ? if (ecc[i] != 0xff)
> + ? ? ? ? ? ? ? ? ? ? ? goto normal_check;
> + ? ? ? /* Erased page, return OK */
> + ? ? ? return 0;
> +
> +normal_check:
> + ? ? ? for (i = 0; i < host->pmecc_sector_number; i++) {
> + ? ? ? ? ? ? ? err_nbr = 0;
> + ? ? ? ? ? ? ? if (pmecc_stat & 0x1) {
> + ? ? ? ? ? ? ? ? ? ? ? buf_pos = buf + i * host->pmecc_sector_size;
> +
> + ? ? ? ? ? ? ? ? ? ? ? pmecc_gen_syndrome(mtd, i);
> + ? ? ? ? ? ? ? ? ? ? ? pmecc_substitute(mtd);
> + ? ? ? ? ? ? ? ? ? ? ? pmecc_get_sigma(mtd);
> +
> + ? ? ? ? ? ? ? ? ? ? ? err_nbr = pmecc_err_location(mtd);
> + ? ? ? ? ? ? ? ? ? ? ? if (err_nbr == -1) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dev_err(host->dev, "PMECC: Too many errors\n");
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mtd->ecc_stats.failed++;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return -EIO;
> + ? ? ? ? ? ? ? ? ? ? ? } else {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pmecc_correct_data(mtd, buf_pos, 0, err_nbr);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mtd->ecc_stats.corrected += err_nbr;
> + ? ? ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? pmecc_stat >>= 1;
> + ? ? ? }
> +
> + ? ? ? return 0;
> +}
> +
> +static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
> + ? ? ? struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
> +{
> + ? ? ? struct atmel_nand_host *host = chip->priv;
> + ? ? ? int eccsize = chip->ecc.size;
> + ? ? ? uint8_t *oob = chip->oob_poi;
> + ? ? ? uint32_t *eccpos = chip->ecc.layout->eccpos;
> + ? ? ? uint32_t stat;
> + ? ? ? unsigned long end_time;
> +
> + ? ? ? pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST);
> + ? ? ? pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
> + ? ? ? pmecc_writel(host->ecc, CFG, (pmecc_readl_relaxed(host->ecc, CFG)
> + ? ? ? ? ? ? ? & ~PMECC_CFG_WRITE_OP) | PMECC_CFG_AUTO_ENABLE);
> +
> + ? ? ? pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE);
> + ? ? ? pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DATA);
> +
> + ? ? ? chip->read_buf(mtd, buf, eccsize);
> + ? ? ? chip->read_buf(mtd, oob, mtd->oobsize);
> +
> + ? ? ? end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS);
> + ? ? ? while ((pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY)) {
> + ? ? ? ? ? ? ? if (unlikely(time_after(jiffies, end_time))) {
> + ? ? ? ? ? ? ? ? ? ? ? dev_err(host->dev, "PMECC: Timeout to get error status.\n");
> + ? ? ? ? ? ? ? ? ? ? ? return -EIO;
> + ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? cpu_relax();
> + ? ? ? }
> +
> + ? ? ? stat = pmecc_readl_relaxed(host->ecc, ISR);
> + ? ? ? if (stat != 0)
> + ? ? ? ? ? ? ? if (pmecc_correction(mtd, stat, buf, &oob[eccpos[0]]) != 0)
> + ? ? ? ? ? ? ? ? ? ? ? return -EIO;
> +
> + ? ? ? return 0;
> +}
> +
> +static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
> + ? ? ? ? ? ? ? struct nand_chip *chip, const uint8_t *buf, int oob_required)
> +{
> + ? ? ? struct atmel_nand_host *host = chip->priv;
> + ? ? ? uint32_t *eccpos = chip->ecc.layout->eccpos;
> + ? ? ? int i, j;
> + ? ? ? unsigned long end_time;
> +
> + ? ? ? pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST);
> + ? ? ? pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
> +
> + ? ? ? pmecc_writel(host->ecc, CFG, (pmecc_readl_relaxed(host->ecc, CFG) |
> + ? ? ? ? ? ? ? PMECC_CFG_WRITE_OP) & ~PMECC_CFG_AUTO_ENABLE);
> +
> + ? ? ? pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE);
> + ? ? ? pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DATA);
> +
> + ? ? ? chip->write_buf(mtd, (u8 *)buf, mtd->writesize);
> +
> + ? ? ? end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS);
> + ? ? ? while ((pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY)) {
> + ? ? ? ? ? ? ? if (unlikely(time_after(jiffies, end_time))) {
> + ? ? ? ? ? ? ? ? ? ? ? dev_err(host->dev, "PMECC: Timeout to get ECC value.\n");
> + ? ? ? ? ? ? ? ? ? ? ? return -EIO;
> + ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? cpu_relax();
> + ? ? ? }
> +
> + ? ? ? for (i = 0; i < host->pmecc_sector_number; i++) {
> + ? ? ? ? ? ? ? for (j = 0; j < host->pmecc_bytes_per_sector; j++) {
> + ? ? ? ? ? ? ? ? ? ? ? int pos;
> +
> + ? ? ? ? ? ? ? ? ? ? ? pos = i * host->pmecc_bytes_per_sector + j;
> + ? ? ? ? ? ? ? ? ? ? ? chip->oob_poi[eccpos[pos]] =
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pmecc_readb_ecc_relaxed(host->ecc, i, j);
> + ? ? ? ? ? ? ? }
> + ? ? ? }
> + ? ? ? chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
> +
> + ? ? ? return 0;
> +}
> +
> +static void atmel_pmecc_core_init(struct mtd_info *mtd)
> +{
> + ? ? ? struct nand_chip *nand_chip = mtd->priv;
> + ? ? ? struct atmel_nand_host *host = nand_chip->priv;
> + ? ? ? uint32_t val = 0;
> + ? ? ? struct nand_ecclayout *ecc_layout;
> +
> + ? ? ? pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST);
> + ? ? ? pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
> +
> + ? ? ? switch (host->pmecc_corr_cap) {
> + ? ? ? case 2:
> + ? ? ? ? ? ? ? val = PMECC_CFG_BCH_ERR2;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case 4:
> + ? ? ? ? ? ? ? val = PMECC_CFG_BCH_ERR4;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case 8:
> + ? ? ? ? ? ? ? val = PMECC_CFG_BCH_ERR8;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case 12:
> + ? ? ? ? ? ? ? val = PMECC_CFG_BCH_ERR12;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case 24:
> + ? ? ? ? ? ? ? val = PMECC_CFG_BCH_ERR24;
> + ? ? ? ? ? ? ? break;
> + ? ? ? }
> +
> + ? ? ? if (host->pmecc_sector_size == 512)
> + ? ? ? ? ? ? ? val |= PMECC_CFG_SECTOR512;
> + ? ? ? else if (host->pmecc_sector_size == 1024)
> + ? ? ? ? ? ? ? val |= PMECC_CFG_SECTOR1024;
> +
> + ? ? ? switch (host->pmecc_sector_number) {
> + ? ? ? case 1:
> + ? ? ? ? ? ? ? val |= PMECC_CFG_PAGE_1SECTOR;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case 2:
> + ? ? ? ? ? ? ? val |= PMECC_CFG_PAGE_2SECTORS;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case 4:
> + ? ? ? ? ? ? ? val |= PMECC_CFG_PAGE_4SECTORS;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case 8:
> + ? ? ? ? ? ? ? val |= PMECC_CFG_PAGE_8SECTORS;
> + ? ? ? ? ? ? ? break;
> + ? ? ? }
> +
> + ? ? ? val |= (PMECC_CFG_READ_OP | PMECC_CFG_SPARE_DISABLE
> + ? ? ? ? ? ? ? | PMECC_CFG_AUTO_DISABLE);
> + ? ? ? pmecc_writel(host->ecc, CFG, val);
> +
> + ? ? ? ecc_layout = nand_chip->ecc.layout;
> + ? ? ? pmecc_writel(host->ecc, SAREA, mtd->oobsize - 1);
> + ? ? ? pmecc_writel(host->ecc, SADDR, ecc_layout->eccpos[0]);
> + ? ? ? pmecc_writel(host->ecc, EADDR,
> + ? ? ? ? ? ? ? ? ? ? ? ecc_layout->eccpos[ecc_layout->eccbytes - 1]);
> + ? ? ? /* See datasheet about PMECC Clock Control Register */
> + ? ? ? pmecc_writel(host->ecc, CLK, 2);
> + ? ? ? pmecc_writel(host->ecc, IDR, 0xff);
> + ? ? ? pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE);
> +}
> +
> +static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct atmel_nand_host *host)
> +{
> + ? ? ? struct mtd_info *mtd = &host->mtd;
> + ? ? ? struct nand_chip *nand_chip = &host->nand_chip;
> + ? ? ? struct resource *regs, *regs_pmerr, *regs_rom;
> + ? ? ? int cap, sector_size, err_no;
> +
> + ? ? ? cap = host->pmecc_corr_cap;
> + ? ? ? sector_size = host->pmecc_sector_size;
> + ? ? ? dev_info(host->dev, "Initialize PMECC params, cap: %d, sector: %d\n",
> + ? ? ? ? ? ? ? ?cap, sector_size);
> +
> + ? ? ? regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> + ? ? ? if (!regs) {
> + ? ? ? ? ? ? ? dev_warn(host->dev,
> + ? ? ? ? ? ? ? ? ? ? ? "Can't get I/O resource regs, rolling back on software ECC\n");
> + ? ? ? ? ? ? ? nand_chip->ecc.mode = NAND_ECC_SOFT;
> + ? ? ? ? ? ? ? return 0;
> + ? ? ? }
> +
> + ? ? ? host->ecc = ioremap(regs->start, resource_size(regs));
> + ? ? ? if (host->ecc == NULL) {
> + ? ? ? ? ? ? ? dev_err(host->dev, "ioremap failed\n");
> + ? ? ? ? ? ? ? err_no = -EIO;
> + ? ? ? ? ? ? ? goto err_pmecc_ioremap;
> + ? ? ? }
> +
> + ? ? ? regs_pmerr = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> + ? ? ? regs_rom = platform_get_resource(pdev, IORESOURCE_MEM, 3);
> + ? ? ? if (regs_pmerr && regs_rom) {
> + ? ? ? ? ? ? ? host->pmerrloc_base = ioremap(regs_pmerr->start,
> + ? ? ? ? ? ? ? ? ? ? ? resource_size(regs_pmerr));
> + ? ? ? ? ? ? ? host->pmecc_rom_base = ioremap(regs_rom->start,
> + ? ? ? ? ? ? ? ? ? ? ? resource_size(regs_rom));
> +
> + ? ? ? ? ? ? ? if (!host->pmerrloc_base || !host->pmecc_rom_base) {
> + ? ? ? ? ? ? ? ? ? ? ? dev_err(host->dev,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "Can not get I/O resource for PMECC controller!\n");
> + ? ? ? ? ? ? ? ? ? ? ? err_no = -EIO;
> + ? ? ? ? ? ? ? ? ? ? ? goto err_pmloc_ioremap;
> + ? ? ? ? ? ? ? }
> + ? ? ? }
If regs in the device tree is not fully populated,
platform_get_resource will fail returning NULL.
host->pmerrloc_base and host->pmecc_rom_base will also be NULL (from
the kzalloc) but no error.
=> it seems that the if (!host->pmerrloc_base ||
!host->pmecc_rom_base) was meant to be outside the if (regs_pmerr &&
regs_rom)
> +
> + ? ? ? /* 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 2048:
> + ? ? ? ? ? ? ? host->pmecc_degree = PMECC_GF_DIMENSION_13;
> + ? ? ? ? ? ? ? host->pmecc_cw_len = (1 << host->pmecc_degree) - 1;
> + ? ? ? ? ? ? ? host->pmecc_sector_number = mtd->writesize / sector_size;
> + ? ? ? ? ? ? ? host->pmecc_bytes_per_sector = pmecc_get_ecc_bytes(
> + ? ? ? ? ? ? ? ? ? ? ? cap, sector_size);
> + ? ? ? ? ? ? ? host->pmecc_alpha_to = pmecc_get_alpha_to(host);
> + ? ? ? ? ? ? ? host->pmecc_index_of = host->pmecc_rom_base +
> + ? ? ? ? ? ? ? ? ? ? ? host->pmecc_lookup_table_offset;
> +
> + ? ? ? ? ? ? ? nand_chip->ecc.steps = 1;
> + ? ? ? ? ? ? ? nand_chip->ecc.strength = cap;
> + ? ? ? ? ? ? ? nand_chip->ecc.bytes = host->pmecc_bytes_per_sector *
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?host->pmecc_sector_number;
> + ? ? ? ? ? ? ? if (nand_chip->ecc.bytes > mtd->oobsize - 2) {
> + ? ? ? ? ? ? ? ? ? ? ? dev_err(host->dev, "No room for ECC bytes\n");
> + ? ? ? ? ? ? ? ? ? ? ? err_no = -EINVAL;
> + ? ? ? ? ? ? ? ? ? ? ? goto err_no_ecc_room;
> + ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? pmecc_config_ecc_layout(&atmel_pmecc_oobinfo,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mtd->oobsize,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? nand_chip->ecc.bytes);
> + ? ? ? ? ? ? ? nand_chip->ecc.layout = &atmel_pmecc_oobinfo;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case 512:
> + ? ? ? case 1024:
> + ? ? ? case 4096:
> + ? ? ? ? ? ? ? /* TODO */
> + ? ? ? ? ? ? ? dev_warn(host->dev,
> + ? ? ? ? ? ? ? ? ? ? ? "Unsupported page size for PMECC, use Software ECC\n");
> + ? ? ? default:
> + ? ? ? ? ? ? ? /* page size not handled by HW ECC */
> + ? ? ? ? ? ? ? /* switching back to soft ECC */
> + ? ? ? ? ? ? ? nand_chip->ecc.mode = NAND_ECC_SOFT;
> + ? ? ? ? ? ? ? return 0;
> + ? ? ? }
> +
> + ? ? ? /* Allocate data for PMECC computation */
> + ? ? ? err_no = pmecc_data_alloc(host);
> + ? ? ? if (err_no) {
> + ? ? ? ? ? ? ? dev_err(host->dev,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "Cannot allocate memory for PMECC computation!\n");
> + ? ? ? ? ? ? ? goto err_pmecc_data_alloc;
> + ? ? ? }
> +
> + ? ? ? nand_chip->ecc.read_page = atmel_nand_pmecc_read_page;
> + ? ? ? nand_chip->ecc.write_page = atmel_nand_pmecc_write_page;
> +
> + ? ? ? atmel_pmecc_core_init(mtd);
> +
> + ? ? ? return 0;
> +
> +err_pmecc_data_alloc:
> +err_no_ecc_room:
> +err_pmloc_ioremap:
> + ? ? ? iounmap(host->ecc);
> + ? ? ? if (host->pmerrloc_base)
> + ? ? ? ? ? ? ? iounmap(host->pmerrloc_base);
> + ? ? ? if (host->pmecc_rom_base)
> + ? ? ? ? ? ? ? iounmap(host->pmecc_rom_base);
> +err_pmecc_ioremap:
> + ? ? ? return err_no;
> +}
> +
> +/*
> ?* Calculate HW ECC
> ?*
> ?* function called after a write
> @@ -747,7 +1459,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> ? ? ? ?}
>
> ? ? ? ?if (nand_chip->ecc.mode == NAND_ECC_HW) {
> - ? ? ? ? ? ? ? res = atmel_hw_nand_init_params(pdev, host);
> + ? ? ? ? ? ? ? if (host->has_pmecc)
> + ? ? ? ? ? ? ? ? ? ? ? res = atmel_pmecc_nand_init_params(pdev, host);
> + ? ? ? ? ? ? ? else
> + ? ? ? ? ? ? ? ? ? ? ? res = atmel_hw_nand_init_params(pdev, host);
> +
> ? ? ? ? ? ? ? ?if (res != 0)
> ? ? ? ? ? ? ? ? ? ? ? ?goto err_hw_ecc;
> ? ? ? ?}
> @@ -766,8 +1482,16 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> ? ? ? ? ? ? ? ?return res;
>
> ?err_scan_tail:
> + ? ? ? if (host->has_pmecc && host->nand_chip.ecc.mode == NAND_ECC_HW) {
> + ? ? ? ? ? ? ? pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
> + ? ? ? ? ? ? ? pmecc_data_free(host);
> + ? ? ? }
> ? ? ? ?if (host->ecc)
> ? ? ? ? ? ? ? ?iounmap(host->ecc);
> + ? ? ? if (host->pmerrloc_base)
> + ? ? ? ? ? ? ? iounmap(host->pmerrloc_base);
> + ? ? ? if (host->pmecc_rom_base)
> + ? ? ? ? ? ? ? iounmap(host->pmecc_rom_base);
> ?err_hw_ecc:
> ?err_scan_ident:
> ?err_no_card:
> @@ -793,8 +1517,19 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
>
> ? ? ? ?atmel_nand_disable(host);
>
> + ? ? ? if (host->has_pmecc && host->nand_chip.ecc.mode == NAND_ECC_HW) {
> + ? ? ? ? ? ? ? pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
> + ? ? ? ? ? ? ? pmerrloc_writel(host->pmerrloc_base, ELDIS,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? PMERRLOC_DISABLE);
> + ? ? ? ? ? ? ? pmecc_data_free(host);
> + ? ? ? }
> +
> ? ? ? ?if (host->ecc)
> ? ? ? ? ? ? ? ?iounmap(host->ecc);
> + ? ? ? if (host->pmecc_rom_base)
> + ? ? ? ? ? ? ? iounmap(host->pmecc_rom_base);
> + ? ? ? if (host->pmerrloc_base)
> + ? ? ? ? ? ? ? iounmap(host->pmerrloc_base);
>
> ? ? ? ?if (host->dma_chan)
> ? ? ? ? ? ? ? ?dma_release_channel(host->dma_chan);
> diff --git a/drivers/mtd/nand/atmel_nand_ecc.h b/drivers/mtd/nand/atmel_nand_ecc.h
> index 578c776..8a1e9a6 100644
> --- a/drivers/mtd/nand/atmel_nand_ecc.h
> +++ b/drivers/mtd/nand/atmel_nand_ecc.h
> @@ -3,7 +3,7 @@
> ?* Based on AT91SAM9260 datasheet revision B.
> ?*
> ?* Copyright (C) 2007 Andrew Victor
> - * Copyright (C) 2007 Atmel Corporation.
> + * Copyright (C) 2007 - 2012 Atmel Corporation.
> ?*
> ?* 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
> @@ -36,4 +36,116 @@
> ?#define ATMEL_ECC_NPR ? ? ? ? ?0x10 ? ? ? ? ? ? ? ? ? ?/* NParity register */
> ?#define ? ? ? ? ? ? ? ?ATMEL_ECC_NPARITY ? ? ? (0xffff << 0) ? ? ? ? ? /* NParity */
>
> +/* PMECC Register Definitions */
> +#define ATMEL_PMECC_CFG ? ? ? ? ? ? ? ? ? ? ? ?0x000 ? /* Configuration Register */
> +#define ? ? ? ? ? ? ? ?PMECC_CFG_BCH_ERR2 ? ? ? ? ? ? ?(0 << 0)
> +#define ? ? ? ? ? ? ? ?PMECC_CFG_BCH_ERR4 ? ? ? ? ? ? ?(1 << 0)
> +#define ? ? ? ? ? ? ? ?PMECC_CFG_BCH_ERR8 ? ? ? ? ? ? ?(2 << 0)
> +#define ? ? ? ? ? ? ? ?PMECC_CFG_BCH_ERR12 ? ? ? ? ? ? (3 << 0)
> +#define ? ? ? ? ? ? ? ?PMECC_CFG_BCH_ERR24 ? ? ? ? ? ? (4 << 0)
> +
> +#define ? ? ? ? ? ? ? ?PMECC_CFG_SECTOR512 ? ? ? ? ? ? (0 << 4)
> +#define ? ? ? ? ? ? ? ?PMECC_CFG_SECTOR1024 ? ? ? ? ? ?(1 << 4)
> +
> +#define ? ? ? ? ? ? ? ?PMECC_CFG_PAGE_1SECTOR ? ? ? ? ?(0 << 8)
> +#define ? ? ? ? ? ? ? ?PMECC_CFG_PAGE_2SECTORS ? ? ? ? (1 << 8)
> +#define ? ? ? ? ? ? ? ?PMECC_CFG_PAGE_4SECTORS ? ? ? ? (2 << 8)
> +#define ? ? ? ? ? ? ? ?PMECC_CFG_PAGE_8SECTORS ? ? ? ? (3 << 8)
> +
> +#define ? ? ? ? ? ? ? ?PMECC_CFG_READ_OP ? ? ? ? ? ? ? (0 << 12)
> +#define ? ? ? ? ? ? ? ?PMECC_CFG_WRITE_OP ? ? ? ? ? ? ?(1 << 12)
> +
> +#define ? ? ? ? ? ? ? ?PMECC_CFG_SPARE_ENABLE ? ? ? ? ?(1 << 16)
> +#define ? ? ? ? ? ? ? ?PMECC_CFG_SPARE_DISABLE ? ? ? ? (0 << 16)
> +
> +#define ? ? ? ? ? ? ? ?PMECC_CFG_AUTO_ENABLE ? ? ? ? ? (1 << 20)
> +#define ? ? ? ? ? ? ? ?PMECC_CFG_AUTO_DISABLE ? ? ? ? ?(0 << 20)
> +
> +#define ATMEL_PMECC_SAREA ? ? ? ? ? ? ?0x004 ? /* Spare area size */
> +#define ATMEL_PMECC_SADDR ? ? ? ? ? ? ?0x008 ? /* PMECC starting address */
> +#define ATMEL_PMECC_EADDR ? ? ? ? ? ? ?0x00c ? /* PMECC ending address */
> +#define ATMEL_PMECC_CLK ? ? ? ? ? ? ? ? ? ? ? ?0x010 ? /* PMECC clock control */
> +#define ? ? ? ? ? ? ? ?PMECC_CLK_133MHZ ? ? ? ? ? ? ? ?(2 << 0)
> +
> +#define ATMEL_PMECC_CTRL ? ? ? ? ? ? ? 0x014 ? /* PMECC control register */
> +#define ? ? ? ? ? ? ? ?PMECC_CTRL_RST ? ? ? ? ? ? ? ? ?(1 << 0)
> +#define ? ? ? ? ? ? ? ?PMECC_CTRL_DATA ? ? ? ? ? ? ? ? (1 << 1)
> +#define ? ? ? ? ? ? ? ?PMECC_CTRL_USER ? ? ? ? ? ? ? ? (1 << 2)
> +#define ? ? ? ? ? ? ? ?PMECC_CTRL_ENABLE ? ? ? ? ? ? ? (1 << 4)
> +#define ? ? ? ? ? ? ? ?PMECC_CTRL_DISABLE ? ? ? ? ? ? ?(1 << 5)
> +
> +#define ATMEL_PMECC_SR ? ? ? ? ? ? ? ? 0x018 ? /* PMECC status register */
> +#define ? ? ? ? ? ? ? ?PMECC_SR_BUSY ? ? ? ? ? ? ? ? ? (1 << 0)
> +#define ? ? ? ? ? ? ? ?PMECC_SR_ENABLE ? ? ? ? ? ? ? ? (1 << 4)
> +
> +#define ATMEL_PMECC_IER ? ? ? ? ? ? ? ? ? ? ? ?0x01c ? /* PMECC interrupt enable */
> +#define ? ? ? ? ? ? ? ?PMECC_IER_ENABLE ? ? ? ? ? ? ? ?(1 << 0)
> +#define ATMEL_PMECC_IDR ? ? ? ? ? ? ? ? ? ? ? ?0x020 ? /* PMECC interrupt disable */
> +#define ? ? ? ? ? ? ? ?PMECC_IER_DISABLE ? ? ? ? ? ? ? (1 << 0)
> +#define ATMEL_PMECC_IMR ? ? ? ? ? ? ? ? ? ? ? ?0x024 ? /* PMECC interrupt mask */
> +#define ? ? ? ? ? ? ? ?PMECC_IER_MASK ? ? ? ? ? ? ? ? ?(1 << 0)
> +#define ATMEL_PMECC_ISR ? ? ? ? ? ? ? ? ? ? ? ?0x028 ? /* PMECC interrupt status */
> +#define ATMEL_PMECC_ECCx ? ? ? ? ? ? ? 0x040 ? /* PMECC ECC x */
> +#define ATMEL_PMECC_REMx ? ? ? ? ? ? ? 0x240 ? /* PMECC REM x */
> +
> +/* PMERRLOC Register Definitions */
> +#define ATMEL_PMERRLOC_ELCFG ? ? ? ? ? 0x000 ? /* Error location config */
> +#define ? ? ? ? ? ? ? ?PMERRLOC_ELCFG_SECTOR_512 ? ? ? (0 << 0)
> +#define ? ? ? ? ? ? ? ?PMERRLOC_ELCFG_SECTOR_1024 ? ? ?(1 << 0)
> +#define ? ? ? ? ? ? ? ?PMERRLOC_ELCFG_NUM_ERRORS(n) ? ?((n) << 16)
> +
> +#define ATMEL_PMERRLOC_ELPRIM ? ? ? ? ?0x004 ? /* Error location primitive */
> +#define ATMEL_PMERRLOC_ELEN ? ? ? ? ? ?0x008 ? /* Error location enable */
> +#define ATMEL_PMERRLOC_ELDIS ? ? ? ? ? 0x00c ? /* Error location disable */
> +#define ? ? ? ? ? ? ? ?PMERRLOC_DISABLE ? ? ? ? ? ? ? ?(1 << 0)
> +
> +#define ATMEL_PMERRLOC_ELSR ? ? ? ? ? ?0x010 ? /* Error location status */
> +#define ? ? ? ? ? ? ? ?PMERRLOC_ELSR_BUSY ? ? ? ? ? ? ?(1 << 0)
> +#define ATMEL_PMERRLOC_ELIER ? ? ? ? ? 0x014 ? /* Error location int enable */
> +#define ATMEL_PMERRLOC_ELIDR ? ? ? ? ? 0x018 ? /* Error location int disable */
> +#define ATMEL_PMERRLOC_ELIMR ? ? ? ? ? 0x01c ? /* Error location int mask */
> +#define ATMEL_PMERRLOC_ELISR ? ? ? ? ? 0x020 ? /* Error location int status */
> +#define ? ? ? ? ? ? ? ?PMERRLOC_ERR_NUM_MASK ? ? ? ? ? (0x1f << 8)
> +#define ? ? ? ? ? ? ? ?PMERRLOC_CALC_DONE ? ? ? ? ? ? ?(1 << 0)
> +#define ATMEL_PMERRLOC_SIGMAx ? ? ? ? ?0x028 ? /* Error location SIGMA x */
> +#define ATMEL_PMERRLOC_ELx ? ? ? ? ? ? 0x08c ? /* Error location x */
> +
> +/* Register access macros for PMECC */
> +#define pmecc_readl_relaxed(addr, reg) \
> + ? ? ? readl_relaxed((addr) + ATMEL_PMECC_##reg)
> +
> +#define pmecc_writel(addr, reg, value) \
> + ? ? ? writel((value), (addr) + ATMEL_PMECC_##reg)
> +
> +#define pmecc_readb_ecc_relaxed(addr, sector, n) \
> + ? ? ? readb_relaxed((addr) + ATMEL_PMECC_ECCx + ((sector) * 0x40) + (n))
> +
> +#define pmecc_readl_rem_relaxed(addr, sector, n) \
> + ? ? ? readl_relaxed((addr) + ATMEL_PMECC_REMx + ((sector) * 0x40) + ((n) * 4))
> +
> +#define pmerrloc_readl_relaxed(addr, reg) \
> + ? ? ? readl_relaxed((addr) + ATMEL_PMERRLOC_##reg)
> +
> +#define pmerrloc_writel(addr, reg, value) \
> + ? ? ? writel((value), (addr) + ATMEL_PMERRLOC_##reg)
> +
> +#define pmerrloc_writel_sigma_relaxed(addr, n, value) \
> + ? ? ? writel_relaxed((value), (addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4))
> +
> +#define pmerrloc_readl_sigma_relaxed(addr, n) \
> + ? ? ? readl_relaxed((addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4))
> +
> +#define pmerrloc_readl_el_relaxed(addr, n) \
> + ? ? ? readl_relaxed((addr) + ATMEL_PMERRLOC_ELx + ((n) * 4))
> +
> +/* Galois field dimension */
> +#define PMECC_GF_DIMENSION_13 ? ? ? ? ? ? ? ? ?13
> +#define PMECC_GF_DIMENSION_14 ? ? ? ? ? ? ? ? ?14
> +
> +#define PMECC_LOOKUP_TABLE_SIZE_512 ? ? ? ? ? ?0x2000
> +#define PMECC_LOOKUP_TABLE_SIZE_1024 ? ? ? ? ? 0x4000
> +
> +/* Time out value for reading PMECC status register */
> +#define PMECC_MAX_TIMEOUT_MS ? ? ? ? ? ? ? ? ? 100
> +
> ?#endif
> --
> 1.7.9.5
>
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
Regards.
Richard.
--
for me, ck means con kolivas and not calvin klein... does it mean I'm a geek ?
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v11 2/4] MTD: at91: add dt parameters for Atmel PMECC
2012-06-25 10:07 ` [PATCH v11 2/4] MTD: at91: add dt parameters for Atmel PMECC Josh Wu
@ 2012-06-25 10:35 ` Richard Genoud
2012-06-26 6:05 ` Josh Wu
0 siblings, 1 reply; 22+ messages in thread
From: Richard Genoud @ 2012-06-25 10:35 UTC (permalink / raw)
To: linux-arm-kernel
2012/6/25 Josh Wu <josh.wu@atmel.com>:
> Add DT support for PMECC parameters.
>
> Signed-off-by: Hong Xu <hong.xu@atmel.com>
> Signed-off-by: Josh Wu <josh.wu@atmel.com>
> ---
> ?.../devicetree/bindings/mtd/atmel-nand.txt ? ? ? ? | ? ?9 ++++
> ?drivers/mtd/nand/atmel_nand.c ? ? ? ? ? ? ? ? ? ? ?| ? 52 +++++++++++++++++++-
> ?2 files changed, 60 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/devicetree/bindings/mtd/atmel-nand.txt b/Documentation/devicetree/bindings/mtd/atmel-nand.txt
> index a200695..ed6927a 100644
> --- a/Documentation/devicetree/bindings/mtd/atmel-nand.txt
> +++ b/Documentation/devicetree/bindings/mtd/atmel-nand.txt
> @@ -16,6 +16,15 @@ Optional properties:
> ?- nand-ecc-mode : String, operation mode of the NAND ecc mode, soft by default.
> ? Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first",
> ? "soft_bch".
> +- atmel,has-pmecc : boolean to enable Programmable Multibit ECC hardware.
> + ?Only supported by at91sam9x5 or later sam9 product.
> +- atmel,pmecc-cap : error correct capability for Programmable Multibit ECC
> + ?Controller. Supported values are: 2, 4, 8, 12, 24.
> +- atmel,pmecc-sector-size : sector size for ECC computation. Supported values
> + ?are: 512, 1024.
> +- atmel,pmecc-lookup-table-offset : includes two offsets of lookup table in ROM
> + ?for different sector size. First one is for sector size 512, the next is for
> + ?sector size 1024.
> ?- nand-bus-width : 8 or 16 bus width if not present 8
> ?- nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
>
Maybe you should update the reg parameter documentation with the
needed resources.
> diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
> index 7a41a04..b97ad9f 100644
> --- a/drivers/mtd/nand/atmel_nand.c
> +++ b/drivers/mtd/nand/atmel_nand.c
> @@ -93,6 +93,11 @@ struct atmel_nand_host {
>
> ? ? ? ?struct completion ? ? ? comp;
> ? ? ? ?struct dma_chan ? ? ? ? *dma_chan;
> +
> + ? ? ? bool ? ? ? ? ? ? ? ? ? ?has_pmecc;
> + ? ? ? u8 ? ? ? ? ? ? ? ? ? ? ?pmecc_corr_cap;
> + ? ? ? u16 ? ? ? ? ? ? ? ? ? ? pmecc_sector_size;
> + ? ? ? u32 ? ? ? ? ? ? ? ? ? ? pmecc_lookup_table_offset;
> ?};
>
> ?static int cpu_has_dma(void)
> @@ -481,7 +486,8 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
> ?static int __devinit atmel_of_init_port(struct atmel_nand_host *host,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct device_node *np)
> ?{
> - ? ? ? u32 val;
> + ? ? ? u32 val, table_offset;
> + ? ? ? u32 offset[2];
> ? ? ? ?int ecc_mode;
> ? ? ? ?struct atmel_nand_data *board = &host->board;
> ? ? ? ?enum of_gpio_flags flags;
> @@ -517,6 +523,50 @@ static int __devinit atmel_of_init_port(struct atmel_nand_host *host,
> ? ? ? ?board->enable_pin = of_get_gpio(np, 1);
> ? ? ? ?board->det_pin = of_get_gpio(np, 2);
>
> + ? ? ? host->has_pmecc = of_property_read_bool(np, "atmel,has-pmecc");
> +
> + ? ? ? if (!(board->ecc_mode == NAND_ECC_HW) || !host->has_pmecc)
> + ? ? ? ? ? ? ? return 0; ? ? ? /* Not using PMECC */
> +
> + ? ? ? /* use PMECC, get correction capability, sector size and lookup
> + ? ? ? ?* table offset.
> + ? ? ? ?*/
> + ? ? ? if (of_property_read_u32(np, "atmel,pmecc-cap", &val) != 0) {
> + ? ? ? ? ? ? ? dev_err(host->dev, "Cannot decide PMECC Capability\n");
> + ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? } else if ((val != 2) && (val != 4) && (val != 8) && (val != 12) &&
> + ? ? ? ? ? (val != 24)) {
> + ? ? ? ? ? ? ? dev_err(host->dev,
> + ? ? ? ? ? ? ? ? ? ? ? "Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n",
> + ? ? ? ? ? ? ? ? ? ? ? val);
> + ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? }
> + ? ? ? host->pmecc_corr_cap = (u8)val;
> +
> + ? ? ? if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) != 0) {
> + ? ? ? ? ? ? ? dev_err(host->dev, "Cannot decide PMECC Sector Size\n");
> + ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? } else if ((val != 512) && (val != 1024)) {
> + ? ? ? ? ? ? ? dev_err(host->dev,
> + ? ? ? ? ? ? ? ? ? ? ? "Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n",
> + ? ? ? ? ? ? ? ? ? ? ? val);
> + ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? }
> + ? ? ? host->pmecc_sector_size = (u16)val;
> +
> + ? ? ? if (of_property_read_u32_array(np, "atmel,pmecc-lookup-table-offset",
> + ? ? ? ? ? ? ? ? ? ? ? offset, 2) != 0) {
> + ? ? ? ? ? ? ? dev_err(host->dev, "Cannot get PMECC lookup table offset\n");
> + ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? }
> + ? ? ? table_offset = host->pmecc_sector_size == 512 ? offset[0] : offset[1];
> +
> + ? ? ? if (!table_offset) {
> + ? ? ? ? ? ? ? dev_err(host->dev, "Invalid PMECC lookup table offset\n");
> + ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? }
> + ? ? ? host->pmecc_lookup_table_offset = table_offset;
> +
> ? ? ? ?return 0;
> ?}
> ?#else
> --
> 1.7.9.5
>
>
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
Richard.
--
for me, ck means con kolivas and not calvin klein... does it mean I'm a geek ?
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v11 4/4] MTD: at91: atmel_nand: Update driver to support Programmable Multibit ECC controller
2012-06-25 10:27 ` Richard Genoud
@ 2012-06-26 6:03 ` Josh Wu
0 siblings, 0 replies; 22+ messages in thread
From: Josh Wu @ 2012-06-26 6:03 UTC (permalink / raw)
To: linux-arm-kernel
Hi, Richard
On 6/25/2012 6:27 PM, Richard Genoud wrote:
> 2012/6/25 Josh Wu <josh.wu@atmel.com>:
>> The Programmable Multibit 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,
>> the sector size and ROM lookup table offsets.
>>
>> This driver has been tested on AT91SAM9X5-EK and AT91SAM9N12-EK with JFFS2,
>> YAFFS2, UBIFS and mtd-utils.
>>
>> Signed-off-by: Hong Xu <hong.xu@atmel.com>
>> Signed-off-by: Josh Wu <josh.wu@atmel.com>
>> ---
>> changes:
>> 1. atmel_nand_pmecc_write_page() will return -EIO when time out to read the pmecc status register.
>> 2. increase the time-out duration to 100ms, which has more toleration.
>> 3. add oob_required argument for pmecc read/write functions to align with v3.5-rc4.
>>
>> drivers/mtd/nand/atmel_nand.c | 737 ++++++++++++++++++++++++++++++++++++-
>> drivers/mtd/nand/atmel_nand_ecc.h | 114 +++++-
>> 2 files changed, 849 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
>> index b97ad9f..8c0f9e33 100644
>> --- a/drivers/mtd/nand/atmel_nand.c
>> +++ b/drivers/mtd/nand/atmel_nand.c
>> @@ -15,6 +15,8 @@
>> * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
>> * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
>> *
>> + * Add Programmable Multibit ECC support for various AT91 SoC
>> + * (C) Copyright 2012 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
>> @@ -98,8 +100,31 @@ struct atmel_nand_host {
>> u8 pmecc_corr_cap;
>> u16 pmecc_sector_size;
>> u32 pmecc_lookup_table_offset;
>> +
>> + int pmecc_bytes_per_sector;
>> + int pmecc_sector_number;
>> + int pmecc_degree; /* Degree of remainders */
>> + int pmecc_cw_len; /* Length of codeword */
>> +
>> + void __iomem *pmerrloc_base;
>> + void __iomem *pmecc_rom_base;
>> +
>> + /* lookup table for alpha_to and index_of */
>> + void __iomem *pmecc_alpha_to;
>> + void __iomem *pmecc_index_of;
>> +
>> + /* data for pmecc computation */
>> + int16_t *pmecc_partial_syn;
>> + int16_t *pmecc_si;
>> + int16_t *pmecc_smu; /* Sigma table */
>> + int16_t *pmecc_lmu; /* polynomal order */
>> + int *pmecc_mu;
>> + int *pmecc_dmu;
>> + int *pmecc_delta;
>> };
>>
>> +static struct nand_ecclayout atmel_pmecc_oobinfo;
>> +
>> static int cpu_has_dma(void)
>> {
>> return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
>> @@ -293,6 +318,693 @@ static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
>> }
>>
>> /*
>> + * Return number of ecc bytes per sector according to sector size and
>> + * correction capability
>> + *
>> + * Following table shows what at91 PMECC supported:
>> + * Correction Capability Sector_512_bytes Sector_1024_bytes
>> + * ===================== ================ =================
>> + * 2-bits 4-bytes 4-bytes
>> + * 4-bits 7-bytes 7-bytes
>> + * 8-bits 13-bytes 14-bytes
>> + * 12-bits 20-bytes 21-bytes
>> + * 24-bits 39-bytes 42-bytes
>> + */
>> +static int __devinit pmecc_get_ecc_bytes(int cap, int sector_size)
>> +{
>> + int m = 12 + sector_size / 512;
>> + return (m * cap + 7) / 8;
>> +}
>> +
>> +static void __devinit pmecc_config_ecc_layout(struct nand_ecclayout *layout,
>> + int oobsize, int ecc_len)
>> +{
>> + int i;
>> +
>> + layout->eccbytes = ecc_len;
>> +
>> + /* ECC will occupy the last ecc_len bytes continuously */
>> + for (i = 0; i < ecc_len; i++)
>> + layout->eccpos[i] = oobsize - ecc_len + i;
>> +
>> + layout->oobfree[0].offset = 2;
>> + layout->oobfree[0].length =
>> + oobsize - ecc_len - layout->oobfree[0].offset;
>> +}
>> +
>> +static void __devinit __iomem *pmecc_get_alpha_to(struct atmel_nand_host *host)
>> +{
>> + int table_size;
>> +
>> + table_size = host->pmecc_sector_size == 512 ?
>> + PMECC_LOOKUP_TABLE_SIZE_512 : PMECC_LOOKUP_TABLE_SIZE_1024;
>> +
>> + return host->pmecc_rom_base + host->pmecc_lookup_table_offset +
>> + table_size * sizeof(int16_t);
>> +}
>> +
>> +static void pmecc_data_free(struct atmel_nand_host *host)
>> +{
>> + kfree(host->pmecc_partial_syn);
>> + kfree(host->pmecc_si);
>> + kfree(host->pmecc_lmu);
>> + kfree(host->pmecc_smu);
>> + kfree(host->pmecc_mu);
>> + kfree(host->pmecc_dmu);
>> + kfree(host->pmecc_delta);
>> +}
>> +
>> +static int __devinit pmecc_data_alloc(struct atmel_nand_host *host)
>> +{
>> + const int cap = host->pmecc_corr_cap;
>> +
>> + host->pmecc_partial_syn = kzalloc((2 * cap + 1) * sizeof(int16_t),
>> + GFP_KERNEL);
>> + host->pmecc_si = kzalloc((2 * cap + 1) * sizeof(int16_t), GFP_KERNEL);
>> + host->pmecc_lmu = kzalloc((cap + 1) * sizeof(int16_t), GFP_KERNEL);
>> + host->pmecc_smu = kzalloc((cap + 2) * (2 * cap + 1) * sizeof(int16_t),
>> + GFP_KERNEL);
>> + host->pmecc_mu = kzalloc((cap + 1) * sizeof(int), GFP_KERNEL);
>> + host->pmecc_dmu = kzalloc((cap + 1) * sizeof(int), GFP_KERNEL);
>> + host->pmecc_delta = kzalloc((cap + 1) * sizeof(int), GFP_KERNEL);
>> +
>> + if (host->pmecc_partial_syn &&
>> + host->pmecc_si &&
>> + host->pmecc_lmu &&
>> + host->pmecc_smu &&
>> + host->pmecc_mu &&
>> + host->pmecc_dmu &&
>> + host->pmecc_delta)
>> + return 0;
>> +
>> + /* error happened */
>> + pmecc_data_free(host);
>> + return -ENOMEM;
>> +}
>> +
>> +static void pmecc_gen_syndrome(struct mtd_info *mtd, int sector)
>> +{
>> + struct nand_chip *nand_chip = mtd->priv;
>> + struct atmel_nand_host *host = nand_chip->priv;
>> + int i;
>> + uint32_t value;
>> +
>> + /* Fill odd syndromes */
>> + for (i = 0; i < host->pmecc_corr_cap; i++) {
>> + value = pmecc_readl_rem_relaxed(host->ecc, sector, i / 2);
>> + if (i & 1)
>> + value >>= 16;
>> + value &= 0xffff;
>> + host->pmecc_partial_syn[(2 * i) + 1] = (int16_t)value;
>> + }
>> +}
>> +
>> +static void pmecc_substitute(struct mtd_info *mtd)
>> +{
>> + struct nand_chip *nand_chip = mtd->priv;
>> + struct atmel_nand_host *host = nand_chip->priv;
>> + int16_t __iomem *alpha_to = host->pmecc_alpha_to;
>> + int16_t __iomem *index_of = host->pmecc_index_of;
>> + int16_t *partial_syn = host->pmecc_partial_syn;
>> + const int cap = host->pmecc_corr_cap;
>> + int16_t *si;
>> + int i, j;
>> +
>> + /* si[] is a table that holds the current syndrome value,
>> + * an element of that table belongs to the field
>> + */
>> + si = host->pmecc_si;
>> +
>> + memset(&si[1], 0, sizeof(int16_t) * (2 * cap - 1));
>> +
>> + /* Computation 2t syndromes based on S(x) */
>> + /* Odd syndromes */
>> + for (i = 1; i < 2 * cap; i += 2) {
>> + for (j = 0; j < host->pmecc_degree; j++) {
>> + if (partial_syn[i] & ((unsigned short)0x1 << j))
>> + si[i] = readw_relaxed(alpha_to + i * j) ^ si[i];
>> + }
>> + }
>> + /* Even syndrome = (Odd syndrome) ** 2 */
>> + for (i = 2, j = 1; j <= cap; i = ++j << 1) {
>> + if (si[j] == 0) {
>> + si[i] = 0;
>> + } else {
>> + int16_t tmp;
>> +
>> + tmp = readw_relaxed(index_of + si[j]);
>> + tmp = (tmp * 2) % host->pmecc_cw_len;
>> + si[i] = readw_relaxed(alpha_to + tmp);
>> + }
>> + }
>> +
>> + return;
>> +}
>> +
>> +static void pmecc_get_sigma(struct mtd_info *mtd)
>> +{
>> + struct nand_chip *nand_chip = mtd->priv;
>> + struct atmel_nand_host *host = nand_chip->priv;
>> +
>> + int16_t *lmu = host->pmecc_lmu;
>> + int16_t *si = host->pmecc_si;
>> + int *mu = host->pmecc_mu;
>> + int *dmu = host->pmecc_dmu; /* Discrepancy */
>> + int *delta = host->pmecc_delta; /* Delta order */
>> + int cw_len = host->pmecc_cw_len;
>> + const int16_t cap = host->pmecc_corr_cap;
>> + int16_t __iomem *index_of = host->pmecc_index_of;
>> + int16_t __iomem *alpha_to = host->pmecc_alpha_to;
>> + int i, j, k;
>> + uint32_t dmu_0_count, tmp;
>> + int16_t (*smu)[2 * cap + 1];
>> +
>> + /* index of largest delta */
>> + int ro;
>> + int largest;
>> + int diff;
>> +
>> + dmu_0_count = 0;
>> + smu = (int16_t(*)[2 * cap + 1])host->pmecc_smu;
>> +
>> + /* First Row */
>> +
>> + /* Mu */
>> + mu[0] = -1;
>> +
>> + memset(&smu[0][0], 0,
>> + sizeof(int16_t) * (2 * cap + 1));
>> + smu[0][0] = 1;
>> +
>> + /* discrepancy set to 1 */
>> + dmu[0] = 1;
>> + /* polynom order set to 0 */
>> + lmu[0] = 0;
>> + delta[0] = (mu[0] * 2 - lmu[0]) >> 1;
>> +
>> + /* Second Row */
>> +
>> + /* Mu */
>> + mu[1] = 0;
>> + /* Sigma(x) set to 1 */
>> + memset(&smu[1][0], 0,
>> + sizeof(int16_t) * (2 * cap + 1));
>> + smu[1][0] = 1;
>> +
>> + /* discrepancy set to S1 */
>> + dmu[1] = si[1];
>> +
>> + /* polynom order set to 0 */
>> + lmu[1] = 0;
>> +
>> + delta[1] = (mu[1] * 2 - lmu[1]) >> 1;
>> +
>> + /* Init the Sigma(x) last row */
>> + memset(&smu[cap + 1][0], 0,
>> + sizeof(int16_t) * (2 * cap + 1));
>> +
>> + for (i = 1; i <= cap; i++) {
>> + mu[i + 1] = i << 1;
>> + /* Begin Computing Sigma (Mu+1) and L(mu) */
>> + /* check if discrepancy is set to 0 */
>> + if (dmu[i] == 0) {
>> + dmu_0_count++;
>> +
>> + tmp = ((cap - (lmu[i] >> 1) - 1) / 2);
>> + if ((cap - (lmu[i] >> 1) - 1) & 0x1)
>> + tmp += 2;
>> + else
>> + tmp += 1;
>> +
>> + if (dmu_0_count == tmp) {
>> + for (j = 0; j <= (lmu[i] >> 1) + 1; j++)
>> + smu[cap + 1][j] = smu[i][j];
>> + lmu[cap + 1] = lmu[i];
>> + return;
>> + }
>> +
>> + /* copy polynom */
>> + for (j = 0; j <= lmu[i] >> 1; j++)
>> + smu[i + 1][j] = smu[i][j];
>> +
>> + /* copy previous polynom order to the next */
>> + lmu[i + 1] = lmu[i];
>> + } else {
>> + ro = 0;
>> + largest = -1;
>> + /* find largest delta with dmu != 0 */
>> + for (j = 0; j < i; j++) {
>> + if ((dmu[j]) && (delta[j] > largest)) {
>> + largest = delta[j];
>> + ro = j;
>> + }
>> + }
>> +
>> + /* compute difference */
>> + diff = (mu[i] - mu[ro]);
>> +
>> + /* Compute degree of the new smu polynomial */
>> + if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff))
>> + lmu[i + 1] = lmu[i];
>> + else
>> + lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2;
>> +
>> + /* Init smu[i+1] with 0 */
>> + for (k = 0; k < (2 * cap + 1); k++)
>> + smu[i + 1][k] = 0;
>> +
>> + /* Compute smu[i+1] */
>> + for (k = 0; k <= lmu[ro] >> 1; k++) {
>> + int16_t a, b, c;
>> +
>> + if (!(smu[ro][k] && dmu[i]))
>> + continue;
>> + a = readw_relaxed(index_of + dmu[i]);
>> + b = readw_relaxed(index_of + dmu[ro]);
>> + c = readw_relaxed(index_of + smu[ro][k]);
>> + tmp = a + (cw_len - b) + c;
>> + a = readw_relaxed(alpha_to + tmp % cw_len);
>> + smu[i + 1][k + diff] = a;
>> + }
>> +
>> + for (k = 0; k <= lmu[i] >> 1; k++)
>> + smu[i + 1][k] ^= smu[i][k];
>> + }
>> +
>> + /* End Computing Sigma (Mu+1) and L(mu) */
>> + /* In either case compute delta */
>> + delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1;
>> +
>> + /* Do not compute discrepancy for the last iteration */
>> + if (i >= cap)
>> + continue;
>> +
>> + for (k = 0; k <= (lmu[i + 1] >> 1); k++) {
>> + tmp = 2 * (i - 1);
>> + if (k == 0) {
>> + dmu[i + 1] = si[tmp + 3];
>> + } else if (smu[i + 1][k] && si[tmp + 3 - k]) {
>> + int16_t a, b, c;
>> + a = readw_relaxed(index_of + smu[i + 1][k]);
>> + b = si[2 * (i - 1) + 3 - k];
>> + c = readw_relaxed(index_of + b);
>> + tmp = a + c;
>> + tmp %= cw_len;
>> + dmu[i + 1] = readw_relaxed(alpha_to + tmp) ^
>> + dmu[i + 1];
>> + }
>> + }
>> + }
>> +
>> + return;
>> +}
>> +
>> +static int pmecc_err_location(struct mtd_info *mtd)
>> +{
>> + struct nand_chip *nand_chip = mtd->priv;
>> + struct atmel_nand_host *host = nand_chip->priv;
>> + unsigned long end_time;
>> + const int cap = host->pmecc_corr_cap;
>> + int sector_size = host->pmecc_sector_size;
>> + int err_nbr = 0; /* number of error */
>> + int roots_nbr; /* number of roots */
>> + int i;
>> + uint32_t val;
>> + int16_t (*smu)[2 * cap + 1];
>> +
>> + smu = (int16_t(*)[2 * cap + 1])host->pmecc_smu;
>> +
>> + pmerrloc_writel(host->pmerrloc_base, ELDIS, PMERRLOC_DISABLE);
>> +
>> + for (i = 0; i <= host->pmecc_lmu[cap + 1] >> 1; i++) {
>> + pmerrloc_writel_sigma_relaxed(host->pmerrloc_base, i,
>> + smu[cap + 1][i]);
>> + err_nbr++;
>> + }
>> +
>> + val = (err_nbr - 1) << 16;
>> + if (sector_size == 1024)
>> + val |= 1;
>> +
>> + pmerrloc_writel(host->pmerrloc_base, ELCFG, val);
>> + pmerrloc_writel(host->pmerrloc_base, ELEN,
>> + sector_size * 8 + host->pmecc_degree * cap);
>> +
>> + end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS);
>> + while (!(pmerrloc_readl_relaxed(host->pmerrloc_base, ELISR)
>> + & PMERRLOC_CALC_DONE)) {
>> + if (unlikely(time_after(jiffies, end_time))) {
>> + dev_err(host->dev, "PMECC: Timeout to calculate error location.\n");
>> + return -1;
>> + }
>> + cpu_relax();
>> + }
>> +
>> + roots_nbr = (pmerrloc_readl_relaxed(host->pmerrloc_base, ELISR)
>> + & PMERRLOC_ERR_NUM_MASK) >> 8;
>> + /* Number of roots == degree of smu hence <= cap */
>> + if (roots_nbr == host->pmecc_lmu[cap + 1] >> 1)
>> + return err_nbr - 1;
>> +
>> + /* Number of roots does not match the degree of smu
>> + * unable to correct error */
>> + return -1;
>> +}
>> +
>> +static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf,
>> + int extra_bytes, int err_nbr)
>> +{
>> + struct nand_chip *nand_chip = mtd->priv;
>> + struct atmel_nand_host *host = nand_chip->priv;
>> + int i = 0;
>> + int byte_pos, bit_pos, sector_size, ecc_size;
>> + uint32_t tmp;
>> +
>> + sector_size = host->pmecc_sector_size;
>> + ecc_size = nand_chip->ecc.bytes;
>> +
>> + while (err_nbr) {
>> + tmp = pmerrloc_readl_el_relaxed(host->pmerrloc_base, i) - 1;
>> + byte_pos = tmp / 8;
>> + bit_pos = tmp % 8;
>> + dev_info(host->dev, "PMECC correction, byte_pos: %d bit_pos: %d\n",
>> + byte_pos, bit_pos);
>> +
>> + if (byte_pos < (sector_size + extra_bytes)) {
>> + tmp = sector_size +
>> + pmecc_readl_relaxed(host->ecc, SADDR);
>> +
>> + if (byte_pos < tmp)
>> + *(buf + byte_pos) ^= (1 << bit_pos);
>> + else
>> + *(buf + byte_pos + ecc_size) ^= (1 << bit_pos);
>> + }
>> +
>> + i++;
>> + err_nbr--;
>> + }
>> +
>> + return;
>> +}
>> +
>> +static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf,
>> + u8 *ecc)
>> +{
>> + struct nand_chip *nand_chip = mtd->priv;
>> + struct atmel_nand_host *host = nand_chip->priv;
>> + int i, err_nbr, eccbytes;
>> + uint8_t *buf_pos;
>> +
>> + eccbytes = nand_chip->ecc.bytes;
>> + for (i = 0; i < eccbytes; i++)
>> + if (ecc[i] != 0xff)
>> + goto normal_check;
>> + /* Erased page, return OK */
>> + return 0;
>> +
>> +normal_check:
>> + for (i = 0; i < host->pmecc_sector_number; i++) {
>> + err_nbr = 0;
>> + if (pmecc_stat & 0x1) {
>> + buf_pos = buf + i * host->pmecc_sector_size;
>> +
>> + pmecc_gen_syndrome(mtd, i);
>> + pmecc_substitute(mtd);
>> + pmecc_get_sigma(mtd);
>> +
>> + err_nbr = pmecc_err_location(mtd);
>> + if (err_nbr == -1) {
>> + dev_err(host->dev, "PMECC: Too many errors\n");
>> + mtd->ecc_stats.failed++;
>> + return -EIO;
>> + } else {
>> + pmecc_correct_data(mtd, buf_pos, 0, err_nbr);
>> + mtd->ecc_stats.corrected += err_nbr;
>> + }
>> + }
>> + pmecc_stat >>= 1;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int atmel_nand_pmecc_read_page(struct mtd_info *mtd,
>> + struct nand_chip *chip, uint8_t *buf, int oob_required, int page)
>> +{
>> + struct atmel_nand_host *host = chip->priv;
>> + int eccsize = chip->ecc.size;
>> + uint8_t *oob = chip->oob_poi;
>> + uint32_t *eccpos = chip->ecc.layout->eccpos;
>> + uint32_t stat;
>> + unsigned long end_time;
>> +
>> + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST);
>> + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
>> + pmecc_writel(host->ecc, CFG, (pmecc_readl_relaxed(host->ecc, CFG)
>> + & ~PMECC_CFG_WRITE_OP) | PMECC_CFG_AUTO_ENABLE);
>> +
>> + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE);
>> + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DATA);
>> +
>> + chip->read_buf(mtd, buf, eccsize);
>> + chip->read_buf(mtd, oob, mtd->oobsize);
>> +
>> + end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS);
>> + while ((pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY)) {
>> + if (unlikely(time_after(jiffies, end_time))) {
>> + dev_err(host->dev, "PMECC: Timeout to get error status.\n");
>> + return -EIO;
>> + }
>> + cpu_relax();
>> + }
>> +
>> + stat = pmecc_readl_relaxed(host->ecc, ISR);
>> + if (stat != 0)
>> + if (pmecc_correction(mtd, stat, buf, &oob[eccpos[0]]) != 0)
>> + return -EIO;
>> +
>> + return 0;
>> +}
>> +
>> +static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
>> + struct nand_chip *chip, const uint8_t *buf, int oob_required)
>> +{
>> + struct atmel_nand_host *host = chip->priv;
>> + uint32_t *eccpos = chip->ecc.layout->eccpos;
>> + int i, j;
>> + unsigned long end_time;
>> +
>> + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST);
>> + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
>> +
>> + pmecc_writel(host->ecc, CFG, (pmecc_readl_relaxed(host->ecc, CFG) |
>> + PMECC_CFG_WRITE_OP) & ~PMECC_CFG_AUTO_ENABLE);
>> +
>> + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE);
>> + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DATA);
>> +
>> + chip->write_buf(mtd, (u8 *)buf, mtd->writesize);
>> +
>> + end_time = jiffies + msecs_to_jiffies(PMECC_MAX_TIMEOUT_MS);
>> + while ((pmecc_readl_relaxed(host->ecc, SR) & PMECC_SR_BUSY)) {
>> + if (unlikely(time_after(jiffies, end_time))) {
>> + dev_err(host->dev, "PMECC: Timeout to get ECC value.\n");
>> + return -EIO;
>> + }
>> + cpu_relax();
>> + }
>> +
>> + for (i = 0; i < host->pmecc_sector_number; i++) {
>> + for (j = 0; j < host->pmecc_bytes_per_sector; j++) {
>> + int pos;
>> +
>> + pos = i * host->pmecc_bytes_per_sector + j;
>> + chip->oob_poi[eccpos[pos]] =
>> + pmecc_readb_ecc_relaxed(host->ecc, i, j);
>> + }
>> + }
>> + chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
>> +
>> + return 0;
>> +}
>> +
>> +static void atmel_pmecc_core_init(struct mtd_info *mtd)
>> +{
>> + struct nand_chip *nand_chip = mtd->priv;
>> + struct atmel_nand_host *host = nand_chip->priv;
>> + uint32_t val = 0;
>> + struct nand_ecclayout *ecc_layout;
>> +
>> + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_RST);
>> + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
>> +
>> + switch (host->pmecc_corr_cap) {
>> + case 2:
>> + val = PMECC_CFG_BCH_ERR2;
>> + break;
>> + case 4:
>> + val = PMECC_CFG_BCH_ERR4;
>> + break;
>> + case 8:
>> + val = PMECC_CFG_BCH_ERR8;
>> + break;
>> + case 12:
>> + val = PMECC_CFG_BCH_ERR12;
>> + break;
>> + case 24:
>> + val = PMECC_CFG_BCH_ERR24;
>> + break;
>> + }
>> +
>> + if (host->pmecc_sector_size == 512)
>> + val |= PMECC_CFG_SECTOR512;
>> + else if (host->pmecc_sector_size == 1024)
>> + val |= PMECC_CFG_SECTOR1024;
>> +
>> + switch (host->pmecc_sector_number) {
>> + case 1:
>> + val |= PMECC_CFG_PAGE_1SECTOR;
>> + break;
>> + case 2:
>> + val |= PMECC_CFG_PAGE_2SECTORS;
>> + break;
>> + case 4:
>> + val |= PMECC_CFG_PAGE_4SECTORS;
>> + break;
>> + case 8:
>> + val |= PMECC_CFG_PAGE_8SECTORS;
>> + break;
>> + }
>> +
>> + val |= (PMECC_CFG_READ_OP | PMECC_CFG_SPARE_DISABLE
>> + | PMECC_CFG_AUTO_DISABLE);
>> + pmecc_writel(host->ecc, CFG, val);
>> +
>> + ecc_layout = nand_chip->ecc.layout;
>> + pmecc_writel(host->ecc, SAREA, mtd->oobsize - 1);
>> + pmecc_writel(host->ecc, SADDR, ecc_layout->eccpos[0]);
>> + pmecc_writel(host->ecc, EADDR,
>> + ecc_layout->eccpos[ecc_layout->eccbytes - 1]);
>> + /* See datasheet about PMECC Clock Control Register */
>> + pmecc_writel(host->ecc, CLK, 2);
>> + pmecc_writel(host->ecc, IDR, 0xff);
>> + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE);
>> +}
>> +
>> +static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev,
>> + struct atmel_nand_host *host)
>> +{
>> + struct mtd_info *mtd = &host->mtd;
>> + struct nand_chip *nand_chip = &host->nand_chip;
>> + struct resource *regs, *regs_pmerr, *regs_rom;
>> + int cap, sector_size, err_no;
>> +
>> + cap = host->pmecc_corr_cap;
>> + sector_size = host->pmecc_sector_size;
>> + dev_info(host->dev, "Initialize PMECC params, cap: %d, sector: %d\n",
>> + cap, sector_size);
>> +
>> + regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>> + if (!regs) {
>> + dev_warn(host->dev,
>> + "Can't get I/O resource regs, rolling back on software ECC\n");
>> + nand_chip->ecc.mode = NAND_ECC_SOFT;
>> + return 0;
>> + }
>> +
>> + host->ecc = ioremap(regs->start, resource_size(regs));
>> + if (host->ecc == NULL) {
>> + dev_err(host->dev, "ioremap failed\n");
>> + err_no = -EIO;
>> + goto err_pmecc_ioremap;
>> + }
>> +
>> + regs_pmerr = platform_get_resource(pdev, IORESOURCE_MEM, 2);
>> + regs_rom = platform_get_resource(pdev, IORESOURCE_MEM, 3);
>> + if (regs_pmerr && regs_rom) {
>> + host->pmerrloc_base = ioremap(regs_pmerr->start,
>> + resource_size(regs_pmerr));
>> + host->pmecc_rom_base = ioremap(regs_rom->start,
>> + resource_size(regs_rom));
>> +
>> + if (!host->pmerrloc_base || !host->pmecc_rom_base) {
>> + dev_err(host->dev,
>> + "Can not get I/O resource for PMECC controller!\n");
>> + err_no = -EIO;
>> + goto err_pmloc_ioremap;
>> + }
>> + }
> If regs in the device tree is not fully populated,
> platform_get_resource will fail returning NULL.
> host->pmerrloc_base and host->pmecc_rom_base will also be NULL (from
> the kzalloc) but no error.
> => it seems that the if (!host->pmerrloc_base ||
> !host->pmecc_rom_base) was meant to be outside the if (regs_pmerr &&
> regs_rom)
>
nice catching. you are right. I will fix this in next version.
>> +
>> + /* 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 2048:
>> + host->pmecc_degree = PMECC_GF_DIMENSION_13;
>> + host->pmecc_cw_len = (1 << host->pmecc_degree) - 1;
>> + host->pmecc_sector_number = mtd->writesize / sector_size;
>> + host->pmecc_bytes_per_sector = pmecc_get_ecc_bytes(
>> + cap, sector_size);
>> + host->pmecc_alpha_to = pmecc_get_alpha_to(host);
>> + host->pmecc_index_of = host->pmecc_rom_base +
>> + host->pmecc_lookup_table_offset;
>> +
>> + nand_chip->ecc.steps = 1;
>> + nand_chip->ecc.strength = cap;
>> + nand_chip->ecc.bytes = host->pmecc_bytes_per_sector *
>> + host->pmecc_sector_number;
>> + if (nand_chip->ecc.bytes > mtd->oobsize - 2) {
>> + dev_err(host->dev, "No room for ECC bytes\n");
>> + err_no = -EINVAL;
>> + goto err_no_ecc_room;
>> + }
>> + pmecc_config_ecc_layout(&atmel_pmecc_oobinfo,
>> + mtd->oobsize,
>> + nand_chip->ecc.bytes);
>> + nand_chip->ecc.layout = &atmel_pmecc_oobinfo;
>> + break;
>> + case 512:
>> + case 1024:
>> + case 4096:
>> + /* TODO */
>> + dev_warn(host->dev,
>> + "Unsupported page size for PMECC, use Software ECC\n");
>> + default:
>> + /* page size not handled by HW ECC */
>> + /* switching back to soft ECC */
>> + nand_chip->ecc.mode = NAND_ECC_SOFT;
>> + return 0;
>> + }
>> +
>> + /* Allocate data for PMECC computation */
>> + err_no = pmecc_data_alloc(host);
>> + if (err_no) {
>> + dev_err(host->dev,
>> + "Cannot allocate memory for PMECC computation!\n");
>> + goto err_pmecc_data_alloc;
>> + }
>> +
>> + nand_chip->ecc.read_page = atmel_nand_pmecc_read_page;
>> + nand_chip->ecc.write_page = atmel_nand_pmecc_write_page;
>> +
>> + atmel_pmecc_core_init(mtd);
>> +
>> + return 0;
>> +
>> +err_pmecc_data_alloc:
>> +err_no_ecc_room:
>> +err_pmloc_ioremap:
>> + iounmap(host->ecc);
>> + if (host->pmerrloc_base)
>> + iounmap(host->pmerrloc_base);
>> + if (host->pmecc_rom_base)
>> + iounmap(host->pmecc_rom_base);
>> +err_pmecc_ioremap:
>> + return err_no;
>> +}
>> +
>> +/*
>> * Calculate HW ECC
>> *
>> * function called after a write
>> @@ -747,7 +1459,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
>> }
>>
>> if (nand_chip->ecc.mode == NAND_ECC_HW) {
>> - res = atmel_hw_nand_init_params(pdev, host);
>> + if (host->has_pmecc)
>> + res = atmel_pmecc_nand_init_params(pdev, host);
>> + else
>> + res = atmel_hw_nand_init_params(pdev, host);
>> +
>> if (res != 0)
>> goto err_hw_ecc;
>> }
>> @@ -766,8 +1482,16 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
>> return res;
>>
>> err_scan_tail:
>> + if (host->has_pmecc && host->nand_chip.ecc.mode == NAND_ECC_HW) {
>> + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
>> + pmecc_data_free(host);
>> + }
>> if (host->ecc)
>> iounmap(host->ecc);
>> + if (host->pmerrloc_base)
>> + iounmap(host->pmerrloc_base);
>> + if (host->pmecc_rom_base)
>> + iounmap(host->pmecc_rom_base);
>> err_hw_ecc:
>> err_scan_ident:
>> err_no_card:
>> @@ -793,8 +1517,19 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
>>
>> atmel_nand_disable(host);
>>
>> + if (host->has_pmecc && host->nand_chip.ecc.mode == NAND_ECC_HW) {
>> + pmecc_writel(host->ecc, CTRL, PMECC_CTRL_DISABLE);
>> + pmerrloc_writel(host->pmerrloc_base, ELDIS,
>> + PMERRLOC_DISABLE);
>> + pmecc_data_free(host);
>> + }
>> +
>> if (host->ecc)
>> iounmap(host->ecc);
>> + if (host->pmecc_rom_base)
>> + iounmap(host->pmecc_rom_base);
>> + if (host->pmerrloc_base)
>> + iounmap(host->pmerrloc_base);
>>
>> if (host->dma_chan)
>> dma_release_channel(host->dma_chan);
>> diff --git a/drivers/mtd/nand/atmel_nand_ecc.h b/drivers/mtd/nand/atmel_nand_ecc.h
>> index 578c776..8a1e9a6 100644
>> --- a/drivers/mtd/nand/atmel_nand_ecc.h
>> +++ b/drivers/mtd/nand/atmel_nand_ecc.h
>> @@ -3,7 +3,7 @@
>> * Based on AT91SAM9260 datasheet revision B.
>> *
>> * Copyright (C) 2007 Andrew Victor
>> - * Copyright (C) 2007 Atmel Corporation.
>> + * Copyright (C) 2007 - 2012 Atmel Corporation.
>> *
>> * 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
>> @@ -36,4 +36,116 @@
>> #define ATMEL_ECC_NPR 0x10 /* NParity register */
>> #define ATMEL_ECC_NPARITY (0xffff << 0) /* NParity */
>>
>> +/* PMECC Register Definitions */
>> +#define ATMEL_PMECC_CFG 0x000 /* Configuration Register */
>> +#define PMECC_CFG_BCH_ERR2 (0 << 0)
>> +#define PMECC_CFG_BCH_ERR4 (1 << 0)
>> +#define PMECC_CFG_BCH_ERR8 (2 << 0)
>> +#define PMECC_CFG_BCH_ERR12 (3 << 0)
>> +#define PMECC_CFG_BCH_ERR24 (4 << 0)
>> +
>> +#define PMECC_CFG_SECTOR512 (0 << 4)
>> +#define PMECC_CFG_SECTOR1024 (1 << 4)
>> +
>> +#define PMECC_CFG_PAGE_1SECTOR (0 << 8)
>> +#define PMECC_CFG_PAGE_2SECTORS (1 << 8)
>> +#define PMECC_CFG_PAGE_4SECTORS (2 << 8)
>> +#define PMECC_CFG_PAGE_8SECTORS (3 << 8)
>> +
>> +#define PMECC_CFG_READ_OP (0 << 12)
>> +#define PMECC_CFG_WRITE_OP (1 << 12)
>> +
>> +#define PMECC_CFG_SPARE_ENABLE (1 << 16)
>> +#define PMECC_CFG_SPARE_DISABLE (0 << 16)
>> +
>> +#define PMECC_CFG_AUTO_ENABLE (1 << 20)
>> +#define PMECC_CFG_AUTO_DISABLE (0 << 20)
>> +
>> +#define ATMEL_PMECC_SAREA 0x004 /* Spare area size */
>> +#define ATMEL_PMECC_SADDR 0x008 /* PMECC starting address */
>> +#define ATMEL_PMECC_EADDR 0x00c /* PMECC ending address */
>> +#define ATMEL_PMECC_CLK 0x010 /* PMECC clock control */
>> +#define PMECC_CLK_133MHZ (2 << 0)
>> +
>> +#define ATMEL_PMECC_CTRL 0x014 /* PMECC control register */
>> +#define PMECC_CTRL_RST (1 << 0)
>> +#define PMECC_CTRL_DATA (1 << 1)
>> +#define PMECC_CTRL_USER (1 << 2)
>> +#define PMECC_CTRL_ENABLE (1 << 4)
>> +#define PMECC_CTRL_DISABLE (1 << 5)
>> +
>> +#define ATMEL_PMECC_SR 0x018 /* PMECC status register */
>> +#define PMECC_SR_BUSY (1 << 0)
>> +#define PMECC_SR_ENABLE (1 << 4)
>> +
>> +#define ATMEL_PMECC_IER 0x01c /* PMECC interrupt enable */
>> +#define PMECC_IER_ENABLE (1 << 0)
>> +#define ATMEL_PMECC_IDR 0x020 /* PMECC interrupt disable */
>> +#define PMECC_IER_DISABLE (1 << 0)
>> +#define ATMEL_PMECC_IMR 0x024 /* PMECC interrupt mask */
>> +#define PMECC_IER_MASK (1 << 0)
>> +#define ATMEL_PMECC_ISR 0x028 /* PMECC interrupt status */
>> +#define ATMEL_PMECC_ECCx 0x040 /* PMECC ECC x */
>> +#define ATMEL_PMECC_REMx 0x240 /* PMECC REM x */
>> +
>> +/* PMERRLOC Register Definitions */
>> +#define ATMEL_PMERRLOC_ELCFG 0x000 /* Error location config */
>> +#define PMERRLOC_ELCFG_SECTOR_512 (0 << 0)
>> +#define PMERRLOC_ELCFG_SECTOR_1024 (1 << 0)
>> +#define PMERRLOC_ELCFG_NUM_ERRORS(n) ((n) << 16)
>> +
>> +#define ATMEL_PMERRLOC_ELPRIM 0x004 /* Error location primitive */
>> +#define ATMEL_PMERRLOC_ELEN 0x008 /* Error location enable */
>> +#define ATMEL_PMERRLOC_ELDIS 0x00c /* Error location disable */
>> +#define PMERRLOC_DISABLE (1 << 0)
>> +
>> +#define ATMEL_PMERRLOC_ELSR 0x010 /* Error location status */
>> +#define PMERRLOC_ELSR_BUSY (1 << 0)
>> +#define ATMEL_PMERRLOC_ELIER 0x014 /* Error location int enable */
>> +#define ATMEL_PMERRLOC_ELIDR 0x018 /* Error location int disable */
>> +#define ATMEL_PMERRLOC_ELIMR 0x01c /* Error location int mask */
>> +#define ATMEL_PMERRLOC_ELISR 0x020 /* Error location int status */
>> +#define PMERRLOC_ERR_NUM_MASK (0x1f << 8)
>> +#define PMERRLOC_CALC_DONE (1 << 0)
>> +#define ATMEL_PMERRLOC_SIGMAx 0x028 /* Error location SIGMA x */
>> +#define ATMEL_PMERRLOC_ELx 0x08c /* Error location x */
>> +
>> +/* Register access macros for PMECC */
>> +#define pmecc_readl_relaxed(addr, reg) \
>> + readl_relaxed((addr) + ATMEL_PMECC_##reg)
>> +
>> +#define pmecc_writel(addr, reg, value) \
>> + writel((value), (addr) + ATMEL_PMECC_##reg)
>> +
>> +#define pmecc_readb_ecc_relaxed(addr, sector, n) \
>> + readb_relaxed((addr) + ATMEL_PMECC_ECCx + ((sector) * 0x40) + (n))
>> +
>> +#define pmecc_readl_rem_relaxed(addr, sector, n) \
>> + readl_relaxed((addr) + ATMEL_PMECC_REMx + ((sector) * 0x40) + ((n) * 4))
>> +
>> +#define pmerrloc_readl_relaxed(addr, reg) \
>> + readl_relaxed((addr) + ATMEL_PMERRLOC_##reg)
>> +
>> +#define pmerrloc_writel(addr, reg, value) \
>> + writel((value), (addr) + ATMEL_PMERRLOC_##reg)
>> +
>> +#define pmerrloc_writel_sigma_relaxed(addr, n, value) \
>> + writel_relaxed((value), (addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4))
>> +
>> +#define pmerrloc_readl_sigma_relaxed(addr, n) \
>> + readl_relaxed((addr) + ATMEL_PMERRLOC_SIGMAx + ((n) * 4))
>> +
>> +#define pmerrloc_readl_el_relaxed(addr, n) \
>> + readl_relaxed((addr) + ATMEL_PMERRLOC_ELx + ((n) * 4))
>> +
>> +/* Galois field dimension */
>> +#define PMECC_GF_DIMENSION_13 13
>> +#define PMECC_GF_DIMENSION_14 14
>> +
>> +#define PMECC_LOOKUP_TABLE_SIZE_512 0x2000
>> +#define PMECC_LOOKUP_TABLE_SIZE_1024 0x4000
>> +
>> +/* Time out value for reading PMECC status register */
>> +#define PMECC_MAX_TIMEOUT_MS 100
>> +
>> #endif
>> --
>> 1.7.9.5
>>
>>
>> ______________________________________________________
>> Linux MTD discussion mailing list
>> http://lists.infradead.org/mailman/listinfo/linux-mtd/
> Regards.
> Richard.
>
Best Regards,
Josh Wu
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v11 2/4] MTD: at91: add dt parameters for Atmel PMECC
2012-06-25 10:35 ` Richard Genoud
@ 2012-06-26 6:05 ` Josh Wu
0 siblings, 0 replies; 22+ messages in thread
From: Josh Wu @ 2012-06-26 6:05 UTC (permalink / raw)
To: linux-arm-kernel
On 6/25/2012 6:35 PM, Richard Genoud wrote:
> 2012/6/25 Josh Wu <josh.wu@atmel.com>:
>> Add DT support for PMECC parameters.
>>
>> Signed-off-by: Hong Xu <hong.xu@atmel.com>
>> Signed-off-by: Josh Wu <josh.wu@atmel.com>
>> ---
>> .../devicetree/bindings/mtd/atmel-nand.txt | 9 ++++
>> drivers/mtd/nand/atmel_nand.c | 52 +++++++++++++++++++-
>> 2 files changed, 60 insertions(+), 1 deletion(-)
>>
>> diff --git a/Documentation/devicetree/bindings/mtd/atmel-nand.txt b/Documentation/devicetree/bindings/mtd/atmel-nand.txt
>> index a200695..ed6927a 100644
>> --- a/Documentation/devicetree/bindings/mtd/atmel-nand.txt
>> +++ b/Documentation/devicetree/bindings/mtd/atmel-nand.txt
>> @@ -16,6 +16,15 @@ Optional properties:
>> - nand-ecc-mode : String, operation mode of the NAND ecc mode, soft by default.
>> Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first",
>> "soft_bch".
>> +- atmel,has-pmecc : boolean to enable Programmable Multibit ECC hardware.
>> + Only supported by at91sam9x5 or later sam9 product.
>> +- atmel,pmecc-cap : error correct capability for Programmable Multibit ECC
>> + Controller. Supported values are: 2, 4, 8, 12, 24.
>> +- atmel,pmecc-sector-size : sector size for ECC computation. Supported values
>> + are: 512, 1024.
>> +- atmel,pmecc-lookup-table-offset : includes two offsets of lookup table in ROM
>> + for different sector size. First one is for sector size 512, the next is for
>> + sector size 1024.
>> - nand-bus-width : 8 or 16 bus width if not present 8
>> - nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
>>
> Maybe you should update the reg parameter documentation with the
> needed resources.
>
I will update the reg parameter documentation and add one example for
PMECC setting. thanks.
>> diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
>> index 7a41a04..b97ad9f 100644
>> --- a/drivers/mtd/nand/atmel_nand.c
>> +++ b/drivers/mtd/nand/atmel_nand.c
>> @@ -93,6 +93,11 @@ struct atmel_nand_host {
>>
>> struct completion comp;
>> struct dma_chan *dma_chan;
>> +
>> + bool has_pmecc;
>> + u8 pmecc_corr_cap;
>> + u16 pmecc_sector_size;
>> + u32 pmecc_lookup_table_offset;
>> };
>>
>> static int cpu_has_dma(void)
>> @@ -481,7 +486,8 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode)
>> static int __devinit atmel_of_init_port(struct atmel_nand_host *host,
>> struct device_node *np)
>> {
>> - u32 val;
>> + u32 val, table_offset;
>> + u32 offset[2];
>> int ecc_mode;
>> struct atmel_nand_data *board = &host->board;
>> enum of_gpio_flags flags;
>> @@ -517,6 +523,50 @@ static int __devinit atmel_of_init_port(struct atmel_nand_host *host,
>> board->enable_pin = of_get_gpio(np, 1);
>> board->det_pin = of_get_gpio(np, 2);
>>
>> + host->has_pmecc = of_property_read_bool(np, "atmel,has-pmecc");
>> +
>> + if (!(board->ecc_mode == NAND_ECC_HW) || !host->has_pmecc)
>> + return 0; /* Not using PMECC */
>> +
>> + /* use PMECC, get correction capability, sector size and lookup
>> + * table offset.
>> + */
>> + if (of_property_read_u32(np, "atmel,pmecc-cap", &val) != 0) {
>> + dev_err(host->dev, "Cannot decide PMECC Capability\n");
>> + return -EINVAL;
>> + } else if ((val != 2) && (val != 4) && (val != 8) && (val != 12) &&
>> + (val != 24)) {
>> + dev_err(host->dev,
>> + "Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n",
>> + val);
>> + return -EINVAL;
>> + }
>> + host->pmecc_corr_cap = (u8)val;
>> +
>> + if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) != 0) {
>> + dev_err(host->dev, "Cannot decide PMECC Sector Size\n");
>> + return -EINVAL;
>> + } else if ((val != 512) && (val != 1024)) {
>> + dev_err(host->dev,
>> + "Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n",
>> + val);
>> + return -EINVAL;
>> + }
>> + host->pmecc_sector_size = (u16)val;
>> +
>> + if (of_property_read_u32_array(np, "atmel,pmecc-lookup-table-offset",
>> + offset, 2) != 0) {
>> + dev_err(host->dev, "Cannot get PMECC lookup table offset\n");
>> + return -EINVAL;
>> + }
>> + table_offset = host->pmecc_sector_size == 512 ? offset[0] : offset[1];
>> +
>> + if (!table_offset) {
>> + dev_err(host->dev, "Invalid PMECC lookup table offset\n");
>> + return -EINVAL;
>> + }
>> + host->pmecc_lookup_table_offset = table_offset;
>> +
>> return 0;
>> }
>> #else
>> --
>> 1.7.9.5
>>
>>
>> ______________________________________________________
>> Linux MTD discussion mailing list
>> http://lists.infradead.org/mailman/listinfo/linux-mtd/
> Richard.
>
Best Regards,
Josh Wu
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v11 4/4] MTD: at91: atmel_nand: Update driver to support Programmable Multibit ECC controller
2012-06-25 10:07 ` [PATCH v11 4/4] MTD: at91: atmel_nand: Update driver to support Programmable Multibit ECC controller Josh Wu
2012-06-25 10:27 ` Richard Genoud
@ 2012-06-26 14:15 ` Richard Genoud
2012-06-26 14:37 ` [PATCH v11 4/4] MTD: at91: atmel_nand: Update driver to support Programmable Multibit ECC controller (REPORT SPAM) William F.
2012-06-27 11:22 ` [PATCH v11 4/4] MTD: at91: atmel_nand: Update driver to support Programmable Multibit ECC controller Josh Wu
1 sibling, 2 replies; 22+ messages in thread
From: Richard Genoud @ 2012-06-26 14:15 UTC (permalink / raw)
To: linux-arm-kernel
2012/6/25 Josh Wu <josh.wu@atmel.com>:
> +static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf,
> + ? ? ? ? ? ? ? int extra_bytes, int err_nbr)
> +{
> + ? ? ? struct nand_chip *nand_chip = mtd->priv;
> + ? ? ? struct atmel_nand_host *host = nand_chip->priv;
> + ? ? ? int i = 0;
> + ? ? ? int byte_pos, bit_pos, sector_size, ecc_size;
> + ? ? ? uint32_t tmp;
> +
> + ? ? ? sector_size = host->pmecc_sector_size;
> + ? ? ? ecc_size = nand_chip->ecc.bytes;
> +
> + ? ? ? while (err_nbr) {
> + ? ? ? ? ? ? ? tmp = pmerrloc_readl_el_relaxed(host->pmerrloc_base, i) - 1;
> + ? ? ? ? ? ? ? byte_pos = tmp / 8;
> + ? ? ? ? ? ? ? bit_pos ?= tmp % 8;
> + ? ? ? ? ? ? ? dev_info(host->dev, "PMECC correction, byte_pos: %d bit_pos: %d\n",
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? byte_pos, bit_pos);
> +
> + ? ? ? ? ? ? ? if (byte_pos < (sector_size + extra_bytes)) {
> + ? ? ? ? ? ? ? ? ? ? ? tmp = sector_size +
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pmecc_readl_relaxed(host->ecc, SADDR);
> +
> + ? ? ? ? ? ? ? ? ? ? ? if (byte_pos < tmp)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? *(buf + byte_pos) ^= (1 << bit_pos);
> + ? ? ? ? ? ? ? ? ? ? ? else
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? *(buf + byte_pos + ecc_size) ^= (1 << bit_pos);
> + ? ? ? ? ? ? ? }
I think there's a problem in there.
On my board (sam9g35-ek), I've got 2KB pages and 4 sectors per page + 64B OOB.
When I flip a bit on the 63rd OOB byte (the last ECC byte), byte_pos is 515.
If I flip a bit on the 51th OOB byte (the 3rd ECC byte), byte_pos is also 515.
So, if I get it correct, byte_pos will always be between 0 and
sector_size + sector_ecc_bytes.
(and I think that sector_ecc_bytes == extra_bytes (cf below))
And, pmecc_readl_relaxed(host->ecc, SADDR) gives me the ECC offset in OOB (48).
So, (byte_pos < sector_size + extra_bytes) will always be correct as
far as the PMECC controller doesn't give us crap.
But, IHMO the "tmp = sector_size + pmecc_readl_relaxed(host->ecc,
SADDR);" line is wrong.
I think that it should be something like this:
static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
int extra_bytes, int err_nbr, int sector_number)
{
[...]
tmp = pmerrloc_readl_el_relaxed(host->pmerrloc_base, i) - 1;
byte_pos = tmp / 8;
bit_pos ?= tmp % 8;
if (byte_pos < (sector_size + extra_bytes)) {
if (byte_pos < sector_size) {
*(buf + byte_pos) ^= (1 << bit_pos);
/* It may be easier to read this info if the byte_pos
* is relative to the page start, not just the sector */
dev_info(host->dev, "PMECC correction, byte_pos: %d bit_pos: %d\n",
sector_number * sector_sz + byte_pos, bit_pos);
} else {
/* number of ecc bytes per sector */
tmp = pmecc_get_ecc_bytes(nand_chip->ecc.strength, sector_size);
/* offset in the ecc of the ecc bytes for current sector */
tmp *= sector_number;
/* now, tmp is the offset of the byte error in the ecc */
tmp += byte_pos - sector_size;
ecc[tmp] ^= (1 << bit_pos);
dev_info(host->dev, "Bit flip in ECC, ecc_byte_pos: %d bit_pos: %d\n",
tmp, bit_pos);
/* or */
dev_info(host->dev, "Bit flip in OOB, oob_byte_pos: %d bit_pos: %d\n",
tmp + nand_chip->ecc_layout.eccpos[0], bit_pos);
}
}
[...]
> +
> + ? ? ? ? ? ? ? i++;
> + ? ? ? ? ? ? ? err_nbr--;
> + ? ? ? }
> +
> + ? ? ? return;
> +}
> +
> +static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf,
> + ? ? ? u8 *ecc)
> +{
> + ? ? ? struct nand_chip *nand_chip = mtd->priv;
> + ? ? ? struct atmel_nand_host *host = nand_chip->priv;
> + ? ? ? int i, err_nbr, eccbytes;
> + ? ? ? uint8_t *buf_pos;
> +
> + ? ? ? eccbytes = nand_chip->ecc.bytes;
> + ? ? ? for (i = 0; i < eccbytes; i++)
> + ? ? ? ? ? ? ? if (ecc[i] != 0xff)
> + ? ? ? ? ? ? ? ? ? ? ? goto normal_check;
> + ? ? ? /* Erased page, return OK */
> + ? ? ? return 0;
> +
> +normal_check:
> + ? ? ? for (i = 0; i < host->pmecc_sector_number; i++) {
> + ? ? ? ? ? ? ? err_nbr = 0;
> + ? ? ? ? ? ? ? if (pmecc_stat & 0x1) {
> + ? ? ? ? ? ? ? ? ? ? ? buf_pos = buf + i * host->pmecc_sector_size;
> +
> + ? ? ? ? ? ? ? ? ? ? ? pmecc_gen_syndrome(mtd, i);
> + ? ? ? ? ? ? ? ? ? ? ? pmecc_substitute(mtd);
> + ? ? ? ? ? ? ? ? ? ? ? pmecc_get_sigma(mtd);
> +
> + ? ? ? ? ? ? ? ? ? ? ? err_nbr = pmecc_err_location(mtd);
> + ? ? ? ? ? ? ? ? ? ? ? if (err_nbr == -1) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? dev_err(host->dev, "PMECC: Too many errors\n");
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mtd->ecc_stats.failed++;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return -EIO;
> + ? ? ? ? ? ? ? ? ? ? ? } else {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? pmecc_correct_data(mtd, buf_pos, 0, err_nbr);
IHMO, extra_bytes should be (eccbytes / host->pmecc_sector_number)
pmecc_correct_data(mtd, buf_pos, ecc, eccbytes /
host->pmecc_sector_number, err_nbr, i);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mtd->ecc_stats.corrected += err_nbr;
> + ? ? ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? pmecc_stat >>= 1;
> + ? ? ? }
> +
> + ? ? ? return 0;
> +}
I tested the patchset against 3.5-rc4, on a at91sam9g35-ek board.
the DTS config I used is:
reg = <0x40000000 0x10000000 0xffffe000 0x600 0xffffe600 0x200
0x00100000 0x00100000 >;
nand-bus-width = <8>;
nand-ecc-mode = "hw";
nand-on-flash-bbt;
atmel,nand-addr-offset = <21>;
atmel,nand-cmd-offset = <22>;
gpios = <&pioD 5 0 &pioD 4 0 0 >;
atmel,has-pmecc;
atmel,pmecc-cap = <2>;
atmel,pmecc-sector-size = <512>;
atmel,pmecc-lookup-table-offset = <0x8000 0x10000>;
(nand on the -ek is 2KiB page size, 128KiB eraseblock)
I tested
- 1 bit flip on a page
- 4 bit flips on a page (1 per sector)
- 5 bit flips on a page (2 on sector 1, and then 1 per sector)
- 1 bit flip on ECC
- 2 bit flips on ECC (on the same byte)
- 3 bit flips => I/O error (ok, since pmecc-cap = 2 )
- 3 bit flips on ECC => I/O error (ok, since pmecc-cap = 2 )
only problem : ECC is not corrected (should be better with the fixes above)
When you have corrected the ECC correction issue, you can add my:
Tested-by: Richard Genoud <richard.genoud@gmail.com>
Regards.
--
for me, ck means con kolivas and not calvin klein... does it mean I'm a geek ?
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v11 4/4] MTD: at91: atmel_nand: Update driver to support Programmable Multibit ECC controller (REPORT SPAM)
2012-06-26 14:15 ` Richard Genoud
@ 2012-06-26 14:37 ` William F.
2012-06-27 11:22 ` [PATCH v11 4/4] MTD: at91: atmel_nand: Update driver to support Programmable Multibit ECC controller Josh Wu
1 sibling, 0 replies; 22+ messages in thread
From: William F. @ 2012-06-26 14:37 UTC (permalink / raw)
To: linux-arm-kernel
Em 26-06-2012 11:15, Richard Genoud escreveu:
> 2012/6/25 Josh Wu<josh.wu@atmel.com>:
>> +static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf,
>> + int extra_bytes, int err_nbr)
>> +{
>> + struct nand_chip *nand_chip = mtd->priv;
>> + struct atmel_nand_host *host = nand_chip->priv;
>> + int i = 0;
>> + int byte_pos, bit_pos, sector_size, ecc_size;
>> + uint32_t tmp;
>> +
>> + sector_size = host->pmecc_sector_size;
>> + ecc_size = nand_chip->ecc.bytes;
>> +
>> + while (err_nbr) {
>> + tmp = pmerrloc_readl_el_relaxed(host->pmerrloc_base, i) - 1;
>> + byte_pos = tmp / 8;
>> + bit_pos = tmp % 8;
>> + dev_info(host->dev, "PMECC correction, byte_pos: %d bit_pos: %d\n",
>> + byte_pos, bit_pos);
>> +
>> + if (byte_pos< (sector_size + extra_bytes)) {
>> + tmp = sector_size +
>> + pmecc_readl_relaxed(host->ecc, SADDR);
>> +
>> + if (byte_pos< tmp)
>> + *(buf + byte_pos) ^= (1<< bit_pos);
>> + else
>> + *(buf + byte_pos + ecc_size) ^= (1<< bit_pos);
>> + }
> I think there's a problem in there.
> On my board (sam9g35-ek), I've got 2KB pages and 4 sectors per page + 64B OOB.
> When I flip a bit on the 63rd OOB byte (the last ECC byte), byte_pos is 515.
> If I flip a bit on the 51th OOB byte (the 3rd ECC byte), byte_pos is also 515.
> So, if I get it correct, byte_pos will always be between 0 and
> sector_size + sector_ecc_bytes.
> (and I think that sector_ecc_bytes == extra_bytes (cf below))
> And, pmecc_readl_relaxed(host->ecc, SADDR) gives me the ECC offset in OOB (48).
> So, (byte_pos< sector_size + extra_bytes) will always be correct as
> far as the PMECC controller doesn't give us crap.
> But, IHMO the "tmp = sector_size + pmecc_readl_relaxed(host->ecc,
> SADDR);" line is wrong.
>
> I think that it should be something like this:
> static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
> int extra_bytes, int err_nbr, int sector_number)
> {
> [...]
> tmp = pmerrloc_readl_el_relaxed(host->pmerrloc_base, i) - 1;
> byte_pos = tmp / 8;
> bit_pos = tmp % 8;
> if (byte_pos< (sector_size + extra_bytes)) {
> if (byte_pos< sector_size) {
> *(buf + byte_pos) ^= (1<< bit_pos);
> /* It may be easier to read this info if the byte_pos
> * is relative to the page start, not just the sector */
> dev_info(host->dev, "PMECC correction, byte_pos: %d bit_pos: %d\n",
> sector_number * sector_sz + byte_pos, bit_pos);
> } else {
> /* number of ecc bytes per sector */
> tmp = pmecc_get_ecc_bytes(nand_chip->ecc.strength, sector_size);
> /* offset in the ecc of the ecc bytes for current sector */
> tmp *= sector_number;
> /* now, tmp is the offset of the byte error in the ecc */
> tmp += byte_pos - sector_size;
> ecc[tmp] ^= (1<< bit_pos);
> dev_info(host->dev, "Bit flip in ECC, ecc_byte_pos: %d bit_pos: %d\n",
> tmp, bit_pos);
> /* or */
> dev_info(host->dev, "Bit flip in OOB, oob_byte_pos: %d bit_pos: %d\n",
> tmp + nand_chip->ecc_layout.eccpos[0], bit_pos);
> }
> }
> [...]
>
>> +
>> + i++;
>> + err_nbr--;
>> + }
>> +
>> + return;
>> +}
>> +
>> +static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf,
>> + u8 *ecc)
>> +{
>> + struct nand_chip *nand_chip = mtd->priv;
>> + struct atmel_nand_host *host = nand_chip->priv;
>> + int i, err_nbr, eccbytes;
>> + uint8_t *buf_pos;
>> +
>> + eccbytes = nand_chip->ecc.bytes;
>> + for (i = 0; i< eccbytes; i++)
>> + if (ecc[i] != 0xff)
>> + goto normal_check;
>> + /* Erased page, return OK */
>> + return 0;
>> +
>> +normal_check:
>> + for (i = 0; i< host->pmecc_sector_number; i++) {
>> + err_nbr = 0;
>> + if (pmecc_stat& 0x1) {
>> + buf_pos = buf + i * host->pmecc_sector_size;
>> +
>> + pmecc_gen_syndrome(mtd, i);
>> + pmecc_substitute(mtd);
>> + pmecc_get_sigma(mtd);
>> +
>> + err_nbr = pmecc_err_location(mtd);
>> + if (err_nbr == -1) {
>> + dev_err(host->dev, "PMECC: Too many errors\n");
>> + mtd->ecc_stats.failed++;
>> + return -EIO;
>> + } else {
>> + pmecc_correct_data(mtd, buf_pos, 0, err_nbr);
> IHMO, extra_bytes should be (eccbytes / host->pmecc_sector_number)
> pmecc_correct_data(mtd, buf_pos, ecc, eccbytes /
> host->pmecc_sector_number, err_nbr, i);
>
>> + mtd->ecc_stats.corrected += err_nbr;
>> + }
>> + }
>> + pmecc_stat>>= 1;
>> + }
>> +
>> + return 0;
>> +}
> I tested the patchset against 3.5-rc4, on a at91sam9g35-ek board.
> the DTS config I used is:
> reg =<0x40000000 0x10000000 0xffffe000 0x600 0xffffe600 0x200
> 0x00100000 0x00100000>;
> nand-bus-width =<8>;
> nand-ecc-mode = "hw";
> nand-on-flash-bbt;
> atmel,nand-addr-offset =<21>;
> atmel,nand-cmd-offset =<22>;
> gpios =<&pioD 5 0&pioD 4 0 0>;
> atmel,has-pmecc;
> atmel,pmecc-cap =<2>;
> atmel,pmecc-sector-size =<512>;
> atmel,pmecc-lookup-table-offset =<0x8000 0x10000>;
>
> (nand on the -ek is 2KiB page size, 128KiB eraseblock)
>
> I tested
> - 1 bit flip on a page
> - 4 bit flips on a page (1 per sector)
> - 5 bit flips on a page (2 on sector 1, and then 1 per sector)
> - 1 bit flip on ECC
> - 2 bit flips on ECC (on the same byte)
> - 3 bit flips => I/O error (ok, since pmecc-cap = 2 )
> - 3 bit flips on ECC => I/O error (ok, since pmecc-cap = 2 )
> only problem : ECC is not corrected (should be better with the fixes above)
>
> When you have corrected the ECC correction issue, you can add my:
> Tested-by: Richard Genoud<richard.genoud@gmail.com>
>
> Regards.
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver
2012-06-25 10:07 [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver Josh Wu
` (3 preceding siblings ...)
2012-06-25 10:07 ` [PATCH v11 4/4] MTD: at91: atmel_nand: Update driver to support Programmable Multibit ECC controller Josh Wu
@ 2012-06-27 3:45 ` Artem Bityutskiy
2012-06-27 3:49 ` Artem Bityutskiy
2012-06-27 9:28 ` Artem Bityutskiy
6 siblings, 0 replies; 22+ messages in thread
From: Artem Bityutskiy @ 2012-06-27 3:45 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, 2012-06-25 at 18:07 +0800, Josh Wu wrote:
> Those patches is based on v3.5-rc4
>
> Changes since v10,
> add one more patch in this patch set, which add 'int' return value for write_page()/write_page_raw() functions in structure of nand_ecc_ctrl.
> Instead of calling BUG(), atmel_nand_pmecc_write_page() will return -EIO when time out to read the pmecc status register.
> increase the time-out duration to 100ms, which has more toleration.
> add oob_required argument for pmecc read/write functions to align with v3.5-rc4.
Please, send patches against the l2-mtd.git tree, not against the
upstream tree.
--
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/20120627/14eecfca/attachment-0001.sig>
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver
2012-06-25 10:07 [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver Josh Wu
` (4 preceding siblings ...)
2012-06-27 3:45 ` [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver Artem Bityutskiy
@ 2012-06-27 3:49 ` Artem Bityutskiy
2012-06-27 4:11 ` Josh Wu
2012-06-27 9:28 ` Artem Bityutskiy
6 siblings, 1 reply; 22+ messages in thread
From: Artem Bityutskiy @ 2012-06-27 3:49 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, 2012-06-25 at 18:07 +0800, Josh Wu wrote:
> Those patches is based on v3.5-rc4
>
> Changes since v10,
> add one more patch in this patch set, which add 'int' return value for write_page()/write_page_raw() functions in structure of nand_ecc_ctrl.
> Instead of calling BUG(), atmel_nand_pmecc_write_page() will return -EIO when time out to read the pmecc status register.
> increase the time-out duration to 100ms, which has more toleration.
> add oob_required argument for pmecc read/write functions to align with v3.5-rc4.
I have issues compiling l2-mtd.git tree (based on 3.5-rc5) with the
attached defconfig. Probably not your fault, but may be you have a
fix/suggestion? I did not have time to look closer. Aiaiai reports:
Failed to build the following commit for configuration "arm-at91cap9_defconfig" (architecture arm)":
bb6ac5c Quick fixes - applied by aiaiai
include/linux/math64.h:55:15: note: each undeclared identifier is reported only once for each function it appears in
In file included from include/linux/mm_types.h:16:0,
from include/linux/sched.h:64,
from arch/arm/kernel/asm-offsets.c:13:
arch/arm/include/asm/page.h: At top level:
arch/arm/include/asm/page.h:107:2: error: #error Unknown user operations model
In file included from include/linux/mm.h:44:0,
from arch/arm/kernel/asm-offsets.c:14:
arch/arm/include/asm/pgtable.h:198:5: warning: "__LINUX_ARM_ARCH__" is not defined [-Wundef]
In file included from arch/arm/include/asm/cacheflush.h:15:0,
from arch/arm/kernel/asm-offsets.c:16:
arch/arm/include/asm/glue-cache.h:129:2: error: #error Unknown cache maintenance model
In file included from arch/arm/include/asm/cacheflush.h:17:0,
from arch/arm/kernel/asm-offsets.c:16:
arch/arm/include/asm/cachetype.h:28:5: warning: "__LINUX_ARM_ARCH__" is not defined [-Wundef]
arch/arm/include/asm/cachetype.h:33:7: warning: "__LINUX_ARM_ARCH__" is not defined [-Wundef]
In file included from arch/arm/kernel/asm-offsets.c:16:0:
arch/arm/include/asm/cacheflush.h:194:7: warning: "__LINUX_ARM_ARCH__" is not defined [-Wundef]
arch/arm/include/asm/cacheflush.h:196:7: warning: "__LINUX_ARM_ARCH__" is not defined [-Wundef]
In file included from arch/arm/kernel/asm-offsets.c:17:0:
arch/arm/include/asm/glue-df.h:99:2: error: #error Unknown data abort handler type
In file included from arch/arm/kernel/asm-offsets.c:18:0:
arch/arm/include/asm/glue-pf.h:54:2: error: #error Unknown prefetch abort handler type
make[2]: *** [arch/arm/kernel/asm-offsets.s] Error 1
--
Best Regards,
Artem Bityutskiy
-------------- next part --------------
CONFIG_EXPERIMENTAL=y
# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_SYSVIPC=y
CONFIG_BSD_PROCESS_ACCT=y
CONFIG_LOG_BUF_SHIFT=18
CONFIG_CGROUPS=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_RESOURCE_COUNTERS=y
CONFIG_CGROUP_SCHED=y
CONFIG_BLK_DEV_INITRD=y
# CONFIG_COMPAT_BRK is not set
CONFIG_PROFILING=y
CONFIG_KPROBES=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_PARTITION_ADVANCED=y
CONFIG_OSF_PARTITION=y
CONFIG_AMIGA_PARTITION=y
CONFIG_MAC_PARTITION=y
CONFIG_BSD_DISKLABEL=y
CONFIG_MINIX_SUBPARTITION=y
CONFIG_SOLARIS_X86_PARTITION=y
CONFIG_UNIXWARE_DISKLABEL=y
CONFIG_SGI_PARTITION=y
CONFIG_SUN_PARTITION=y
CONFIG_KARMA_PARTITION=y
CONFIG_EFI_PARTITION=y
CONFIG_ARCH_AT91=y
CONFIG_MACH_ONEARM=y
CONFIG_ARCH_AT91RM9200DK=y
CONFIG_MACH_AT91RM9200EK=y
CONFIG_MACH_CSB337=y
CONFIG_MACH_CSB637=y
CONFIG_MACH_CARMEVA=y
CONFIG_MACH_ATEB9200=y
CONFIG_MACH_KB9200=y
CONFIG_MACH_PICOTUX2XX=y
CONFIG_MACH_KAFA=y
CONFIG_MACH_ECBAT91=y
CONFIG_MACH_YL9200=y
CONFIG_MACH_CPUAT91=y
CONFIG_MACH_ECO920=y
CONFIG_MACH_RSI_EWS=y
CONFIG_MTD_AT91_DATAFLASH_CARD=y
CONFIG_AT91_PROGRAMMABLE_CLOCKS=y
CONFIG_PCCARD=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_PREEMPT_VOLUNTARY=y
CONFIG_USE_OF=y
CONFIG_KEXEC=y
CONFIG_CRASH_DUMP=y
CONFIG_BINFMT_MISC=y
# CONFIG_SUSPEND is not set
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_DEBUG_DEVRES=y
CONFIG_MTD=y
CONFIG_MTD_TESTS=m
CONFIG_MTD_REDBOOT_PARTS=m
CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED=y
CONFIG_MTD_REDBOOT_PARTS_READONLY=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_AFS_PARTS=y
CONFIG_MTD_OF_PARTS=y
CONFIG_MTD_AR7_PARTS=m
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_FTL=m
CONFIG_NFTL=m
CONFIG_NFTL_RW=y
CONFIG_INFTL=m
CONFIG_RFD_FTL=m
CONFIG_SSFDC=y
CONFIG_SM_FTL=m
CONFIG_MTD_OOPS=m
CONFIG_MTD_SWAP=y
CONFIG_MTD_CFI=m
CONFIG_MTD_JEDECPROBE=m
CONFIG_MTD_CFI_ADV_OPTIONS=y
CONFIG_MTD_CFI_GEOMETRY=y
# CONFIG_MTD_MAP_BANK_WIDTH_1 is not set
CONFIG_MTD_MAP_BANK_WIDTH_16=y
# CONFIG_MTD_CFI_I1 is not set
CONFIG_MTD_CFI_I4=y
CONFIG_MTD_OTP=y
CONFIG_MTD_CFI_INTELEXT=m
CONFIG_MTD_CFI_AMDSTD=m
CONFIG_MTD_CFI_STAA=m
CONFIG_MTD_ROM=m
CONFIG_MTD_ABSENT=m
CONFIG_MTD_COMPLEX_MAPPINGS=y
CONFIG_MTD_PHYSMAP=m
CONFIG_MTD_PHYSMAP_COMPAT=y
CONFIG_MTD_PHYSMAP_OF=m
CONFIG_MTD_IMPA7=m
CONFIG_MTD_PCMCIA=m
CONFIG_MTD_PCMCIA_ANONYMOUS=y
CONFIG_MTD_GPIO_ADDR=y
CONFIG_MTD_PLATRAM=y
CONFIG_MTD_LATCH_ADDR=m
CONFIG_MTD_DATAFLASH=m
CONFIG_MTD_DATAFLASH_WRITE_VERIFY=y
CONFIG_MTD_DATAFLASH_OTP=y
CONFIG_MTD_M25P80=m
CONFIG_MTD_SST25L=m
CONFIG_MTD_SLRAM=y
CONFIG_MTD_PHRAM=y
CONFIG_MTD_MTDRAM=y
CONFIG_MTD_BLOCK2MTD=m
CONFIG_MTD_DOC2000=m
CONFIG_MTD_DOC2001=m
CONFIG_MTD_DOC2001PLUS=m
CONFIG_MTD_DOCG3=m
CONFIG_MTD_DOCPROBE_ADVANCED=y
CONFIG_MTD_DOCPROBE_HIGH=y
CONFIG_MTD_NAND_ECC_SMC=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_ECC_BCH=y
CONFIG_MTD_NAND_MUSEUM_IDS=y
CONFIG_MTD_NAND_GPIO=y
CONFIG_MTD_NAND_DISKONCHIP=m
CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE=y
CONFIG_MTD_NAND_DOCG4=m
CONFIG_MTD_NAND_ATMEL=m
CONFIG_MTD_NAND_NANDSIM=y
CONFIG_MTD_NAND_PLATFORM=m
CONFIG_MTD_ALAUDA=m
CONFIG_MTD_ONENAND=y
CONFIG_MTD_ONENAND_VERIFY_WRITE=y
CONFIG_MTD_ONENAND_GENERIC=y
CONFIG_MTD_ONENAND_2X_PROGRAM=y
CONFIG_MTD_ONENAND_SIM=m
CONFIG_MTD_LPDDR=y
CONFIG_MTD_UBI=y
CONFIG_MTD_UBI_GLUEBI=m
# CONFIG_BLK_DEV is not set
CONFIG_INPUT_FF_MEMLESS=y
# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO is not set
CONFIG_VT_HW_CONSOLE_BINDING=y
# CONFIG_LEGACY_PTYS is not set
# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_NR_UARTS=32
CONFIG_SERIAL_8250_EXTENDED=y
CONFIG_SERIAL_8250_MANY_PORTS=y
CONFIG_SERIAL_8250_SHARE_IRQ=y
CONFIG_SERIAL_8250_DETECT_IRQ=y
CONFIG_SERIAL_8250_RSA=y
# CONFIG_HW_RANDOM is not set
CONFIG_SPI=y
# CONFIG_HWMON is not set
CONFIG_DRM=y
# CONFIG_HID_SUPPORT is not set
CONFIG_USB=m
# CONFIG_IOMMU_SUPPORT is not set
CONFIG_QUOTA=y
# CONFIG_PRINT_QUOTA_WARNING is not set
CONFIG_QFMT_V2=y
CONFIG_JFFS2_FS=y
# CONFIG_JFFS2_FS_WRITEBUFFER is not set
CONFIG_JFFS2_COMPRESSION_OPTIONS=y
CONFIG_JFFS2_LZO=y
CONFIG_JFFS2_RUBIN=y
CONFIG_JFFS2_CMODE_FAVOURLZO=y
CONFIG_UBIFS_FS=m
CONFIG_UBIFS_FS_ADVANCED_COMPR=y
# CONFIG_UBIFS_FS_ZLIB is not set
CONFIG_UBIFS_FS_DEBUG=y
CONFIG_LOGFS=m
CONFIG_CRAMFS=m
CONFIG_NLS=y
CONFIG_NLS_DEFAULT="utf8"
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_UTF8=y
CONFIG_PRINTK_TIME=y
# CONFIG_ENABLE_WARN_DEPRECATED is not set
CONFIG_FRAME_WARN=2048
CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_KERNEL=y
# CONFIG_SCHED_DEBUG is not set
CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
CONFIG_DEBUG_STACK_USAGE=y
CONFIG_BLK_DEV_IO_TRACE=y
CONFIG_CRYPTO=y
# CONFIG_CRYPTO_ANSI_CPRNG is not set
# CONFIG_CRYPTO_HW is not set
-------------- 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/20120627/4d2aff94/attachment.sig>
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver
2012-06-27 3:49 ` Artem Bityutskiy
@ 2012-06-27 4:11 ` Josh Wu
2012-06-27 4:28 ` Artem Bityutskiy
0 siblings, 1 reply; 22+ messages in thread
From: Josh Wu @ 2012-06-27 4:11 UTC (permalink / raw)
To: linux-arm-kernel
Hi, Artem
On 6/27/2012 11:49 AM, Artem Bityutskiy wrote:
> On Mon, 2012-06-25 at 18:07 +0800, Josh Wu wrote:
>> Those patches is based on v3.5-rc4
>>
>> Changes since v10,
>> add one more patch in this patch set, which add 'int' return value for write_page()/write_page_raw() functions in structure of nand_ecc_ctrl.
>> Instead of calling BUG(), atmel_nand_pmecc_write_page() will return -EIO when time out to read the pmecc status register.
>> increase the time-out duration to 100ms, which has more toleration.
>> add oob_required argument for pmecc read/write functions to align with v3.5-rc4.
> I have issues compiling l2-mtd.git tree (based on 3.5-rc5) with the
> attached defconfig. Probably not your fault, but may be you have a
> fix/suggestion? I did not have time to look closer. Aiaiai reports:
I think the name of kernel config for AT91 is changed, so you can try
add following two line in your original config file.
CONFIG_SOC_AT91SAM9=y
CONFIG_SOC_AT91RM9200=y
run the make menuconfig will get new config, that can pass the compile.
>
> Failed to build the following commit for configuration "arm-at91cap9_defconfig" (architecture arm)":
>
> bb6ac5c Quick fixes - applied by aiaiai
>
> include/linux/math64.h:55:15: note: each undeclared identifier is reported only once for each function it appears in
> In file included from include/linux/mm_types.h:16:0,
> from include/linux/sched.h:64,
> from arch/arm/kernel/asm-offsets.c:13:
> arch/arm/include/asm/page.h: At top level:
> arch/arm/include/asm/page.h:107:2: error: #error Unknown user operations model
> In file included from include/linux/mm.h:44:0,
> from arch/arm/kernel/asm-offsets.c:14:
> arch/arm/include/asm/pgtable.h:198:5: warning: "__LINUX_ARM_ARCH__" is not defined [-Wundef]
> In file included from arch/arm/include/asm/cacheflush.h:15:0,
> from arch/arm/kernel/asm-offsets.c:16:
> arch/arm/include/asm/glue-cache.h:129:2: error: #error Unknown cache maintenance model
> In file included from arch/arm/include/asm/cacheflush.h:17:0,
> from arch/arm/kernel/asm-offsets.c:16:
> arch/arm/include/asm/cachetype.h:28:5: warning: "__LINUX_ARM_ARCH__" is not defined [-Wundef]
> arch/arm/include/asm/cachetype.h:33:7: warning: "__LINUX_ARM_ARCH__" is not defined [-Wundef]
> In file included from arch/arm/kernel/asm-offsets.c:16:0:
> arch/arm/include/asm/cacheflush.h:194:7: warning: "__LINUX_ARM_ARCH__" is not defined [-Wundef]
> arch/arm/include/asm/cacheflush.h:196:7: warning: "__LINUX_ARM_ARCH__" is not defined [-Wundef]
> In file included from arch/arm/kernel/asm-offsets.c:17:0:
> arch/arm/include/asm/glue-df.h:99:2: error: #error Unknown data abort handler type
> In file included from arch/arm/kernel/asm-offsets.c:18:0:
> arch/arm/include/asm/glue-pf.h:54:2: error: #error Unknown prefetch abort handler type
> make[2]: *** [arch/arm/kernel/asm-offsets.s] Error 1
>
>
>
Best Regards,
Josh Wu
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver
2012-06-27 4:11 ` Josh Wu
@ 2012-06-27 4:28 ` Artem Bityutskiy
2012-06-27 5:34 ` Josh Wu
2012-06-27 8:39 ` Nicolas Ferre
0 siblings, 2 replies; 22+ messages in thread
From: Artem Bityutskiy @ 2012-06-27 4:28 UTC (permalink / raw)
To: linux-arm-kernel
On Wed, 2012-06-27 at 12:11 +0800, Josh Wu wrote:
> Hi, Artem
>
> On 6/27/2012 11:49 AM, Artem Bityutskiy wrote:
> > On Mon, 2012-06-25 at 18:07 +0800, Josh Wu wrote:
> >> Those patches is based on v3.5-rc4
> >>
> >> Changes since v10,
> >> add one more patch in this patch set, which add 'int' return value for write_page()/write_page_raw() functions in structure of nand_ecc_ctrl.
> >> Instead of calling BUG(), atmel_nand_pmecc_write_page() will return -EIO when time out to read the pmecc status register.
> >> increase the time-out duration to 100ms, which has more toleration.
> >> add oob_required argument for pmecc read/write functions to align with v3.5-rc4.
> > I have issues compiling l2-mtd.git tree (based on 3.5-rc5) with the
> > attached defconfig. Probably not your fault, but may be you have a
> > fix/suggestion? I did not have time to look closer. Aiaiai reports:
>
> I think the name of kernel config for AT91 is changed, so you can try
> add following two line in your original config file.
>
> CONFIG_SOC_AT91SAM9=y
> CONFIG_SOC_AT91RM9200=y
>
> run the make menuconfig will get new config, that can pass the compile.
I get "arm-unknown-linux-gnueabi-ld: no machine record defined". I think
it wants me to select the board. I've tried one and get another
compilation issue:
arch/arm/mach-at91/built-in.o:(.arch.info.init+0xbc): undefined
reference to `at91sam926x_timer'
It looks like AT91 stuff is not looked after carefully.
Can you please send me a working defconfig based on the one I sent you -
just modify it.
--
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/20120627/eba33f8d/attachment-0001.sig>
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver
2012-06-27 4:28 ` Artem Bityutskiy
@ 2012-06-27 5:34 ` Josh Wu
2012-06-27 8:39 ` Nicolas Ferre
1 sibling, 0 replies; 22+ messages in thread
From: Josh Wu @ 2012-06-27 5:34 UTC (permalink / raw)
To: linux-arm-kernel
On 6/27/2012 12:28 PM, Artem Bityutskiy wrote:
> On Wed, 2012-06-27 at 12:11 +0800, Josh Wu wrote:
>> Hi, Artem
>>
>> On 6/27/2012 11:49 AM, Artem Bityutskiy wrote:
>>> On Mon, 2012-06-25 at 18:07 +0800, Josh Wu wrote:
>>>> Those patches is based on v3.5-rc4
>>>>
>>>> Changes since v10,
>>>> add one more patch in this patch set, which add 'int' return value for write_page()/write_page_raw() functions in structure of nand_ecc_ctrl.
>>>> Instead of calling BUG(), atmel_nand_pmecc_write_page() will return -EIO when time out to read the pmecc status register.
>>>> increase the time-out duration to 100ms, which has more toleration.
>>>> add oob_required argument for pmecc read/write functions to align with v3.5-rc4.
>>> I have issues compiling l2-mtd.git tree (based on 3.5-rc5) with the
>>> attached defconfig. Probably not your fault, but may be you have a
>>> fix/suggestion? I did not have time to look closer. Aiaiai reports:
>> I think the name of kernel config for AT91 is changed, so you can try
>> add following two line in your original config file.
>>
>> CONFIG_SOC_AT91SAM9=y
>> CONFIG_SOC_AT91RM9200=y
>>
>> run the make menuconfig will get new config, that can pass the compile.
> I get "arm-unknown-linux-gnueabi-ld: no machine record defined". I think
> it wants me to select the board. I've tried one and get another
> compilation issue:
>
> arch/arm/mach-at91/built-in.o:(.arch.info.init+0xbc): undefined
> reference to `at91sam926x_timer'
>
> It looks like AT91 stuff is not looked after carefully.
I find out the reason, we need add "CONFIG_ARCH_AT91RM9200=y" in your
config file. The ARCH_AT91xxx is for non DT board.
more information can be found in:
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=1e3ce2b8545390a2aee8dbfcd49ca4161b636000
which introduce SOC_AT91xxx definition for SoC core support.
> Can you please send me a working defconfig based on the one I sent you -
> just modify it.
>
I attached the defconfig file.
Best Regards,
Josh Wu
-------------- next part --------------
CONFIG_EXPERIMENTAL=y
# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_SYSVIPC=y
CONFIG_BSD_PROCESS_ACCT=y
CONFIG_LOG_BUF_SHIFT=18
CONFIG_CGROUPS=y
CONFIG_CGROUP_FREEZER=y
CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_RESOURCE_COUNTERS=y
CONFIG_CGROUP_SCHED=y
CONFIG_BLK_DEV_INITRD=y
# CONFIG_COMPAT_BRK is not set
CONFIG_PROFILING=y
CONFIG_KPROBES=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_PARTITION_ADVANCED=y
CONFIG_OSF_PARTITION=y
CONFIG_AMIGA_PARTITION=y
CONFIG_MAC_PARTITION=y
CONFIG_BSD_DISKLABEL=y
CONFIG_MINIX_SUBPARTITION=y
CONFIG_SOLARIS_X86_PARTITION=y
CONFIG_UNIXWARE_DISKLABEL=y
CONFIG_SGI_PARTITION=y
CONFIG_SUN_PARTITION=y
CONFIG_KARMA_PARTITION=y
CONFIG_EFI_PARTITION=y
CONFIG_ARCH_AT91=y
CONFIG_MACH_ONEARM=y
CONFIG_ARCH_AT91RM9200=y
CONFIG_ARCH_AT91RM9200DK=y
CONFIG_MACH_AT91RM9200EK=y
CONFIG_MACH_CSB337=y
CONFIG_MACH_CSB637=y
CONFIG_MACH_CARMEVA=y
CONFIG_MACH_ATEB9200=y
CONFIG_MACH_KB9200=y
CONFIG_MACH_PICOTUX2XX=y
CONFIG_MACH_KAFA=y
CONFIG_MACH_ECBAT91=y
CONFIG_MACH_YL9200=y
CONFIG_MACH_CPUAT91=y
CONFIG_MACH_ECO920=y
CONFIG_MACH_RSI_EWS=y
CONFIG_MTD_AT91_DATAFLASH_CARD=y
CONFIG_AT91_PROGRAMMABLE_CLOCKS=y
CONFIG_PCCARD=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_PREEMPT_VOLUNTARY=y
CONFIG_USE_OF=y
CONFIG_KEXEC=y
CONFIG_CRASH_DUMP=y
CONFIG_BINFMT_MISC=y
# CONFIG_SUSPEND is not set
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_DEBUG_DEVRES=y
CONFIG_MTD=y
CONFIG_MTD_TESTS=m
CONFIG_MTD_REDBOOT_PARTS=m
CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED=y
CONFIG_MTD_REDBOOT_PARTS_READONLY=y
CONFIG_MTD_CMDLINE_PARTS=y
CONFIG_MTD_AFS_PARTS=y
CONFIG_MTD_OF_PARTS=y
CONFIG_MTD_AR7_PARTS=m
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLOCK=y
CONFIG_FTL=m
CONFIG_NFTL=m
CONFIG_NFTL_RW=y
CONFIG_INFTL=m
CONFIG_RFD_FTL=m
CONFIG_SSFDC=y
CONFIG_SM_FTL=m
CONFIG_MTD_OOPS=m
CONFIG_MTD_SWAP=y
CONFIG_MTD_CFI=m
CONFIG_MTD_JEDECPROBE=m
CONFIG_MTD_CFI_ADV_OPTIONS=y
CONFIG_MTD_CFI_GEOMETRY=y
# CONFIG_MTD_MAP_BANK_WIDTH_1 is not set
CONFIG_MTD_MAP_BANK_WIDTH_16=y
# CONFIG_MTD_CFI_I1 is not set
CONFIG_MTD_CFI_I4=y
CONFIG_MTD_OTP=y
CONFIG_MTD_CFI_INTELEXT=m
CONFIG_MTD_CFI_AMDSTD=m
CONFIG_MTD_CFI_STAA=m
CONFIG_MTD_ROM=m
CONFIG_MTD_ABSENT=m
CONFIG_MTD_COMPLEX_MAPPINGS=y
CONFIG_MTD_PHYSMAP=m
CONFIG_MTD_PHYSMAP_COMPAT=y
CONFIG_MTD_PHYSMAP_OF=m
CONFIG_MTD_IMPA7=m
CONFIG_MTD_PCMCIA=m
CONFIG_MTD_PCMCIA_ANONYMOUS=y
CONFIG_MTD_GPIO_ADDR=y
CONFIG_MTD_PLATRAM=y
CONFIG_MTD_LATCH_ADDR=m
CONFIG_MTD_DATAFLASH=m
CONFIG_MTD_DATAFLASH_WRITE_VERIFY=y
CONFIG_MTD_DATAFLASH_OTP=y
CONFIG_MTD_M25P80=m
CONFIG_MTD_SST25L=m
CONFIG_MTD_SLRAM=y
CONFIG_MTD_PHRAM=y
CONFIG_MTD_MTDRAM=y
CONFIG_MTD_BLOCK2MTD=m
CONFIG_MTD_DOC2000=m
CONFIG_MTD_DOC2001=m
CONFIG_MTD_DOC2001PLUS=m
CONFIG_MTD_DOCG3=m
CONFIG_MTD_DOCPROBE_ADVANCED=y
CONFIG_MTD_DOCPROBE_HIGH=y
CONFIG_MTD_NAND_ECC_SMC=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_VERIFY_WRITE=y
CONFIG_MTD_NAND_ECC_BCH=y
CONFIG_MTD_NAND_MUSEUM_IDS=y
CONFIG_MTD_NAND_GPIO=y
CONFIG_MTD_NAND_DISKONCHIP=m
CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE=y
CONFIG_MTD_NAND_DOCG4=m
CONFIG_MTD_NAND_ATMEL=m
CONFIG_MTD_NAND_NANDSIM=y
CONFIG_MTD_NAND_PLATFORM=m
CONFIG_MTD_ALAUDA=m
CONFIG_MTD_ONENAND=y
CONFIG_MTD_ONENAND_VERIFY_WRITE=y
CONFIG_MTD_ONENAND_GENERIC=y
CONFIG_MTD_ONENAND_2X_PROGRAM=y
CONFIG_MTD_ONENAND_SIM=m
CONFIG_MTD_LPDDR=y
CONFIG_MTD_UBI=y
CONFIG_MTD_UBI_GLUEBI=m
# CONFIG_BLK_DEV is not set
CONFIG_INPUT_FF_MEMLESS=y
# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_SERIO is not set
CONFIG_VT_HW_CONSOLE_BINDING=y
# CONFIG_LEGACY_PTYS is not set
# CONFIG_DEVKMEM is not set
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_NR_UARTS=32
CONFIG_SERIAL_8250_EXTENDED=y
CONFIG_SERIAL_8250_MANY_PORTS=y
CONFIG_SERIAL_8250_SHARE_IRQ=y
CONFIG_SERIAL_8250_DETECT_IRQ=y
CONFIG_SERIAL_8250_RSA=y
# CONFIG_HW_RANDOM is not set
CONFIG_SPI=y
# CONFIG_HWMON is not set
CONFIG_DRM=y
# CONFIG_HID_SUPPORT is not set
CONFIG_USB=m
# CONFIG_IOMMU_SUPPORT is not set
CONFIG_QUOTA=y
# CONFIG_PRINT_QUOTA_WARNING is not set
CONFIG_QFMT_V2=y
CONFIG_JFFS2_FS=y
# CONFIG_JFFS2_FS_WRITEBUFFER is not set
CONFIG_JFFS2_COMPRESSION_OPTIONS=y
CONFIG_JFFS2_LZO=y
CONFIG_JFFS2_RUBIN=y
CONFIG_JFFS2_CMODE_FAVOURLZO=y
CONFIG_UBIFS_FS=m
CONFIG_UBIFS_FS_ADVANCED_COMPR=y
# CONFIG_UBIFS_FS_ZLIB is not set
CONFIG_UBIFS_FS_DEBUG=y
CONFIG_LOGFS=m
CONFIG_CRAMFS=m
CONFIG_NLS=y
CONFIG_NLS_DEFAULT="utf8"
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_UTF8=y
CONFIG_PRINTK_TIME=y
# CONFIG_ENABLE_WARN_DEPRECATED is not set
CONFIG_FRAME_WARN=2048
CONFIG_MAGIC_SYSRQ=y
CONFIG_DEBUG_KERNEL=y
# CONFIG_SCHED_DEBUG is not set
CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
CONFIG_DEBUG_STACK_USAGE=y
CONFIG_BLK_DEV_IO_TRACE=y
CONFIG_CRYPTO=y
# CONFIG_CRYPTO_ANSI_CPRNG is not set
# CONFIG_CRYPTO_HW is not set
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver
2012-06-27 4:28 ` Artem Bityutskiy
2012-06-27 5:34 ` Josh Wu
@ 2012-06-27 8:39 ` Nicolas Ferre
2012-06-27 9:14 ` Artem Bityutskiy
1 sibling, 1 reply; 22+ messages in thread
From: Nicolas Ferre @ 2012-06-27 8:39 UTC (permalink / raw)
To: linux-arm-kernel
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
On 06/27/2012 06:28 AM, Artem Bityutskiy :
> On Wed, 2012-06-27 at 12:11 +0800, Josh Wu wrote:
>> Hi, Artem
>>
>> On 6/27/2012 11:49 AM, Artem Bityutskiy wrote:
>>> On Mon, 2012-06-25 at 18:07 +0800, Josh Wu wrote:
>>>> Those patches is based on v3.5-rc4
>>>>
>>>> Changes since v10, add one more patch in this patch set,
>>>> which add 'int' return value for
>>>> write_page()/write_page_raw() functions in structure of
>>>> nand_ecc_ctrl. Instead of calling BUG(),
>>>> atmel_nand_pmecc_write_page() will return -EIO when time out
>>>> to read the pmecc status register. increase the time-out
>>>> duration to 100ms, which has more toleration. add
>>>> oob_required argument for pmecc read/write functions to align
>>>> with v3.5-rc4.
>>> I have issues compiling l2-mtd.git tree (based on 3.5-rc5) with
>>> the attached defconfig. Probably not your fault, but may be you
>>> have a fix/suggestion? I did not have time to look closer.
>>> Aiaiai reports:
>>
>> I think the name of kernel config for AT91 is changed, so you can
>> try add following two line in your original config file.
>>
>> CONFIG_SOC_AT91SAM9=y CONFIG_SOC_AT91RM9200=y
>>
>> run the make menuconfig will get new config, that can pass the
>> compile.
>
> I get "arm-unknown-linux-gnueabi-ld: no machine record defined". I
> think it wants me to select the board. I've tried one and get
> another compilation issue:
>
> arch/arm/mach-at91/built-in.o:(.arch.info.init+0xbc): undefined
> reference to `at91sam926x_timer'
>
> It looks like AT91 stuff is not looked after carefully.
AT91 is looked after carefully, yes it is.
The thing is that CAP9 SoC (the defconfig that you are using)
has been obsoleted then removed for 3.4:
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=9918ceafd4a9e013572e03983f528017c29bb1cb
Defconfigs for AT91 may need some update though.
Best regards,
- --
Nicolas Ferre
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
iQEcBAEBAgAGBQJP6sORAAoJEAf03oE53VmQyEoIAIhnUw2Vjv5xNQymHDXBqzEK
PwiLwOQROSQ2bFBqfYmV5vZ5FglYa0v+dnNu8bJ7Fpah5KlHPNDWb3r+0HKQfaep
CzyZnDHhNA+QL3GnirCiyal9GtDrhJzPdnkrO4SFvS6c72AptqSAyOEGMZGenhaT
dnyLsHj7m5Po5OGmiSpKwJfv2S8YFQLLWYyMcNe/lkyE/QhYVb3kX8C3gm24nSnA
s80j9O1a6SLdRBZRh+jPFjKNUZMcWVN+DIuJnoUOUeDpU4en3PRGJ/88VSxs4k/c
xUXkpx7m8QXVCs5khMpKXkK5fj5dATuUjlzslI3FFZTDCjYf5eXzv/6ZupVutI0=
=8MqL
-----END PGP SIGNATURE-----
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver
2012-06-27 8:39 ` Nicolas Ferre
@ 2012-06-27 9:14 ` Artem Bityutskiy
0 siblings, 0 replies; 22+ messages in thread
From: Artem Bityutskiy @ 2012-06-27 9:14 UTC (permalink / raw)
To: linux-arm-kernel
On Wed, 2012-06-27 at 10:39 +0200, Nicolas Ferre wrote:
> AT91 is looked after carefully, yes it is.
>
> The thing is that CAP9 SoC (the defconfig that you are using)
> has been obsoleted then removed for 3.4:
> http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=9918ceafd4a9e013572e03983f528017c29bb1cb
Ok, sorry for that phrase. I basically only need to do compile-testing.
And I only care about atmel_nand, so I take a defconfig, disable most of
the stuff, and use it for compilation. And when this minimal defconfig
stops working, I get upstet. If you have any suggestion what should I
base my configuration on, please, let me know.
Thanks!
--
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/20120627/bf5b8266/attachment.sig>
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v11 1/4] MTD: at91: extract hw ecc initialization to one function
2012-06-25 10:07 ` [PATCH v11 1/4] MTD: at91: extract hw ecc initialization to one function Josh Wu
@ 2012-06-27 9:26 ` Artem Bityutskiy
0 siblings, 0 replies; 22+ messages in thread
From: Artem Bityutskiy @ 2012-06-27 9:26 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, 2012-06-25 at 18:07 +0800, Josh Wu wrote:
> This patch moves hw ecc initialization code to one function.
>
> Signed-off-by: Hong Xu <hong.xu@atmel.com>
> Signed-off-by: Josh Wu <josh.wu@atmel.com>
Pushed this patch to l2-mtd.git, thanks!
--
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/20120627/9577fbb5/attachment.sig>
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v11 3/4] MTD: nand: add return value for write_page()/write_page_raw() functions in structure of nand_ecc_ctrl.
2012-06-25 10:07 ` [PATCH v11 3/4] MTD: nand: add return value for write_page()/write_page_raw() functions in structure of nand_ecc_ctrl Josh Wu
@ 2012-06-27 9:26 ` Artem Bityutskiy
0 siblings, 0 replies; 22+ messages in thread
From: Artem Bityutskiy @ 2012-06-27 9:26 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, 2012-06-25 at 18:07 +0800, Josh Wu wrote:
> There is an implemention of hardware ECC write page function which may return an
> error indication.
> For instance, using Atmel HW PMECC to write one page into a nand flash, the hardware
> engine will compute the BCH ecc code for this page. so we need read a the
> status register to theck whether the ecc code is generated.
> But we cannot assume the status register always can be ready, for example,
> incorrect hardware configuration or hardware issue, in such case we need
> write_page() to return a error code.
>
> Since the definition of 'write_page' function in struct nand_ecc_ctrl is 'void'.
> So this patch will:
> 1. add return 'int' value for 'write_page' function.
> 2. to be consitent, add return 'int' value for 'write_page_raw' fuctions too.
> 3. add code to test the return value, and if negative, indicate an
> error happend when write page with ECC.
> 4. fix the compile warning in all impacted nand flash driver.
>
> Note: I couldn't compile-test all of these easily, as some had ARCH dependencies.
>
> Signed-off-by: Josh Wu <josh.wu@atmel.com>
Pushed this patch to l2-mtd.git, thanks!
--
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/20120627/9ff84927/attachment.sig>
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver
2012-06-25 10:07 [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver Josh Wu
` (5 preceding siblings ...)
2012-06-27 3:49 ` Artem Bityutskiy
@ 2012-06-27 9:28 ` Artem Bityutskiy
6 siblings, 0 replies; 22+ messages in thread
From: Artem Bityutskiy @ 2012-06-27 9:28 UTC (permalink / raw)
To: linux-arm-kernel
On Mon, 2012-06-25 at 18:07 +0800, Josh Wu wrote:
> Those patches is based on v3.5-rc4
Pushed patches 1 and 3. The other ones were not pushed because you was
going to address review comments and re-send. Please, base them on the
l2-mtd.git three, not v3.5-rcX. Thanks!
The l2-mtd.git tree is here:
git://git.infradead.org/users/dedekind/l2-mtd.git
--
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/20120627/bb2e8c71/attachment-0001.sig>
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v11 4/4] MTD: at91: atmel_nand: Update driver to support Programmable Multibit ECC controller
2012-06-26 14:15 ` Richard Genoud
2012-06-26 14:37 ` [PATCH v11 4/4] MTD: at91: atmel_nand: Update driver to support Programmable Multibit ECC controller (REPORT SPAM) William F.
@ 2012-06-27 11:22 ` Josh Wu
1 sibling, 0 replies; 22+ messages in thread
From: Josh Wu @ 2012-06-27 11:22 UTC (permalink / raw)
To: linux-arm-kernel
Hi, Richard
On 6/26/2012 10:15 PM, Richard Genoud wrote:
> 2012/6/25 Josh Wu <josh.wu@atmel.com>:
>> +static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf,
>> + int extra_bytes, int err_nbr)
>> +{
>> + struct nand_chip *nand_chip = mtd->priv;
>> + struct atmel_nand_host *host = nand_chip->priv;
>> + int i = 0;
>> + int byte_pos, bit_pos, sector_size, ecc_size;
>> + uint32_t tmp;
>> +
>> + sector_size = host->pmecc_sector_size;
>> + ecc_size = nand_chip->ecc.bytes;
>> +
>> + while (err_nbr) {
>> + tmp = pmerrloc_readl_el_relaxed(host->pmerrloc_base, i) - 1;
>> + byte_pos = tmp / 8;
>> + bit_pos = tmp % 8;
>> + dev_info(host->dev, "PMECC correction, byte_pos: %d bit_pos: %d\n",
>> + byte_pos, bit_pos);
>> +
>> + if (byte_pos < (sector_size + extra_bytes)) {
>> + tmp = sector_size +
>> + pmecc_readl_relaxed(host->ecc, SADDR);
>> +
>> + if (byte_pos < tmp)
>> + *(buf + byte_pos) ^= (1 << bit_pos);
>> + else
>> + *(buf + byte_pos + ecc_size) ^= (1 << bit_pos);
>> + }
> I think there's a problem in there.
> On my board (sam9g35-ek), I've got 2KB pages and 4 sectors per page + 64B OOB.
> When I flip a bit on the 63rd OOB byte (the last ECC byte), byte_pos is 515.
> If I flip a bit on the 51th OOB byte (the 3rd ECC byte), byte_pos is also 515.
> So, if I get it correct, byte_pos will always be between 0 and
> sector_size + sector_ecc_bytes.
> (and I think that sector_ecc_bytes == extra_bytes (cf below))
> And, pmecc_readl_relaxed(host->ecc, SADDR) gives me the ECC offset in OOB (48).
> So, (byte_pos < sector_size + extra_bytes) will always be correct as
> far as the PMECC controller doesn't give us crap.
> But, IHMO the "tmp = sector_size + pmecc_readl_relaxed(host->ecc,
> SADDR);" line is wrong.
Yes, you are right, I didn't test the ECC bytes bit flip. thanks.
>
> I think that it should be something like this:
> static void pmecc_correct_data(struct mtd_info *mtd, uint8_t *buf, uint8_t *ecc,
> int extra_bytes, int err_nbr, int sector_number)
> {
> [...]
> tmp = pmerrloc_readl_el_relaxed(host->pmerrloc_base, i) - 1;
> byte_pos = tmp / 8;
> bit_pos = tmp % 8;
> if (byte_pos < (sector_size + extra_bytes)) {
> if (byte_pos < sector_size) {
> *(buf + byte_pos) ^= (1 << bit_pos);
> /* It may be easier to read this info if the byte_pos
> * is relative to the page start, not just the sector */
> dev_info(host->dev, "PMECC correction, byte_pos: %d bit_pos: %d\n",
> sector_number * sector_sz + byte_pos, bit_pos);
> } else {
> /* number of ecc bytes per sector */
> tmp = pmecc_get_ecc_bytes(nand_chip->ecc.strength, sector_size);
> /* offset in the ecc of the ecc bytes for current sector */
> tmp *= sector_number;
> /* now, tmp is the offset of the byte error in the ecc */
> tmp += byte_pos - sector_size;
> ecc[tmp] ^= (1 << bit_pos);
> dev_info(host->dev, "Bit flip in ECC, ecc_byte_pos: %d bit_pos: %d\n",
> tmp, bit_pos);
> /* or */
> dev_info(host->dev, "Bit flip in OOB, oob_byte_pos: %d bit_pos: %d\n",
> tmp + nand_chip->ecc_layout.eccpos[0], bit_pos);
> }
> }
> [...]
Your code is nice, I will refine this part code base on your code. Thank
you very much.
>> +
>> + i++;
>> + err_nbr--;
>> + }
>> +
>> + return;
>> +}
>> +
>> +static int pmecc_correction(struct mtd_info *mtd, u32 pmecc_stat, uint8_t *buf,
>> + u8 *ecc)
>> +{
>> + struct nand_chip *nand_chip = mtd->priv;
>> + struct atmel_nand_host *host = nand_chip->priv;
>> + int i, err_nbr, eccbytes;
>> + uint8_t *buf_pos;
>> +
>> + eccbytes = nand_chip->ecc.bytes;
>> + for (i = 0; i < eccbytes; i++)
>> + if (ecc[i] != 0xff)
>> + goto normal_check;
>> + /* Erased page, return OK */
>> + return 0;
>> +
>> +normal_check:
>> + for (i = 0; i < host->pmecc_sector_number; i++) {
>> + err_nbr = 0;
>> + if (pmecc_stat & 0x1) {
>> + buf_pos = buf + i * host->pmecc_sector_size;
>> +
>> + pmecc_gen_syndrome(mtd, i);
>> + pmecc_substitute(mtd);
>> + pmecc_get_sigma(mtd);
>> +
>> + err_nbr = pmecc_err_location(mtd);
>> + if (err_nbr == -1) {
>> + dev_err(host->dev, "PMECC: Too many errors\n");
>> + mtd->ecc_stats.failed++;
>> + return -EIO;
>> + } else {
>> + pmecc_correct_data(mtd, buf_pos, 0, err_nbr);
> IHMO, extra_bytes should be (eccbytes / host->pmecc_sector_number)
> pmecc_correct_data(mtd, buf_pos, ecc, eccbytes /
> host->pmecc_sector_number, err_nbr, i);
sure. I'll fix this too
>> + mtd->ecc_stats.corrected += err_nbr;
>> + }
>> + }
>> + pmecc_stat >>= 1;
>> + }
>> +
>> + return 0;
>> +}
> I tested the patchset against 3.5-rc4, on a at91sam9g35-ek board.
> the DTS config I used is:
> reg = <0x40000000 0x10000000 0xffffe000 0x600 0xffffe600 0x200
> 0x00100000 0x00100000 >;
> nand-bus-width = <8>;
> nand-ecc-mode = "hw";
> nand-on-flash-bbt;
> atmel,nand-addr-offset = <21>;
> atmel,nand-cmd-offset = <22>;
> gpios = <&pioD 5 0 &pioD 4 0 0 >;
> atmel,has-pmecc;
> atmel,pmecc-cap = <2>;
> atmel,pmecc-sector-size = <512>;
> atmel,pmecc-lookup-table-offset = <0x8000 0x10000>;
>
> (nand on the -ek is 2KiB page size, 128KiB eraseblock)
>
> I tested
> - 1 bit flip on a page
> - 4 bit flips on a page (1 per sector)
> - 5 bit flips on a page (2 on sector 1, and then 1 per sector)
> - 1 bit flip on ECC
> - 2 bit flips on ECC (on the same byte)
> - 3 bit flips => I/O error (ok, since pmecc-cap = 2 )
> - 3 bit flips on ECC => I/O error (ok, since pmecc-cap = 2 )
> only problem : ECC is not corrected (should be better with the fixes above)
>
> When you have corrected the ECC correction issue, you can add my:
> Tested-by: Richard Genoud <richard.genoud@gmail.com>
Thanks again. I will send out the next version tomorrow.
> Regards.
Best Regards,
Josh Wu
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2012-06-27 11:22 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-06-25 10:07 [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver Josh Wu
2012-06-25 10:07 ` [PATCH v11 1/4] MTD: at91: extract hw ecc initialization to one function Josh Wu
2012-06-27 9:26 ` Artem Bityutskiy
2012-06-25 10:07 ` [PATCH v11 2/4] MTD: at91: add dt parameters for Atmel PMECC Josh Wu
2012-06-25 10:35 ` Richard Genoud
2012-06-26 6:05 ` Josh Wu
2012-06-25 10:07 ` [PATCH v11 3/4] MTD: nand: add return value for write_page()/write_page_raw() functions in structure of nand_ecc_ctrl Josh Wu
2012-06-27 9:26 ` Artem Bityutskiy
2012-06-25 10:07 ` [PATCH v11 4/4] MTD: at91: atmel_nand: Update driver to support Programmable Multibit ECC controller Josh Wu
2012-06-25 10:27 ` Richard Genoud
2012-06-26 6:03 ` Josh Wu
2012-06-26 14:15 ` Richard Genoud
2012-06-26 14:37 ` [PATCH v11 4/4] MTD: at91: atmel_nand: Update driver to support Programmable Multibit ECC controller (REPORT SPAM) William F.
2012-06-27 11:22 ` [PATCH v11 4/4] MTD: at91: atmel_nand: Update driver to support Programmable Multibit ECC controller Josh Wu
2012-06-27 3:45 ` [PATCH v11 0/4] MTD: at91: Add PMECC support for at91 nand flash driver Artem Bityutskiy
2012-06-27 3:49 ` Artem Bityutskiy
2012-06-27 4:11 ` Josh Wu
2012-06-27 4:28 ` Artem Bityutskiy
2012-06-27 5:34 ` Josh Wu
2012-06-27 8:39 ` Nicolas Ferre
2012-06-27 9:14 ` Artem Bityutskiy
2012-06-27 9:28 ` Artem Bityutskiy
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).