aboutsummaryrefslogtreecommitdiff
path: root/drivers/mtd/nand
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-19 13:34:11 -0800
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-19 13:34:11 -0800
commit4935361766cc73949fe032cd157d314f288922ba (patch)
tree1584f81525ae05a04d515b13a4787cd8eed46029 /drivers/mtd/nand
parent2874b391bd78a5b8cb84be67297a345fbdec4ac8 (diff)
parent4f65992381112acd7d2732665a9eae492c2c9de6 (diff)
Merge git://git.infradead.org/mtd-2.6
* git://git.infradead.org/mtd-2.6: (49 commits) [MTD] [NAND] S3C2412 fix hw ecc [MTD] [NAND] Work around false compiler warning in CAFÉ driver [JFFS2] printk warning fixes [MTD] [MAPS] ichxrom warning fix [MTD] [MAPS] amd76xrom warning fix [MTD] [MAPS] esb2rom warning fixes [MTD] [MAPS] ck804xrom warning fix [MTD] [MAPS] netsc520 warning fix [MTD] [MAPS] sc520cdp warning fix [MTD] [ONENAND] onenand_base warning fix [MTD] [NAND] eXcite nand flash driver [MTD] Improve heuristic for detecting wrong-endian RedBoot partition table [MTD] Fix RedBoot partition parsing regression harder. [MTD] [NAND] S3C2410: Hardware ECC correction code [JFFS2] Use MTD_OOB_AUTO to automatically place cleanmarker on NAND [MTD] Clarify OOB-operation interface comments [MTD] remove unused ecctype,eccsize fields from struct mtd_info [MTD] [NOR] Intel: remove ugly PROGREGION macros [MTD] [NOR] STAA: use writesize instead off eccsize to represent ECC block [MTD] OneNAND: Invalidate bufferRAM after erase ...
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r--drivers/mtd/nand/Kconfig14
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/cafe.c89
-rw-r--r--drivers/mtd/nand/cafe_ecc.c2
-rw-r--r--drivers/mtd/nand/excite_nandflash.c248
-rw-r--r--drivers/mtd/nand/nand_base.c47
-rw-r--r--drivers/mtd/nand/s3c2410.c96
7 files changed, 424 insertions, 73 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 358f55a82db..2d12dcdd740 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -126,10 +126,6 @@ config MTD_NAND_S3C2410_HWECC
incorrect ECC generation, and if using these, the default of
software ECC is preferable.
- If you lay down a device with the hardware ECC, then you will
- currently not be able to switch to software, as there is no
- implementation for ECC method used by the S3C2410
-
config MTD_NAND_NDFC
tristate "NDFC NanD Flash Controller"
depends on MTD_NAND && 44x
@@ -221,9 +217,17 @@ config MTD_NAND_SHARPSL
tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
depends on MTD_NAND && ARCH_PXA
+config MTD_NAND_BASLER_EXCITE
+ tristate "Support for NAND Flash on Basler eXcite"
+ depends on MTD_NAND && BASLER_EXCITE
+ help
+ This enables the driver for the NAND flash device found on the
+ Basler eXcite Smart Camera. If built as a module, the driver
+ will be named "excite_nandflash.ko".
+
config MTD_NAND_CAFE
tristate "NAND support for OLPC CAFÉ chip"
- depends on PCI
+ depends on MTD_NAND && PCI
help
Use NAND flash attached to the CAFÉ chip designed for the $100
laptop.
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index f7a53f0b701..80f1dfc7794 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o
+obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
nand-objs := nand_base.o nand_bbt.o
cafe_nand-objs := cafe.o cafe_ecc.o
diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c
index 08cb060dfa3..fd6bb3ed40d 100644
--- a/drivers/mtd/nand/cafe.c
+++ b/drivers/mtd/nand/cafe.c
@@ -78,8 +78,9 @@ module_param(regdebug, int, 0644);
static int checkecc = 1;
module_param(checkecc, int, 0644);
-static int slowtiming = 0;
-module_param(slowtiming, int, 0644);
+static int numtimings;
+static int timing[3];
+module_param_array(timing, int, &numtimings, 0644);
/* Hrm. Why isn't this already conditional on something in the struct device? */
#define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0)
@@ -264,10 +265,10 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command,
ndelay(100);
if (1) {
- int c = 500000;
+ int c;
uint32_t irqs;
- while (c--) {
+ for (c = 500000; c != 0; c--) {
irqs = cafe_readl(cafe, NAND_IRQ);
if (irqs & doneint)
break;
@@ -529,6 +530,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
{
struct mtd_info *mtd;
struct cafe_priv *cafe;
+ uint32_t timing1, timing2, timing3;
uint32_t ctrl;
int err = 0;
@@ -580,31 +582,45 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
cafe->nand.block_bad = cafe_nand_block_bad;
}
+ if (numtimings && numtimings != 3) {
+ dev_warn(&cafe->pdev->dev, "%d timing register values ignored; precisely three are required\n", numtimings);
+ }
+
+ if (numtimings == 3) {
+ timing1 = timing[0];
+ timing2 = timing[1];
+ timing3 = timing[2];
+ cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n",
+ timing1, timing2, timing3);
+ } else {
+ timing1 = cafe_readl(cafe, NAND_TIMING1);
+ timing2 = cafe_readl(cafe, NAND_TIMING2);
+ timing3 = cafe_readl(cafe, NAND_TIMING3);
+
+ if (timing1 | timing2 | timing3) {
+ cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", timing1, timing2, timing3);
+ } else {
+ dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n");
+ timing1 = timing2 = timing3 = 0xffffffff;
+ }
+ }
+
/* Start off by resetting the NAND controller completely */
cafe_writel(cafe, 1, NAND_RESET);
cafe_writel(cafe, 0, NAND_RESET);
- cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK);
+ cafe_writel(cafe, timing1, NAND_TIMING1);
+ cafe_writel(cafe, timing2, NAND_TIMING2);
+ cafe_writel(cafe, timing3, NAND_TIMING3);
- /* Timings from Marvell's test code (not verified or calculated by us) */
- if (!slowtiming) {
- cafe_writel(cafe, 0x01010a0a, NAND_TIMING1);
- cafe_writel(cafe, 0x24121212, NAND_TIMING2);
- cafe_writel(cafe, 0x11000000, NAND_TIMING3);
- } else {
- cafe_writel(cafe, 0xffffffff, NAND_TIMING1);
- cafe_writel(cafe, 0xffffffff, NAND_TIMING2);
- cafe_writel(cafe, 0xffffffff, NAND_TIMING3);
- }
cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK);
err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED,
"CAFE NAND", mtd);
if (err) {
dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq);
-
goto out_free_dma;
}
-#if 1
+
/* Disable master reset, enable NAND clock */
ctrl = cafe_readl(cafe, GLOBAL_CTRL);
ctrl &= 0xffffeff0;
@@ -631,32 +647,8 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n",
cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK));
-#endif
-#if 1
- mtd->writesize=2048;
- mtd->oobsize = 0x40;
- memset(cafe->dmabuf, 0x5a, 2112);
- cafe->nand.cmdfunc(mtd, NAND_CMD_READID, 0, -1);
- cafe->nand.read_byte(mtd);
- cafe->nand.read_byte(mtd);
- cafe->nand.read_byte(mtd);
- cafe->nand.read_byte(mtd);
- cafe->nand.read_byte(mtd);
-#endif
-#if 0
- cafe->nand.cmdfunc(mtd, NAND_CMD_READ0, 0, 0);
- // nand_wait_ready(mtd);
- cafe->nand.read_byte(mtd);
- cafe->nand.read_byte(mtd);
- cafe->nand.read_byte(mtd);
- cafe->nand.read_byte(mtd);
-#endif
-#if 0
- writel(0x84600070, cafe->mmio);
- udelay(10);
- cafe_dev_dbg(&cafe->pdev->dev, "Status %x\n", cafe_readl(cafe, NAND_NONMEM));
-#endif
- /* Scan to find existance of the device */
+
+ /* Scan to find existence of the device */
if (nand_scan_ident(mtd, 1)) {
err = -ENXIO;
goto out_irq;
@@ -760,13 +752,4 @@ module_exit(cafe_nand_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
-MODULE_DESCRIPTION("NAND flash driver for OLPC CAFE chip");
-
-/* Correct ECC for 2048 bytes of 0xff:
- 41 a0 71 65 54 27 f3 93 ec a9 be ed 0b a1 */
-
-/* dwmw2's B-test board, in case of completely screwing it:
-Bad eraseblock 2394 at 0x12b40000
-Bad eraseblock 2627 at 0x14860000
-Bad eraseblock 3349 at 0x1a2a0000
-*/
+MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ chip");
diff --git a/drivers/mtd/nand/cafe_ecc.c b/drivers/mtd/nand/cafe_ecc.c
index 1b9fa05a447..ea5c8491d2c 100644
--- a/drivers/mtd/nand/cafe_ecc.c
+++ b/drivers/mtd/nand/cafe_ecc.c
@@ -1045,7 +1045,7 @@ static unsigned short err_pos_lut[4096] = {
static unsigned short err_pos(unsigned short din)
{
- BUG_ON(din > 4096);
+ BUG_ON(din >= ARRAY_SIZE(err_pos_lut));
return err_pos_lut[din];
}
static int chk_no_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info)
diff --git a/drivers/mtd/nand/excite_nandflash.c b/drivers/mtd/nand/excite_nandflash.c
new file mode 100644
index 00000000000..7e9afc4c775
--- /dev/null
+++ b/drivers/mtd/nand/excite_nandflash.c
@@ -0,0 +1,248 @@
+/*
+* Copyright (C) 2005 - 2007 by Basler Vision Technologies AG
+* Author: Thomas Koeller <thomas.koeller.qbaslerweb.com>
+* Original code by Thies Moeller <thies.moeller@baslerweb.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/rm9k-ocd.h>
+
+#include <excite_nandflash.h>
+
+#define EXCITE_NANDFLASH_VERSION "0.1"
+
+/* I/O register offsets */
+#define EXCITE_NANDFLASH_DATA_BYTE 0x00
+#define EXCITE_NANDFLASH_STATUS_BYTE 0x0c
+#define EXCITE_NANDFLASH_ADDR_BYTE 0x10
+#define EXCITE_NANDFLASH_CMD_BYTE 0x14
+
+/* prefix for debug output */
+static const char module_id[] = "excite_nandflash";
+
+/*
+ * partition definition
+ */
+static const struct mtd_partition partition_info[] = {
+ {
+ .name = "eXcite RootFS",
+ .offset = 0,
+ .size = MTDPART_SIZ_FULL
+ }
+};
+
+static inline const struct resource *
+excite_nand_get_resource(struct platform_device *d, unsigned long flags,
+ const char *basename)
+{
+ char buf[80];
+
+ if (snprintf(buf, sizeof buf, "%s_%u", basename, d->id) >= sizeof buf)
+ return NULL;
+ return platform_get_resource_byname(d, flags, buf);
+}
+
+static inline void __iomem *
+excite_nand_map_regs(struct platform_device *d, const char *basename)
+{
+ void *result = NULL;
+ const struct resource *const r =
+ excite_nand_get_resource(d, IORESOURCE_MEM, basename);
+
+ if (r)
+ result = ioremap_nocache(r->start, r->end + 1 - r->start);
+ return result;
+}
+
+/* controller and mtd information */
+struct excite_nand_drvdata {
+ struct mtd_info board_mtd;
+ struct nand_chip board_chip;
+ void __iomem *regs;
+ void __iomem *tgt;
+};
+
+/* Control function */
+static void excite_nand_control(struct mtd_info *mtd, int cmd,
+ unsigned int ctrl)
+{
+ struct excite_nand_drvdata * const d =
+ container_of(mtd, struct excite_nand_drvdata, board_mtd);
+
+ switch (ctrl) {
+ case NAND_CTRL_CHANGE | NAND_CTRL_CLE:
+ d->tgt = d->regs + EXCITE_NANDFLASH_CMD_BYTE;
+ break;
+ case NAND_CTRL_CHANGE | NAND_CTRL_ALE:
+ d->tgt = d->regs + EXCITE_NANDFLASH_ADDR_BYTE;
+ break;
+ case NAND_CTRL_CHANGE | NAND_NCE:
+ d->tgt = d->regs + EXCITE_NANDFLASH_DATA_BYTE;
+ break;
+ }
+
+ if (cmd != NAND_CMD_NONE)
+ __raw_writeb(cmd, d->tgt);
+}
+
+/* Return 0 if flash is busy, 1 if ready */
+static int excite_nand_devready(struct mtd_info *mtd)
+{
+ struct excite_nand_drvdata * const drvdata =
+ container_of(mtd, struct excite_nand_drvdata, board_mtd);
+
+ return __raw_readb(drvdata->regs + EXCITE_NANDFLASH_STATUS_BYTE);
+}
+
+/*
+ * Called by device layer to remove the driver.
+ * The binding to the mtd and all allocated
+ * resources are released.
+ */
+static int __exit excite_nand_remove(struct device *dev)
+{
+ struct excite_nand_drvdata * const this = dev_get_drvdata(dev);
+
+ dev_set_drvdata(dev, NULL);
+
+ if (unlikely(!this)) {
+ printk(KERN_ERR "%s: called %s without private data!!",
+ module_id, __func__);
+ return -EINVAL;
+ }
+
+ /* first thing we need to do is release our mtd
+ * then go through freeing the resource used
+ */
+ nand_release(&this->board_mtd);
+
+ /* free the common resources */
+ iounmap(this->regs);
+ kfree(this);
+
+ DEBUG(MTD_DEBUG_LEVEL1, "%s: removed\n", module_id);
+ return 0;
+}
+
+/*
+ * Called by device layer when it finds a device matching
+ * one our driver can handle. This code checks to see if
+ * it can allocate all necessary resources then calls the
+ * nand layer to look for devices.
+*/
+static int __init excite_nand_probe(struct device *dev)
+{
+ struct platform_device * const pdev = to_platform_device(dev);
+ struct excite_nand_drvdata *drvdata; /* private driver data */
+ struct nand_chip *board_chip; /* private flash chip data */
+ struct mtd_info *board_mtd; /* mtd info for this board */
+ int scan_res;
+
+ drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
+ if (unlikely(!drvdata)) {
+ printk(KERN_ERR "%s: no memory for drvdata\n",
+ module_id);
+ return -ENOMEM;
+ }
+
+ /* bind private data into driver */
+ dev_set_drvdata(dev, drvdata);
+
+ /* allocate and map the resource */
+ drvdata->regs =
+ excite_nand_map_regs(pdev, EXCITE_NANDFLASH_RESOURCE_REGS);
+
+ if (unlikely(!drvdata->regs)) {
+ printk(KERN_ERR "%s: cannot reserve register region\n",
+ module_id);
+ kfree(drvdata);
+ return -ENXIO;
+ }
+
+ drvdata->tgt = drvdata->regs + EXCITE_NANDFLASH_DATA_BYTE;
+
+ /* initialise our chip */
+ board_chip = &drvdata->board_chip;
+ board_chip->IO_ADDR_R = board_chip->IO_ADDR_W =
+ drvdata->regs + EXCITE_NANDFLASH_DATA_BYTE;
+ board_chip->cmd_ctrl = excite_nand_control;
+ board_chip->dev_ready = excite_nand_devready;
+ board_chip->chip_delay = 25;
+ board_chip->ecc.mode = NAND_ECC_SOFT;
+
+ /* link chip to mtd */
+ board_mtd = &drvdata->board_mtd;
+ board_mtd->priv = board_chip;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "%s: device scan\n", module_id);
+ scan_res = nand_scan(&drvdata->board_mtd, 1);
+
+ if (likely(!scan_res)) {
+ DEBUG(MTD_DEBUG_LEVEL2, "%s: register partitions\n", module_id);
+ add_mtd_partitions(&drvdata->board_mtd, partition_info,
+ sizeof partition_info / sizeof partition_info[0]);
+ } else {
+ iounmap(drvdata->regs);
+ kfree(drvdata);
+ printk(KERN_ERR "%s: device scan failed\n", module_id);
+ return -EIO;
+ }
+ return 0;
+}
+
+static struct device_driver excite_nand_driver = {
+ .name = "excite_nand",
+ .bus = &platform_bus_type,
+ .probe = excite_nand_probe,
+ .remove = __exit_p(excite_nand_remove)
+};
+
+static int __init excite_nand_init(void)
+{
+ pr_info("Basler eXcite nand flash driver Version "
+ EXCITE_NANDFLASH_VERSION "\n");
+ return driver_register(&excite_nand_driver);
+}
+
+static void __exit excite_nand_exit(void)
+{
+ driver_unregister(&excite_nand_driver);
+}
+
+module_init(excite_nand_init);
+module_exit(excite_nand_exit);
+
+MODULE_AUTHOR("Thomas Koeller <thomas.koeller@baslerweb.com>");
+MODULE_DESCRIPTION("Basler eXcite NAND-Flash driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(EXCITE_NANDFLASH_VERSION)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index dfe56e03e48..acaf97bc80d 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1272,10 +1272,25 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n",
(unsigned long long)from, readlen);
- if (ops->mode == MTD_OOB_RAW)
- len = mtd->oobsize;
- else
+ if (ops->mode == MTD_OOB_AUTO)
len = chip->ecc.layout->oobavail;
+ else
+ len = mtd->oobsize;
+
+ if (unlikely(ops->ooboffs >= len)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
+ "Attempt to start read outside oob\n");
+ return -EINVAL;
+ }
+
+ /* Do not allow reads past end of device */
+ if (unlikely(from >= mtd->size ||
+ ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) -
+ (from >> chip->page_shift)) * len)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
+ "Attempt read beyond end of device\n");
+ return -EINVAL;
+ }
chipnr = (int)(from >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
@@ -1742,19 +1757,40 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
- int chipnr, page, status;
+ int chipnr, page, status, len;
struct nand_chip *chip = mtd->priv;
DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n",
(unsigned int)to, (int)ops->ooblen);
+ if (ops->mode == MTD_OOB_AUTO)
+ len = chip->ecc.layout->oobavail;
+ else
+ len = mtd->oobsize;
+
/* Do not allow write past end of page */
- if ((ops->ooboffs + ops->ooblen) > mtd->oobsize) {
+ if ((ops->ooboffs + ops->ooblen) > len) {
DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
"Attempt to write past end of page\n");
return -EINVAL;
}
+ if (unlikely(ops->ooboffs >= len)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
+ "Attempt to start write outside oob\n");
+ return -EINVAL;
+ }
+
+ /* Do not allow reads past end of device */
+ if (unlikely(to >= mtd->size ||
+ ops->ooboffs + ops->ooblen >
+ ((mtd->size >> chip->page_shift) -
+ (to >> chip->page_shift)) * len)) {
+ DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
+ "Attempt write beyond end of device\n");
+ return -EINVAL;
+ }
+
chipnr = (int)(to >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
@@ -2530,7 +2566,6 @@ int nand_scan_tail(struct mtd_info *mtd)
/* Fill in remaining MTD driver data */
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH;
- mtd->ecctype = MTD_ECC_SW;
mtd->erase = nand_erase;
mtd->point = NULL;
mtd->unpoint = NULL;
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 8b3203571ee..0ddfd6de75c 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -337,17 +337,69 @@ static int s3c2412_nand_devready(struct mtd_info *mtd)
static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
- pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", mtd, dat, read_ecc, calc_ecc);
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ unsigned int diff0, diff1, diff2;
+ unsigned int bit, byte;
- pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n",
- read_ecc[0], read_ecc[1], read_ecc[2], calc_ecc[0], calc_ecc[1], calc_ecc[2]);
+ pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc);
- if (read_ecc[0] == calc_ecc[0] && read_ecc[1] == calc_ecc[1] && read_ecc[2] == calc_ecc[2])
- return 0;
+ diff0 = read_ecc[0] ^ calc_ecc[0];
+ diff1 = read_ecc[1] ^ calc_ecc[1];
+ diff2 = read_ecc[2] ^ calc_ecc[2];
+
+ pr_debug("%s: rd %02x%02x%02x calc %02x%02x%02x diff %02x%02x%02x\n",
+ __func__,
+ read_ecc[0], read_ecc[1], read_ecc[2],
+ calc_ecc[0], calc_ecc[1], calc_ecc[2],
+ diff0, diff1, diff2);
+
+ if (diff0 == 0 && diff1 == 0 && diff2 == 0)
+ return 0; /* ECC is ok */
+
+ /* Can we correct this ECC (ie, one row and column change).
+ * Note, this is similar to the 256 error code on smartmedia */
+
+ if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 &&
+ ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 &&
+ ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) {
+ /* calculate the bit position of the error */
+
+ bit = (diff2 >> 2) & 1;
+ bit |= (diff2 >> 3) & 2;
+ bit |= (diff2 >> 4) & 4;
+
+ /* calculate the byte position of the error */
+
+ byte = (diff1 << 1) & 0x80;
+ byte |= (diff1 << 2) & 0x40;
+ byte |= (diff1 << 3) & 0x20;
+ byte |= (diff1 << 4) & 0x10;
+
+ byte |= (diff0 >> 3) & 0x08;
+ byte |= (diff0 >> 2) & 0x04;
+ byte |= (diff0 >> 1) & 0x02;
+ byte |= (diff0 >> 0) & 0x01;
- /* we curently have no method for correcting the error */
+ byte |= (diff2 << 8) & 0x100;
- return -1;
+ dev_dbg(info->device, "correcting error bit %d, byte %d\n",
+ bit, byte);
+
+ dat[byte] ^= (1 << bit);
+ return 1;
+ }
+
+ /* if there is only one bit difference in the ECC, then
+ * one of only a row or column parity has changed, which
+ * means the error is most probably in the ECC itself */
+
+ diff0 |= (diff1 << 8);
+ diff0 |= (diff2 << 16);
+
+ if ((diff0 & ~(1<<fls(diff0))) == 0)
+ return 1;
+
+ return 0;
}
/* ECC functions
@@ -366,6 +418,15 @@ static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
writel(ctrl, info->regs + S3C2410_NFCONF);
}
+static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ unsigned long ctrl;
+
+ ctrl = readl(info->regs + S3C2440_NFCONT);
+ writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC, info->regs + S3C2440_NFCONT);
+}
+
static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
@@ -383,6 +444,21 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u
ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1);
ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2);
+ pr_debug("%s: returning ecc %02x%02x%02x\n", __func__,
+ ecc_code[0], ecc_code[1], ecc_code[2]);
+
+ return 0;
+}
+
+static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ unsigned long ecc = readl(info->regs + S3C2412_NFMECC0);
+
+ ecc_code[0] = ecc;
+ ecc_code[1] = ecc >> 8;
+ ecc_code[2] = ecc >> 16;
+
pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]);
return 0;
@@ -397,7 +473,7 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u
ecc_code[1] = ecc >> 8;
ecc_code[2] = ecc >> 16;
- pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]);
+ pr_debug("%s: returning ecc %06x\n", __func__, ecc);
return 0;
}
@@ -565,6 +641,10 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
break;
case TYPE_S3C2412:
+ chip->ecc.hwctl = s3c2412_nand_enable_hwecc;
+ chip->ecc.calculate = s3c2412_nand_calculate_ecc;
+ break;
+
case TYPE_S3C2440:
chip->ecc.hwctl = s3c2440_nand_enable_hwecc;
chip->ecc.calculate = s3c2440_nand_calculate_ecc;