From 08d2503ecc2337275942c1f52822b7a464c0c0a3 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 4 Mar 2009 12:01:29 -0800 Subject: [MTD] [NAND] Blackfin NFC Driver: do not clobber DMAC1_PERIMUX Only set DMAC1_PERIMUX once we have requested and been granted the dma channel to prevent breaking other peripherals in the error case Signed-off-by: Mike Frysinger Signed-off-by: Bryan Wu Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/nand/bf5xx_nand.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 9af2a2cc115..5c6056e0242 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,6 +566,12 @@ static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info) return ret; } +#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, (void *) bf5xx_nand_dma_irq, (void *) info); /* Turn off the DMA channel first */ -- cgit v1.2.3 From 8d30cab06920f9efb7e089f2c69e451b2a637d8a Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 4 Mar 2009 12:01:29 -0800 Subject: [MTD] [NAND] Blackfin NFC Driver: mark bf5xx_nand_add_partition() as __devinit The bf5xx_nand_add_partition() func is only called by __devinit functions, so put it into the __devinit section as well Signed-off-by: Mike Frysinger Signed-off-by: Bryan Wu Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/nand/bf5xx_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 5c6056e0242..520314d4a80 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -630,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; -- cgit v1.2.3 From bfc492571e7ad1eb4b9fe3ac8ab0a7cdaecf8eca Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 4 Mar 2009 12:01:30 -0800 Subject: [MTD] [NAND] Blackfin NFC Driver: drop pointless casts with set_dma_callback() Signed-off-by: Mike Frysinger Signed-off-by: Bryan Wu Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/nand/bf5xx_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 520314d4a80..4c2a67ca801 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -572,7 +572,7 @@ static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info) SSYNC(); #endif - set_dma_callback(CH_NFC, (void *) bf5xx_nand_dma_irq, (void *) info); + set_dma_callback(CH_NFC, bf5xx_nand_dma_irq, info); /* Turn off the DMA channel first */ disable_dma(CH_NFC); -- cgit v1.2.3 From 52ff49df7fab18e56fa31b43143c4150c2547836 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 4 Mar 2009 12:01:36 -0800 Subject: [MTD] [NAND] fix "raw" reads with ECC syndrome layouts The syndrome based page read/write routines store ECC, and possibly other "OOB" data, right after each chunk of ECC'd data. With ECC chunk size of 512 bytes and a large page (2KiB) NAND, the layout is: data-0 OOB-0 data-1 OOB-1 data-2 OOB-2 data-3 OOB-3 OOB-leftover Where OOBx is (prepad, ECC, postpad). However, the current "raw" routines use a traditional layout -- data OOB, disregarding the prepad and postpad values -- so when they're used with that type of ECC hardware, those calls mix up the data and OOB. Which means, in particular, that bad block tables won't be found on startup, with data corruption and related chaos ensuing. The current syndrome-based drivers in mainline all seem to use one chunk per page; presumably they haven't noticed such bugs. Fix this, by adding read/write page_raw_syndrome() routines as siblings of the existing non-raw routines; "raw" just means to bypass the ECC computations, not change data and OOB layout. Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 99 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 4 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 0c3afccde8a..d902370df3e 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -748,6 +748,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) @@ -757,6 +759,47 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, return 0; } +/** + * 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 @@ -1482,6 +1525,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) @@ -1490,6 +1535,44 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); } +/** + * 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 @@ -2569,10 +2652,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 +2660,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 +2685,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 +2707,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 +2721,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; -- cgit v1.2.3 From ff4569c752c577c7e71e03c9d59e6ef85ca763c0 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 4 Mar 2009 12:01:37 -0800 Subject: [MTD] [NAND] davinci_nand driver 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 upper bits of the address bus) as used with some multichip packages. (Including the 2 GiB chips used on some TI devel boards.) The 1-bit ECC hardware is supported (3 bytes ECC per 512 bytes data); but not yet the newer 4-bit ECC (10 bytes ECC per 512 bytes data), as available on chips like the DM355 or OMAP-L137 and needed with the more error-prone MLC NAND chips. This is a cleaned-up version of code that's been in use for several years now; sanity checked with the new drivers/mtd/tests. Signed-off-by: David Brownell Signed-off-by: Sudhakar Rajashekhara Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 7 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/davinci_nand.c | 584 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 592 insertions(+) create mode 100644 drivers/mtd/nand/davinci_nand.c (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 8b12e6e109d..30d6fbc30c3 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -427,4 +427,11 @@ 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. + endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index b661586afbf..26dc0bef68a 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 diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c new file mode 100644 index 00000000000..4bedd8dec61 --- /dev/null +++ b/drivers/mtd/nand/davinci_nand.c @@ -0,0 +1,584 @@ +/* + * davinci_nand.c - NAND Flash Driver for DaVinci family chips + * + * Copyright © 2006 Texas Instruments. + * + * Port to 2.6.23 Copyright © 2008 by: + * Sander Huijsen + * Troy Kisky + * Dirk Behme + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + + +#ifdef CONFIG_MTD_PARTITIONS +static inline int mtd_has_partitions(void) { return 1; } +#else +static inline int mtd_has_partitions(void) { return 0; } +#endif + +#ifdef CONFIG_MTD_CMDLINE_PARTS +static inline int mtd_has_cmdlinepart(void) { return 1; } +#else +static inline int mtd_has_cmdlinepart(void) { return 0; } +#endif + + +/* + * 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->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 if (cpu_is_davinci_dm355()) + ecc_mode = NAND_ECC_HW_SYNDROME; + 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_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"); + -- cgit v1.2.3 From 374555aeb64f450db04edb8c7c218d763f2a8781 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 4 Mar 2009 12:01:38 -0800 Subject: [MTD] [NAND] fix broken debug messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix incorrect debug messages (*write* not read); someone committed some cut'n'paste bugs.  There might be more, I only noticed these since I was looking for nand_read usage and landed in some very wrong functions. IMO all MTD debugging message framework is goofed, anyway. It uses "DEBUG" in a way that's incompatible with usage most everywhere else in the kernel, and which prevents normal pr_dbg() and dev_dbg() calls from working right. [True. It predates those by a long way, and should probably be updated to use them. dwmw2] Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index d902370df3e..0793ca39cc8 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1946,7 +1946,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; } @@ -1956,7 +1956,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; } @@ -2012,8 +2012,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; } -- cgit v1.2.3 From d5e539ad7d8305f12d04b4a278f8cf791e3de4db Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Wed, 4 Mar 2009 12:01:39 -0800 Subject: [MTD] [NAND] davinci: drop usage of cpu_is_* macro Usage of davinci-specific cpu_is macros is not allowed in drivers. These options should be passed in through platform_data. Signed-off-by: Kevin Hilman Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/nand/davinci_nand.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 4bedd8dec61..4034ac94504 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -33,7 +33,6 @@ #include #include -#include #include #include @@ -392,8 +391,6 @@ static int __init nand_davinci_probe(struct platform_device *pdev) /* use board-specific ECC config; else, the best available */ if (pdata) ecc_mode = pdata->ecc_mode; - else if (cpu_is_davinci_dm355()) - ecc_mode = NAND_ECC_HW_SYNDROME; else ecc_mode = NAND_ECC_HW; -- cgit v1.2.3 From a4b6d516a6079c6ba8dc97d185371439035a35d0 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 4 Mar 2009 12:01:41 -0800 Subject: [MTD] partitioning utility predicates Move mtd_has_partitions() and mtd_has_cmdlinepart() inlines from a DaVinci-specific driver to the header. Use those to eliminate #ifdefs in two drivers which had their own definitions of mtd_has_partitions(). Quite a lot of other MTD drivers could benefit from using use one or both of these to remove #ifdeffery. Maybe some Janitors would like to help. Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/nand/davinci_nand.c | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 4034ac94504..81f7ecd23c6 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -38,19 +38,6 @@ #include -#ifdef CONFIG_MTD_PARTITIONS -static inline int mtd_has_partitions(void) { return 1; } -#else -static inline int mtd_has_partitions(void) { return 0; } -#endif - -#ifdef CONFIG_MTD_CMDLINE_PARTS -static inline int mtd_has_cmdlinepart(void) { return 1; } -#else -static inline int mtd_has_cmdlinepart(void) { return 0; } -#endif - - /* * This is a device driver for the NAND flash controller found on the * various DaVinci family chips. It handles up to four SoC chipselects, -- cgit v1.2.3 From 64fb65baffa5b8f6f2eb3f628dec43c22cd1031f Mon Sep 17 00:00:00 2001 From: Atsushi Nemoto Date: Wed, 4 Mar 2009 12:01:34 -0800 Subject: [MTD] TXx9 SoC NAND Flash Memory Controller driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for the integrated NAND flash controller of the TXx9 family. Once upon a time there were tx4925ndfmc and tx4938ndfmc driver. They were removed due to bitrot in 2005. This new driver is completely rewritten based on a driver in CELF patch archive. Signed-off-by: Atsushi Nemoto Cc: Ralf Bächle Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 6 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/txx9ndfmc.c | 428 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 435 insertions(+) create mode 100644 drivers/mtd/nand/txx9ndfmc.c (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 30d6fbc30c3..68ae144ce3b 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -434,4 +434,10 @@ config MTD_NAND_DAVINCI 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. + endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 26dc0bef68a..d228622d5dc 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -37,5 +37,6 @@ 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_TXX9NDFMC) += txx9ndfmc.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c new file mode 100644 index 00000000000..81247926489 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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"); -- cgit v1.2.3 From b2ed3680553b451e5c45064de26ea8fa5201c6d4 Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Tue, 17 Feb 2009 13:54:45 +0200 Subject: [MTD] [NAND] pxa3xx_nand: use resource_size instead of 'r->end - r->start + 1' Signed-off-by: Mike Rapoport Signed-off-by: David Woodhouse --- drivers/mtd/nand/pxa3xx_nand.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index cc55cbc2b30..ffa960baa7e 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1118,14 +1118,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; @@ -1174,7 +1174,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); -- cgit v1.2.3 From 82a72d108b4fbcc8f651b7c4e34c6f18a605d58d Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Tue, 17 Feb 2009 13:54:46 +0200 Subject: [MTD] [NAND] pxa3xx_nand: allow building as module Signed-off-by: Mike Rapoport Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 2 +- drivers/mtd/nand/pxa3xx_nand.c | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 68ae144ce3b..4e7073954e5 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 diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index ffa960baa7e..ead4a7a72d4 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1079,6 +1079,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)) { @@ -1187,6 +1188,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); @@ -1199,6 +1201,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; } -- cgit v1.2.3 From f271049e2010b918f83dc1c7bbd5d75f4710506a Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Tue, 17 Feb 2009 13:54:47 +0200 Subject: [MTD] [NAND] pxa3xx_nand: add ability to keep controller settings defined by OBM/bootloader Signed-off-by: Mike Rapoport Acked-by: Eric Miao Signed-off-by: David Woodhouse --- drivers/mtd/nand/pxa3xx_nand.c | 103 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index ead4a7a72d4..2857a6a37b5 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -171,7 +171,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, @@ -198,6 +204,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, @@ -297,9 +304,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) { @@ -921,6 +942,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) { @@ -928,6 +1025,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; inum_flash; ++i) { f = pdata->flash + i; -- cgit v1.2.3 From c0e6616ae69774f42fda7a8cc5dc699aff311b40 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 24 Mar 2009 18:27:24 +0900 Subject: [MTD] [NAND] sh_flctl: fix hardware ecc handling for 2048 byte page Signed-off-by: Jeremy Baker Signed-off-by: Yoshihiro Shimoda Signed-off-by: David Woodhouse --- drivers/mtd/nand/sh_flctl.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 821acb08ff1..2bc896623e2 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; -- cgit v1.2.3 From 9d63287a461c269edb39941744f4ff22223cf349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Sat, 28 Mar 2009 00:26:58 +0100 Subject: [MTD] [NAND] move gen_nand's probe function to .devinit.text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A pointer to plat_nand_probe is passed to the core via platform_driver_register and so the function must not disappear when the .init sections are discarded. Otherwise (if also having HOTPLUG=y) unbinding and binding a device to the driver via sysfs will result in an oops as does a device being registered late. An alternative to this patch is using platform_driver_probe instead of platform_driver_register plus removing the pointer to the probe function from the struct platform_driver. Signed-off-by: Uwe Kleine-König Cc: Kay Sievers Cc: Hamish Moffatt Cc: David Brownell Cc: Andrew Morton Cc: Li Zefan Cc: Vitaly Wool Cc: Thomas Gleixner Signed-off-by: David Woodhouse --- drivers/mtd/nand/plat_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 75f9f4874ec..86e1d08eee0 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; -- cgit v1.2.3 From 87f39f0493edf7051b1b87c6e9eb7f9a74be8e85 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 26 Mar 2009 00:42:50 -0700 Subject: [MTD] support driver model updates Follow-on patch to the previous driver model patch for the MTD framework. This one makes various MTD drivers connect to the driver model tree, so /sys/devices/virtual/mtd/* nodes are no longer present ... mostly drivers used on boards I have handy. Based on a patch from Kay Sievers. Signed-off-by: David Brownell Signed-off-by: David Woodhouse --- drivers/mtd/nand/davinci_nand.c | 2 ++ drivers/mtd/nand/mxc_nand.c | 1 + 2 files changed, 3 insertions(+) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 81f7ecd23c6..0119220de7d 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -343,6 +343,8 @@ static int __init nand_davinci_probe(struct platform_device *pdev) 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; diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 21fd4f1c480..bfde74a9ba6 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; -- cgit v1.2.3 From c451c7c4c9c4f59c7808a7d397d32bef160c14d9 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sat, 4 Apr 2009 15:27:45 +0100 Subject: =?UTF-8?q?[MTD]=20[NAND]=20Add=20parent=20info=20for=20CAF=C3=89?= =?UTF-8?q?=20controller?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: David Woodhouse --- drivers/mtd/nand/cafe_nand.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 22a6b2e50e9..7c5b257ce8e 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; -- cgit v1.2.3 From 81ec5364a58c0545b694dee02fe65b9ae48f37b6 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 12 Dec 2007 17:27:03 +0100 Subject: [MTD] [NAND] Add support for 4KiB pages. Signed-off-by: Thomas Gleixner Signed-off-by: Sebastian Siewior Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 0793ca39cc8..facee262b4b 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); @@ -2638,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); @@ -2767,6 +2784,7 @@ int nand_scan_tail(struct mtd_info *mtd) break; case 4: case 8: + case 16: mtd->subpage_sft = 2; break; } -- cgit v1.2.3 From 1b578193af3b94c3d55d9aaf6b53275b1cb59a41 Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Wed, 25 Mar 2009 11:48:38 +0100 Subject: [MTD] [NAND] Add support for NAND on the Socrates board Signed-off-by: Ilya Yanok Acked-by: Wolfgang Grandegger Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 6 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/socrates_nand.c | 325 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 332 insertions(+) create mode 100644 drivers/mtd/nand/socrates_nand.c (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 4e7073954e5..3419944c889 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -440,4 +440,10 @@ config MTD_NAND_TXX9NDFMC 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 d228622d5dc..d33860ac42c 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -37,6 +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/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c new file mode 100644 index 00000000000..a4519a7bd68 --- /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 +#include +#include +#include +#include +#include +#include + +#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"); -- cgit v1.2.3 From b6e0e8c07754c8aefd6ff3536463fed5f71405a0 Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Mon, 30 Mar 2009 12:02:42 +0200 Subject: [MTD] [NAND] FSL-UPM: add multi chip support This patch adds support for multi-chip NAND devices to the FSL-UPM driver. This requires support for multiple GPIOs for the RNB pins. The NAND chips are selected through address lines defined by the FDT property "fsl,upm-addr-line-cs-offsets". Signed-off-by: Wolfgang Grandegger Acked-by: Anton Vorontsov Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsl_upm.c | 99 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 76 insertions(+), 23 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index 7815a404a63..430de6de9ac 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -36,7 +36,10 @@ 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; }; @@ -46,7 +49,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 +58,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 +72,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,11 +92,29 @@ 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); 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); + + 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) { struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd); @@ -137,8 +160,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 +180,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 +210,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 +235,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 +243,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); @@ -240,7 +286,7 @@ static int __devinit fun_probe(struct of_device *ofdev, fun->chip_delay = 50; 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 +303,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 +317,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); -- cgit v1.2.3 From ade92a636f15d7e5b92e2df22e8fcb6c7640cd4f Mon Sep 17 00:00:00 2001 From: Wolfgang Grandegger Date: Mon, 30 Mar 2009 12:02:43 +0200 Subject: [MTD] [NAND] FSL-UPM: Add wait flags to support board/chip specific delays The NAND flash on the TQM8548_BE modules requires a short delay after running the UPM pattern. The TQM8548_BE requires a further short delay after writing out a buffer. Normally the R/B pin should be checked, but it's not connected on the TQM8548_BE. The existing driver uses similar fixed delay points. To manage these extra delays in a more general way, I introduced the "fsl,ump-wait-flags" property allowing the board- specific driver to specify various types of extra delay. Signed-off-by: Wolfgang Grandegger Acked-by: Anton Vorontsov Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsl_upm.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index 430de6de9ac..d120cd8d726 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -23,6 +23,10 @@ #include #include +#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; @@ -41,6 +45,7 @@ struct fsl_upm_nand { 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) @@ -96,7 +101,8 @@ static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) fun->mchip_offsets[fun->mchip_number]; fsl_upm_run_pattern(&fun->upm, chip->IO_ADDR_R, mar); - fun_wait_rnb(fun); + 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) @@ -138,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, @@ -285,6 +294,13 @@ 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); if (!fun->io_base) { -- cgit v1.2.3 From 30bbf1406714cf464c56e96e4ad6a291907f5023 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Tue, 24 Mar 2009 17:26:10 -0700 Subject: [MTD] struct device - replace bus_id with dev_name(), dev_set_name() Acked-by: Greg Kroah-Hartman Signed-off-by: Kay Sievers Signed-off-by: David Woodhouse --- drivers/mtd/nand/ndfc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mtd/nand') diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index 582cf80f555..89bf85af642 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -187,7 +187,7 @@ static int ndfc_chip_init(struct ndfc_controller *ndfc, return -ENODEV; ndfc->mtd.name = kasprintf(GFP_KERNEL, "%s.%s", - ndfc->ofdev->dev.bus_id, flash_np->name); + dev_name(&ndfc->ofdev->dev), flash_np->name); if (!ndfc->mtd.name) { ret = -ENOMEM; goto err; -- cgit v1.2.3