diff options
author | Paul Mackerras | 2009-04-22 13:02:09 +1000 |
---|---|---|
committer | Paul Mackerras | 2009-04-22 13:02:09 +1000 |
commit | 5bd3ef84d73c2ea7b4babbad060909753c4828d4 (patch) | |
tree | fdf2bafb48ae1ed03175f6c77a7548a181e69ee9 /drivers/mtd/nand | |
parent | 0658c16056660886ea2f35c4f038be70a94b1532 (diff) | |
parent | 6d25b688ecc488753af3c9e6f6a9a575b863cf37 (diff) |
Merge branch 'merge' of git://git.secretlab.ca/git/linux-2.6 into merge
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r-- | drivers/mtd/nand/Kconfig | 21 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 3 | ||||
-rw-r--r-- | drivers/mtd/nand/bf5xx_nand.c | 18 | ||||
-rw-r--r-- | drivers/mtd/nand/cafe_nand.c | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/davinci_nand.c | 570 | ||||
-rw-r--r-- | drivers/mtd/nand/fsl_upm.c | 119 | ||||
-rw-r--r-- | drivers/mtd/nand/mxc_nand.c | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 125 | ||||
-rw-r--r-- | drivers/mtd/nand/plat_nand.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/pxa3xx_nand.c | 119 | ||||
-rw-r--r-- | drivers/mtd/nand/sh_flctl.c | 18 | ||||
-rw-r--r-- | drivers/mtd/nand/socrates_nand.c | 325 | ||||
-rw-r--r-- | drivers/mtd/nand/txx9ndfmc.c | 428 |
13 files changed, 1695 insertions, 55 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 2ff88791cebc..890936d0275e 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -334,7 +334,7 @@ config MTD_NAND_ATMEL_ECC_NONE endchoice config MTD_NAND_PXA3xx - bool "Support for NAND flash devices on PXA3xx" + tristate "Support for NAND flash devices on PXA3xx" depends on MTD_NAND && PXA3xx help This enables the driver for the NAND flash device found on @@ -427,4 +427,23 @@ config MTD_NAND_SH_FLCTL Several Renesas SuperH CPU has FLCTL. This option enables support for NAND Flash using FLCTL. This driver support SH7723. +config MTD_NAND_DAVINCI + tristate "Support NAND on DaVinci SoC" + depends on ARCH_DAVINCI + help + Enable the driver for NAND flash chips on Texas Instruments + DaVinci processors. + +config MTD_NAND_TXX9NDFMC + tristate "NAND Flash support for TXx9 SoC" + depends on SOC_TX4938 || SOC_TX4939 + help + This enables the NAND flash controller on the TXx9 SoCs. + +config MTD_NAND_SOCRATES + tristate "Support for NAND on Socrates board" + depends on MTD_NAND && SOCRATES + help + Enables support for NAND Flash chips wired onto Socrates board. + endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index b661586afbfc..d33860ac42c3 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o +obj-$(CONFIG_MTD_NAND_DAVINCI) += davinci_nand.o obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o obj-$(CONFIG_MTD_NAND_H1900) += h1910.o obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o @@ -36,5 +37,7 @@ 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 +obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o +obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 9af2a2cc1153..4c2a67ca801e 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -552,7 +552,6 @@ static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd, static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info) { int ret; - unsigned short val; /* Do not use dma */ if (!hardware_ecc) @@ -560,13 +559,6 @@ static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info) init_completion(&info->dma_completion); -#ifdef CONFIG_BF54x - /* Setup DMAC1 channel mux for NFC which shared with SDH */ - val = bfin_read_DMAC1_PERIMUX(); - val &= 0xFFFE; - bfin_write_DMAC1_PERIMUX(val); - SSYNC(); -#endif /* Request NFC DMA channel */ ret = request_dma(CH_NFC, "BF5XX NFC driver"); if (ret < 0) { @@ -574,7 +566,13 @@ static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info) return ret; } - set_dma_callback(CH_NFC, (void *) bf5xx_nand_dma_irq, (void *) info); +#ifdef CONFIG_BF54x + /* Setup DMAC1 channel mux for NFC which shared with SDH */ + bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() & ~1); + SSYNC(); +#endif + + set_dma_callback(CH_NFC, bf5xx_nand_dma_irq, info); /* Turn off the DMA channel first */ disable_dma(CH_NFC); @@ -632,7 +630,7 @@ static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info) /* * Device management interface */ -static int bf5xx_nand_add_partition(struct bf5xx_nand_info *info) +static int __devinit bf5xx_nand_add_partition(struct bf5xx_nand_info *info) { struct mtd_info *mtd = &info->mtd; diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 22a6b2e50e91..7c5b257ce8e4 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -654,6 +654,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, } cafe = (void *)(&mtd[1]); + mtd->dev.parent = &pdev->dev; mtd->priv = cafe; mtd->owner = THIS_MODULE; diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c new file mode 100644 index 000000000000..0119220de7d0 --- /dev/null +++ b/drivers/mtd/nand/davinci_nand.c @@ -0,0 +1,570 @@ +/* + * davinci_nand.c - NAND Flash Driver for DaVinci family chips + * + * Copyright © 2006 Texas Instruments. + * + * Port to 2.6.23 Copyright © 2008 by: + * Sander Huijsen <Shuijsen@optelecom-nkf.com> + * Troy Kisky <troy.kisky@boundarydevices.com> + * Dirk Behme <Dirk.Behme@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> + +#include <mach/nand.h> + +#include <asm/mach-types.h> + + +/* + * This is a device driver for the NAND flash controller found on the + * various DaVinci family chips. It handles up to four SoC chipselects, + * and some flavors of secondary chipselect (e.g. based on A12) as used + * with multichip packages. + * + * The 1-bit ECC hardware is supported, but not yet the newer 4-bit ECC + * available on chips like the DM355 and OMAP-L137 and needed with the + * more error-prone MLC NAND chips. + * + * This driver assumes EM_WAIT connects all the NAND devices' RDY/nBUSY + * outputs in a "wire-AND" configuration, with no per-chip signals. + */ +struct davinci_nand_info { + struct mtd_info mtd; + struct nand_chip chip; + + struct device *dev; + struct clk *clk; + bool partitioned; + + void __iomem *base; + void __iomem *vaddr; + + uint32_t ioaddr; + uint32_t current_cs; + + uint32_t mask_chipsel; + uint32_t mask_ale; + uint32_t mask_cle; + + uint32_t core_chipsel; +}; + +static DEFINE_SPINLOCK(davinci_nand_lock); + +#define to_davinci_nand(m) container_of(m, struct davinci_nand_info, mtd) + + +static inline unsigned int davinci_nand_readl(struct davinci_nand_info *info, + int offset) +{ + return __raw_readl(info->base + offset); +} + +static inline void davinci_nand_writel(struct davinci_nand_info *info, + int offset, unsigned long value) +{ + __raw_writel(value, info->base + offset); +} + +/*----------------------------------------------------------------------*/ + +/* + * Access to hardware control lines: ALE, CLE, secondary chipselect. + */ + +static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct davinci_nand_info *info = to_davinci_nand(mtd); + uint32_t addr = info->current_cs; + struct nand_chip *nand = mtd->priv; + + /* Did the control lines change? */ + if (ctrl & NAND_CTRL_CHANGE) { + if ((ctrl & NAND_CTRL_CLE) == NAND_CTRL_CLE) + addr |= info->mask_cle; + else if ((ctrl & NAND_CTRL_ALE) == NAND_CTRL_ALE) + addr |= info->mask_ale; + + nand->IO_ADDR_W = (void __iomem __force *)addr; + } + + if (cmd != NAND_CMD_NONE) + iowrite8(cmd, nand->IO_ADDR_W); +} + +static void nand_davinci_select_chip(struct mtd_info *mtd, int chip) +{ + struct davinci_nand_info *info = to_davinci_nand(mtd); + uint32_t addr = info->ioaddr; + + /* maybe kick in a second chipselect */ + if (chip > 0) + addr |= info->mask_chipsel; + info->current_cs = addr; + + info->chip.IO_ADDR_W = (void __iomem __force *)addr; + info->chip.IO_ADDR_R = info->chip.IO_ADDR_W; +} + +/*----------------------------------------------------------------------*/ + +/* + * 1-bit hardware ECC ... context maintained for each core chipselect + */ + +static inline uint32_t nand_davinci_readecc_1bit(struct mtd_info *mtd) +{ + struct davinci_nand_info *info = to_davinci_nand(mtd); + + return davinci_nand_readl(info, NANDF1ECC_OFFSET + + 4 * info->core_chipsel); +} + +static void nand_davinci_hwctl_1bit(struct mtd_info *mtd, int mode) +{ + struct davinci_nand_info *info; + uint32_t nandcfr; + unsigned long flags; + + info = to_davinci_nand(mtd); + + /* Reset ECC hardware */ + nand_davinci_readecc_1bit(mtd); + + spin_lock_irqsave(&davinci_nand_lock, flags); + + /* Restart ECC hardware */ + nandcfr = davinci_nand_readl(info, NANDFCR_OFFSET); + nandcfr |= BIT(8 + info->core_chipsel); + davinci_nand_writel(info, NANDFCR_OFFSET, nandcfr); + + spin_unlock_irqrestore(&davinci_nand_lock, flags); +} + +/* + * Read hardware ECC value and pack into three bytes + */ +static int nand_davinci_calculate_1bit(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_code) +{ + unsigned int ecc_val = nand_davinci_readecc_1bit(mtd); + unsigned int ecc24 = (ecc_val & 0x0fff) | ((ecc_val & 0x0fff0000) >> 4); + + /* invert so that erased block ecc is correct */ + ecc24 = ~ecc24; + ecc_code[0] = (u_char)(ecc24); + ecc_code[1] = (u_char)(ecc24 >> 8); + ecc_code[2] = (u_char)(ecc24 >> 16); + + return 0; +} + +static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct nand_chip *chip = mtd->priv; + uint32_t eccNand = read_ecc[0] | (read_ecc[1] << 8) | + (read_ecc[2] << 16); + uint32_t eccCalc = calc_ecc[0] | (calc_ecc[1] << 8) | + (calc_ecc[2] << 16); + uint32_t diff = eccCalc ^ eccNand; + + if (diff) { + if ((((diff >> 12) ^ diff) & 0xfff) == 0xfff) { + /* Correctable error */ + if ((diff >> (12 + 3)) < chip->ecc.size) { + dat[diff >> (12 + 3)] ^= BIT((diff >> 12) & 7); + return 1; + } else { + return -1; + } + } else if (!(diff & (diff - 1))) { + /* Single bit ECC error in the ECC itself, + * nothing to fix */ + return 1; + } else { + /* Uncorrectable error */ + return -1; + } + + } + return 0; +} + +/*----------------------------------------------------------------------*/ + +/* + * NOTE: NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's + * how these chips are normally wired. This translates to both 8 and 16 + * bit busses using ALE == BIT(3) in byte addresses, and CLE == BIT(4). + * + * For now we assume that configuration, or any other one which ignores + * the two LSBs for NAND access ... so we can issue 32-bit reads/writes + * and have that transparently morphed into multiple NAND operations. + */ +static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + + if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0) + ioread32_rep(chip->IO_ADDR_R, buf, len >> 2); + else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0) + ioread16_rep(chip->IO_ADDR_R, buf, len >> 1); + else + ioread8_rep(chip->IO_ADDR_R, buf, len); +} + +static void nand_davinci_write_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + + if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0) + iowrite32_rep(chip->IO_ADDR_R, buf, len >> 2); + else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0) + iowrite16_rep(chip->IO_ADDR_R, buf, len >> 1); + else + iowrite8_rep(chip->IO_ADDR_R, buf, len); +} + +/* + * Check hardware register for wait status. Returns 1 if device is ready, + * 0 if it is still busy. + */ +static int nand_davinci_dev_ready(struct mtd_info *mtd) +{ + struct davinci_nand_info *info = to_davinci_nand(mtd); + + return davinci_nand_readl(info, NANDFSR_OFFSET) & BIT(0); +} + +static void __init nand_dm6446evm_flash_init(struct davinci_nand_info *info) +{ + uint32_t regval, a1cr; + + /* + * NAND FLASH timings @ PLL1 == 459 MHz + * - AEMIF.CLK freq = PLL1/6 = 459/6 = 76.5 MHz + * - AEMIF.CLK period = 1/76.5 MHz = 13.1 ns + */ + regval = 0 + | (0 << 31) /* selectStrobe */ + | (0 << 30) /* extWait (never with NAND) */ + | (1 << 26) /* writeSetup 10 ns */ + | (3 << 20) /* writeStrobe 40 ns */ + | (1 << 17) /* writeHold 10 ns */ + | (0 << 13) /* readSetup 10 ns */ + | (3 << 7) /* readStrobe 60 ns */ + | (0 << 4) /* readHold 10 ns */ + | (3 << 2) /* turnAround ?? ns */ + | (0 << 0) /* asyncSize 8-bit bus */ + ; + a1cr = davinci_nand_readl(info, A1CR_OFFSET); + if (a1cr != regval) { + dev_dbg(info->dev, "Warning: NAND config: Set A1CR " \ + "reg to 0x%08x, was 0x%08x, should be done by " \ + "bootloader.\n", regval, a1cr); + davinci_nand_writel(info, A1CR_OFFSET, regval); + } +} + +/*----------------------------------------------------------------------*/ + +static int __init nand_davinci_probe(struct platform_device *pdev) +{ + struct davinci_nand_pdata *pdata = pdev->dev.platform_data; + struct davinci_nand_info *info; + struct resource *res1; + struct resource *res2; + void __iomem *vaddr; + void __iomem *base; + int ret; + uint32_t val; + nand_ecc_modes_t ecc_mode; + + /* which external chipselect will we be managing? */ + if (pdev->id < 0 || pdev->id > 3) + return -ENODEV; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + dev_err(&pdev->dev, "unable to allocate memory\n"); + ret = -ENOMEM; + goto err_nomem; + } + + platform_set_drvdata(pdev, info); + + res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res1 || !res2) { + dev_err(&pdev->dev, "resource missing\n"); + ret = -EINVAL; + goto err_nomem; + } + + vaddr = ioremap(res1->start, res1->end - res1->start); + base = ioremap(res2->start, res2->end - res2->start); + if (!vaddr || !base) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -EINVAL; + goto err_ioremap; + } + + info->dev = &pdev->dev; + info->base = base; + info->vaddr = vaddr; + + info->mtd.priv = &info->chip; + info->mtd.name = dev_name(&pdev->dev); + info->mtd.owner = THIS_MODULE; + + info->mtd.dev.parent = &pdev->dev; + + info->chip.IO_ADDR_R = vaddr; + info->chip.IO_ADDR_W = vaddr; + info->chip.chip_delay = 0; + info->chip.select_chip = nand_davinci_select_chip; + + /* options such as NAND_USE_FLASH_BBT or 16-bit widths */ + info->chip.options = pdata ? pdata->options : 0; + + info->ioaddr = (uint32_t __force) vaddr; + + info->current_cs = info->ioaddr; + info->core_chipsel = pdev->id; + info->mask_chipsel = pdata->mask_chipsel; + + /* use nandboot-capable ALE/CLE masks by default */ + if (pdata && pdata->mask_ale) + info->mask_ale = pdata->mask_cle; + else + info->mask_ale = MASK_ALE; + if (pdata && pdata->mask_cle) + info->mask_cle = pdata->mask_cle; + else + info->mask_cle = MASK_CLE; + + /* Set address of hardware control function */ + info->chip.cmd_ctrl = nand_davinci_hwcontrol; + info->chip.dev_ready = nand_davinci_dev_ready; + + /* Speed up buffer I/O */ + info->chip.read_buf = nand_davinci_read_buf; + info->chip.write_buf = nand_davinci_write_buf; + + /* use board-specific ECC config; else, the best available */ + if (pdata) + ecc_mode = pdata->ecc_mode; + else + ecc_mode = NAND_ECC_HW; + + switch (ecc_mode) { + case NAND_ECC_NONE: + case NAND_ECC_SOFT: + break; + case NAND_ECC_HW: + info->chip.ecc.calculate = nand_davinci_calculate_1bit; + info->chip.ecc.correct = nand_davinci_correct_1bit; + info->chip.ecc.hwctl = nand_davinci_hwctl_1bit; + info->chip.ecc.size = 512; + info->chip.ecc.bytes = 3; + break; + case NAND_ECC_HW_SYNDROME: + /* FIXME implement */ + info->chip.ecc.size = 512; + info->chip.ecc.bytes = 10; + + dev_warn(&pdev->dev, "4-bit ECC nyet supported\n"); + /* FALL THROUGH */ + default: + ret = -EINVAL; + goto err_ecc; + } + info->chip.ecc.mode = ecc_mode; + + info->clk = clk_get(&pdev->dev, "AEMIFCLK"); + if (IS_ERR(info->clk)) { + ret = PTR_ERR(info->clk); + dev_dbg(&pdev->dev, "unable to get AEMIFCLK, err %d\n", ret); + goto err_clk; + } + + ret = clk_enable(info->clk); + if (ret < 0) { + dev_dbg(&pdev->dev, "unable to enable AEMIFCLK, err %d\n", ret); + goto err_clk_enable; + } + + /* EMIF timings should normally be set by the boot loader, + * especially after boot-from-NAND. The *only* reason to + * have this special casing for the DM6446 EVM is to work + * with boot-from-NOR ... with CS0 manually re-jumpered + * (after startup) so it addresses the NAND flash, not NOR. + * Even for dev boards, that's unusually rude... + */ + if (machine_is_davinci_evm()) + nand_dm6446evm_flash_init(info); + + spin_lock_irq(&davinci_nand_lock); + + /* put CSxNAND into NAND mode */ + val = davinci_nand_readl(info, NANDFCR_OFFSET); + val |= BIT(info->core_chipsel); + davinci_nand_writel(info, NANDFCR_OFFSET, val); + + spin_unlock_irq(&davinci_nand_lock); + + /* Scan to find existence of the device(s) */ + ret = nand_scan(&info->mtd, pdata->mask_chipsel ? 2 : 1); + if (ret < 0) { + dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); + goto err_scan; + } + + if (mtd_has_partitions()) { + struct mtd_partition *mtd_parts = NULL; + int mtd_parts_nb = 0; + + if (mtd_has_cmdlinepart()) { + static const char *probes[] __initconst = + { "cmdlinepart", NULL }; + + const char *master_name; + + /* Set info->mtd.name = 0 temporarily */ + master_name = info->mtd.name; + info->mtd.name = (char *)0; + + /* info->mtd.name == 0, means: don't bother checking + <mtd-id> */ + mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes, + &mtd_parts, 0); + + /* Restore info->mtd.name */ + info->mtd.name = master_name; + } + + if (mtd_parts_nb <= 0 && pdata) { + mtd_parts = pdata->parts; + mtd_parts_nb = pdata->nr_parts; + } + + /* Register any partitions */ + if (mtd_parts_nb > 0) { + ret = add_mtd_partitions(&info->mtd, + mtd_parts, mtd_parts_nb); + if (ret == 0) + info->partitioned = true; + } + + } else if (pdata && pdata->nr_parts) { + dev_warn(&pdev->dev, "ignoring %d default partitions on %s\n", + pdata->nr_parts, info->mtd.name); + } + + /* If there's no partition info, just package the whole chip + * as a single MTD device. + */ + if (!info->partitioned) + ret = add_mtd_device(&info->mtd) ? -ENODEV : 0; + + if (ret < 0) + goto err_scan; + + val = davinci_nand_readl(info, NRCSR_OFFSET); + dev_info(&pdev->dev, "controller rev. %d.%d\n", + (val >> 8) & 0xff, val & 0xff); + + return 0; + +err_scan: + clk_disable(info->clk); + +err_clk_enable: + clk_put(info->clk); + +err_ecc: +err_clk: +err_ioremap: + if (base) + iounmap(base); + if (vaddr) + iounmap(vaddr); + +err_nomem: + kfree(info); + return ret; +} + +static int __exit nand_davinci_remove(struct platform_device *pdev) +{ + struct davinci_nand_info *info = platform_get_drvdata(pdev); + int status; + + if (mtd_has_partitions() && info->partitioned) + status = del_mtd_partitions(&info->mtd); + else + status = del_mtd_device(&info->mtd); + + iounmap(info->base); + iounmap(info->vaddr); + + nand_release(&info->mtd); + + clk_disable(info->clk); + clk_put(info->clk); + + kfree(info); + + return 0; +} + +static struct platform_driver nand_davinci_driver = { + .remove = __exit_p(nand_davinci_remove), + .driver = { + .name = "davinci_nand", + }, +}; +MODULE_ALIAS("platform:davinci_nand"); + +static int __init nand_davinci_init(void) +{ + return platform_driver_probe(&nand_davinci_driver, nand_davinci_probe); +} +module_init(nand_davinci_init); + +static void __exit nand_davinci_exit(void) +{ + platform_driver_unregister(&nand_davinci_driver); +} +module_exit(nand_davinci_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("Davinci NAND flash driver"); + diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index 7815a404a632..d120cd8d7267 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -23,6 +23,10 @@ #include <linux/io.h> #include <asm/fsl_lbc.h> +#define FSL_UPM_WAIT_RUN_PATTERN 0x1 +#define FSL_UPM_WAIT_WRITE_BYTE 0x2 +#define FSL_UPM_WAIT_WRITE_BUFFER 0x4 + struct fsl_upm_nand { struct device *dev; struct mtd_info mtd; @@ -36,8 +40,12 @@ struct fsl_upm_nand { uint8_t upm_addr_offset; uint8_t upm_cmd_offset; void __iomem *io_base; - int rnb_gpio; + int rnb_gpio[NAND_MAX_CHIPS]; + uint32_t mchip_offsets[NAND_MAX_CHIPS]; + uint32_t mchip_count; + uint32_t mchip_number; int chip_delay; + uint32_t wait_flags; }; #define to_fsl_upm_nand(mtd) container_of(mtd, struct fsl_upm_nand, mtd) @@ -46,7 +54,7 @@ static int fun_chip_ready(struct mtd_info *mtd) { struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); - if (gpio_get_value(fun->rnb_gpio)) + if (gpio_get_value(fun->rnb_gpio[fun->mchip_number])) return 1; dev_vdbg(fun->dev, "busy\n"); @@ -55,9 +63,9 @@ static int fun_chip_ready(struct mtd_info *mtd) static void fun_wait_rnb(struct fsl_upm_nand *fun) { - int cnt = 1000000; + if (fun->rnb_gpio[fun->mchip_number] >= 0) { + int cnt = 1000000; - if (fun->rnb_gpio >= 0) { while (--cnt && !fun_chip_ready(&fun->mtd)) cpu_relax(); if (!cnt) @@ -69,7 +77,9 @@ static void fun_wait_rnb(struct fsl_upm_nand *fun) static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) { + struct nand_chip *chip = mtd->priv; struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); + u32 mar; if (!(ctrl & fun->last_ctrl)) { fsl_upm_end_pattern(&fun->upm); @@ -87,9 +97,28 @@ static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset); } - fsl_upm_run_pattern(&fun->upm, fun->io_base, cmd); + mar = (cmd << (32 - fun->upm.width)) | + fun->mchip_offsets[fun->mchip_number]; + fsl_upm_run_pattern(&fun->upm, chip->IO_ADDR_R, mar); + + if (fun->wait_flags & FSL_UPM_WAIT_RUN_PATTERN) + fun_wait_rnb(fun); +} + +static void fun_select_chip(struct mtd_info *mtd, int mchip_nr) +{ + struct nand_chip *chip = mtd->priv; + struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); - fun_wait_rnb(fun); + if (mchip_nr == -1) { + chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE); + } else if (mchip_nr >= 0) { + fun->mchip_number = mchip_nr; + chip->IO_ADDR_R = fun->io_base + fun->mchip_offsets[mchip_nr]; + chip->IO_ADDR_W = chip->IO_ADDR_R; + } else { + BUG(); + } } static uint8_t fun_read_byte(struct mtd_info *mtd) @@ -115,8 +144,11 @@ 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]); - fun_wait_rnb(fun); + if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BYTE) + fun_wait_rnb(fun); } + if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BUFFER) + fun_wait_rnb(fun); } static int __devinit fun_chip_init(struct fsl_upm_nand *fun, @@ -137,8 +169,10 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun, fun->chip.read_buf = fun_read_buf; fun->chip.write_buf = fun_write_buf; fun->chip.ecc.mode = NAND_ECC_SOFT; + if (fun->mchip_count > 1) + fun->chip.select_chip = fun_select_chip; - if (fun->rnb_gpio >= 0) + if (fun->rnb_gpio[0] >= 0) fun->chip.dev_ready = fun_chip_ready; fun->mtd.priv = &fun->chip; @@ -155,7 +189,7 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun, goto err; } - ret = nand_scan(&fun->mtd, 1); + ret = nand_scan(&fun->mtd, fun->mchip_count); if (ret) goto err; @@ -185,8 +219,10 @@ static int __devinit fun_probe(struct of_device *ofdev, struct fsl_upm_nand *fun; struct resource io_res; const uint32_t *prop; + int rnb_gpio; int ret; int size; + int i; fun = kzalloc(sizeof(*fun), GFP_KERNEL); if (!fun) @@ -208,7 +244,7 @@ static int __devinit fun_probe(struct of_device *ofdev, if (!prop || size != sizeof(uint32_t)) { dev_err(&ofdev->dev, "can't get UPM address offset\n"); ret = -EINVAL; - goto err2; + goto err1; } fun->upm_addr_offset = *prop; @@ -216,21 +252,40 @@ static int __devinit fun_probe(struct of_device *ofdev, if (!prop || size != sizeof(uint32_t)) { dev_err(&ofdev->dev, "can't get UPM command offset\n"); ret = -EINVAL; - goto err2; + goto err1; } fun->upm_cmd_offset = *prop; - fun->rnb_gpio = of_get_gpio(ofdev->node, 0); - if (fun->rnb_gpio >= 0) { - ret = gpio_request(fun->rnb_gpio, dev_name(&ofdev->dev)); - if (ret) { - dev_err(&ofdev->dev, "can't request RNB gpio\n"); + prop = of_get_property(ofdev->node, + "fsl,upm-addr-line-cs-offsets", &size); + if (prop && (size / sizeof(uint32_t)) > 0) { + fun->mchip_count = size / sizeof(uint32_t); + if (fun->mchip_count >= NAND_MAX_CHIPS) { + dev_err(&ofdev->dev, "too much multiple chips\n"); + goto err1; + } + for (i = 0; i < fun->mchip_count; i++) + fun->mchip_offsets[i] = prop[i]; + } else { + fun->mchip_count = 1; + } + + for (i = 0; i < fun->mchip_count; i++) { + fun->rnb_gpio[i] = -1; + rnb_gpio = of_get_gpio(ofdev->node, i); + if (rnb_gpio >= 0) { + ret = gpio_request(rnb_gpio, dev_name(&ofdev->dev)); + if (ret) { + dev_err(&ofdev->dev, + "can't request RNB gpio #%d\n", i); + goto err2; + } + gpio_direction_input(rnb_gpio); + fun->rnb_gpio[i] = rnb_gpio; + } else if (rnb_gpio == -EINVAL) { + dev_err(&ofdev->dev, "RNB gpio #%d is invalid\n", i); goto err2; } - gpio_direction_input(fun->rnb_gpio); - } else if (fun->rnb_gpio == -EINVAL) { - dev_err(&ofdev->dev, "specified RNB gpio is invalid\n"); - goto err2; } prop = of_get_property(ofdev->node, "chip-delay", NULL); @@ -239,8 +294,15 @@ static int __devinit fun_probe(struct of_device *ofdev, else fun->chip_delay = 50; + prop = of_get_property(ofdev->node, "fsl,upm-wait-flags", &size); + if (prop && size == sizeof(uint32_t)) + fun->wait_flags = *prop; + else + fun->wait_flags = FSL_UPM_WAIT_RUN_PATTERN | + FSL_UPM_WAIT_WRITE_BYTE; + fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start, - io_res.end - io_res.start + 1); + io_res.end - io_res.start + 1); if (!fun->io_base) { ret = -ENOMEM; goto err2; @@ -257,8 +319,11 @@ static int __devinit fun_probe(struct of_device *ofdev, return 0; err2: - if (fun->rnb_gpio >= 0) - gpio_free(fun->rnb_gpio); + for (i = 0; i < fun->mchip_count; i++) { + if (fun->rnb_gpio[i] < 0) + break; + gpio_free(fun->rnb_gpio[i]); + } err1: kfree(fun); @@ -268,12 +333,16 @@ err1: static int __devexit fun_remove(struct of_device *ofdev) { struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev); + int i; nand_release(&fun->mtd); kfree(fun->mtd.name); - if (fun->rnb_gpio >= 0) - gpio_free(fun->rnb_gpio); + for (i = 0; i < fun->mchip_count; i++) { + if (fun->rnb_gpio[i] < 0) + break; + gpio_free(fun->rnb_gpio[i]); + } kfree(fun); diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index bad048aca89a..f3548d048014 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -866,6 +866,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) mtd = &host->mtd; mtd->priv = this; mtd->owner = THIS_MODULE; + mtd->dev.parent = &pdev->dev; /* 50 us command delay time */ this->chip_delay = 5; diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 5f71371eb1b0..3d7ed432fa41 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -82,6 +82,20 @@ static struct nand_ecclayout nand_oob_64 = { .length = 38}} }; +static struct nand_ecclayout nand_oob_128 = { + .eccbytes = 48, + .eccpos = { + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127}, + .oobfree = { + {.offset = 2, + .length = 78}} +}; + static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state); @@ -748,6 +762,8 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data + * + * Not for syndrome calculating ecc controllers, which use a special oob layout */ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf) @@ -758,6 +774,47 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, } /** + * nand_read_page_raw_syndrome - [Intern] read raw page data without ecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * + * We need a special oob layout and handling even when OOB isn't used. + */ +static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf) +{ + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + uint8_t *oob = chip->oob_poi; + int steps, size; + + for (steps = chip->ecc.steps; steps > 0; steps--) { + chip->read_buf(mtd, buf, eccsize); + buf += eccsize; + + if (chip->ecc.prepad) { + chip->read_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->read_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->read_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + size = mtd->oobsize - (oob - chip->oob_poi); + if (size) + chip->read_buf(mtd, oob, size); + + return 0; +} + +/** * nand_read_page_swecc - [REPLACABLE] software ecc based page read function * @mtd: mtd info structure * @chip: nand chip info structure @@ -1482,6 +1539,8 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from, * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer + * + * Not for syndrome calculating ecc controllers, which use a special oob layout */ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf) @@ -1491,6 +1550,44 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, } /** + * nand_write_page_raw_syndrome - [Intern] raw page write function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: data buffer + * + * We need a special oob layout and handling even when ECC isn't checked. + */ +static void nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + int eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + uint8_t *oob = chip->oob_poi; + int steps, size; + + for (steps = chip->ecc.steps; steps > 0; steps--) { + chip->write_buf(mtd, buf, eccsize); + buf += eccsize; + + if (chip->ecc.prepad) { + chip->write_buf(mtd, oob, chip->ecc.prepad); + oob += chip->ecc.prepad; + } + + chip->read_buf(mtd, oob, eccbytes); + oob += eccbytes; + + if (chip->ecc.postpad) { + chip->write_buf(mtd, oob, chip->ecc.postpad); + oob += chip->ecc.postpad; + } + } + + size = mtd->oobsize - (oob - chip->oob_poi); + if (size) + chip->write_buf(mtd, oob, size); +} +/** * nand_write_page_swecc - [REPLACABLE] software ecc based page write function * @mtd: mtd info structure * @chip: nand chip info structure @@ -1863,7 +1960,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, } if (unlikely(ops->ooboffs >= len)) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + DEBUG(MTD_DEBUG_LEVEL0, "nand_do_write_oob: " "Attempt to start write outside oob\n"); return -EINVAL; } @@ -1873,7 +1970,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, ops->ooboffs + ops->ooblen > ((mtd->size >> chip->page_shift) - (to >> chip->page_shift)) * len)) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + DEBUG(MTD_DEBUG_LEVEL0, "nand_do_write_oob: " "Attempt write beyond end of device\n"); return -EINVAL; } @@ -1929,8 +2026,8 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to, /* Do not allow writes past end of device */ if (ops->datbuf && (to + ops->len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " - "Attempt read beyond end of device\n"); + DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " + "Attempt write beyond end of device\n"); return -EINVAL; } @@ -2555,6 +2652,9 @@ int nand_scan_tail(struct mtd_info *mtd) case 64: chip->ecc.layout = &nand_oob_64; break; + case 128: + chip->ecc.layout = &nand_oob_128; + break; default: printk(KERN_WARNING "No oob scheme defined for " "oobsize %d\n", mtd->oobsize); @@ -2569,10 +2669,6 @@ int nand_scan_tail(struct mtd_info *mtd) * check ECC mode, default to software if 3byte/512byte hardware ECC is * selected and we have 256 byte pagesize fallback to software ECC */ - if (!chip->ecc.read_page_raw) - chip->ecc.read_page_raw = nand_read_page_raw; - if (!chip->ecc.write_page_raw) - chip->ecc.write_page_raw = nand_write_page_raw; switch (chip->ecc.mode) { case NAND_ECC_HW: @@ -2581,6 +2677,10 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.read_page = nand_read_page_hwecc; if (!chip->ecc.write_page) chip->ecc.write_page = nand_write_page_hwecc; + if (!chip->ecc.read_page_raw) + chip->ecc.read_page_raw = nand_read_page_raw; + if (!chip->ecc.write_page_raw) + chip->ecc.write_page_raw = nand_write_page_raw; if (!chip->ecc.read_oob) chip->ecc.read_oob = nand_read_oob_std; if (!chip->ecc.write_oob) @@ -2602,6 +2702,10 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.read_page = nand_read_page_syndrome; if (!chip->ecc.write_page) chip->ecc.write_page = nand_write_page_syndrome; + if (!chip->ecc.read_page_raw) + chip->ecc.read_page_raw = nand_read_page_raw_syndrome; + if (!chip->ecc.write_page_raw) + chip->ecc.write_page_raw = nand_write_page_raw_syndrome; if (!chip->ecc.read_oob) chip->ecc.read_oob = nand_read_oob_syndrome; if (!chip->ecc.write_oob) @@ -2620,6 +2724,8 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.read_page = nand_read_page_swecc; chip->ecc.read_subpage = nand_read_subpage; chip->ecc.write_page = nand_write_page_swecc; + chip->ecc.read_page_raw = nand_read_page_raw; + chip->ecc.write_page_raw = nand_write_page_raw; chip->ecc.read_oob = nand_read_oob_std; chip->ecc.write_oob = nand_write_oob_std; chip->ecc.size = 256; @@ -2632,6 +2738,8 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.read_page = nand_read_page_raw; chip->ecc.write_page = nand_write_page_raw; chip->ecc.read_oob = nand_read_oob_std; + chip->ecc.read_page_raw = nand_read_page_raw; + chip->ecc.write_page_raw = nand_write_page_raw; chip->ecc.write_oob = nand_write_oob_std; chip->ecc.size = mtd->writesize; chip->ecc.bytes = 0; @@ -2676,6 +2784,7 @@ int nand_scan_tail(struct mtd_info *mtd) break; case 4: case 8: + case 16: mtd->subpage_sft = 2; break; } diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 75f9f4874ecf..86e1d08eee00 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -30,7 +30,7 @@ struct plat_nand_data { /* * Probe for the NAND device. */ -static int __init plat_nand_probe(struct platform_device *pdev) +static int __devinit plat_nand_probe(struct platform_device *pdev) { struct platform_nand_data *pdata = pdev->dev.platform_data; struct plat_nand_data *data; diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 61b69cc40009..30a8ce6d3e69 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -170,7 +170,13 @@ 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 +/* + * Default NAND flash controller configuration setup by the + * bootloader. This configuration is used only when pdata->keep_config is set + */ +static struct pxa3xx_nand_timing default_timing; +static struct pxa3xx_nand_flash default_flash; + static struct pxa3xx_nand_cmdset smallpage_cmdset = { .read1 = 0x0000, .read2 = 0x0050, @@ -197,6 +203,7 @@ static struct pxa3xx_nand_cmdset largepage_cmdset = { .lock_status = 0x007A, }; +#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN static struct pxa3xx_nand_timing samsung512MbX16_timing = { .tCH = 10, .tCS = 0, @@ -296,9 +303,23 @@ static struct pxa3xx_nand_flash *builtin_flash_types[] = { #define NDTR1_tWHR(c) (min((c), 15) << 4) #define NDTR1_tAR(c) (min((c), 15) << 0) +#define tCH_NDTR0(r) (((r) >> 19) & 0x7) +#define tCS_NDTR0(r) (((r) >> 16) & 0x7) +#define tWH_NDTR0(r) (((r) >> 11) & 0x7) +#define tWP_NDTR0(r) (((r) >> 8) & 0x7) +#define tRH_NDTR0(r) (((r) >> 3) & 0x7) +#define tRP_NDTR0(r) (((r) >> 0) & 0x7) + +#define tR_NDTR1(r) (((r) >> 16) & 0xffff) +#define tWHR_NDTR1(r) (((r) >> 4) & 0xf) +#define tAR_NDTR1(r) (((r) >> 0) & 0xf) + /* convert nano-seconds to nand flash controller clock cycles */ #define ns2cycle(ns, clk) (int)(((ns) * (clk / 1000000) / 1000) - 1) +/* convert nand flash controller clock cycles to nano-seconds */ +#define cycle2ns(c, clk) ((((c) + 1) * 1000000 + clk / 500) / (clk / 1000)) + static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info, const struct pxa3xx_nand_timing *t) { @@ -920,6 +941,82 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info, return 0; } +static void pxa3xx_nand_detect_timing(struct pxa3xx_nand_info *info, + struct pxa3xx_nand_timing *t) +{ + unsigned long nand_clk = clk_get_rate(info->clk); + uint32_t ndtr0 = nand_readl(info, NDTR0CS0); + uint32_t ndtr1 = nand_readl(info, NDTR1CS0); + + t->tCH = cycle2ns(tCH_NDTR0(ndtr0), nand_clk); + t->tCS = cycle2ns(tCS_NDTR0(ndtr0), nand_clk); + t->tWH = cycle2ns(tWH_NDTR0(ndtr0), nand_clk); + t->tWP = cycle2ns(tWP_NDTR0(ndtr0), nand_clk); + t->tRH = cycle2ns(tRH_NDTR0(ndtr0), nand_clk); + t->tRP = cycle2ns(tRP_NDTR0(ndtr0), nand_clk); + + t->tR = cycle2ns(tR_NDTR1(ndtr1), nand_clk); + t->tWHR = cycle2ns(tWHR_NDTR1(ndtr1), nand_clk); + t->tAR = cycle2ns(tAR_NDTR1(ndtr1), nand_clk); +} + +static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info) +{ + uint32_t ndcr = nand_readl(info, NDCR); + struct nand_flash_dev *type = NULL; + uint32_t id = -1; + int i; + + default_flash.page_per_block = ndcr & NDCR_PG_PER_BLK ? 64 : 32; + default_flash.page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512; + default_flash.flash_width = ndcr & NDCR_DWIDTH_M ? 16 : 8; + default_flash.dfc_width = ndcr & NDCR_DWIDTH_C ? 16 : 8; + + if (default_flash.page_size == 2048) + default_flash.cmdset = &largepage_cmdset; + else + default_flash.cmdset = &smallpage_cmdset; + + /* set info fields needed to __readid */ + info->flash_info = &default_flash; + info->read_id_bytes = (default_flash.page_size == 2048) ? 4 : 2; + info->reg_ndcr = ndcr; + + if (__readid(info, &id)) + return -ENODEV; + + /* Lookup the flash id */ + id = (id >> 8) & 0xff; /* device id is byte 2 */ + for (i = 0; nand_flash_ids[i].name != NULL; i++) { + if (id == nand_flash_ids[i].id) { + type = &nand_flash_ids[i]; + break; + } + } + + if (!type) + return -ENODEV; + + /* fill the missing flash information */ + i = __ffs(default_flash.page_per_block * default_flash.page_size); + default_flash.num_blocks = type->chipsize << (20 - i); + + info->oob_size = (default_flash.page_size == 2048) ? 64 : 16; + + /* calculate addressing information */ + info->col_addr_cycles = (default_flash.page_size == 2048) ? 2 : 1; + + if (default_flash.num_blocks * default_flash.page_per_block > 65536) + info->row_addr_cycles = 3; + else + info->row_addr_cycles = 2; + + pxa3xx_nand_detect_timing(info, &default_timing); + default_flash.timing = &default_timing; + + return 0; +} + static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info, const struct pxa3xx_nand_platform_data *pdata) { @@ -927,6 +1024,10 @@ static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info, uint32_t id = -1; int i; + if (pdata->keep_config) + if (pxa3xx_nand_detect_config(info) == 0) + return 0; + for (i = 0; i<pdata->num_flash; ++i) { f = pdata->flash + i; @@ -1078,6 +1179,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) this = &info->nand_chip; mtd->priv = info; + mtd->owner = THIS_MODULE; info->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(info->clk)) { @@ -1117,14 +1219,14 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) goto fail_put_clk; } - r = request_mem_region(r->start, r->end - r->start + 1, pdev->name); + r = request_mem_region(r->start, resource_size(r), pdev->name); if (r == NULL) { dev_err(&pdev->dev, "failed to request memory resource\n"); ret = -EBUSY; goto fail_put_clk; } - info->mmio_base = ioremap(r->start, r->end - r->start + 1); + info->mmio_base = ioremap(r->start, resource_size(r)); if (info->mmio_base == NULL) { dev_err(&pdev->dev, "ioremap() failed\n"); ret = -ENODEV; @@ -1173,7 +1275,7 @@ fail_free_buf: fail_free_io: iounmap(info->mmio_base); fail_free_res: - release_mem_region(r->start, r->end - r->start + 1); + release_mem_region(r->start, resource_size(r)); fail_put_clk: clk_disable(info->clk); clk_put(info->clk); @@ -1186,6 +1288,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) { struct mtd_info *mtd = platform_get_drvdata(pdev); struct pxa3xx_nand_info *info = mtd->priv; + struct resource *r; platform_set_drvdata(pdev, NULL); @@ -1198,6 +1301,14 @@ static int pxa3xx_nand_remove(struct platform_device *pdev) info->data_buff, info->data_buff_phys); } else kfree(info->data_buff); + + iounmap(info->mmio_base); + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(r->start, resource_size(r)); + + clk_disable(info->clk); + clk_put(info->clk); + kfree(mtd); return 0; } diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 821acb08ff1c..2bc896623e2d 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -58,7 +58,7 @@ static struct nand_bbt_descr flctl_4secc_smallpage = { }; static struct nand_bbt_descr flctl_4secc_largepage = { - .options = 0, + .options = NAND_BBT_SCAN2NDPAGE, .offs = 58, .len = 2, .pattern = scan_ff_pattern, @@ -149,7 +149,7 @@ static void wait_wfifo_ready(struct sh_flctl *flctl) printk(KERN_ERR "wait_wfifo_ready(): Timeout occured \n"); } -static int wait_recfifo_ready(struct sh_flctl *flctl) +static int wait_recfifo_ready(struct sh_flctl *flctl, int sector_number) { uint32_t timeout = LOOP_TIMEOUT_MAX; int checked[4]; @@ -183,7 +183,12 @@ static int wait_recfifo_ready(struct sh_flctl *flctl) uint8_t org; int index; - index = data >> 16; + if (flctl->page_size) + index = (512 * sector_number) + + (data >> 16); + else + index = data >> 16; + org = flctl->done_buff[index]; flctl->done_buff[index] = org ^ (data & 0xFF); checked[i] = 1; @@ -238,14 +243,14 @@ static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset) } } -static int read_ecfiforeg(struct sh_flctl *flctl, uint8_t *buff) +static int read_ecfiforeg(struct sh_flctl *flctl, uint8_t *buff, int sector) { 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)) + if (wait_recfifo_ready(flctl , sector)) return 1; ecc_buf[i] = readl(fifo_addr); ecc_buf[i] = be32_to_cpu(ecc_buf[i]); @@ -384,7 +389,8 @@ static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr) read_fiforeg(flctl, 512, 512 * sector); ret = read_ecfiforeg(flctl, - &flctl->done_buff[mtd->writesize + 16 * sector]); + &flctl->done_buff[mtd->writesize + 16 * sector], + sector); if (ret) flctl->hwecc_cant_correct[sector] = 1; diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c new file mode 100644 index 000000000000..a4519a7bd683 --- /dev/null +++ b/drivers/mtd/nand/socrates_nand.c @@ -0,0 +1,325 @@ +/* + * drivers/mtd/nand/socrates_nand.c + * + * Copyright © 2008 Ilya Yanok, Emcraft Systems + * + * + * 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/slab.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/of_platform.h> +#include <linux/io.h> + +#define FPGA_NAND_CMD_MASK (0x7 << 28) +#define FPGA_NAND_CMD_COMMAND (0x0 << 28) +#define FPGA_NAND_CMD_ADDR (0x1 << 28) +#define FPGA_NAND_CMD_READ (0x2 << 28) +#define FPGA_NAND_CMD_WRITE (0x3 << 28) +#define FPGA_NAND_BUSY (0x1 << 15) +#define FPGA_NAND_ENABLE (0x1 << 31) +#define FPGA_NAND_DATA_SHIFT 16 + +struct socrates_nand_host { + struct nand_chip nand_chip; + struct mtd_info mtd; + void __iomem *io_base; + struct device *dev; +}; + +/** + * socrates_nand_write_buf - write buffer to chip + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void socrates_nand_write_buf(struct mtd_info *mtd, + const uint8_t *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + struct socrates_nand_host *host = this->priv; + + for (i = 0; i < len; i++) { + out_be32(host->io_base, FPGA_NAND_ENABLE | + FPGA_NAND_CMD_WRITE | + (buf[i] << FPGA_NAND_DATA_SHIFT)); + } +} + +/** + * socrates_nand_read_buf - read chip data into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void socrates_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + struct socrates_nand_host *host = this->priv; + uint32_t val; + + val = FPGA_NAND_ENABLE | FPGA_NAND_CMD_READ; + + out_be32(host->io_base, val); + for (i = 0; i < len; i++) { + buf[i] = (in_be32(host->io_base) >> + FPGA_NAND_DATA_SHIFT) & 0xff; + } +} + +/** + * socrates_nand_read_byte - read one byte from the chip + * @mtd: MTD device structure + */ +static uint8_t socrates_nand_read_byte(struct mtd_info *mtd) +{ + uint8_t byte; + socrates_nand_read_buf(mtd, &byte, sizeof(byte)); + return byte; +} + +/** + * socrates_nand_read_word - read one word from the chip + * @mtd: MTD device structure + */ +static uint16_t socrates_nand_read_word(struct mtd_info *mtd) +{ + uint16_t word; + socrates_nand_read_buf(mtd, (uint8_t *)&word, sizeof(word)); + return word; +} + +/** + * socrates_nand_verify_buf - Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + */ +static int socrates_nand_verify_buf(struct mtd_info *mtd, const u8 *buf, + int len) +{ + int i; + + for (i = 0; i < len; i++) { + if (buf[i] != socrates_nand_read_byte(mtd)) + return -EFAULT; + } + return 0; +} + +/* + * Hardware specific access to control-lines + */ +static void socrates_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *nand_chip = mtd->priv; + struct socrates_nand_host *host = nand_chip->priv; + uint32_t val; + + if (cmd == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) + val = FPGA_NAND_CMD_COMMAND; + else + val = FPGA_NAND_CMD_ADDR; + + if (ctrl & NAND_NCE) + val |= FPGA_NAND_ENABLE; + + val |= (cmd & 0xff) << FPGA_NAND_DATA_SHIFT; + + out_be32(host->io_base, val); +} + +/* + * Read the Device Ready pin. + */ +static int socrates_nand_device_ready(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct socrates_nand_host *host = nand_chip->priv; + + if (in_be32(host->io_base) & FPGA_NAND_BUSY) + return 0; /* busy */ + return 1; +} + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probes[] = { "cmdlinepart", NULL }; +#endif + +/* + * Probe for the NAND device. + */ +static int __devinit socrates_nand_probe(struct of_device *ofdev, + const struct of_device_id *ofid) +{ + struct socrates_nand_host *host; + struct mtd_info *mtd; + struct nand_chip *nand_chip; + int res; + +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *partitions = NULL; + int num_partitions = 0; +#endif + + /* Allocate memory for the device structure (and zero it) */ + host = kzalloc(sizeof(struct socrates_nand_host), GFP_KERNEL); + if (!host) { + printk(KERN_ERR + "socrates_nand: failed to allocate device structure.\n"); + return -ENOMEM; + } + + host->io_base = of_iomap(ofdev->node, 0); + if (host->io_base == NULL) { + printk(KERN_ERR "socrates_nand: ioremap failed\n"); + kfree(host); + return -EIO; + } + + mtd = &host->mtd; + nand_chip = &host->nand_chip; + host->dev = &ofdev->dev; + + nand_chip->priv = host; /* link the private data structures */ + mtd->priv = nand_chip; + mtd->name = "socrates_nand"; + mtd->owner = THIS_MODULE; + mtd->dev.parent = &ofdev->dev; + + /*should never be accessed directly */ + nand_chip->IO_ADDR_R = (void *)0xdeadbeef; + nand_chip->IO_ADDR_W = (void *)0xdeadbeef; + + nand_chip->cmd_ctrl = socrates_nand_cmd_ctrl; + nand_chip->read_byte = socrates_nand_read_byte; + nand_chip->read_word = socrates_nand_read_word; + nand_chip->write_buf = socrates_nand_write_buf; + nand_chip->read_buf = socrates_nand_read_buf; + nand_chip->verify_buf = socrates_nand_verify_buf; + nand_chip->dev_ready = socrates_nand_device_ready; + + nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */ + + /* TODO: I have no idea what real delay is. */ + nand_chip->chip_delay = 20; /* 20us command delay time */ + + dev_set_drvdata(&ofdev->dev, host); + + /* first scan to find the device and get the page size */ + if (nand_scan_ident(mtd, 1)) { + res = -ENXIO; + goto out; + } + + /* second phase scan */ + if (nand_scan_tail(mtd)) { + res = -ENXIO; + goto out; + } + +#ifdef CONFIG_MTD_PARTITIONS +#ifdef CONFIG_MTD_CMDLINE_PARTS + num_partitions = parse_mtd_partitions(mtd, part_probes, + &partitions, 0); + if (num_partitions < 0) { + res = num_partitions; + goto release; + } +#endif + +#ifdef CONFIG_MTD_OF_PARTS + if (num_partitions == 0) { + num_partitions = of_mtd_parse_partitions(&ofdev->dev, + ofdev->node, + &partitions); + if (num_partitions < 0) { + res = num_partitions; + goto release; + } + } +#endif + if (partitions && (num_partitions > 0)) + res = add_mtd_partitions(mtd, partitions, num_partitions); + else +#endif + res = add_mtd_device(mtd); + + if (!res) + return res; + +#ifdef CONFIG_MTD_PARTITIONS +release: +#endif + nand_release(mtd); + +out: + dev_set_drvdata(&ofdev->dev, NULL); + iounmap(host->io_base); + kfree(host); + return res; +} + +/* + * Remove a NAND device. + */ +static int __devexit socrates_nand_remove(struct of_device *ofdev) +{ + struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev); + struct mtd_info *mtd = &host->mtd; + + nand_release(mtd); + + dev_set_drvdata(&ofdev->dev, NULL); + iounmap(host->io_base); + kfree(host); + + return 0; +} + +static struct of_device_id socrates_nand_match[] = +{ + { + .compatible = "abb,socrates-nand", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, socrates_nand_match); + +static struct of_platform_driver socrates_nand_driver = { + .name = "socrates_nand", + .match_table = socrates_nand_match, + .probe = socrates_nand_probe, + .remove = __devexit_p(socrates_nand_remove), +}; + +static int __init socrates_nand_init(void) +{ + return of_register_platform_driver(&socrates_nand_driver); +} + +static void __exit socrates_nand_exit(void) +{ + of_unregister_platform_driver(&socrates_nand_driver); +} + +module_init(socrates_nand_init); +module_exit(socrates_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ilya Yanok"); +MODULE_DESCRIPTION("NAND driver for Socrates board"); diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c new file mode 100644 index 000000000000..812479264896 --- /dev/null +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -0,0 +1,428 @@ +/* + * TXx9 NAND flash memory controller driver + * Based on RBTX49xx patch from CELF patch archive. + * + * 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. + * + * (C) Copyright TOSHIBA CORPORATION 2004-2007 + * All Rights Reserved. + */ +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/mtd/partitions.h> +#include <linux/io.h> +#include <asm/txx9/ndfmc.h> + +/* TXX9 NDFMC Registers */ +#define TXX9_NDFDTR 0x00 +#define TXX9_NDFMCR 0x04 +#define TXX9_NDFSR 0x08 +#define TXX9_NDFISR 0x0c +#define TXX9_NDFIMR 0x10 +#define TXX9_NDFSPR 0x14 +#define TXX9_NDFRSTR 0x18 /* not TX4939 */ + +/* NDFMCR : NDFMC Mode Control */ +#define TXX9_NDFMCR_WE 0x80 +#define TXX9_NDFMCR_ECC_ALL 0x60 +#define TXX9_NDFMCR_ECC_RESET 0x60 +#define TXX9_NDFMCR_ECC_READ 0x40 +#define TXX9_NDFMCR_ECC_ON 0x20 +#define TXX9_NDFMCR_ECC_OFF 0x00 +#define TXX9_NDFMCR_CE 0x10 +#define TXX9_NDFMCR_BSPRT 0x04 /* TX4925/TX4926 only */ +#define TXX9_NDFMCR_ALE 0x02 +#define TXX9_NDFMCR_CLE 0x01 +/* TX4939 only */ +#define TXX9_NDFMCR_X16 0x0400 +#define TXX9_NDFMCR_DMAREQ_MASK 0x0300 +#define TXX9_NDFMCR_DMAREQ_NODMA 0x0000 +#define TXX9_NDFMCR_DMAREQ_128 0x0100 +#define TXX9_NDFMCR_DMAREQ_256 0x0200 +#define TXX9_NDFMCR_DMAREQ_512 0x0300 +#define TXX9_NDFMCR_CS_MASK 0x0c +#define TXX9_NDFMCR_CS(ch) ((ch) << 2) + +/* NDFMCR : NDFMC Status */ +#define TXX9_NDFSR_BUSY 0x80 +/* TX4939 only */ +#define TXX9_NDFSR_DMARUN 0x40 + +/* NDFMCR : NDFMC Reset */ +#define TXX9_NDFRSTR_RST 0x01 + +struct txx9ndfmc_priv { + struct platform_device *dev; + struct nand_chip chip; + struct mtd_info mtd; + int cs; + char mtdname[BUS_ID_SIZE + 2]; +}; + +#define MAX_TXX9NDFMC_DEV 4 +struct txx9ndfmc_drvdata { + struct mtd_info *mtds[MAX_TXX9NDFMC_DEV]; + void __iomem *base; + unsigned char hold; /* in gbusclock */ + unsigned char spw; /* in gbusclock */ + struct nand_hw_control hw_control; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts[MAX_TXX9NDFMC_DEV]; +#endif +}; + +static struct platform_device *mtd_to_platdev(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct txx9ndfmc_priv *txx9_priv = chip->priv; + return txx9_priv->dev; +} + +static void __iomem *ndregaddr(struct platform_device *dev, unsigned int reg) +{ + struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); + struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; + + return drvdata->base + (reg << plat->shift); +} + +static u32 txx9ndfmc_read(struct platform_device *dev, unsigned int reg) +{ + return __raw_readl(ndregaddr(dev, reg)); +} + +static void txx9ndfmc_write(struct platform_device *dev, + u32 val, unsigned int reg) +{ + __raw_writel(val, ndregaddr(dev, reg)); +} + +static uint8_t txx9ndfmc_read_byte(struct mtd_info *mtd) +{ + struct platform_device *dev = mtd_to_platdev(mtd); + + return txx9ndfmc_read(dev, TXX9_NDFDTR); +} + +static void txx9ndfmc_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct platform_device *dev = mtd_to_platdev(mtd); + void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR); + u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); + + txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_WE, TXX9_NDFMCR); + while (len--) + __raw_writel(*buf++, ndfdtr); + txx9ndfmc_write(dev, mcr, TXX9_NDFMCR); +} + +static void txx9ndfmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct platform_device *dev = mtd_to_platdev(mtd); + void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR); + + while (len--) + *buf++ = __raw_readl(ndfdtr); +} + +static int txx9ndfmc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct platform_device *dev = mtd_to_platdev(mtd); + void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR); + + while (len--) + if (*buf++ != (uint8_t)__raw_readl(ndfdtr)) + return -EFAULT; + return 0; +} + +static void txx9ndfmc_cmd_ctrl(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct nand_chip *chip = mtd->priv; + struct txx9ndfmc_priv *txx9_priv = chip->priv; + struct platform_device *dev = txx9_priv->dev; + struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; + + if (ctrl & NAND_CTRL_CHANGE) { + u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); + + mcr &= ~(TXX9_NDFMCR_CLE | TXX9_NDFMCR_ALE | TXX9_NDFMCR_CE); + mcr |= ctrl & NAND_CLE ? TXX9_NDFMCR_CLE : 0; + mcr |= ctrl & NAND_ALE ? TXX9_NDFMCR_ALE : 0; + /* TXX9_NDFMCR_CE bit is 0:high 1:low */ + mcr |= ctrl & NAND_NCE ? TXX9_NDFMCR_CE : 0; + if (txx9_priv->cs >= 0 && (ctrl & NAND_NCE)) { + mcr &= ~TXX9_NDFMCR_CS_MASK; + mcr |= TXX9_NDFMCR_CS(txx9_priv->cs); + } + txx9ndfmc_write(dev, mcr, TXX9_NDFMCR); + } + if (cmd != NAND_CMD_NONE) + txx9ndfmc_write(dev, cmd & 0xff, TXX9_NDFDTR); + if (plat->flags & NDFMC_PLAT_FLAG_DUMMYWRITE) { + /* dummy write to update external latch */ + if ((ctrl & NAND_CTRL_CHANGE) && cmd == NAND_CMD_NONE) + txx9ndfmc_write(dev, 0, TXX9_NDFDTR); + } + mmiowb(); +} + +static int txx9ndfmc_dev_ready(struct mtd_info *mtd) +{ + struct platform_device *dev = mtd_to_platdev(mtd); + + return !(txx9ndfmc_read(dev, TXX9_NDFSR) & TXX9_NDFSR_BUSY); +} + +static int txx9ndfmc_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + struct platform_device *dev = mtd_to_platdev(mtd); + u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); + + mcr &= ~TXX9_NDFMCR_ECC_ALL; + txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); + txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_READ, TXX9_NDFMCR); + ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR); + ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR); + ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR); + txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); + return 0; +} + +static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct platform_device *dev = mtd_to_platdev(mtd); + u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR); + + mcr &= ~TXX9_NDFMCR_ECC_ALL; + txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_RESET, TXX9_NDFMCR); + txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR); + txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_ON, TXX9_NDFMCR); +} + +static void txx9ndfmc_initialize(struct platform_device *dev) +{ + struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; + struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); + int tmout = 100; + + if (plat->flags & NDFMC_PLAT_FLAG_NO_RSTR) + ; /* no NDFRSTR. Write to NDFSPR resets the NDFMC. */ + else { + /* reset NDFMC */ + txx9ndfmc_write(dev, + txx9ndfmc_read(dev, TXX9_NDFRSTR) | + TXX9_NDFRSTR_RST, + TXX9_NDFRSTR); + while (txx9ndfmc_read(dev, TXX9_NDFRSTR) & TXX9_NDFRSTR_RST) { + if (--tmout == 0) { + dev_err(&dev->dev, "reset failed.\n"); + break; + } + udelay(1); + } + } + /* setup Hold Time, Strobe Pulse Width */ + txx9ndfmc_write(dev, (drvdata->hold << 4) | drvdata->spw, TXX9_NDFSPR); + txx9ndfmc_write(dev, + (plat->flags & NDFMC_PLAT_FLAG_USE_BSPRT) ? + TXX9_NDFMCR_BSPRT : 0, TXX9_NDFMCR); +} + +#define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \ + DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000) + +static int __init txx9ndfmc_probe(struct platform_device *dev) +{ + struct txx9ndfmc_platform_data *plat = dev->dev.platform_data; +#ifdef CONFIG_MTD_PARTITIONS + static const char *probes[] = { "cmdlinepart", NULL }; +#endif + int hold, spw; + int i; + struct txx9ndfmc_drvdata *drvdata; + unsigned long gbusclk = plat->gbus_clock; + struct resource *res; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + drvdata = devm_kzalloc(&dev->dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + if (!devm_request_mem_region(&dev->dev, res->start, + resource_size(res), dev_name(&dev->dev))) + return -EBUSY; + drvdata->base = devm_ioremap(&dev->dev, res->start, + resource_size(res)); + if (!drvdata->base) + return -EBUSY; + + hold = plat->hold ?: 20; /* tDH */ + spw = plat->spw ?: 90; /* max(tREADID, tWP, tRP) */ + + hold = TXX9NDFMC_NS_TO_CYC(gbusclk, hold); + spw = TXX9NDFMC_NS_TO_CYC(gbusclk, spw); + if (plat->flags & NDFMC_PLAT_FLAG_HOLDADD) + hold -= 2; /* actual hold time : (HOLD + 2) BUSCLK */ + spw -= 1; /* actual wait time : (SPW + 1) BUSCLK */ + hold = clamp(hold, 1, 15); + drvdata->hold = hold; + spw = clamp(spw, 1, 15); + drvdata->spw = spw; + dev_info(&dev->dev, "CLK:%ldMHz HOLD:%d SPW:%d\n", + (gbusclk + 500000) / 1000000, hold, spw); + + spin_lock_init(&drvdata->hw_control.lock); + init_waitqueue_head(&drvdata->hw_control.wq); + + platform_set_drvdata(dev, drvdata); + txx9ndfmc_initialize(dev); + + for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) { + struct txx9ndfmc_priv *txx9_priv; + struct nand_chip *chip; + struct mtd_info *mtd; +#ifdef CONFIG_MTD_PARTITIONS + int nr_parts; +#endif + + if (!(plat->ch_mask & (1 << i))) + continue; + txx9_priv = kzalloc(sizeof(struct txx9ndfmc_priv), + GFP_KERNEL); + if (!txx9_priv) { + dev_err(&dev->dev, "Unable to allocate " + "TXx9 NDFMC MTD device structure.\n"); + continue; + } + chip = &txx9_priv->chip; + mtd = &txx9_priv->mtd; + mtd->owner = THIS_MODULE; + + mtd->priv = chip; + + chip->read_byte = txx9ndfmc_read_byte; + chip->read_buf = txx9ndfmc_read_buf; + chip->write_buf = txx9ndfmc_write_buf; + chip->verify_buf = txx9ndfmc_verify_buf; + chip->cmd_ctrl = txx9ndfmc_cmd_ctrl; + chip->dev_ready = txx9ndfmc_dev_ready; + chip->ecc.calculate = txx9ndfmc_calculate_ecc; + chip->ecc.correct = nand_correct_data; + chip->ecc.hwctl = txx9ndfmc_enable_hwecc; + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.size = 256; + chip->ecc.bytes = 3; + chip->chip_delay = 100; + chip->controller = &drvdata->hw_control; + + chip->priv = txx9_priv; + txx9_priv->dev = dev; + + if (plat->ch_mask != 1) { + txx9_priv->cs = i; + sprintf(txx9_priv->mtdname, "%s.%u", + dev_name(&dev->dev), i); + } else { + txx9_priv->cs = -1; + strcpy(txx9_priv->mtdname, dev_name(&dev->dev)); + } + if (plat->wide_mask & (1 << i)) + chip->options |= NAND_BUSWIDTH_16; + + if (nand_scan(mtd, 1)) { + kfree(txx9_priv); + continue; + } + mtd->name = txx9_priv->mtdname; + +#ifdef CONFIG_MTD_PARTITIONS + nr_parts = parse_mtd_partitions(mtd, probes, + &drvdata->parts[i], 0); + if (nr_parts > 0) + add_mtd_partitions(mtd, drvdata->parts[i], nr_parts); +#endif + add_mtd_device(mtd); + drvdata->mtds[i] = mtd; + } + + return 0; +} + +static int __exit txx9ndfmc_remove(struct platform_device *dev) +{ + struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev); + int i; + + platform_set_drvdata(dev, NULL); + if (!drvdata) + return 0; + for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) { + struct mtd_info *mtd = drvdata->mtds[i]; + struct nand_chip *chip; + struct txx9ndfmc_priv *txx9_priv; + + if (!mtd) + continue; + chip = mtd->priv; + txx9_priv = chip->priv; + +#ifdef CONFIG_MTD_PARTITIONS + del_mtd_partitions(mtd); + kfree(drvdata->parts[i]); +#endif + del_mtd_device(mtd); + kfree(txx9_priv); + } + return 0; +} + +#ifdef CONFIG_PM +static int txx9ndfmc_resume(struct platform_device *dev) +{ + if (platform_get_drvdata(dev)) + txx9ndfmc_initialize(dev); + return 0; +} +#else +#define txx9ndfmc_resume NULL +#endif + +static struct platform_driver txx9ndfmc_driver = { + .remove = __exit_p(txx9ndfmc_remove), + .resume = txx9ndfmc_resume, + .driver = { + .name = "txx9ndfmc", + .owner = THIS_MODULE, + }, +}; + +static int __init txx9ndfmc_init(void) +{ + return platform_driver_probe(&txx9ndfmc_driver, txx9ndfmc_probe); +} + +static void __exit txx9ndfmc_exit(void) +{ + platform_driver_unregister(&txx9ndfmc_driver); +} + +module_init(txx9ndfmc_init); +module_exit(txx9ndfmc_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("TXx9 SoC NAND flash controller driver"); +MODULE_ALIAS("platform:txx9ndfmc"); |