diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-10-20 09:03:12 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-10-20 09:03:12 -0700 |
commit | 2be508d847392e431759e370d21cea9412848758 (patch) | |
tree | be5e00a4d7be4ef353ffe4d550fb80a251d321c3 /drivers/mtd/nand | |
parent | 01e8ef11bc1a74e65678ed55795f59266d4add01 (diff) | |
parent | 8a1a6272057e2ad90ab531a70330165888866e60 (diff) |
Merge git://git.infradead.org/mtd-2.6
* git://git.infradead.org/mtd-2.6: (69 commits)
Revert "[MTD] m25p80.c code cleanup"
[MTD] [NAND] GPIO driver depends on ARM... for now.
[MTD] [NAND] sh_flctl: fix compile error
[MTD] [NOR] AT49BV6416 has swapped erase regions
[MTD] [NAND] GPIO NAND flash driver
[MTD] cmdlineparts documentation change - explain where mtd-id comes from
[MTD] cfi_cmdset_0002.c: Add Macronix CFI V1.0 TopBottom detection
[MTD] [NAND] Fix compilation warnings in drivers/mtd/nand/cs553x_nand.c
[JFFS2] Write buffer offset adjustment for NOR-ECC (Sibley) flash
[MTD] mtdoops: Fix a bug where block may not be erased
[MTD] mtdoops: Add a magic number to logged kernel oops
[MTD] mtdoops: Fix an off by one error
[JFFS2] Correct parameter names of jffs2_compress() in comments
[MTD] [NAND] sh_flctl: add support for Renesas SuperH FLCTL
[MTD] [NAND] Bug on atmel_nand HW ECC : OOB info not correctly written
[MTD] [MAPS] Remove unused variable after ROM API cleanup.
[MTD] m25p80.c extended jedec support (v2)
[MTD] remove unused mtd parameter in of_mtd_parse_partitions()
[MTD] [NAND] remove dead Kconfig associated with !CONFIG_PPC_MERGE
[MTD] [NAND] driver extension to support NAND on TQM85xx modules
...
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r-- | drivers/mtd/nand/Kconfig | 42 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/atmel_nand.c | 58 | ||||
-rw-r--r-- | drivers/mtd/nand/cs553x_nand.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/fsl_elbc_nand.c | 3 | ||||
-rw-r--r-- | drivers/mtd/nand/fsl_upm.c | 68 | ||||
-rw-r--r-- | drivers/mtd/nand/gpio.c | 375 | ||||
-rw-r--r-- | drivers/mtd/nand/mxc_nand.c | 1077 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 16 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_ecc.c | 554 | ||||
-rw-r--r-- | drivers/mtd/nand/nandsim.c | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/pxa3xx_nand.c | 147 | ||||
-rw-r--r-- | drivers/mtd/nand/sh_flctl.c | 878 | ||||
-rw-r--r-- | drivers/mtd/nand/toto.c | 206 |
14 files changed, 2932 insertions, 499 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 41f361c49b32..1c2e9450d663 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -56,6 +56,12 @@ config MTD_NAND_H1900 help This enables the driver for the iPAQ h1900 flash. +config MTD_NAND_GPIO + tristate "GPIO NAND Flash driver" + depends on GENERIC_GPIO && ARM + help + This enables a GPIO based NAND flash driver. + config MTD_NAND_SPIA tristate "NAND Flash device on SPIA board" depends on ARCH_P720T @@ -68,12 +74,6 @@ config MTD_NAND_AMS_DELTA help Support for NAND flash on Amstrad E3 (Delta). -config MTD_NAND_TOTO - tristate "NAND Flash device on TOTO board" - depends on ARCH_OMAP && BROKEN - help - Support for NAND flash on Texas Instruments Toto platform. - config MTD_NAND_TS7250 tristate "NAND Flash device on TS-7250 board" depends on MACH_TS72XX @@ -163,13 +163,6 @@ config MTD_NAND_S3C2410_HWECC incorrect ECC generation, and if using these, the default of software ECC is preferable. -config MTD_NAND_NDFC - tristate "NDFC NanD Flash Controller" - depends on 4xx && !PPC_MERGE - select MTD_NAND_ECC_SMC - help - NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs - config MTD_NAND_S3C2410_CLKSTOP bool "S3C2410 NAND IDLE clock stop" depends on MTD_NAND_S3C2410 @@ -340,6 +333,13 @@ config MTD_NAND_PXA3xx This enables the driver for the NAND flash device found on PXA3xx processors +config MTD_NAND_PXA3xx_BUILTIN + bool "Use builtin definitions for some NAND chips (deprecated)" + depends on MTD_NAND_PXA3xx + help + This enables builtin definitions for some NAND chips. This + is deprecated in favor of platform specific data. + config MTD_NAND_CM_X270 tristate "Support for NAND Flash on CM-X270 modules" depends on MTD_NAND && MACH_ARMCORE @@ -400,10 +400,24 @@ config MTD_NAND_FSL_ELBC config MTD_NAND_FSL_UPM tristate "Support for NAND on Freescale UPM" - depends on MTD_NAND && OF_GPIO && (PPC_83xx || PPC_85xx) + depends on MTD_NAND && (PPC_83xx || PPC_85xx) select FSL_LBC help Enables support for NAND Flash chips wired onto Freescale PowerPC processor localbus with User-Programmable Machine support. +config MTD_NAND_MXC + tristate "MXC NAND support" + depends on ARCH_MX2 + help + This enables the driver for the NAND flash controller on the + MXC processors. + +config MTD_NAND_SH_FLCTL + tristate "Support for NAND on Renesas SuperH FLCTL" + depends on MTD_NAND && SUPERH && CPU_SUBTYPE_SH7723 + help + Several Renesas SuperH CPU has FLCTL. This option enables support + for NAND Flash using FLCTL. This driver support SH7723. + endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index b786c5da82da..b661586afbfc 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -8,7 +8,6 @@ obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o obj-$(CONFIG_MTD_NAND_SPIA) += spia.o obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o -obj-$(CONFIG_MTD_NAND_TOTO) += toto.o obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o @@ -24,6 +23,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_ATMEL) += atmel_nand.o +obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o @@ -34,5 +34,7 @@ obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o obj-$(CONFIG_MTD_NAND_FSL_UPM) += fsl_upm.o +obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o +obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 3387e0d5076b..c98c1570a40b 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -174,48 +174,6 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len) } /* - * write oob for small pages - */ -static int atmel_nand_write_oob_512(struct mtd_info *mtd, - struct nand_chip *chip, int page) -{ - int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; - int eccsize = chip->ecc.size, length = mtd->oobsize; - int len, pos, status = 0; - const uint8_t *bufpoi = chip->oob_poi; - - pos = eccsize + chunk; - - chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page); - len = min_t(int, length, chunk); - chip->write_buf(mtd, bufpoi, len); - bufpoi += len; - length -= len; - if (length > 0) - chip->write_buf(mtd, bufpoi, length); - - chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); - status = chip->waitfunc(mtd, chip); - - return status & NAND_STATUS_FAIL ? -EIO : 0; - -} - -/* - * read oob for small pages - */ -static int atmel_nand_read_oob_512(struct mtd_info *mtd, - struct nand_chip *chip, int page, int sndcmd) -{ - if (sndcmd) { - chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - sndcmd = 0; - } - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); - return sndcmd; -} - -/* * Calculate HW ECC * * function called after a write @@ -235,14 +193,14 @@ static int atmel_nand_calculate(struct mtd_info *mtd, /* get the first 2 ECC bytes */ ecc_value = ecc_readl(host->ecc, PR); - ecc_code[eccpos[0]] = ecc_value & 0xFF; - ecc_code[eccpos[1]] = (ecc_value >> 8) & 0xFF; + ecc_code[0] = ecc_value & 0xFF; + ecc_code[1] = (ecc_value >> 8) & 0xFF; /* get the last 2 ECC bytes */ ecc_value = ecc_readl(host->ecc, NPR) & ATMEL_ECC_NPARITY; - ecc_code[eccpos[2]] = ecc_value & 0xFF; - ecc_code[eccpos[3]] = (ecc_value >> 8) & 0xFF; + ecc_code[2] = ecc_value & 0xFF; + ecc_code[3] = (ecc_value >> 8) & 0xFF; return 0; } @@ -476,14 +434,12 @@ static int __init atmel_nand_probe(struct platform_device *pdev) res = -EIO; goto err_ecc_ioremap; } - nand_chip->ecc.mode = NAND_ECC_HW_SYNDROME; + nand_chip->ecc.mode = NAND_ECC_HW; nand_chip->ecc.calculate = atmel_nand_calculate; nand_chip->ecc.correct = atmel_nand_correct; nand_chip->ecc.hwctl = atmel_nand_hwctl; nand_chip->ecc.read_page = atmel_nand_read_page; nand_chip->ecc.bytes = 4; - nand_chip->ecc.prepad = 0; - nand_chip->ecc.postpad = 0; } nand_chip->chip_delay = 20; /* 20us command delay time */ @@ -514,7 +470,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) goto err_scan_ident; } - if (nand_chip->ecc.mode == NAND_ECC_HW_SYNDROME) { + if (nand_chip->ecc.mode == NAND_ECC_HW) { /* ECC is calculated for the whole page (1 step) */ nand_chip->ecc.size = mtd->writesize; @@ -522,8 +478,6 @@ static int __init atmel_nand_probe(struct platform_device *pdev) switch (mtd->writesize) { case 512: nand_chip->ecc.layout = &atmel_oobinfo_small; - nand_chip->ecc.read_oob = atmel_nand_read_oob_512; - nand_chip->ecc.write_oob = atmel_nand_write_oob_512; ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_528); break; case 1024: diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c index 3370a800fd36..9f1b451005ca 100644 --- a/drivers/mtd/nand/cs553x_nand.c +++ b/drivers/mtd/nand/cs553x_nand.c @@ -289,8 +289,10 @@ static int __init cs553x_init(void) int i; uint64_t val; +#ifdef CONFIG_MTD_PARTITIONS int mtd_parts_nb = 0; struct mtd_partition *mtd_parts = NULL; +#endif /* If the CPU isn't a Geode GX or LX, abort */ if (!is_geode()) diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 98ad3cefcaf4..4aa5bd6158da 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -918,8 +918,7 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, #ifdef CONFIG_MTD_OF_PARTS if (ret == 0) { - ret = of_mtd_parse_partitions(priv->dev, &priv->mtd, - node, &parts); + ret = of_mtd_parse_partitions(priv->dev, node, &parts); if (ret < 0) goto err; } diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index 1ebfd87f00b4..024e3fffd4bb 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -13,6 +13,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/delay.h> #include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> #include <linux/mtd/partitions.h> @@ -36,8 +37,6 @@ struct fsl_upm_nand { uint8_t upm_cmd_offset; void __iomem *io_base; int rnb_gpio; - const uint32_t *wait_pattern; - const uint32_t *wait_write; int chip_delay; }; @@ -61,10 +60,11 @@ static void fun_wait_rnb(struct fsl_upm_nand *fun) if (fun->rnb_gpio >= 0) { while (--cnt && !fun_chip_ready(&fun->mtd)) cpu_relax(); + if (!cnt) + dev_err(fun->dev, "tired waiting for RNB\n"); + } else { + ndelay(100); } - - if (!cnt) - dev_err(fun->dev, "tired waiting for RNB\n"); } static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) @@ -89,8 +89,7 @@ static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) fsl_upm_run_pattern(&fun->upm, fun->io_base, cmd); - if (fun->wait_pattern) - fun_wait_rnb(fun); + fun_wait_rnb(fun); } static uint8_t fun_read_byte(struct mtd_info *mtd) @@ -116,14 +115,16 @@ static void fun_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) for (i = 0; i < len; i++) { out_8(fun->chip.IO_ADDR_W, buf[i]); - if (fun->wait_write) - fun_wait_rnb(fun); + fun_wait_rnb(fun); } } -static int __devinit fun_chip_init(struct fsl_upm_nand *fun) +static int __devinit fun_chip_init(struct fsl_upm_nand *fun, + const struct device_node *upm_np, + const struct resource *io_res) { int ret; + struct device_node *flash_np; #ifdef CONFIG_MTD_PARTITIONS static const char *part_types[] = { "cmdlinepart", NULL, }; #endif @@ -143,18 +144,37 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun) fun->mtd.priv = &fun->chip; fun->mtd.owner = THIS_MODULE; + flash_np = of_get_next_child(upm_np, NULL); + if (!flash_np) + return -ENODEV; + + fun->mtd.name = kasprintf(GFP_KERNEL, "%x.%s", io_res->start, + flash_np->name); + if (!fun->mtd.name) { + ret = -ENOMEM; + goto err; + } + ret = nand_scan(&fun->mtd, 1); if (ret) - return ret; - - fun->mtd.name = fun->dev->bus_id; + goto err; #ifdef CONFIG_MTD_PARTITIONS ret = parse_mtd_partitions(&fun->mtd, part_types, &fun->parts, 0); + +#ifdef CONFIG_MTD_OF_PARTS + if (ret == 0) + ret = of_mtd_parse_partitions(fun->dev, &fun->mtd, + flash_np, &fun->parts); +#endif if (ret > 0) - return add_mtd_partitions(&fun->mtd, fun->parts, ret); + ret = add_mtd_partitions(&fun->mtd, fun->parts, ret); + else #endif - return add_mtd_device(&fun->mtd); + ret = add_mtd_device(&fun->mtd); +err: + of_node_put(flash_np); + return ret; } static int __devinit fun_probe(struct of_device *ofdev, @@ -211,6 +231,12 @@ static int __devinit fun_probe(struct of_device *ofdev, goto err2; } + prop = of_get_property(ofdev->node, "chip-delay", NULL); + if (prop) + fun->chip_delay = *prop; + else + fun->chip_delay = 50; + fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start, io_res.end - io_res.start + 1); if (!fun->io_base) { @@ -220,17 +246,8 @@ static int __devinit fun_probe(struct of_device *ofdev, fun->dev = &ofdev->dev; fun->last_ctrl = NAND_CLE; - fun->wait_pattern = of_get_property(ofdev->node, "fsl,wait-pattern", - NULL); - fun->wait_write = of_get_property(ofdev->node, "fsl,wait-write", NULL); - - prop = of_get_property(ofdev->node, "chip-delay", NULL); - if (prop) - fun->chip_delay = *prop; - else - fun->chip_delay = 50; - ret = fun_chip_init(fun); + ret = fun_chip_init(fun, ofdev->node, &io_res); if (ret) goto err2; @@ -251,6 +268,7 @@ static int __devexit fun_remove(struct of_device *ofdev) struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev); nand_release(&fun->mtd); + kfree(fun->mtd.name); if (fun->rnb_gpio >= 0) gpio_free(fun->rnb_gpio); diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c new file mode 100644 index 000000000000..8f902e75aa85 --- /dev/null +++ b/drivers/mtd/nand/gpio.c @@ -0,0 +1,375 @@ +/* + * drivers/mtd/nand/gpio.c + * + * Updated, and converted to generic GPIO based driver by Russell King. + * + * Written by Ben Dooks <ben@simtec.co.uk> + * Based on 2.4 version by Mark Whittaker + * + * © 2004 Simtec Electronics + * + * Device driver for NAND connected via GPIO + * + * 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 + * published by the Free Software Foundation. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/io.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/nand-gpio.h> + +struct gpiomtd { + void __iomem *io_sync; + struct mtd_info mtd_info; + struct nand_chip nand_chip; + struct gpio_nand_platdata plat; +}; + +#define gpio_nand_getpriv(x) container_of(x, struct gpiomtd, mtd_info) + + +#ifdef CONFIG_ARM +/* gpio_nand_dosync() + * + * Make sure the GPIO state changes occur in-order with writes to NAND + * memory region. + * Needed on PXA due to bus-reordering within the SoC itself (see section on + * I/O ordering in PXA manual (section 2.3, p35) + */ +static void gpio_nand_dosync(struct gpiomtd *gpiomtd) +{ + unsigned long tmp; + + if (gpiomtd->io_sync) { + /* + * Linux memory barriers don't cater for what's required here. + * What's required is what's here - a read from a separate + * region with a dependency on that read. + */ + tmp = readl(gpiomtd->io_sync); + asm volatile("mov %1, %0\n" : "=r" (tmp) : "r" (tmp)); + } +} +#else +static inline void gpio_nand_dosync(struct gpiomtd *gpiomtd) {} +#endif + +static void gpio_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); + + gpio_nand_dosync(gpiomtd); + + if (ctrl & NAND_CTRL_CHANGE) { + gpio_set_value(gpiomtd->plat.gpio_nce, !(ctrl & NAND_NCE)); + gpio_set_value(gpiomtd->plat.gpio_cle, !!(ctrl & NAND_CLE)); + gpio_set_value(gpiomtd->plat.gpio_ale, !!(ctrl & NAND_ALE)); + gpio_nand_dosync(gpiomtd); + } + if (cmd == NAND_CMD_NONE) + return; + + writeb(cmd, gpiomtd->nand_chip.IO_ADDR_W); + gpio_nand_dosync(gpiomtd); +} + +static void gpio_nand_writebuf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + + writesb(this->IO_ADDR_W, buf, len); +} + +static void gpio_nand_readbuf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + + readsb(this->IO_ADDR_R, buf, len); +} + +static int gpio_nand_verifybuf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + unsigned char read, *p = (unsigned char *) buf; + int i, err = 0; + + for (i = 0; i < len; i++) { + read = readb(this->IO_ADDR_R); + if (read != p[i]) { + pr_debug("%s: err at %d (read %04x vs %04x)\n", + __func__, i, read, p[i]); + err = -EFAULT; + } + } + return err; +} + +static void gpio_nand_writebuf16(struct mtd_info *mtd, const u_char *buf, + int len) +{ + struct nand_chip *this = mtd->priv; + + if (IS_ALIGNED((unsigned long)buf, 2)) { + writesw(this->IO_ADDR_W, buf, len>>1); + } else { + int i; + unsigned short *ptr = (unsigned short *)buf; + + for (i = 0; i < len; i += 2, ptr++) + writew(*ptr, this->IO_ADDR_W); + } +} + +static void gpio_nand_readbuf16(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *this = mtd->priv; + + if (IS_ALIGNED((unsigned long)buf, 2)) { + readsw(this->IO_ADDR_R, buf, len>>1); + } else { + int i; + unsigned short *ptr = (unsigned short *)buf; + + for (i = 0; i < len; i += 2, ptr++) + *ptr = readw(this->IO_ADDR_R); + } +} + +static int gpio_nand_verifybuf16(struct mtd_info *mtd, const u_char *buf, + int len) +{ + struct nand_chip *this = mtd->priv; + unsigned short read, *p = (unsigned short *) buf; + int i, err = 0; + len >>= 1; + + for (i = 0; i < len; i++) { + read = readw(this->IO_ADDR_R); + if (read != p[i]) { + pr_debug("%s: err at %d (read %04x vs %04x)\n", + __func__, i, read, p[i]); + err = -EFAULT; + } + } + return err; +} + + +static int gpio_nand_devready(struct mtd_info *mtd) +{ + struct gpiomtd *gpiomtd = gpio_nand_getpriv(mtd); + return gpio_get_value(gpiomtd->plat.gpio_rdy); +} + +static int __devexit gpio_nand_remove(struct platform_device *dev) +{ + struct gpiomtd *gpiomtd = platform_get_drvdata(dev); + struct resource *res; + + nand_release(&gpiomtd->mtd_info); + + res = platform_get_resource(dev, IORESOURCE_MEM, 1); + iounmap(gpiomtd->io_sync); + if (res) + release_mem_region(res->start, res->end - res->start + 1); + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + iounmap(gpiomtd->nand_chip.IO_ADDR_R); + release_mem_region(res->start, res->end - res->start + 1); + + if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) + gpio_set_value(gpiomtd->plat.gpio_nwp, 0); + gpio_set_value(gpiomtd->plat.gpio_nce, 1); + + gpio_free(gpiomtd->plat.gpio_cle); + gpio_free(gpiomtd->plat.gpio_ale); + gpio_free(gpiomtd->plat.gpio_nce); + if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) + gpio_free(gpiomtd->plat.gpio_nwp); + gpio_free(gpiomtd->plat.gpio_rdy); + + kfree(gpiomtd); + + return 0; +} + +static void __iomem *request_and_remap(struct resource *res, size_t size, + const char *name, int *err) +{ + void __iomem *ptr; + + if (!request_mem_region(res->start, res->end - res->start + 1, name)) { + *err = -EBUSY; + return NULL; + } + + ptr = ioremap(res->start, size); + if (!ptr) { + release_mem_region(res->start, res->end - res->start + 1); + *err = -ENOMEM; + } + return ptr; +} + +static int __devinit gpio_nand_probe(struct platform_device *dev) +{ + struct gpiomtd *gpiomtd; + struct nand_chip *this; + struct resource *res0, *res1; + int ret; + + if (!dev->dev.platform_data) + return -EINVAL; + + res0 = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res0) + return -EINVAL; + + gpiomtd = kzalloc(sizeof(*gpiomtd), GFP_KERNEL); + if (gpiomtd == NULL) { + dev_err(&dev->dev, "failed to create NAND MTD\n"); + return -ENOMEM; + } + + this = &gpiomtd->nand_chip; + this->IO_ADDR_R = request_and_remap(res0, 2, "NAND", &ret); + if (!this->IO_ADDR_R) { + dev_err(&dev->dev, "unable to map NAND\n"); + goto err_map; + } + + res1 = platform_get_resource(dev, IORESOURCE_MEM, 1); + if (res1) { + gpiomtd->io_sync = request_and_remap(res1, 4, "NAND sync", &ret); + if (!gpiomtd->io_sync) { + dev_err(&dev->dev, "unable to map sync NAND\n"); + goto err_sync; + } + } + + memcpy(&gpiomtd->plat, dev->dev.platform_data, sizeof(gpiomtd->plat)); + + ret = gpio_request(gpiomtd->plat.gpio_nce, "NAND NCE"); + if (ret) + goto err_nce; + gpio_direction_output(gpiomtd->plat.gpio_nce, 1); + if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) { + ret = gpio_request(gpiomtd->plat.gpio_nwp, "NAND NWP"); + if (ret) + goto err_nwp; + gpio_direction_output(gpiomtd->plat.gpio_nwp, 1); + } + ret = gpio_request(gpiomtd->plat.gpio_ale, "NAND ALE"); + if (ret) + goto err_ale; + gpio_direction_output(gpiomtd->plat.gpio_ale, 0); + ret = gpio_request(gpiomtd->plat.gpio_cle, "NAND CLE"); + if (ret) + goto err_cle; + gpio_direction_output(gpiomtd->plat.gpio_cle, 0); + ret = gpio_request(gpiomtd->plat.gpio_rdy, "NAND RDY"); + if (ret) + goto err_rdy; + gpio_direction_input(gpiomtd->plat.gpio_rdy); + + + this->IO_ADDR_W = this->IO_ADDR_R; + this->ecc.mode = NAND_ECC_SOFT; + this->options = gpiomtd->plat.options; + this->chip_delay = gpiomtd->plat.chip_delay; + + /* install our routines */ + this->cmd_ctrl = gpio_nand_cmd_ctrl; + this->dev_ready = gpio_nand_devready; + + if (this->options & NAND_BUSWIDTH_16) { + this->read_buf = gpio_nand_readbuf16; + this->write_buf = gpio_nand_writebuf16; + this->verify_buf = gpio_nand_verifybuf16; + } else { + this->read_buf = gpio_nand_readbuf; + this->write_buf = gpio_nand_writebuf; + this->verify_buf = gpio_nand_verifybuf; + } + + /* set the mtd private data for the nand driver */ + gpiomtd->mtd_info.priv = this; + gpiomtd->mtd_info.owner = THIS_MODULE; + + if (nand_scan(&gpiomtd->mtd_info, 1)) { + dev_err(&dev->dev, "no nand chips found?\n"); + ret = -ENXIO; + goto err_wp; + } + + if (gpiomtd->plat.adjust_parts) + gpiomtd->plat.adjust_parts(&gpiomtd->plat, + gpiomtd->mtd_info.size); + + add_mtd_partitions(&gpiomtd->mtd_info, gpiomtd->plat.parts, + gpiomtd->plat.num_parts); + platform_set_drvdata(dev, gpiomtd); + + return 0; + +err_wp: + if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) + gpio_set_value(gpiomtd->plat.gpio_nwp, 0); + gpio_free(gpiomtd->plat.gpio_rdy); +err_rdy: + gpio_free(gpiomtd->plat.gpio_cle); +err_cle: + gpio_free(gpiomtd->plat.gpio_ale); +err_ale: + if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) + gpio_free(gpiomtd->plat.gpio_nwp); +err_nwp: + gpio_free(gpiomtd->plat.gpio_nce); +err_nce: + iounmap(gpiomtd->io_sync); + if (res1) + release_mem_region(res1->start, res1->end - res1->start + 1); +err_sync: + iounmap(gpiomtd->nand_chip.IO_ADDR_R); + release_mem_region(res0->start, res0->end - res0->start + 1); +err_map: + kfree(gpiomtd); + return ret; +} + +static struct platform_driver gpio_nand_driver = { + .probe = gpio_nand_probe, + .remove = gpio_nand_remove, + .driver = { + .name = "gpio-nand", + }, +}; + +static int __init gpio_nand_init(void) +{ + printk(KERN_INFO "GPIO NAND driver, © 2004 Simtec Electronics\n"); + + return platform_driver_register(&gpio_nand_driver); +} + +static void __exit gpio_nand_exit(void) +{ + platform_driver_unregister(&gpio_nand_driver); +} + +module_init(gpio_nand_init); +module_exit(gpio_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); +MODULE_DESCRIPTION("GPIO NAND Driver"); diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c new file mode 100644 index 000000000000..21fd4f1c4806 --- /dev/null +++ b/drivers/mtd/nand/mxc_nand.c @@ -0,0 +1,1077 @@ +/* + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Sascha Hauer, kernel@pengutronix.de + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> + +#include <asm/mach/flash.h> +#include <mach/mxc_nand.h> + +#define DRIVER_NAME "mxc_nand" + +/* Addresses for NFC registers */ +#define NFC_BUF_SIZE 0xE00 +#define NFC_BUF_ADDR 0xE04 +#define NFC_FLASH_ADDR 0xE06 +#define NFC_FLASH_CMD 0xE08 +#define NFC_CONFIG 0xE0A +#define NFC_ECC_STATUS_RESULT 0xE0C +#define NFC_RSLTMAIN_AREA 0xE0E +#define NFC_RSLTSPARE_AREA 0xE10 +#define NFC_WRPROT 0xE12 +#define NFC_UNLOCKSTART_BLKADDR 0xE14 +#define NFC_UNLOCKEND_BLKADDR 0xE16 +#define NFC_NF_WRPRST 0xE18 +#define NFC_CONFIG1 0xE1A +#define NFC_CONFIG2 0xE1C + +/* Addresses for NFC RAM BUFFER Main area 0 */ +#define MAIN_AREA0 0x000 +#define MAIN_AREA1 0x200 +#define MAIN_AREA2 0x400 +#define MAIN_AREA3 0x600 + +/* Addresses for NFC SPARE BUFFER Spare area 0 */ +#define SPARE_AREA0 0x800 +#define SPARE_AREA1 0x810 +#define SPARE_AREA2 0x820 +#define SPARE_AREA3 0x830 + +/* Set INT to 0, FCMD to 1, rest to 0 in NFC_CONFIG2 Register + * for Command operation */ +#define NFC_CMD 0x1 + +/* Set INT to 0, FADD to 1, rest to 0 in NFC_CONFIG2 Register + * for Address operation */ +#define NFC_ADDR 0x2 + +/* Set INT to 0, FDI to 1, rest to 0 in NFC_CONFIG2 Register + * for Input operation */ +#define NFC_INPUT 0x4 + +/* Set INT to 0, FDO to 001, rest to 0 in NFC_CONFIG2 Register + * for Data Output operation */ +#define NFC_OUTPUT 0x8 + +/* Set INT to 0, FD0 to 010, rest to 0 in NFC_CONFIG2 Register + * for Read ID operation */ +#define NFC_ID 0x10 + +/* Set INT to 0, FDO to 100, rest to 0 in NFC_CONFIG2 Register + * for Read Status operation */ +#define NFC_STATUS 0x20 + +/* Set INT to 1, rest to 0 in NFC_CONFIG2 Register for Read + * Status operation */ +#define NFC_INT 0x8000 + +#define NFC_SP_EN (1 << 2) +#define NFC_ECC_EN (1 << 3) +#define NFC_INT_MSK (1 << 4) +#define NFC_BIG (1 << 5) +#define NFC_RST (1 << 6) +#define NFC_CE (1 << 7) +#define NFC_ONE_CYCLE (1 << 8) + +struct mxc_nand_host { + struct mtd_info mtd; + struct nand_chip nand; + struct mtd_partition *parts; + struct device *dev; + + void __iomem *regs; + int spare_only; + int status_request; + int pagesize_2k; + uint16_t col_addr; + struct clk *clk; + int clk_act; + int irq; + + wait_queue_head_t irq_waitq; +}; + +/* Define delays in microsec for NAND device operations */ +#define TROP_US_DELAY 2000 +/* Macros to get byte and bit positions of ECC */ +#define COLPOS(x) ((x) >> 3) +#define BITPOS(x) ((x) & 0xf) + +/* Define single bit Error positions in Main & Spare area */ +#define MAIN_SINGLEBIT_ERROR 0x4 +#define SPARE_SINGLEBIT_ERROR 0x1 + +/* OOB placement block for use with hardware ecc generation */ +static struct nand_ecclayout nand_hw_eccoob_8 = { + .eccbytes = 5, + .eccpos = {6, 7, 8, 9, 10}, + .oobfree = {{0, 5}, {11, 5}, } +}; + +static struct nand_ecclayout nand_hw_eccoob_16 = { + .eccbytes = 5, + .eccpos = {6, 7, 8, 9, 10}, + .oobfree = {{0, 6}, {12, 4}, } +}; + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probes[] = { "RedBoot", "cmdlinepart", NULL }; +#endif + +static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) +{ + struct mxc_nand_host *host = dev_id; + + uint16_t tmp; + + tmp = readw(host->regs + NFC_CONFIG1); + tmp |= NFC_INT_MSK; /* Disable interrupt */ + writew(tmp, host->regs + NFC_CONFIG1); + + wake_up(&host->irq_waitq); + + return IRQ_HANDLED; +} + +/* This function polls the NANDFC to wait for the basic operation to + * complete by checking the INT bit of config2 register. + */ +static void wait_op_done(struct mxc_nand_host *host, int max_retries, + uint16_t param, int useirq) +{ + uint32_t tmp; + + if (useirq) { + if ((readw(host->regs + NFC_CONFIG2) & NFC_INT) == 0) { + + tmp = readw(host->regs + NFC_CONFIG1); + tmp &= ~NFC_INT_MSK; /* Enable interrupt */ + writew(tmp, host->regs + NFC_CONFIG1); + + wait_event(host->irq_waitq, + readw(host->regs + NFC_CONFIG2) & NFC_INT); + + tmp = readw(host->regs + NFC_CONFIG2); + tmp &= ~NFC_INT; + writew(tmp, host->regs + NFC_CONFIG2); + } + } else { + while (max_retries-- > 0) { + if (readw(host->regs + NFC_CONFIG2) & NFC_INT) { + tmp = readw(host->regs + NFC_CONFIG2); + tmp &= ~NFC_INT; + writew(tmp, host->regs + NFC_CONFIG2); + break; + } + udelay(1); + } + if (max_retries <= 0) + DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n", + __func__, param); + } +} + +/* This function issues the specified command to the NAND device and + * waits for completion. */ +static void send_cmd(struct mxc_nand_host *host, uint16_t cmd, int useirq) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_cmd(host, 0x%x, %d)\n", cmd, useirq); + + writew(cmd, host->regs + NFC_FLASH_CMD); + writew(NFC_CMD, host->regs + NFC_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, cmd, useirq); +} + +/* This function sends an address (or partial address) to the + * NAND device. The address is used to select the source/destination for + * a NAND command. */ +static void send_addr(struct mxc_nand_host *host, uint16_t addr, int islast) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_addr(host, 0x%x %d)\n", addr, islast); + + writew(addr, host->regs + NFC_FLASH_ADDR); + writew(NFC_ADDR, host->regs + NFC_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, addr, islast); +} + +/* This function requests the NANDFC to initate the transfer + * of data currently in the NANDFC RAM buffer to the NAND device. */ +static void send_prog_page(struct mxc_nand_host *host, uint8_t buf_id, + int spare_only) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_prog_page (%d)\n", spare_only); + + /* NANDFC buffer 0 is used for page read/write */ + writew(buf_id, host->regs + NFC_BUF_ADDR); + + /* Configure spare or page+spare access */ + if (!host->pagesize_2k) { + uint16_t config1 = readw(host->regs + NFC_CONFIG1); + if (spare_only) + config1 |= NFC_SP_EN; + else + config1 &= ~(NFC_SP_EN); + writew(config1, host->regs + NFC_CONFIG1); + } + + writew(NFC_INPUT, host->regs + NFC_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, spare_only, true); +} + +/* Requests NANDFC to initated the transfer of data from the + * NAND device into in the NANDFC ram buffer. */ +static void send_read_page(struct mxc_nand_host *host, uint8_t buf_id, + int spare_only) +{ + DEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", spare_only); + + /* NANDFC buffer 0 is used for page read/write */ + writew(buf_id, host->regs + NFC_BUF_ADDR); + + /* Configure spare or page+spare access */ + if (!host->pagesize_2k) { + uint32_t config1 = readw(host->regs + NFC_CONFIG1); + if (spare_only) + config1 |= NFC_SP_EN; + else + config1 &= ~NFC_SP_EN; + writew(config1, host->regs + NFC_CONFIG1); + } + + writew(NFC_OUTPUT, host->regs + NFC_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, spare_only, true); +} + +/* Request the NANDFC to perform a read of the NAND device ID. */ +static void send_read_id(struct mxc_nand_host *host) +{ + struct nand_chip *this = &host->nand; + uint16_t tmp; + + /* NANDFC buffer 0 is used for device ID output */ + writew(0x0, host->regs + NFC_BUF_ADDR); + + /* Read ID into main buffer */ + tmp = readw(host->regs + NFC_CONFIG1); + tmp &= ~NFC_SP_EN; + writew(tmp, host->regs + NFC_CONFIG1); + + writew(NFC_ID, host->regs + NFC_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, 0, true); + + if (this->options & NAND_BUSWIDTH_16) { + void __iomem *main_buf = host->regs + MAIN_AREA0; + /* compress the ID info */ + writeb(readb(main_buf + 2), main_buf + 1); + writeb(readb(main_buf + 4), main_buf + 2); + writeb(readb(main_buf + 6), main_buf + 3); + writeb(readb(main_buf + 8), main_buf + 4); + writeb(readb(main_buf + 10), main_buf + 5); + } +} + +/* This function requests the NANDFC to perform a read of the + * NAND device status and returns the current status. */ +static uint16_t get_dev_status(struct mxc_nand_host *host) +{ + void __iomem *main_buf = host->regs + MAIN_AREA1; + uint32_t store; + uint16_t ret, tmp; + /* Issue status request to NAND device */ + + /* store the main area1 first word, later do recovery */ + store = readl(main_buf); + /* NANDFC buffer 1 is used for device status to prevent + * corruption of read/write buffer on status requests. */ + writew(1, host->regs + NFC_BUF_ADDR); + + /* Read status into main buffer */ + tmp = readw(host->regs + NFC_CONFIG1); + tmp &= ~NFC_SP_EN; + writew(tmp, host->regs + NFC_CONFIG1); + + writew(NFC_STATUS, host->regs + NFC_CONFIG2); + + /* Wait for operation to complete */ + wait_op_done(host, TROP_US_DELAY, 0, true); + + /* Status is placed in first word of main buffer */ + /* get status, then recovery area 1 data */ + ret = readw(main_buf); + writel(store, main_buf); + + return ret; +} + +/* This functions is used by upper layer to checks if device is ready */ +static int mxc_nand_dev_ready(struct mtd_info *mtd) +{ + /* + * NFC handles R/B internally. Therefore, this function + * always returns status as ready. + */ + return 1; +} + +static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + /* + * If HW ECC is enabled, we turn it on during init. There is + * no need to enable again here. + */ +} + +static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + + /* + * 1-Bit errors are automatically corrected in HW. No need for + * additional correction. 2-Bit errors cannot be corrected by + * HW ECC, so we need to return failure + */ + uint16_t ecc_status = readw(host->regs + NFC_ECC_STATUS_RESULT); + + if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) { + DEBUG(MTD_DEBUG_LEVEL0, + "MXC_NAND: HWECC uncorrectable 2-bit ECC error\n"); + return -1; + } + + return 0; +} + +static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + return 0; +} + +static u_char mxc_nand_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + uint8_t ret = 0; + uint16_t col, rd_word; + uint16_t __iomem *main_buf = host->regs + MAIN_AREA0; + uint16_t __iomem *spare_buf = host->regs + SPARE_AREA0; + + /* Check for status request */ + if (host->status_request) + return get_dev_status(host) & 0xFF; + + /* Get column for 16-bit access */ + col = host->col_addr >> 1; + + /* If we are accessing the spare region */ + if (host->spare_only) + rd_word = readw(&spare_buf[col]); + else + rd_word = readw(&main_buf[col]); + + /* Pick upper/lower byte of word from RAM buffer */ + if (host->col_addr & 0x1) + ret = (rd_word >> 8) & 0xFF; + else + ret = rd_word & 0xFF; + + /* Update saved column address */ + host->col_addr++; + + return ret; +} + +static uint16_t mxc_nand_read_word(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + uint16_t col, rd_word, ret; + uint16_t __iomem *p; + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_read_word(col = %d)\n", host->col_addr); + + col = host->col_addr; + /* Adjust saved column address */ + if (col < mtd->writesize && host->spare_only) + col += mtd->writesize; + + if (col < mtd->writesize) + p = (host->regs + MAIN_AREA0) + (col >> 1); + else + p = (host->regs + SPARE_AREA0) + ((col - mtd->writesize) >> 1); + + if (col & 1) { + rd_word = readw(p); + ret = (rd_word >> 8) & 0xff; + rd_word = readw(&p[1]); + ret |= (rd_word << 8) & 0xff00; + + } else + ret = readw(p); + + /* Update saved column address */ + host->col_addr = col + 2; + + return ret; +} + +/* Write data of length len to buffer buf. The data to be + * written on NAND Flash is first copied to RAMbuffer. After the Data Input + * Operation by the NFC, the data is written to NAND Flash */ +static void mxc_nand_write_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + int n, col, i = 0; + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_write_buf(col = %d, len = %d)\n", host->col_addr, + len); + + col = host->col_addr; + + /* Adjust saved column address */ + if (col < mtd->writesize && host->spare_only) + col += mtd->writesize; + + n = mtd->writesize + mtd->oobsize - col; + n = min(len, n); + + DEBUG(MTD_DEBUG_LEVEL3, + "%s:%d: col = %d, n = %d\n", __func__, __LINE__, col, n); + + while (n) { + void __iomem *p; + + if (col < mtd->writesize) + p = host->regs + MAIN_AREA0 + (col & ~3); + else + p = host->regs + SPARE_AREA0 - + mtd->writesize + (col & ~3); + + DEBUG(MTD_DEBUG_LEVEL3, "%s:%d: p = %p\n", __func__, + __LINE__, p); + + if (((col | (int)&buf[i]) & 3) || n < 16) { + uint32_t data = 0; + + if (col & 3 || n < 4) + data = readl(p); + + switch (col & 3) { + case 0: + if (n) { + data = (data & 0xffffff00) | + (buf[i++] << 0); + n--; + col++; + } + case 1: + if (n) { + data = (data & 0xffff00ff) | + (buf[i++] << 8); + n--; + col++; + } + case 2: + if (n) { + data = (data & 0xff00ffff) | + (buf[i++] << 16); + n--; + col++; + } + case 3: + if (n) { + data = (data & 0x00ffffff) | + (buf[i++] << 24); + n--; + col++; + } + } + + writel(data, p); + } else { + int m = mtd->writesize - col; + + if (col >= mtd->writesize) + m += mtd->oobsize; + + m = min(n, m) & ~3; + + DEBUG(MTD_DEBUG_LEVEL3, + "%s:%d: n = %d, m = %d, i = %d, col = %d\n", + __func__, __LINE__, n, m, i, col); + + memcpy(p, &buf[i], m); + col += m; + i += m; + n -= m; + } + } + /* Update saved column address */ + host->col_addr = col; +} + +/* Read the data buffer from the NAND Flash. To read the data from NAND + * Flash first the data output cycle is initiated by the NFC, which copies + * the data to RAMbuffer. This data of length len is then copied to buffer buf. + */ +static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + int n, col, i = 0; + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_read_buf(col = %d, len = %d)\n", host->col_addr, len); + + col = host->col_addr; + + /* Adjust saved column address */ + if (col < mtd->writesize && host->spare_only) + col += mtd->writesize; + + n = mtd->writesize + mtd->oobsize - col; + n = min(len, n); + + while (n) { + void __iomem *p; + + if (col < mtd->writesize) + p = host->regs + MAIN_AREA0 + (col & ~3); + else + p = host->regs + SPARE_AREA0 - + mtd->writesize + (col & ~3); + + if (((col | (int)&buf[i]) & 3) || n < 16) { + uint32_t data; + + data = readl(p); + switch (col & 3) { + case 0: + if (n) { + buf[i++] = (uint8_t) (data); + n--; + col++; + } + case 1: + if (n) { + buf[i++] = (uint8_t) (data >> 8); + n--; + col++; + } + case 2: + if (n) { + buf[i++] = (uint8_t) (data >> 16); + n--; + col++; + } + case 3: + if (n) { + buf[i++] = (uint8_t) (data >> 24); + n--; + col++; + } + } + } else { + int m = mtd->writesize - col; + + if (col >= mtd->writesize) + m += mtd->oobsize; + + m = min(n, m) & ~3; + memcpy(&buf[i], p, m); + col += m; + i += m; + n -= m; + } + } + /* Update saved column address */ + host->col_addr = col; + +} + +/* Used by the upper layer to verify the data in NAND Flash + * with the data in the buf. */ +static int mxc_nand_verify_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + return -EFAULT; +} + +/* This function is used by upper layer for select and + * deselect of the NAND chip */ +static void mxc_nand_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + +#ifdef CONFIG_MTD_NAND_MXC_FORCE_CE + if (chip > 0) { + DEBUG(MTD_DEBUG_LEVEL0, + "ERROR: Illegal chip select (chip = %d)\n", chip); + return; + } + + if (chip == -1) { + writew(readw(host->regs + NFC_CONFIG1) & ~NFC_CE, + host->regs + NFC_CONFIG1); + return; + } + + writew(readw(host->regs + NFC_CONFIG1) | NFC_CE, + host->regs + NFC_CONFIG1); +#endif + + switch (chip) { + case -1: + /* Disable the NFC clock */ + if (host->clk_act) { + clk_disable(host->clk); + host->clk_act = 0; + } + break; + case 0: + /* Enable the NFC clock */ + if (!host->clk_act) { + clk_enable(host->clk); + host->clk_act = 1; + } + break; + + default: + break; + } +} + +/* Used by the upper layer to write command to NAND Flash for + * different operations to be carried out on NAND Flash */ +static void mxc_nand_command(struct mtd_info *mtd, unsigned command, + int column, int page_addr) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + int useirq = true; + + DEBUG(MTD_DEBUG_LEVEL3, + "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n", + command, column, page_addr); + + /* Reset command state information */ + host->status_request = false; + + /* Command pre-processing step */ + switch (command) { + + case NAND_CMD_STATUS: + host->col_addr = 0; + host->status_request = true; + break; + + case NAND_CMD_READ0: + host->col_addr = column; + host->spare_only = false; + useirq = false; + break; + + case NAND_CMD_READOOB: + host->col_addr = column; + host->spare_only = true; + useirq = false; + if (host->pagesize_2k) + command = NAND_CMD_READ0; /* only READ0 is valid */ + break; + + case NAND_CMD_SEQIN: + if (column >= mtd->writesize) { + /* + * FIXME: before send SEQIN command for write OOB, + * We must read one page out. + * For K9F1GXX has no READ1 command to set current HW + * pointer to spare area, we must write the whole page + * including OOB together. + */ + if (host->pagesize_2k) + /* call ourself to read a page */ + mxc_nand_command(mtd, NAND_CMD_READ0, 0, + page_addr); + + host->col_addr = column - mtd->writesize; + host->spare_only = true; + + /* Set program pointer to spare region */ + if (!host->pagesize_2k) + send_cmd(host, NAND_CMD_READOOB, false); + } else { + host->spare_only = false; + host->col_addr = column; + + /* Set program pointer to page start */ + if (!host->pagesize_2k) + send_cmd(host, NAND_CMD_READ0, false); + } + useirq = false; + break; + + case NAND_CMD_PAGEPROG: + send_prog_page(host, 0, host->spare_only); + + if (host->pagesize_2k) { + /* data in 4 areas datas */ + send_prog_page(host, 1, host->spare_only); + send_prog_page(host, 2, host->spare_only); + send_prog_page(host, 3, host->spare_only); + } + + break; + + case NAND_CMD_ERASE1: + useirq = false; + break; + } + + /* Write out the command to the device. */ + send_cmd(host, command, useirq); + + /* Write out column address, if necessary */ + if (column != -1) { + /* + * MXC NANDFC can only perform full page+spare or + * spare-only read/write. When the upper layers + * layers perform a read/write buf operation, + * we will used the saved column adress to index into + * the full page. + */ + send_addr(host, 0, page_addr == -1); + if (host->pagesize_2k) + /* another col addr cycle for 2k page */ + send_addr(host, 0, false); + } + + /* Write out page address, if necessary */ + if (page_addr != -1) { + /* paddr_0 - p_addr_7 */ + send_addr(host, (page_addr & 0xff), false); + + if (host->pagesize_2k) { + send_addr(host, (page_addr >> 8) & 0xFF, false); + if (mtd->size >= 0x40000000) + send_addr(host, (page_addr >> 16) & 0xff, true); + } else { + /* One more address cycle for higher density devices */ + if (mtd->size >= 0x4000000) { + /* paddr_8 - paddr_15 */ + send_addr(host, (page_addr >> 8) & 0xff, false); + send_addr(host, (page_addr >> 16) & 0xff, true); + } else + /* paddr_8 - paddr_15 */ + send_addr(host, (page_addr >> 8) & 0xff, true); + } + } + + /* Command post-processing step */ + switch (command) { + + case NAND_CMD_RESET: + break; + + case NAND_CMD_READOOB: + case NAND_CMD_READ0: + if (host->pagesize_2k) { + /* send read confirm command */ + send_cmd(host, NAND_CMD_READSTART, true); + /* read for each AREA */ + send_read_page(host, 0, host->spare_only); + send_read_page(host, 1, host->spare_only); + send_read_page(host, 2, host->spare_only); + send_read_page(host, 3, host->spare_only); + } else + send_read_page(host, 0, host->spare_only); + break; + + case NAND_CMD_READID: + send_read_id(host); + break; + + case NAND_CMD_PAGEPROG: + break; + + case NAND_CMD_STATUS: + break; + + case NAND_CMD_ERASE2: + break; + } +} + +static int __init mxcnd_probe(struct platform_device *pdev) +{ + struct nand_chip *this; + struct mtd_info *mtd; + struct mxc_nand_platform_data *pdata = pdev->dev.platform_data; + struct mxc_nand_host *host; + struct resource *res; + uint16_t tmp; + int err = 0, nr_parts = 0; + + /* Allocate memory for MTD device structure and private data */ + host = kzalloc(sizeof(struct mxc_nand_host), GFP_KERNEL); + if (!host) + return -ENOMEM; + + host->dev = &pdev->dev; + /* structures must be linked */ + this = &host->nand; + mtd = &host->mtd; + mtd->priv = this; + mtd->owner = THIS_MODULE; + + /* 50 us command delay time */ + this->chip_delay = 5; + + this->priv = host; + this->dev_ready = mxc_nand_dev_ready; + this->cmdfunc = mxc_nand_command; + this->select_chip = mxc_nand_select_chip; + this->read_byte = mxc_nand_read_byte; + this->read_word = mxc_nand_read_word; + this->write_buf = mxc_nand_write_buf; + this->read_buf = mxc_nand_read_buf; + this->verify_buf = mxc_nand_verify_buf; + + host->clk = clk_get(&pdev->dev, "nfc_clk"); + if (IS_ERR(host->clk)) + goto eclk; + + clk_enable(host->clk); + host->clk_act = 1; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + err = -ENODEV; + goto eres; + } + + host->regs = ioremap(res->start, res->end - res->start + 1); + if (!host->regs) { + err = -EIO; + goto eres; + } + + tmp = readw(host->regs + NFC_CONFIG1); + tmp |= NFC_INT_MSK; + writew(tmp, host->regs + NFC_CONFIG1); + + init_waitqueue_head(&host->irq_waitq); + + host->irq = platform_get_irq(pdev, 0); + + err = request_irq(host->irq, mxc_nfc_irq, 0, "mxc_nd", host); + if (err) + goto eirq; + + if (pdata->hw_ecc) { + this->ecc.calculate = mxc_nand_calculate_ecc; + this->ecc.hwctl = mxc_nand_enable_hwecc; + this->ecc.correct = mxc_nand_correct_data; + this->ecc.mode = NAND_ECC_HW; + this->ecc.size = 512; + this->ecc.bytes = 3; + this->ecc.layout = &nand_hw_eccoob_8; + tmp = readw(host->regs + NFC_CONFIG1); + tmp |= NFC_ECC_EN; + writew(tmp, host->regs + NFC_CONFIG1); + } else { + this->ecc.size = 512; + this->ecc.bytes = 3; + this->ecc.layout = &nand_hw_eccoob_8; + this->ecc.mode = NAND_ECC_SOFT; + tmp = readw(host->regs + NFC_CONFIG1); + tmp &= ~NFC_ECC_EN; + writew(tmp, host->regs + NFC_CONFIG1); + } + + /* Reset NAND */ + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* preset operation */ + /* Unlock the internal RAM Buffer */ + writew(0x2, host->regs + NFC_CONFIG); + + /* Blocks to be unlocked */ + writew(0x0, host->regs + NFC_UNLOCKSTART_BLKADDR); + writew(0x4000, host->regs + NFC_UNLOCKEND_BLKADDR); + + /* Unlock Block Command for given address range */ + writew(0x4, host->regs + NFC_WRPROT); + + /* NAND bus width determines access funtions used by upper layer */ + if (pdata->width == 2) { + this->options |= NAND_BUSWIDTH_16; + this->ecc.layout = &nand_hw_eccoob_16; + } + + host->pagesize_2k = 0; + + /* Scan to find existence of the device */ + if (nand_scan(mtd, 1)) { + DEBUG(MTD_DEBUG_LEVEL0, + "MXC_ND: Unable to find any NAND device.\n"); + err = -ENXIO; + goto escan; + } + + /* Register the partitions */ +#ifdef CONFIG_MTD_PARTITIONS + nr_parts = + parse_mtd_partitions(mtd, part_probes, &host->parts, 0); + if (nr_parts > 0) + add_mtd_partitions(mtd, host->parts, nr_parts); + else +#endif + { + pr_info("Registering %s as whole device\n", mtd->name); + add_mtd_device(mtd); + } + + platform_set_drvdata(pdev, host); + + return 0; + +escan: + free_irq(host->irq, NULL); +eirq: + iounmap(host->regs); +eres: + clk_put(host->clk); +eclk: + kfree(host); + + return err; +} + +static int __devexit mxcnd_remove(struct platform_device *pdev) +{ + struct mxc_nand_host *host = platform_get_drvdata(pdev); + + clk_put(host->clk); + + platform_set_drvdata(pdev, NULL); + + nand_release(&host->mtd); + free_irq(host->irq, NULL); + iounmap(host->regs); + kfree(host); + + return 0; +} + +#ifdef CONFIG_PM +static int mxcnd_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct mtd_info *info = platform_get_drvdata(pdev); + int ret = 0; + + DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND suspend\n"); + if (info) + ret = info->suspend(info); + + /* Disable the NFC clock */ + clk_disable(nfc_clk); /* FIXME */ + + return ret; +} + +static int mxcnd_resume(struct platform_device *pdev) +{ + struct mtd_info *info = platform_get_drvdata(pdev); + int ret = 0; + + DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND resume\n"); + /* Enable the NFC clock */ + clk_enable(nfc_clk); /* FIXME */ + + if (info) + info->resume(info); + + return ret; +} + +#else +# define mxcnd_suspend NULL +# define mxcnd_resume NULL +#endif /* CONFIG_PM */ + +static struct platform_driver mxcnd_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .remove = __exit_p(mxcnd_remove), + .suspend = mxcnd_suspend, + .resume = mxcnd_resume, +}; + +static int __init mxc_nd_init(void) +{ + /* Register the device driver structure. */ + pr_info("MXC MTD nand Driver\n"); + if (platform_driver_probe(&mxcnd_driver, mxcnd_probe) != 0) { + printk(KERN_ERR "Driver register failed for mxcnd_driver\n"); + return -ENODEV; + } + return 0; +} + +static void __exit mxc_nd_cleanup(void) +{ + /* Unregister the device structure */ + platform_driver_unregister(&mxcnd_driver); +} + +module_init(mxc_nd_init); +module_exit(mxc_nd_cleanup); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MXC NAND MTD driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index d1129bae6c27..0a9c9cd33f96 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -801,9 +801,9 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function * @mtd: mtd info structure * @chip: nand chip info structure - * @dataofs offset of requested data within the page - * @readlen data length - * @buf: buffer to store read data + * @data_offs: offset of requested data within the page + * @readlen: data length + * @bufpoi: buffer to store read data */ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) { @@ -2042,7 +2042,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, return -EINVAL; } - instr->fail_addr = 0xffffffff; + instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; /* Grab the lock and see if the device is available */ nand_get_device(chip, mtd, FL_ERASING); @@ -2318,6 +2318,12 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, /* Select the device */ chip->select_chip(mtd, 0); + /* + * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx) + * after power-up + */ + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + /* Send the command for reading device ID */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); @@ -2488,6 +2494,8 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips) /* Check for a chip array */ for (i = 1; i < maxchips; i++) { chip->select_chip(mtd, i); + /* See comment in nand_get_flash_type for reset */ + chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); /* Send the command for reading device ID */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */ diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index 918a806a8471..868147acce2c 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -1,13 +1,18 @@ /* - * This file contains an ECC algorithm from Toshiba that detects and - * corrects 1 bit errors in a 256 byte block of data. + * This file contains an ECC algorithm that detects and corrects 1 bit + * errors in a 256 byte block of data. * * drivers/mtd/nand/nand_ecc.c * - * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com) - * Toshiba America Electronics Components, Inc. + * Copyright © 2008 Koninklijke Philips Electronics NV. + * Author: Frans Meulenbroeks * - * Copyright (C) 2006 Thomas Gleixner <tglx@linutronix.de> + * Completely replaces the previous ECC implementation which was written by: + * Steven J. Hill (sjhill@realitydiluted.com) + * Thomas Gleixner (tglx@linutronix.de) + * + * Information on how this algorithm works and how it was developed + * can be found in Documentation/mtd/nand_ecc.txt * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -23,174 +28,475 @@ * with this file; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * - * As a special exception, if other files instantiate templates or use - * macros or inline functions from these files, or you compile these - * files and link them with other works to produce a work based on these - * files, these files do not by themselves cause the resulting work to be - * covered by the GNU General Public License. However the source code for - * these files must still be made available in accordance with section (3) - * of the GNU General Public License. - * - * This exception does not invalidate any other reasons why a work based on - * this file might be covered by the GNU General Public License. */ +/* + * The STANDALONE macro is useful when running the code outside the kernel + * e.g. when running the code in a testbed or a benchmark program. + * When STANDALONE is used, the module related macros are commented out + * as well as the linux include files. + * Instead a private definition of mtd_info is given to satisfy the compiler + * (the code does not use mtd_info, so the code does not care) + */ +#ifndef STANDALONE #include <linux/types.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> #include <linux/mtd/nand_ecc.h> +#include <asm/byteorder.h> +#else +#include <stdint.h> +struct mtd_info; +#define EXPORT_SYMBOL(x) /* x */ + +#define MODULE_LICENSE(x) /* x */ +#define MODULE_AUTHOR(x) /* x */ +#define MODULE_DESCRIPTION(x) /* x */ + +#define printk printf +#define KERN_ERR "" +#endif + +/* + * invparity is a 256 byte table that contains the odd parity + * for each byte. So if the number of bits in a byte is even, + * the array element is 1, and when the number of bits is odd + * the array eleemnt is 0. + */ +static const char invparity[256] = { + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 +}; + +/* + * bitsperbyte contains the number of bits per byte + * this is only used for testing and repairing parity + * (a precalculated value slightly improves performance) + */ +static const char bitsperbyte[256] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, +}; /* - * Pre-calculated 256-way 1 byte column parity + * addressbits is a lookup table to filter out the bits from the xor-ed + * ecc data that identify the faulty location. + * this is only used for repairing parity + * see the comments in nand_correct_data for more details */ -static const u_char nand_ecc_precalc_table[] = { - 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, - 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, - 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, - 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, - 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, - 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, - 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, - 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, - 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, - 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, - 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, - 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, - 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, - 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, - 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, - 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00 +static const char addressbits[256] = { + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, + 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, + 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, + 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, + 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x01, + 0x02, 0x02, 0x03, 0x03, 0x02, 0x02, 0x03, 0x03, + 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, + 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, + 0x04, 0x04, 0x05, 0x05, 0x04, 0x04, 0x05, 0x05, + 0x06, 0x06, 0x07, 0x07, 0x06, 0x06, 0x07, 0x07, + 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, + 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, + 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, + 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, + 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, + 0x08, 0x08, 0x09, 0x09, 0x08, 0x08, 0x09, 0x09, + 0x0a, 0x0a, 0x0b, 0x0b, 0x0a, 0x0a, 0x0b, 0x0b, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, + 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f, + 0x0c, 0x0c, 0x0d, 0x0d, 0x0c, 0x0c, 0x0d, 0x0d, + 0x0e, 0x0e, 0x0f, 0x0f, 0x0e, 0x0e, 0x0f, 0x0f }; /** - * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256-byte block + * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512-byte + * block * @mtd: MTD block structure - * @dat: raw data - * @ecc_code: buffer for ECC + * @buf: input buffer with raw data + * @code: output buffer with ECC */ -int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, - u_char *ecc_code) +int nand_calculate_ecc(struct mtd_info *mtd, const unsigned char *buf, + unsigned char *code) { - uint8_t idx, reg1, reg2, reg3, tmp1, tmp2; int i; + const uint32_t *bp = (uint32_t *)buf; + /* 256 or 512 bytes/ecc */ + const uint32_t eccsize_mult = + (((struct nand_chip *)mtd->priv)->ecc.size) >> 8; + uint32_t cur; /* current value in buffer */ + /* rp0..rp15..rp17 are the various accumulated parities (per byte) */ + uint32_t rp0, rp1, rp2, rp3, rp4, rp5, rp6, rp7; + uint32_t rp8, rp9, rp10, rp11, rp12, rp13, rp14, rp15, rp16; + uint32_t uninitialized_var(rp17); /* to make compiler happy */ + uint32_t par; /* the cumulative parity for all data */ + uint32_t tmppar; /* the cumulative parity for this iteration; + for rp12, rp14 and rp16 at the end of the + loop */ + + par = 0; + rp4 = 0; + rp6 = 0; + rp8 = 0; + rp10 = 0; + rp12 = 0; + rp14 = 0; + rp16 = 0; + + /* + * The loop is unrolled a number of times; + * This avoids if statements to decide on which rp value to update + * Also we process the data by longwords. + * Note: passing unaligned data might give a performance penalty. + * It is assumed that the buffers are aligned. + * tmppar is the cumulative sum of this iteration. + * needed for calculating rp12, rp14, rp16 and par + * also used as a performance improvement for rp6, rp8 and rp10 + */ + for (i = 0; i < eccsize_mult << 2; i++) { + cur = *bp++; + tmppar = cur; + rp4 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp6 ^= tmppar; + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp8 ^= tmppar; - /* Initialize variables */ - reg1 = reg2 = reg3 = 0; + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + rp6 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp6 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp10 ^= tmppar; - /* Build up column parity */ - for(i = 0; i < 256; i++) { - /* Get CP0 - CP5 from table */ - idx = nand_ecc_precalc_table[*dat++]; - reg1 ^= (idx & 0x3f); + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + rp6 ^= cur; + rp8 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp6 ^= cur; + rp8 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + rp8 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp8 ^= cur; - /* All bit XOR = 1 ? */ - if (idx & 0x40) { - reg3 ^= (uint8_t) i; - reg2 ^= ~((uint8_t) i); - } + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + rp6 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp6 ^= cur; + cur = *bp++; + tmppar ^= cur; + rp4 ^= cur; + cur = *bp++; + tmppar ^= cur; + + par ^= tmppar; + if ((i & 0x1) == 0) + rp12 ^= tmppar; + if ((i & 0x2) == 0) + rp14 ^= tmppar; + if (eccsize_mult == 2 && (i & 0x4) == 0) + rp16 ^= tmppar; } - /* Create non-inverted ECC code from line parity */ - tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */ - tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */ - tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */ - tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */ - tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */ - tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */ - tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */ - tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */ - - tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */ - tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */ - tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */ - tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */ - tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */ - tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */ - tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */ - tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */ - - /* Calculate final ECC code */ -#ifdef CONFIG_MTD_NAND_ECC_SMC - ecc_code[0] = ~tmp2; - ecc_code[1] = ~tmp1; + /* + * handle the fact that we use longword operations + * we'll bring rp4..rp14..rp16 back to single byte entities by + * shifting and xoring first fold the upper and lower 16 bits, + * then the upper and lower 8 bits. + */ + rp4 ^= (rp4 >> 16); + rp4 ^= (rp4 >> 8); + rp4 &= 0xff; + rp6 ^= (rp6 >> 16); + rp6 ^= (rp6 >> 8); + rp6 &= 0xff; + rp8 ^= (rp8 >> 16); + rp8 ^= (rp8 >> 8); + rp8 &= 0xff; + rp10 ^= (rp10 >> 16); + rp10 ^= (rp10 >> 8); + rp10 &= 0xff; + rp12 ^= (rp12 >> 16); + rp12 ^= (rp12 >> 8); + rp12 &= 0xff; + rp14 ^= (rp14 >> 16); + rp14 ^= (rp14 >> 8); + rp14 &= 0xff; + if (eccsize_mult == 2) { + rp16 ^= (rp16 >> 16); + rp16 ^= (rp16 >> 8); + rp16 &= 0xff; + } + + /* + * we also need to calculate the row parity for rp0..rp3 + * This is present in par, because par is now + * rp3 rp3 rp2 rp2 in little endian and + * rp2 rp2 rp3 rp3 in big endian + * as well as + * rp1 rp0 rp1 rp0 in little endian and + * rp0 rp1 rp0 rp1 in big endian + * First calculate rp2 and rp3 + */ +#ifdef __BIG_ENDIAN + rp2 = (par >> 16); + rp2 ^= (rp2 >> 8); + rp2 &= 0xff; + rp3 = par & 0xffff; + rp3 ^= (rp3 >> 8); + rp3 &= 0xff; #else - ecc_code[0] = ~tmp1; - ecc_code[1] = ~tmp2; + rp3 = (par >> 16); + rp3 ^= (rp3 >> 8); + rp3 &= 0xff; + rp2 = par & 0xffff; + rp2 ^= (rp2 >> 8); + rp2 &= 0xff; #endif - ecc_code[2] = ((~reg1) << 2) | 0x03; - return 0; -} -EXPORT_SYMBOL(nand_calculate_ecc); + /* reduce par to 16 bits then calculate rp1 and rp0 */ + par ^= (par >> 16); +#ifdef __BIG_ENDIAN + rp0 = (par >> 8) & 0xff; + rp1 = (par & 0xff); +#else + rp1 = (par >> 8) & 0xff; + rp0 = (par & 0xff); +#endif -static inline int countbits(uint32_t byte) -{ - int res = 0; + /* finally reduce par to 8 bits */ + par ^= (par >> 8); + par &= 0xff; - for (;byte; byte >>= 1) - res += byte & 0x01; - return res; + /* + * and calculate rp5..rp15..rp17 + * note that par = rp4 ^ rp5 and due to the commutative property + * of the ^ operator we can say: + * rp5 = (par ^ rp4); + * The & 0xff seems superfluous, but benchmarking learned that + * leaving it out gives slightly worse results. No idea why, probably + * it has to do with the way the pipeline in pentium is organized. + */ + rp5 = (par ^ rp4) & 0xff; + rp7 = (par ^ rp6) & 0xff; + rp9 = (par ^ rp8) & 0xff; + rp11 = (par ^ rp10) & 0xff; + rp13 = (par ^ rp12) & 0xff; + rp15 = (par ^ rp14) & 0xff; + if (eccsize_mult == 2) + rp17 = (par ^ rp16) & 0xff; + + /* + * Finally calculate the ecc bits. + * Again here it might seem that there are performance optimisations + * possible, but benchmarks showed that on the system this is developed + * the code below is the fastest + */ +#ifdef CONFIG_MTD_NAND_ECC_SMC + code[0] = + (invparity[rp7] << 7) | + (invparity[rp6] << 6) | + (invparity[rp5] << 5) | + (invparity[rp4] << 4) | + (invparity[rp3] << 3) | + (invparity[rp2] << 2) | + (invparity[rp1] << 1) | + (invparity[rp0]); + code[1] = + (invparity[rp15] << 7) | + (invparity[rp14] << 6) | + (invparity[rp13] << 5) | + (invparity[rp12] << 4) | + (invparity[rp11] << 3) | + (invparity[rp10] << 2) | + (invparity[rp9] << 1) | + (invparity[rp8]); +#else + code[1] = + (invparity[rp7] << 7) | + (invparity[rp6] << 6) | + (invparity[rp5] << 5) | + (invparity[rp4] << 4) | + (invparity[rp3] << 3) | + (invparity[rp2] << 2) | + (invparity[rp1] << 1) | + (invparity[rp0]); + code[0] = + (invparity[rp15] << 7) | + (invparity[rp14] << 6) | + (invparity[rp13] << 5) | + (invparity[rp12] << 4) | + (invparity[rp11] << 3) | + (invparity[rp10] << 2) | + (invparity[rp9] << 1) | + (invparity[rp8]); +#endif + if (eccsize_mult == 1) + code[2] = + (invparity[par & 0xf0] << 7) | + (invparity[par & 0x0f] << 6) | + (invparity[par & 0xcc] << 5) | + (invparity[par & 0x33] << 4) | + (invparity[par & 0xaa] << 3) | + (invparity[par & 0x55] << 2) | + 3; + else + code[2] = + (invparity[par & 0xf0] << 7) | + (invparity[par & 0x0f] << 6) | + (invparity[par & 0xcc] << 5) | + (invparity[par & 0x33] << 4) | + (invparity[par & 0xaa] << 3) | + (invparity[par & 0x55] << 2) | + (invparity[rp17] << 1) | + (invparity[rp16] << 0); + return 0; } +EXPORT_SYMBOL(nand_calculate_ecc); /** * nand_correct_data - [NAND Interface] Detect and correct bit error(s) * @mtd: MTD block structure - * @dat: raw data read from the chip + * @buf: raw data read from the chip * @read_ecc: ECC from the chip * @calc_ecc: the ECC calculated from raw data * - * Detect and correct a 1 bit error for 256 byte block + * Detect and correct a 1 bit error for 256/512 byte block */ -int nand_correct_data(struct mtd_info *mtd, u_char *dat, - u_char *read_ecc, u_char *calc_ecc) +int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, + unsigned char *read_ecc, unsigned char *calc_ecc) { - uint8_t s0, s1, s2; + unsigned char b0, b1, b2; + unsigned char byte_addr, bit_addr; + /* 256 or 512 bytes/ecc */ + const uint32_t eccsize_mult = + (((struct nand_chip *)mtd->priv)->ecc.size) >> 8; + /* + * b0 to b2 indicate which bit is faulty (if any) + * we might need the xor result more than once, + * so keep them in a local var + */ #ifdef CONFIG_MTD_NAND_ECC_SMC - s0 = calc_ecc[0] ^ read_ecc[0]; - s1 = calc_ecc[1] ^ read_ecc[1]; - s2 = calc_ecc[2] ^ read_ecc[2]; + b0 = read_ecc[0] ^ calc_ecc[0]; + b1 = read_ecc[1] ^ calc_ecc[1]; #else - s1 = calc_ecc[0] ^ read_ecc[0]; - s0 = calc_ecc[1] ^ read_ecc[1]; - s2 = calc_ecc[2] ^ read_ecc[2]; + b0 = read_ecc[1] ^ calc_ecc[1]; + b1 = read_ecc[0] ^ calc_ecc[0]; #endif - if ((s0 | s1 | s2) == 0) - return 0; - - /* Check for a single bit error */ - if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 && - ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 && - ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) { + b2 = read_ecc[2] ^ calc_ecc[2]; - uint32_t byteoffs, bitnum; + /* check if there are any bitfaults */ - byteoffs = (s1 << 0) & 0x80; - byteoffs |= (s1 << 1) & 0x40; - byteoffs |= (s1 << 2) & 0x20; - byteoffs |= (s1 << 3) & 0x10; + /* repeated if statements are slightly more efficient than switch ... */ + /* ordered in order of likelihood */ - byteoffs |= (s0 >> 4) & 0x08; - byteoffs |= (s0 >> 3) & 0x04; - byteoffs |= (s0 >> 2) & 0x02; - byteoffs |= (s0 >> 1) & 0x01; - - bitnum = (s2 >> 5) & 0x04; - bitnum |= (s2 >> 4) & 0x02; - bitnum |= (s2 >> 3) & 0x01; - - dat[byteoffs] ^= (1 << bitnum); + if ((b0 | b1 | b2) == 0) + return 0; /* no error */ + if ((((b0 ^ (b0 >> 1)) & 0x55) == 0x55) && + (((b1 ^ (b1 >> 1)) & 0x55) == 0x55) && + ((eccsize_mult == 1 && ((b2 ^ (b2 >> 1)) & 0x54) == 0x54) || + (eccsize_mult == 2 && ((b2 ^ (b2 >> 1)) & 0x55) == 0x55))) { + /* single bit error */ + /* + * rp17/rp15/13/11/9/7/5/3/1 indicate which byte is the faulty + * byte, cp 5/3/1 indicate the faulty bit. + * A lookup table (called addressbits) is used to filter + * the bits from the byte they are in. + * A marginal optimisation is possible by having three + * different lookup tables. + * One as we have now (for b0), one for b2 + * (that would avoid the >> 1), and one for b1 (with all values + * << 4). However it was felt that introducing two more tables + * hardly justify the gain. + * + * The b2 shift is there to get rid of the lowest two bits. + * We could also do addressbits[b2] >> 1 but for the + * performace it does not make any difference + */ + if (eccsize_mult == 1) + byte_addr = (addressbits[b1] << 4) + addressbits[b0]; + else + byte_addr = (addressbits[b2 & 0x3] << 8) + + (addressbits[b1] << 4) + addressbits[b0]; + bit_addr = addressbits[b2 >> 2]; + /* flip the bit */ + buf[byte_addr] ^= (1 << bit_addr); return 1; - } - if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1) - return 1; + } + /* count nr of bits; use table lookup, faster than calculating it */ + if ((bitsperbyte[b0] + bitsperbyte[b1] + bitsperbyte[b2]) == 1) + return 1; /* error in ecc data; no action needed */ - return -EBADMSG; + printk(KERN_ERR "uncorrectable error : "); + return -1; } EXPORT_SYMBOL(nand_correct_data); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>"); +MODULE_AUTHOR("Frans Meulenbroeks <fransmeulenbroeks@gmail.com>"); MODULE_DESCRIPTION("Generic NAND ECC support"); diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 556e8131ecdc..ae7c57781a68 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -38,7 +38,6 @@ #include <linux/delay.h> #include <linux/list.h> #include <linux/random.h> -#include <asm/div64.h> /* Default simulator parameters values */ #if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index a64ad15b8fdd..c0fa9c9edf08 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -115,55 +115,11 @@ enum { STATE_PIO_WRITING, }; -struct pxa3xx_nand_timing { - unsigned int tCH; /* Enable signal hold time */ - unsigned int tCS; /* Enable signal setup time */ - unsigned int tWH; /* ND_nWE high duration */ - unsigned int tWP; /* ND_nWE pulse time */ - unsigned int tRH; /* ND_nRE high duration */ - unsigned int tRP; /* ND_nRE pulse width */ - unsigned int tR; /* ND_nWE high to ND_nRE low for read */ - unsigned int tWHR; /* ND_nWE high to ND_nRE low for status read */ - unsigned int tAR; /* ND_ALE low to ND_nRE low delay */ -}; - -struct pxa3xx_nand_cmdset { - uint16_t read1; - uint16_t read2; - uint16_t program; - uint16_t read_status; - uint16_t read_id; - uint16_t erase; - uint16_t reset; - uint16_t lock; - uint16_t unlock; - uint16_t lock_status; -}; - -struct pxa3xx_nand_flash { - struct pxa3xx_nand_timing *timing; /* NAND Flash timing */ - struct pxa3xx_nand_cmdset *cmdset; - - uint32_t page_per_block;/* Pages per block (PG_PER_BLK) */ - uint32_t page_size; /* Page size in bytes (PAGE_SZ) */ - uint32_t flash_width; /* Width of Flash memory (DWIDTH_M) */ - uint32_t dfc_width; /* Width of flash controller(DWIDTH_C) */ - uint32_t num_blocks; /* Number of physical blocks in Flash */ - uint32_t chip_id; - - /* NOTE: these are automatically calculated, do not define */ - size_t oob_size; - size_t read_id_bytes; - - unsigned int col_addr_cycles; - unsigned int row_addr_cycles; -}; - struct pxa3xx_nand_info { struct nand_chip nand_chip; struct platform_device *pdev; - struct pxa3xx_nand_flash *flash_info; + const struct pxa3xx_nand_flash *flash_info; struct clk *clk; void __iomem *mmio_base; @@ -202,12 +158,20 @@ struct pxa3xx_nand_info { uint32_t ndcb0; uint32_t ndcb1; uint32_t ndcb2; + + /* calculated from pxa3xx_nand_flash data */ + size_t oob_size; + size_t read_id_bytes; + + unsigned int col_addr_cycles; + unsigned int row_addr_cycles; }; static int use_dma = 1; module_param(use_dma, bool, 0444); MODULE_PARM_DESC(use_dma, "enable DMA for data transfering to/from NAND HW"); +#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN static struct pxa3xx_nand_cmdset smallpage_cmdset = { .read1 = 0x0000, .read2 = 0x0050, @@ -291,11 +255,35 @@ static struct pxa3xx_nand_flash micron1GbX16 = { .chip_id = 0xb12c, }; +static struct pxa3xx_nand_timing stm2GbX16_timing = { + .tCH = 10, + .tCS = 35, + .tWH = 15, + .tWP = 25, + .tRH = 15, + .tRP = 25, + .tR = 25000, + .tWHR = 60, + .tAR = 10, +}; + +static struct pxa3xx_nand_flash stm2GbX16 = { + .timing = &stm2GbX16_timing, + .page_per_block = 64, + .page_size = 2048, + .flash_width = 16, + .dfc_width = 16, + .num_blocks = 2048, + .chip_id = 0xba20, +}; + static struct pxa3xx_nand_flash *builtin_flash_types[] = { &samsung512MbX16, µn1GbX8, µn1GbX16, + &stm2GbX16, }; +#endif /* CONFIG_MTD_NAND_PXA3xx_BUILTIN */ #define NDTR0_tCH(c) (min((c), 7) << 19) #define NDTR0_tCS(c) (min((c), 7) << 16) @@ -312,7 +300,7 @@ static struct pxa3xx_nand_flash *builtin_flash_types[] = { #define ns2cycle(ns, clk) (int)(((ns) * (clk / 1000000) / 1000) + 1) static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info, - struct pxa3xx_nand_timing *t) + const struct pxa3xx_nand_timing *t) { unsigned long nand_clk = clk_get_rate(info->clk); uint32_t ndtr0, ndtr1; @@ -354,8 +342,8 @@ static int wait_for_event(struct pxa3xx_nand_info *info, uint32_t event) static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info, uint16_t cmd, int column, int page_addr) { - struct pxa3xx_nand_flash *f = info->flash_info; - struct pxa3xx_nand_cmdset *cmdset = f->cmdset; + const struct pxa3xx_nand_flash *f = info->flash_info; + const struct pxa3xx_nand_cmdset *cmdset = f->cmdset; /* calculate data size */ switch (f->page_size) { @@ -373,14 +361,14 @@ static int prepare_read_prog_cmd(struct pxa3xx_nand_info *info, info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); info->ndcb1 = 0; info->ndcb2 = 0; - info->ndcb0 |= NDCB0_ADDR_CYC(f->row_addr_cycles + f->col_addr_cycles); + info->ndcb0 |= NDCB0_ADDR_CYC(info->row_addr_cycles + info->col_addr_cycles); - if (f->col_addr_cycles == 2) { + if (info->col_addr_cycles == 2) { /* large block, 2 cycles for column address * row address starts from 3rd cycle */ info->ndcb1 |= (page_addr << 16) | (column & 0xffff); - if (f->row_addr_cycles == 3) + if (info->row_addr_cycles == 3) info->ndcb2 = (page_addr >> 16) & 0xff; } else /* small block, 1 cycles for column address @@ -406,7 +394,7 @@ static int prepare_erase_cmd(struct pxa3xx_nand_info *info, static int prepare_other_cmd(struct pxa3xx_nand_info *info, uint16_t cmd) { - struct pxa3xx_nand_cmdset *cmdset = info->flash_info->cmdset; + const struct pxa3xx_nand_cmdset *cmdset = info->flash_info->cmdset; info->ndcb0 = cmd | ((cmd & 0xff00) ? NDCB0_DBC : 0); info->ndcb1 = 0; @@ -641,8 +629,8 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, int column, int page_addr) { struct pxa3xx_nand_info *info = mtd->priv; - struct pxa3xx_nand_flash *flash_info = info->flash_info; - struct pxa3xx_nand_cmdset *cmdset = flash_info->cmdset; + const struct pxa3xx_nand_flash *flash_info = info->flash_info; + const struct pxa3xx_nand_cmdset *cmdset = flash_info->cmdset; int ret; info->use_dma = (use_dma) ? 1 : 0; @@ -720,7 +708,7 @@ static void pxa3xx_nand_cmdfunc(struct mtd_info *mtd, unsigned command, info->use_dma = 0; /* force PIO read */ info->buf_start = 0; info->buf_count = (command == NAND_CMD_READID) ? - flash_info->read_id_bytes : 1; + info->read_id_bytes : 1; if (prepare_other_cmd(info, (command == NAND_CMD_READID) ? cmdset->read_id : cmdset->read_status)) @@ -861,8 +849,8 @@ static int pxa3xx_nand_ecc_correct(struct mtd_info *mtd, static int __readid(struct pxa3xx_nand_info *info, uint32_t *id) { - struct pxa3xx_nand_flash *f = info->flash_info; - struct pxa3xx_nand_cmdset *cmdset = f->cmdset; + const struct pxa3xx_nand_flash *f = info->flash_info; + const struct pxa3xx_nand_cmdset *cmdset = f->cmdset; uint32_t ndcr; uint8_t id_buff[8]; @@ -891,7 +879,7 @@ fail_timeout: } static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, - struct pxa3xx_nand_flash *f) + const struct pxa3xx_nand_flash *f) { struct platform_device *pdev = info->pdev; struct pxa3xx_nand_platform_data *pdata = pdev->dev.platform_data; @@ -904,25 +892,25 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, return -EINVAL; /* calculate flash information */ - f->oob_size = (f->page_size == 2048) ? 64 : 16; - f->read_id_bytes = (f->page_size == 2048) ? 4 : 2; + info->oob_size = (f->page_size == 2048) ? 64 : 16; + info->read_id_bytes = (f->page_size == 2048) ? 4 : 2; /* calculate addressing information */ - f->col_addr_cycles = (f->page_size == 2048) ? 2 : 1; + info->col_addr_cycles = (f->page_size == 2048) ? 2 : 1; if (f->num_blocks * f->page_per_block > 65536) - f->row_addr_cycles = 3; + info->row_addr_cycles = 3; else - f->row_addr_cycles = 2; + info->row_addr_cycles = 2; ndcr |= (pdata->enable_arbiter) ? NDCR_ND_ARB_EN : 0; - ndcr |= (f->col_addr_cycles == 2) ? NDCR_RA_START : 0; + ndcr |= (info->col_addr_cycles == 2) ? NDCR_RA_START : 0; ndcr |= (f->page_per_block == 64) ? NDCR_PG_PER_BLK : 0; ndcr |= (f->page_size == 2048) ? NDCR_PAGE_SZ : 0; ndcr |= (f->flash_width == 16) ? NDCR_DWIDTH_M : 0; ndcr |= (f->dfc_width == 16) ? NDCR_DWIDTH_C : 0; - ndcr |= NDCR_RD_ID_CNT(f->read_id_bytes); + ndcr |= NDCR_RD_ID_CNT(info->read_id_bytes); ndcr |= NDCR_SPARE_EN; /* enable spare by default */ info->reg_ndcr = ndcr; @@ -932,12 +920,27 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, return 0; } -static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info) +static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info, + const struct pxa3xx_nand_platform_data *pdata) { - struct pxa3xx_nand_flash *f; - uint32_t id; + const struct pxa3xx_nand_flash *f; + uint32_t id = -1; int i; + for (i = 0; i<pdata->num_flash; ++i) { + f = pdata->flash + i; + + if (pxa3xx_nand_config_flash(info, f)) + continue; + + if (__readid(info, &id)) + continue; + + if (id == f->chip_id) + return 0; + } + +#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN for (i = 0; i < ARRAY_SIZE(builtin_flash_types); i++) { f = builtin_flash_types[i]; @@ -951,7 +954,11 @@ static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info) if (id == f->chip_id) return 0; } +#endif + dev_warn(&info->pdev->dev, + "failed to detect configured nand flash; found %04x instead of\n", + id); return -ENODEV; } @@ -1014,7 +1021,7 @@ static struct nand_ecclayout hw_largepage_ecclayout = { static void pxa3xx_nand_init_mtd(struct mtd_info *mtd, struct pxa3xx_nand_info *info) { - struct pxa3xx_nand_flash *f = info->flash_info; + const struct pxa3xx_nand_flash *f = info->flash_info; struct nand_chip *this = &info->nand_chip; this->options = (f->flash_width == 16) ? NAND_BUSWIDTH_16: 0; @@ -1135,7 +1142,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) goto fail_free_buf; } - ret = pxa3xx_nand_detect_flash(info); + ret = pxa3xx_nand_detect_flash(info, pdata); if (ret) { dev_err(&pdev->dev, "failed to detect flash\n"); ret = -ENODEV; diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c new file mode 100644 index 000000000000..821acb08ff1c --- /dev/null +++ b/drivers/mtd/nand/sh_flctl.c @@ -0,0 +1,878 @@ +/* + * SuperH FLCTL nand controller + * + * Copyright © 2008 Renesas Solutions Corp. + * Copyright © 2008 Atom Create Engineering Co., Ltd. + * + * Based on fsl_elbc_nand.c, Copyright © 2006-2007 Freescale Semiconductor + * + * 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; version 2 of the License. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/platform_device.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/sh_flctl.h> + +static struct nand_ecclayout flctl_4secc_oob_16 = { + .eccbytes = 10, + .eccpos = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, + .oobfree = { + {.offset = 12, + . length = 4} }, +}; + +static struct nand_ecclayout flctl_4secc_oob_64 = { + .eccbytes = 10, + .eccpos = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57}, + .oobfree = { + {.offset = 60, + . length = 4} }, +}; + +static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; + +static struct nand_bbt_descr flctl_4secc_smallpage = { + .options = NAND_BBT_SCAN2NDPAGE, + .offs = 11, + .len = 1, + .pattern = scan_ff_pattern, +}; + +static struct nand_bbt_descr flctl_4secc_largepage = { + .options = 0, + .offs = 58, + .len = 2, + .pattern = scan_ff_pattern, +}; + +static void empty_fifo(struct sh_flctl *flctl) +{ + writel(0x000c0000, FLINTDMACR(flctl)); /* FIFO Clear */ + writel(0x00000000, FLINTDMACR(flctl)); /* Clear Error flags */ +} + +static void start_translation(struct sh_flctl *flctl) +{ + writeb(TRSTRT, FLTRCR(flctl)); +} + +static void wait_completion(struct sh_flctl *flctl) +{ + uint32_t timeout = LOOP_TIMEOUT_MAX; + + while (timeout--) { + if (readb(FLTRCR(flctl)) & TREND) { + writeb(0x0, FLTRCR(flctl)); + return; + } + udelay(1); + } + + printk(KERN_ERR "wait_completion(): Timeout occured \n"); + writeb(0x0, FLTRCR(flctl)); +} + +static void set_addr(struct mtd_info *mtd, int column, int page_addr) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + uint32_t addr = 0; + + if (column == -1) { + addr = page_addr; /* ERASE1 */ + } else if (page_addr != -1) { + /* SEQIN, READ0, etc.. */ + if (flctl->page_size) { + addr = column & 0x0FFF; + addr |= (page_addr & 0xff) << 16; + addr |= ((page_addr >> 8) & 0xff) << 24; + /* big than 128MB */ + if (flctl->rw_ADRCNT == ADRCNT2_E) { + uint32_t addr2; + addr2 = (page_addr >> 16) & 0xff; + writel(addr2, FLADR2(flctl)); + } + } else { + addr = column; + addr |= (page_addr & 0xff) << 8; + addr |= ((page_addr >> 8) & 0xff) << 16; + addr |= ((page_addr >> 16) & 0xff) << 24; + } + } + writel(addr, FLADR(flctl)); +} + +static void wait_rfifo_ready(struct sh_flctl *flctl) +{ + uint32_t timeout = LOOP_TIMEOUT_MAX; + + while (timeout--) { + uint32_t val; + /* check FIFO */ + val = readl(FLDTCNTR(flctl)) >> 16; + if (val & 0xFF) + return; + udelay(1); + } + printk(KERN_ERR "wait_rfifo_ready(): Timeout occured \n"); +} + +static void wait_wfifo_ready(struct sh_flctl *flctl) +{ + uint32_t len, timeout = LOOP_TIMEOUT_MAX; + + while (timeout--) { + /* check FIFO */ + len = (readl(FLDTCNTR(flctl)) >> 16) & 0xFF; + if (len >= 4) + return; + udelay(1); + } + printk(KERN_ERR "wait_wfifo_ready(): Timeout occured \n"); +} + +static int wait_recfifo_ready(struct sh_flctl *flctl) +{ + uint32_t timeout = LOOP_TIMEOUT_MAX; + int checked[4]; + void __iomem *ecc_reg[4]; + int i; + uint32_t data, size; + + memset(checked, 0, sizeof(checked)); + + while (timeout--) { + size = readl(FLDTCNTR(flctl)) >> 24; + if (size & 0xFF) + return 0; /* success */ + + if (readl(FL4ECCCR(flctl)) & _4ECCFA) + return 1; /* can't correct */ + + udelay(1); + if (!(readl(FL4ECCCR(flctl)) & _4ECCEND)) + continue; + + /* start error correction */ + ecc_reg[0] = FL4ECCRESULT0(flctl); + ecc_reg[1] = FL4ECCRESULT1(flctl); + ecc_reg[2] = FL4ECCRESULT2(flctl); + ecc_reg[3] = FL4ECCRESULT3(flctl); + + for (i = 0; i < 3; i++) { + data = readl(ecc_reg[i]); + if (data != INIT_FL4ECCRESULT_VAL && !checked[i]) { + uint8_t org; + int index; + + index = data >> 16; + org = flctl->done_buff[index]; + flctl->done_buff[index] = org ^ (data & 0xFF); + checked[i] = 1; + } + } + + writel(0, FL4ECCCR(flctl)); + } + + printk(KERN_ERR "wait_recfifo_ready(): Timeout occured \n"); + return 1; /* timeout */ +} + +static void wait_wecfifo_ready(struct sh_flctl *flctl) +{ + uint32_t timeout = LOOP_TIMEOUT_MAX; + uint32_t len; + + while (timeout--) { + /* check FLECFIFO */ + len = (readl(FLDTCNTR(flctl)) >> 24) & 0xFF; + if (len >= 4) + return; + udelay(1); + } + printk(KERN_ERR "wait_wecfifo_ready(): Timeout occured \n"); +} + +static void read_datareg(struct sh_flctl *flctl, int offset) +{ + unsigned long data; + unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; + + wait_completion(flctl); + + data = readl(FLDATAR(flctl)); + *buf = le32_to_cpu(data); +} + +static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset) +{ + int i, len_4align; + unsigned long *buf = (unsigned long *)&flctl->done_buff[offset]; + void *fifo_addr = (void *)FLDTFIFO(flctl); + + len_4align = (rlen + 3) / 4; + + for (i = 0; i < len_4align; i++) { + wait_rfifo_ready(flctl); + buf[i] = readl(fifo_addr); + buf[i] = be32_to_cpu(buf[i]); + } +} + +static int read_ecfiforeg(struct sh_flctl *flctl, uint8_t *buff) +{ + int i; + unsigned long *ecc_buf = (unsigned long *)buff; + void *fifo_addr = (void *)FLECFIFO(flctl); + + for (i = 0; i < 4; i++) { + if (wait_recfifo_ready(flctl)) + return 1; + ecc_buf[i] = readl(fifo_addr); + ecc_buf[i] = be32_to_cpu(ecc_buf[i]); + } + + return 0; +} + +static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset) +{ + int i, len_4align; + unsigned long *data = (unsigned long *)&flctl->done_buff[offset]; + void *fifo_addr = (void *)FLDTFIFO(flctl); + + len_4align = (rlen + 3) / 4; + for (i = 0; i < len_4align; i++) { + wait_wfifo_ready(flctl); + writel(cpu_to_be32(data[i]), fifo_addr); + } +} + +static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + uint32_t flcmncr_val = readl(FLCMNCR(flctl)); + uint32_t flcmdcr_val, addr_len_bytes = 0; + + /* Set SNAND bit if page size is 2048byte */ + if (flctl->page_size) + flcmncr_val |= SNAND_E; + else + flcmncr_val &= ~SNAND_E; + + /* default FLCMDCR val */ + flcmdcr_val = DOCMD1_E | DOADR_E; + + /* Set for FLCMDCR */ + switch (cmd) { + case NAND_CMD_ERASE1: + addr_len_bytes = flctl->erase_ADRCNT; + flcmdcr_val |= DOCMD2_E; + break; + case NAND_CMD_READ0: + case NAND_CMD_READOOB: + addr_len_bytes = flctl->rw_ADRCNT; + flcmdcr_val |= CDSRC_E; + break; + case NAND_CMD_SEQIN: + /* This case is that cmd is READ0 or READ1 or READ00 */ + flcmdcr_val &= ~DOADR_E; /* ONLY execute 1st cmd */ + break; + case NAND_CMD_PAGEPROG: + addr_len_bytes = flctl->rw_ADRCNT; + flcmdcr_val |= DOCMD2_E | CDSRC_E | SELRW; + break; + case NAND_CMD_READID: + flcmncr_val &= ~SNAND_E; + addr_len_bytes = ADRCNT_1; + break; + case NAND_CMD_STATUS: + case NAND_CMD_RESET: + flcmncr_val &= ~SNAND_E; + flcmdcr_val &= ~(DOADR_E | DOSR_E); + break; + default: + break; + } + + /* Set address bytes parameter */ + flcmdcr_val |= addr_len_bytes; + + /* Now actually write */ + writel(flcmncr_val, FLCMNCR(flctl)); + writel(flcmdcr_val, FLCMDCR(flctl)); + writel(flcmcdr_val, FLCMCDR(flctl)); +} + +static int flctl_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + struct sh_flctl *flctl = mtd_to_flctl(mtd); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->read_buf(mtd, p, eccsize); + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { + if (flctl->hwecc_cant_correct[i]) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += 0; + } + + return 0; +} + +static void flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + int i, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + const uint8_t *p = buf; + + for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) + chip->write_buf(mtd, p, eccsize); +} + +static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + int sector, page_sectors; + + if (flctl->page_size) + page_sectors = 4; + else + page_sectors = 1; + + writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE | _4ECCCORRECT, + FLCMNCR(flctl)); + + set_cmd_regs(mtd, NAND_CMD_READ0, + (NAND_CMD_READSTART << 8) | NAND_CMD_READ0); + + for (sector = 0; sector < page_sectors; sector++) { + int ret; + + empty_fifo(flctl); + writel(readl(FLCMDCR(flctl)) | 1, FLCMDCR(flctl)); + writel(page_addr << 2 | sector, FLADR(flctl)); + + start_translation(flctl); + read_fiforeg(flctl, 512, 512 * sector); + + ret = read_ecfiforeg(flctl, + &flctl->done_buff[mtd->writesize + 16 * sector]); + + if (ret) + flctl->hwecc_cant_correct[sector] = 1; + + writel(0x0, FL4ECCCR(flctl)); + wait_completion(flctl); + } + writel(readl(FLCMNCR(flctl)) & ~(ACM_SACCES_MODE | _4ECCCORRECT), + FLCMNCR(flctl)); +} + +static void execmd_read_oob(struct mtd_info *mtd, int page_addr) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + + set_cmd_regs(mtd, NAND_CMD_READ0, + (NAND_CMD_READSTART << 8) | NAND_CMD_READ0); + + empty_fifo(flctl); + if (flctl->page_size) { + int i; + /* In case that the page size is 2k */ + for (i = 0; i < 16 * 3; i++) + flctl->done_buff[i] = 0xFF; + + set_addr(mtd, 3 * 528 + 512, page_addr); + writel(16, FLDTCNTR(flctl)); + + start_translation(flctl); + read_fiforeg(flctl, 16, 16 * 3); + wait_completion(flctl); + } else { + /* In case that the page size is 512b */ + set_addr(mtd, 512, page_addr); + writel(16, FLDTCNTR(flctl)); + + start_translation(flctl); + read_fiforeg(flctl, 16, 0); + wait_completion(flctl); + } +} + +static void execmd_write_page_sector(struct mtd_info *mtd) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + int i, page_addr = flctl->seqin_page_addr; + int sector, page_sectors; + + if (flctl->page_size) + page_sectors = 4; + else + page_sectors = 1; + + writel(readl(FLCMNCR(flctl)) | ACM_SACCES_MODE, FLCMNCR(flctl)); + + set_cmd_regs(mtd, NAND_CMD_PAGEPROG, + (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN); + + for (sector = 0; sector < page_sectors; sector++) { + empty_fifo(flctl); + writel(readl(FLCMDCR(flctl)) | 1, FLCMDCR(flctl)); + writel(page_addr << 2 | sector, FLADR(flctl)); + + start_translation(flctl); + write_fiforeg(flctl, 512, 512 * sector); + + for (i = 0; i < 4; i++) { + wait_wecfifo_ready(flctl); /* wait for write ready */ + writel(0xFFFFFFFF, FLECFIFO(flctl)); + } + wait_completion(flctl); + } + + writel(readl(FLCMNCR(flctl)) & ~ACM_SACCES_MODE, FLCMNCR(flctl)); +} + +static void execmd_write_oob(struct mtd_info *mtd) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + int page_addr = flctl->seqin_page_addr; + int sector, page_sectors; + + if (flctl->page_size) { + sector = 3; + page_sectors = 4; + } else { + sector = 0; + page_sectors = 1; + } + + set_cmd_regs(mtd, NAND_CMD_PAGEPROG, + (NAND_CMD_PAGEPROG << 8) | NAND_CMD_SEQIN); + + for (; sector < page_sectors; sector++) { + empty_fifo(flctl); + set_addr(mtd, sector * 528 + 512, page_addr); + writel(16, FLDTCNTR(flctl)); /* set read size */ + + start_translation(flctl); + write_fiforeg(flctl, 16, 16 * sector); + wait_completion(flctl); + } +} + +static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + uint32_t read_cmd = 0; + + flctl->read_bytes = 0; + if (command != NAND_CMD_PAGEPROG) + flctl->index = 0; + + switch (command) { + case NAND_CMD_READ1: + case NAND_CMD_READ0: + if (flctl->hwecc) { + /* read page with hwecc */ + execmd_read_page_sector(mtd, page_addr); + break; + } + empty_fifo(flctl); + if (flctl->page_size) + set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) + | command); + else + set_cmd_regs(mtd, command, command); + + set_addr(mtd, 0, page_addr); + + flctl->read_bytes = mtd->writesize + mtd->oobsize; + flctl->index += column; + goto read_normal_exit; + + case NAND_CMD_READOOB: + if (flctl->hwecc) { + /* read page with hwecc */ + execmd_read_oob(mtd, page_addr); + break; + } + + empty_fifo(flctl); + if (flctl->page_size) { + set_cmd_regs(mtd, command, (NAND_CMD_READSTART << 8) + | NAND_CMD_READ0); + set_addr(mtd, mtd->writesize, page_addr); + } else { + set_cmd_regs(mtd, command, command); + set_addr(mtd, 0, page_addr); + } + flctl->read_bytes = mtd->oobsize; + goto read_normal_exit; + + case NAND_CMD_READID: + empty_fifo(flctl); + set_cmd_regs(mtd, command, command); + set_addr(mtd, 0, 0); + + flctl->read_bytes = 4; + writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ + start_translation(flctl); + read_datareg(flctl, 0); /* read and end */ + break; + + case NAND_CMD_ERASE1: + flctl->erase1_page_addr = page_addr; + break; + + case NAND_CMD_ERASE2: + set_cmd_regs(mtd, NAND_CMD_ERASE1, + (command << 8) | NAND_CMD_ERASE1); + set_addr(mtd, -1, flctl->erase1_page_addr); + start_translation(flctl); + wait_completion(flctl); + break; + + case NAND_CMD_SEQIN: + if (!flctl->page_size) { + /* output read command */ + if (column >= mtd->writesize) { + column -= mtd->writesize; + read_cmd = NAND_CMD_READOOB; + } else if (column < 256) { + read_cmd = NAND_CMD_READ0; + } else { + column -= 256; + read_cmd = NAND_CMD_READ1; + } + } + flctl->seqin_column = column; + flctl->seqin_page_addr = page_addr; + flctl->seqin_read_cmd = read_cmd; + break; + + case NAND_CMD_PAGEPROG: + empty_fifo(flctl); + if (!flctl->page_size) { + set_cmd_regs(mtd, NAND_CMD_SEQIN, + flctl->seqin_read_cmd); + set_addr(mtd, -1, -1); + writel(0, FLDTCNTR(flctl)); /* set 0 size */ + start_translation(flctl); + wait_completion(flctl); + } + if (flctl->hwecc) { + /* write page with hwecc */ + if (flctl->seqin_column == mtd->writesize) + execmd_write_oob(mtd); + else if (!flctl->seqin_column) + execmd_write_page_sector(mtd); + else + printk(KERN_ERR "Invalid address !?\n"); + break; + } + set_cmd_regs(mtd, command, (command << 8) | NAND_CMD_SEQIN); + set_addr(mtd, flctl->seqin_column, flctl->seqin_page_addr); + writel(flctl->index, FLDTCNTR(flctl)); /* set write size */ + start_translation(flctl); + write_fiforeg(flctl, flctl->index, 0); + wait_completion(flctl); + break; + + case NAND_CMD_STATUS: + set_cmd_regs(mtd, command, command); + set_addr(mtd, -1, -1); + + flctl->read_bytes = 1; + writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ + start_translation(flctl); + read_datareg(flctl, 0); /* read and end */ + break; + + case NAND_CMD_RESET: + set_cmd_regs(mtd, command, command); + set_addr(mtd, -1, -1); + + writel(0, FLDTCNTR(flctl)); /* set 0 size */ + start_translation(flctl); + wait_completion(flctl); + break; + + default: + break; + } + return; + +read_normal_exit: + writel(flctl->read_bytes, FLDTCNTR(flctl)); /* set read size */ + start_translation(flctl); + read_fiforeg(flctl, flctl->read_bytes, 0); + wait_completion(flctl); + return; +} + +static void flctl_select_chip(struct mtd_info *mtd, int chipnr) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + uint32_t flcmncr_val = readl(FLCMNCR(flctl)); + + switch (chipnr) { + case -1: + flcmncr_val &= ~CE0_ENABLE; + writel(flcmncr_val, FLCMNCR(flctl)); + break; + case 0: + flcmncr_val |= CE0_ENABLE; + writel(flcmncr_val, FLCMNCR(flctl)); + break; + default: + BUG(); + } +} + +static void flctl_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + int i, index = flctl->index; + + for (i = 0; i < len; i++) + flctl->done_buff[index + i] = buf[i]; + flctl->index += len; +} + +static uint8_t flctl_read_byte(struct mtd_info *mtd) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + int index = flctl->index; + uint8_t data; + + data = flctl->done_buff[index]; + flctl->index++; + return data; +} + +static void flctl_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + buf[i] = flctl_read_byte(mtd); +} + +static int flctl_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + + for (i = 0; i < len; i++) + if (buf[i] != flctl_read_byte(mtd)) + return -EFAULT; + return 0; +} + +static void flctl_register_init(struct sh_flctl *flctl, unsigned long val) +{ + writel(val, FLCMNCR(flctl)); +} + +static int flctl_chip_init_tail(struct mtd_info *mtd) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + struct nand_chip *chip = &flctl->chip; + + if (mtd->writesize == 512) { + flctl->page_size = 0; + if (chip->chipsize > (32 << 20)) { + /* big than 32MB */ + flctl->rw_ADRCNT = ADRCNT_4; + flctl->erase_ADRCNT = ADRCNT_3; + } else if (chip->chipsize > (2 << 16)) { + /* big than 128KB */ + flctl->rw_ADRCNT = ADRCNT_3; + flctl->erase_ADRCNT = ADRCNT_2; + } else { + flctl->rw_ADRCNT = ADRCNT_2; + flctl->erase_ADRCNT = ADRCNT_1; + } + } else { + flctl->page_size = 1; + if (chip->chipsize > (128 << 20)) { + /* big than 128MB */ + flctl->rw_ADRCNT = ADRCNT2_E; + flctl->erase_ADRCNT = ADRCNT_3; + } else if (chip->chipsize > (8 << 16)) { + /* big than 512KB */ + flctl->rw_ADRCNT = ADRCNT_4; + flctl->erase_ADRCNT = ADRCNT_2; + } else { + flctl->rw_ADRCNT = ADRCNT_3; + flctl->erase_ADRCNT = ADRCNT_1; + } + } + + if (flctl->hwecc) { + if (mtd->writesize == 512) { + chip->ecc.layout = &flctl_4secc_oob_16; + chip->badblock_pattern = &flctl_4secc_smallpage; + } else { + chip->ecc.layout = &flctl_4secc_oob_64; + chip->badblock_pattern = &flctl_4secc_largepage; + } + + chip->ecc.size = 512; + chip->ecc.bytes = 10; + chip->ecc.read_page = flctl_read_page_hwecc; + chip->ecc.write_page = flctl_write_page_hwecc; + chip->ecc.mode = NAND_ECC_HW; + + /* 4 symbols ECC enabled */ + writel(readl(FLCMNCR(flctl)) | _4ECCEN | ECCPOS2 | ECCPOS_02, + FLCMNCR(flctl)); + } else { + chip->ecc.mode = NAND_ECC_SOFT; + } + + return 0; +} + +static int __init flctl_probe(struct platform_device *pdev) +{ + struct resource *res; + struct sh_flctl *flctl; + struct mtd_info *flctl_mtd; + struct nand_chip *nand; + struct sh_flctl_platform_data *pdata; + int ret; + + pdata = pdev->dev.platform_data; + if (pdata == NULL) { + printk(KERN_ERR "sh_flctl platform_data not found.\n"); + return -ENODEV; + } + + flctl = kzalloc(sizeof(struct sh_flctl), GFP_KERNEL); + if (!flctl) { + printk(KERN_ERR "Unable to allocate NAND MTD dev structure.\n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + printk(KERN_ERR "%s: resource not found.\n", __func__); + ret = -ENODEV; + goto err; + } + + flctl->reg = ioremap(res->start, res->end - res->start + 1); + if (flctl->reg == NULL) { + printk(KERN_ERR "%s: ioremap error.\n", __func__); + ret = -ENOMEM; + goto err; + } + + platform_set_drvdata(pdev, flctl); + flctl_mtd = &flctl->mtd; + nand = &flctl->chip; + flctl_mtd->priv = nand; + flctl->hwecc = pdata->has_hwecc; + + flctl_register_init(flctl, pdata->flcmncr_val); + + nand->options = NAND_NO_AUTOINCR; + + /* Set address of hardware control function */ + /* 20 us command delay time */ + nand->chip_delay = 20; + + nand->read_byte = flctl_read_byte; + nand->write_buf = flctl_write_buf; + nand->read_buf = flctl_read_buf; + nand->verify_buf = flctl_verify_buf; + nand->select_chip = flctl_select_chip; + nand->cmdfunc = flctl_cmdfunc; + + ret = nand_scan_ident(flctl_mtd, 1); + if (ret) + goto err; + + ret = flctl_chip_init_tail(flctl_mtd); + if (ret) + goto err; + + ret = nand_scan_tail(flctl_mtd); + if (ret) + goto err; + + add_mtd_partitions(flctl_mtd, pdata->parts, pdata->nr_parts); + + return 0; + +err: + kfree(flctl); + return ret; +} + +static int __exit flctl_remove(struct platform_device *pdev) +{ + struct sh_flctl *flctl = platform_get_drvdata(pdev); + + nand_release(&flctl->mtd); + kfree(flctl); + + return 0; +} + +static struct platform_driver flctl_driver = { + .probe = flctl_probe, + .remove = flctl_remove, + .driver = { + .name = "sh_flctl", + .owner = THIS_MODULE, + }, +}; + +static int __init flctl_nand_init(void) +{ + return platform_driver_register(&flctl_driver); +} + +static void __exit flctl_nand_cleanup(void) +{ + platform_driver_unregister(&flctl_driver); +} + +module_init(flctl_nand_init); +module_exit(flctl_nand_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yoshihiro Shimoda"); +MODULE_DESCRIPTION("SuperH FLCTL driver"); +MODULE_ALIAS("platform:sh_flctl"); diff --git a/drivers/mtd/nand/toto.c b/drivers/mtd/nand/toto.c deleted file mode 100644 index bbf492e6830d..000000000000 --- a/drivers/mtd/nand/toto.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * drivers/mtd/nand/toto.c - * - * Copyright (c) 2003 Texas Instruments - * - * Derived from drivers/mtd/autcpu12.c - * - * Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de> - * - * 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 - * published by the Free Software Foundation. - * - * Overview: - * This is a device driver for the NAND flash device found on the - * TI fido board. It supports 32MiB and 64MiB cards - */ - -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/nand.h> -#include <linux/mtd/partitions.h> -#include <asm/io.h> -#include <asm/arch/hardware.h> -#include <asm/sizes.h> -#include <asm/arch/toto.h> -#include <asm/arch-omap1510/hardware.h> -#include <asm/arch/gpio.h> - -#define CONFIG_NAND_WORKAROUND 1 - -/* - * MTD structure for TOTO board - */ -static struct mtd_info *toto_mtd = NULL; - -static unsigned long toto_io_base = OMAP_FLASH_1_BASE; - -/* - * Define partitions for flash devices - */ - -static struct mtd_partition partition_info64M[] = { - { .name = "toto kernel partition 1", - .offset = 0, - .size = 2 * SZ_1M }, - { .name = "toto file sys partition 2", - .offset = 2 * SZ_1M, - .size = 14 * SZ_1M }, - { .name = "toto user partition 3", - .offset = 16 * SZ_1M, - .size = 16 * SZ_1M }, - { .name = "toto devboard extra partition 4", - .offset = 32 * SZ_1M, - .size = 32 * SZ_1M }, -}; - -static struct mtd_partition partition_info32M[] = { - { .name = "toto kernel partition 1", - .offset = 0, - .size = 2 * SZ_1M }, - { .name = "toto file sys partition 2", - .offset = 2 * SZ_1M, - .size = 14 * SZ_1M }, - { .name = "toto user partition 3", - .offset = 16 * SZ_1M, - .size = 16 * SZ_1M }, -}; - -#define NUM_PARTITIONS32M 3 -#define NUM_PARTITIONS64M 4 - -/* - * hardware specific access to control-lines - * - * ctrl: - * NAND_NCE: bit 0 -> bit 14 (0x4000) - * NAND_CLE: bit 1 -> bit 12 (0x1000) - * NAND_ALE: bit 2 -> bit 1 (0x0002) - */ -static void toto_hwcontrol(struct mtd_info *mtd, int cmd, - unsigned int ctrl) -{ - struct nand_chip *chip = mtd->priv; - - if (ctrl & NAND_CTRL_CHANGE) { - unsigned long bits; - - /* hopefully enough time for tc make proceding write to clear */ - udelay(1); - - bits = (~ctrl & NAND_NCE) << 14; - bits |= (ctrl & NAND_CLE) << 12; - bits |= (ctrl & NAND_ALE) >> 1; - -#warning Wild guess as gpiosetout() is nowhere defined in the kernel source - tglx - gpiosetout(0x5002, bits); - -#ifdef CONFIG_NAND_WORKAROUND - /* "some" dev boards busted, blue wired to rts2 :( */ - rts2setout(2, (ctrl & NAND_CLE) << 1); -#endif - /* allow time to ensure gpio state to over take memory write */ - udelay(1); - } - - if (cmd != NAND_CMD_NONE) - writeb(cmd, chip->IO_ADDR_W); -} - -/* - * Main initialization routine - */ -static int __init toto_init(void) -{ - struct nand_chip *this; - int err = 0; - - /* Allocate memory for MTD device structure and private data */ - toto_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); - if (!toto_mtd) { - printk(KERN_WARNING "Unable to allocate toto NAND MTD device structure.\n"); - err = -ENOMEM; - goto out; - } - - /* Get pointer to private data */ - this = (struct nand_chip *)(&toto_mtd[1]); - - /* Initialize structures */ - memset(toto_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - - /* Link the private data with the MTD structure */ - toto_mtd->priv = this; - toto_mtd->owner = THIS_MODULE; - - /* Set address of NAND IO lines */ - this->IO_ADDR_R = toto_io_base; - this->IO_ADDR_W = toto_io_base; - this->cmd_ctrl = toto_hwcontrol; - this->dev_ready = NULL; - /* 25 us command delay time */ - this->chip_delay = 30; - this->ecc.mode = NAND_ECC_SOFT; - - /* Scan to find existance of the device */ - if (nand_scan(toto_mtd, 1)) { - err = -ENXIO; - goto out_mtd; - } - - /* Register the partitions */ - switch (toto_mtd->size) { - case SZ_64M: - add_mtd_partitions(toto_mtd, partition_info64M, NUM_PARTITIONS64M); - break; - case SZ_32M: - add_mtd_partitions(toto_mtd, partition_info32M, NUM_PARTITIONS32M); - break; - default:{ - printk(KERN_WARNING "Unsupported Nand device\n"); - err = -ENXIO; - goto out_buf; - } - } - - gpioreserve(NAND_MASK); /* claim our gpios */ - archflashwp(0, 0); /* open up flash for writing */ - - goto out; - - out_mtd: - kfree(toto_mtd); - out: - return err; -} - -module_init(toto_init); - -/* - * Clean up routine - */ -static void __exit toto_cleanup(void) -{ - /* Release resources, unregister device */ - nand_release(toto_mtd); - - /* Free the MTD device structure */ - kfree(toto_mtd); - - /* stop flash writes */ - archflashwp(0, 1); - - /* release gpios to system */ - gpiorelease(NAND_MASK); -} - -module_exit(toto_cleanup); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Richard Woodruff <r-woodruff2@ti.com>"); -MODULE_DESCRIPTION("Glue layer for NAND flash on toto board"); |