diff options
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r-- | drivers/mtd/nand/Kconfig | 28 | ||||
-rw-r--r-- | drivers/mtd/nand/Makefile | 3 | ||||
-rw-r--r-- | drivers/mtd/nand/atmel_nand.c (renamed from drivers/mtd/nand/at91_nand.c) | 278 | ||||
-rw-r--r-- | drivers/mtd/nand/atmel_nand_ecc.h | 36 | ||||
-rw-r--r-- | drivers/mtd/nand/au1550nd.c | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/autcpu12.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/cafe_nand.c | 6 | ||||
-rw-r--r-- | drivers/mtd/nand/diskonchip.c | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/edb7312.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/excite_nandflash.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/fsl_elbc_nand.c | 63 | ||||
-rw-r--r-- | drivers/mtd/nand/h1910.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_base.c | 87 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_bbt.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_ecc.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_ids.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/nandsim.c | 41 | ||||
-rw-r--r-- | drivers/mtd/nand/ppchameleonevb.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/rtc_from4.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/s3c2410.c | 168 | ||||
-rw-r--r-- | drivers/mtd/nand/sharpsl.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/spia.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/toto.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/ts7250.c | 2 |
24 files changed, 512 insertions, 232 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 5076faf9ca66..71406e517857 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,5 +1,4 @@ # drivers/mtd/nand/Kconfig -# $Id: Kconfig,v 1.35 2005/11/07 11:14:30 gleixner Exp $ menuconfig MTD_NAND tristate "NAND Device Support" @@ -272,22 +271,23 @@ config MTD_NAND_CS553X If you say "m", the module will be called "cs553x_nand.ko". -config MTD_NAND_AT91 - bool "Support for NAND Flash / SmartMedia on AT91" - depends on ARCH_AT91 +config MTD_NAND_ATMEL + tristate "Support for NAND Flash / SmartMedia on AT91 and AVR32" + depends on ARCH_AT91 || AVR32 help Enables support for NAND Flash / Smart Media Card interface - on Atmel AT91 processors. + on Atmel AT91 and AVR32 processors. choice - prompt "ECC management for NAND Flash / SmartMedia on AT91" - depends on MTD_NAND_AT91 + prompt "ECC management for NAND Flash / SmartMedia on AT91 / AVR32" + depends on MTD_NAND_ATMEL -config MTD_NAND_AT91_ECC_HW +config MTD_NAND_ATMEL_ECC_HW bool "Hardware ECC" - depends on ARCH_AT91SAM9263 || ARCH_AT91SAM9260 + depends on ARCH_AT91SAM9263 || ARCH_AT91SAM9260 || AVR32 help - Uses hardware ECC provided by the at91sam9260/at91sam9263 chip - instead of software ECC. + Use hardware ECC instead of software ECC when the chip + supports it. + The hardware ECC controller is capable of single bit error correction and 2-bit random detection per page. @@ -297,16 +297,16 @@ config MTD_NAND_AT91_ECC_HW If unsure, say Y -config MTD_NAND_AT91_ECC_SOFT +config MTD_NAND_ATMEL_ECC_SOFT bool "Software ECC" help - Uses software ECC. + Use software ECC. NB : hardware and software ECC schemes are incompatible. If you switch from one to another, you'll have to erase your mtd partition. -config MTD_NAND_AT91_ECC_NONE +config MTD_NAND_ATMEL_ECC_NONE bool "No ECC (testing only, DANGEROUS)" depends on DEBUG_KERNEL help diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index a6e74a46992a..d772581de573 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -1,7 +1,6 @@ # # linux/drivers/nand/Makefile # -# $Id: Makefile.common,v 1.15 2004/11/26 12:28:22 dedekind Exp $ obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o @@ -24,7 +23,7 @@ obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o -obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o +obj-$(CONFIG_MTD_NAND_ATMEL) += atmel_nand.o obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o diff --git a/drivers/mtd/nand/at91_nand.c b/drivers/mtd/nand/atmel_nand.c index 0adb287027a2..99aec46e2145 100644 --- a/drivers/mtd/nand/at91_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -1,6 +1,4 @@ /* - * drivers/mtd/nand/at91_nand.c - * * Copyright (C) 2003 Rick Bronson * * Derived from drivers/mtd/nand/autcpu12.c @@ -31,20 +29,19 @@ #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> -#include <asm/io.h> -#include <asm/sizes.h> +#include <linux/gpio.h> +#include <linux/io.h> -#include <asm/hardware.h> #include <asm/arch/board.h> -#include <asm/arch/gpio.h> +#include <asm/arch/cpu.h> -#ifdef CONFIG_MTD_NAND_AT91_ECC_HW +#ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW #define hard_ecc 1 #else #define hard_ecc 0 #endif -#ifdef CONFIG_MTD_NAND_AT91_ECC_NONE +#ifdef CONFIG_MTD_NAND_ATMEL_ECC_NONE #define no_ecc 1 #else #define no_ecc 0 @@ -52,18 +49,18 @@ /* Register access macros */ #define ecc_readl(add, reg) \ - __raw_readl(add + AT91_ECC_##reg) + __raw_readl(add + ATMEL_ECC_##reg) #define ecc_writel(add, reg, value) \ - __raw_writel((value), add + AT91_ECC_##reg) + __raw_writel((value), add + ATMEL_ECC_##reg) -#include <asm/arch/at91_ecc.h> /* AT91SAM9260/3 ECC registers */ +#include "atmel_nand_ecc.h" /* Hardware ECC registers */ /* oob layout for large page size * bad block info is on bytes 0 and 1 * the bytes have to be consecutives to avoid * several NAND_CMD_RNDOUT during read */ -static struct nand_ecclayout at91_oobinfo_large = { +static struct nand_ecclayout atmel_oobinfo_large = { .eccbytes = 4, .eccpos = {60, 61, 62, 63}, .oobfree = { @@ -76,7 +73,7 @@ static struct nand_ecclayout at91_oobinfo_large = { * the bytes have to be consecutives to avoid * several NAND_CMD_RNDOUT during read */ -static struct nand_ecclayout at91_oobinfo_small = { +static struct nand_ecclayout atmel_oobinfo_small = { .eccbytes = 4, .eccpos = {0, 1, 2, 3}, .oobfree = { @@ -84,11 +81,11 @@ static struct nand_ecclayout at91_oobinfo_small = { }, }; -struct at91_nand_host { +struct atmel_nand_host { struct nand_chip nand_chip; struct mtd_info mtd; void __iomem *io_base; - struct at91_nand_data *board; + struct atmel_nand_data *board; struct device *dev; void __iomem *ecc; }; @@ -96,34 +93,34 @@ struct at91_nand_host { /* * Enable NAND. */ -static void at91_nand_enable(struct at91_nand_host *host) +static void atmel_nand_enable(struct atmel_nand_host *host) { if (host->board->enable_pin) - at91_set_gpio_value(host->board->enable_pin, 0); + gpio_set_value(host->board->enable_pin, 0); } /* * Disable NAND. */ -static void at91_nand_disable(struct at91_nand_host *host) +static void atmel_nand_disable(struct atmel_nand_host *host) { if (host->board->enable_pin) - at91_set_gpio_value(host->board->enable_pin, 1); + gpio_set_value(host->board->enable_pin, 1); } /* * Hardware specific access to control-lines */ -static void at91_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) +static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) { struct nand_chip *nand_chip = mtd->priv; - struct at91_nand_host *host = nand_chip->priv; + struct atmel_nand_host *host = nand_chip->priv; if (ctrl & NAND_CTRL_CHANGE) { if (ctrl & NAND_NCE) - at91_nand_enable(host); + atmel_nand_enable(host); else - at91_nand_disable(host); + atmel_nand_disable(host); } if (cmd == NAND_CMD_NONE) return; @@ -137,18 +134,49 @@ static void at91_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl) /* * Read the Device Ready pin. */ -static int at91_nand_device_ready(struct mtd_info *mtd) +static int atmel_nand_device_ready(struct mtd_info *mtd) { struct nand_chip *nand_chip = mtd->priv; - struct at91_nand_host *host = nand_chip->priv; + struct atmel_nand_host *host = nand_chip->priv; - return at91_get_gpio_value(host->board->rdy_pin); + return gpio_get_value(host->board->rdy_pin); +} + +/* + * Minimal-overhead PIO for data access. + */ +static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + + __raw_readsb(nand_chip->IO_ADDR_R, buf, len); +} + +static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + + __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2); +} + +static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + + __raw_writesb(nand_chip->IO_ADDR_W, buf, len); +} + +static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len) +{ + struct nand_chip *nand_chip = mtd->priv; + + __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2); } /* * write oob for small pages */ -static int at91_nand_write_oob_512(struct mtd_info *mtd, +static int atmel_nand_write_oob_512(struct mtd_info *mtd, struct nand_chip *chip, int page) { int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; @@ -176,7 +204,7 @@ static int at91_nand_write_oob_512(struct mtd_info *mtd, /* * read oob for small pages */ -static int at91_nand_read_oob_512(struct mtd_info *mtd, +static int atmel_nand_read_oob_512(struct mtd_info *mtd, struct nand_chip *chip, int page, int sndcmd) { if (sndcmd) { @@ -196,11 +224,11 @@ static int at91_nand_read_oob_512(struct mtd_info *mtd, * dat: raw data (unused) * ecc_code: buffer for ECC */ -static int at91_nand_calculate(struct mtd_info *mtd, +static int atmel_nand_calculate(struct mtd_info *mtd, const u_char *dat, unsigned char *ecc_code) { struct nand_chip *nand_chip = mtd->priv; - struct at91_nand_host *host = nand_chip->priv; + struct atmel_nand_host *host = nand_chip->priv; uint32_t *eccpos = nand_chip->ecc.layout->eccpos; unsigned int ecc_value; @@ -211,7 +239,7 @@ static int at91_nand_calculate(struct mtd_info *mtd, ecc_code[eccpos[1]] = (ecc_value >> 8) & 0xFF; /* get the last 2 ECC bytes */ - ecc_value = ecc_readl(host->ecc, NPR) & AT91_ECC_NPARITY; + ecc_value = ecc_readl(host->ecc, NPR) & ATMEL_ECC_NPARITY; ecc_code[eccpos[2]] = ecc_value & 0xFF; ecc_code[eccpos[3]] = (ecc_value >> 8) & 0xFF; @@ -226,7 +254,7 @@ static int at91_nand_calculate(struct mtd_info *mtd, * chip: nand chip info structure * buf: buffer to store read data */ -static int at91_nand_read_page(struct mtd_info *mtd, +static int atmel_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf) { int eccsize = chip->ecc.size; @@ -237,6 +265,19 @@ static int at91_nand_read_page(struct mtd_info *mtd, uint8_t *ecc_pos; int stat; + /* + * Errata: ALE is incorrectly wired up to the ECC controller + * on the AP7000, so it will include the address cycles in the + * ECC calculation. + * + * Workaround: Reset the parity registers before reading the + * actual data. + */ + if (cpu_is_at32ap7000()) { + struct atmel_nand_host *host = chip->priv; + ecc_writel(host->ecc, CR, ATMEL_ECC_RST); + } + /* read the page */ chip->read_buf(mtd, p, eccsize); @@ -285,11 +326,11 @@ static int at91_nand_read_page(struct mtd_info *mtd, * * Detect and correct a 1 bit error for a page */ -static int at91_nand_correct(struct mtd_info *mtd, u_char *dat, +static int atmel_nand_correct(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *isnull) { struct nand_chip *nand_chip = mtd->priv; - struct at91_nand_host *host = nand_chip->priv; + struct atmel_nand_host *host = nand_chip->priv; unsigned int ecc_status; unsigned int ecc_word, ecc_bit; @@ -297,43 +338,43 @@ static int at91_nand_correct(struct mtd_info *mtd, u_char *dat, ecc_status = ecc_readl(host->ecc, SR); /* if there's no error */ - if (likely(!(ecc_status & AT91_ECC_RECERR))) + if (likely(!(ecc_status & ATMEL_ECC_RECERR))) return 0; /* get error bit offset (4 bits) */ - ecc_bit = ecc_readl(host->ecc, PR) & AT91_ECC_BITADDR; + ecc_bit = ecc_readl(host->ecc, PR) & ATMEL_ECC_BITADDR; /* get word address (12 bits) */ - ecc_word = ecc_readl(host->ecc, PR) & AT91_ECC_WORDADDR; + ecc_word = ecc_readl(host->ecc, PR) & ATMEL_ECC_WORDADDR; ecc_word >>= 4; /* if there are multiple errors */ - if (ecc_status & AT91_ECC_MULERR) { + if (ecc_status & ATMEL_ECC_MULERR) { /* check if it is a freshly erased block * (filled with 0xff) */ - if ((ecc_bit == AT91_ECC_BITADDR) - && (ecc_word == (AT91_ECC_WORDADDR >> 4))) { + if ((ecc_bit == ATMEL_ECC_BITADDR) + && (ecc_word == (ATMEL_ECC_WORDADDR >> 4))) { /* the block has just been erased, return OK */ return 0; } /* it doesn't seems to be a freshly * erased block. * We can't correct so many errors */ - dev_dbg(host->dev, "at91_nand : multiple errors detected." + dev_dbg(host->dev, "atmel_nand : multiple errors detected." " Unable to correct.\n"); return -EIO; } /* if there's a single bit error : we can correct it */ - if (ecc_status & AT91_ECC_ECCERR) { + if (ecc_status & ATMEL_ECC_ECCERR) { /* there's nothing much to do here. * the bit error is on the ECC itself. */ - dev_dbg(host->dev, "at91_nand : one bit error on ECC code." + dev_dbg(host->dev, "atmel_nand : one bit error on ECC code." " Nothing to correct\n"); return 0; } - dev_dbg(host->dev, "at91_nand : one bit error on data." + dev_dbg(host->dev, "atmel_nand : one bit error on data." " (word offset in the page :" " 0x%x bit offset : 0x%x)\n", ecc_word, ecc_bit); @@ -345,14 +386,21 @@ static int at91_nand_correct(struct mtd_info *mtd, u_char *dat, /* 8 bits words */ dat[ecc_word] ^= (1 << ecc_bit); } - dev_dbg(host->dev, "at91_nand : error corrected\n"); + dev_dbg(host->dev, "atmel_nand : error corrected\n"); return 1; } /* - * Enable HW ECC : unsused + * Enable HW ECC : unused on most chips */ -static void at91_nand_hwctl(struct mtd_info *mtd, int mode) { ; } +static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) +{ + if (cpu_is_at32ap7000()) { + struct nand_chip *nand_chip = mtd->priv; + struct atmel_nand_host *host = nand_chip->priv; + ecc_writel(host->ecc, CR, ATMEL_ECC_RST); + } +} #ifdef CONFIG_MTD_PARTITIONS static const char *part_probes[] = { "cmdlinepart", NULL }; @@ -361,9 +409,9 @@ static const char *part_probes[] = { "cmdlinepart", NULL }; /* * Probe for the NAND device. */ -static int __init at91_nand_probe(struct platform_device *pdev) +static int __init atmel_nand_probe(struct platform_device *pdev) { - struct at91_nand_host *host; + struct atmel_nand_host *host; struct mtd_info *mtd; struct nand_chip *nand_chip; struct resource *regs; @@ -375,24 +423,24 @@ static int __init at91_nand_probe(struct platform_device *pdev) int num_partitions = 0; #endif - /* Allocate memory for the device structure (and zero it) */ - host = kzalloc(sizeof(struct at91_nand_host), GFP_KERNEL); - if (!host) { - printk(KERN_ERR "at91_nand: failed to allocate device structure.\n"); - return -ENOMEM; - } - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) { - printk(KERN_ERR "at91_nand: can't get I/O resource mem\n"); + printk(KERN_ERR "atmel_nand: can't get I/O resource mem\n"); return -ENXIO; } + /* Allocate memory for the device structure (and zero it) */ + host = kzalloc(sizeof(struct atmel_nand_host), GFP_KERNEL); + if (!host) { + printk(KERN_ERR "atmel_nand: failed to allocate device structure.\n"); + return -ENOMEM; + } + host->io_base = ioremap(mem->start, mem->end - mem->start + 1); if (host->io_base == NULL) { - printk(KERN_ERR "at91_nand: ioremap failed\n"); - kfree(host); - return -EIO; + printk(KERN_ERR "atmel_nand: ioremap failed\n"); + res = -EIO; + goto err_nand_ioremap; } mtd = &host->mtd; @@ -407,14 +455,14 @@ static int __init at91_nand_probe(struct platform_device *pdev) /* Set address of NAND IO lines */ nand_chip->IO_ADDR_R = host->io_base; nand_chip->IO_ADDR_W = host->io_base; - nand_chip->cmd_ctrl = at91_nand_cmd_ctrl; + nand_chip->cmd_ctrl = atmel_nand_cmd_ctrl; if (host->board->rdy_pin) - nand_chip->dev_ready = at91_nand_device_ready; + nand_chip->dev_ready = atmel_nand_device_ready; regs = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!regs && hard_ecc) { - printk(KERN_ERR "at91_nand: can't get I/O resource " + printk(KERN_ERR "atmel_nand: can't get I/O resource " "regs\nFalling back on software ECC\n"); } @@ -424,15 +472,15 @@ static int __init at91_nand_probe(struct platform_device *pdev) if (hard_ecc && regs) { host->ecc = ioremap(regs->start, regs->end - regs->start + 1); if (host->ecc == NULL) { - printk(KERN_ERR "at91_nand: ioremap failed\n"); + printk(KERN_ERR "atmel_nand: ioremap failed\n"); res = -EIO; goto err_ecc_ioremap; } nand_chip->ecc.mode = NAND_ECC_HW_SYNDROME; - nand_chip->ecc.calculate = at91_nand_calculate; - nand_chip->ecc.correct = at91_nand_correct; - nand_chip->ecc.hwctl = at91_nand_hwctl; - nand_chip->ecc.read_page = at91_nand_read_page; + nand_chip->ecc.calculate = atmel_nand_calculate; + nand_chip->ecc.correct = atmel_nand_correct; + nand_chip->ecc.hwctl = atmel_nand_hwctl; + nand_chip->ecc.read_page = atmel_nand_read_page; nand_chip->ecc.bytes = 4; nand_chip->ecc.prepad = 0; nand_chip->ecc.postpad = 0; @@ -440,24 +488,30 @@ static int __init at91_nand_probe(struct platform_device *pdev) nand_chip->chip_delay = 20; /* 20us command delay time */ - if (host->board->bus_width_16) /* 16-bit bus width */ + if (host->board->bus_width_16) { /* 16-bit bus width */ nand_chip->options |= NAND_BUSWIDTH_16; + nand_chip->read_buf = atmel_read_buf16; + nand_chip->write_buf = atmel_write_buf16; + } else { + nand_chip->read_buf = atmel_read_buf; + nand_chip->write_buf = atmel_write_buf; + } platform_set_drvdata(pdev, host); - at91_nand_enable(host); + atmel_nand_enable(host); if (host->board->det_pin) { - if (at91_get_gpio_value(host->board->det_pin)) { - printk ("No SmartMedia card inserted.\n"); + if (gpio_get_value(host->board->det_pin)) { + printk("No SmartMedia card inserted.\n"); res = ENXIO; - goto out; + goto err_no_card; } } /* first scan to find the device and get the page size */ if (nand_scan_ident(mtd, 1)) { res = -ENXIO; - goto out; + goto err_scan_ident; } if (nand_chip->ecc.mode == NAND_ECC_HW_SYNDROME) { @@ -467,22 +521,22 @@ static int __init at91_nand_probe(struct platform_device *pdev) /* set ECC page size and oob layout */ switch (mtd->writesize) { case 512: - nand_chip->ecc.layout = &at91_oobinfo_small; - nand_chip->ecc.read_oob = at91_nand_read_oob_512; - nand_chip->ecc.write_oob = at91_nand_write_oob_512; - ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_528); + nand_chip->ecc.layout = &atmel_oobinfo_small; + nand_chip->ecc.read_oob = atmel_nand_read_oob_512; + nand_chip->ecc.write_oob = atmel_nand_write_oob_512; + ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_528); break; case 1024: - nand_chip->ecc.layout = &at91_oobinfo_large; - ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_1056); + nand_chip->ecc.layout = &atmel_oobinfo_large; + ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_1056); break; case 2048: - nand_chip->ecc.layout = &at91_oobinfo_large; - ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_2112); + nand_chip->ecc.layout = &atmel_oobinfo_large; + ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_2112); break; case 4096: - nand_chip->ecc.layout = &at91_oobinfo_large; - ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_4224); + nand_chip->ecc.layout = &atmel_oobinfo_large; + ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_4224); break; default: /* page size not handled by HW ECC */ @@ -502,12 +556,12 @@ static int __init at91_nand_probe(struct platform_device *pdev) /* second phase scan */ if (nand_scan_tail(mtd)) { res = -ENXIO; - goto out; + goto err_scan_tail; } #ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_CMDLINE_PARTS - mtd->name = "at91_nand"; + mtd->name = "atmel_nand"; num_partitions = parse_mtd_partitions(mtd, part_probes, &partitions, 0); #endif @@ -516,9 +570,9 @@ static int __init at91_nand_probe(struct platform_device *pdev) &num_partitions); if ((!partitions) || (num_partitions == 0)) { - printk(KERN_ERR "at91_nand: No parititions defined, or unsupported device.\n"); + printk(KERN_ERR "atmel_nand: No parititions defined, or unsupported device.\n"); res = ENXIO; - goto release; + goto err_no_partitions; } res = add_mtd_partitions(mtd, partitions, num_partitions); @@ -530,17 +584,19 @@ static int __init at91_nand_probe(struct platform_device *pdev) return res; #ifdef CONFIG_MTD_PARTITIONS -release: +err_no_partitions: #endif nand_release(mtd); - -out: - iounmap(host->ecc); - -err_ecc_ioremap: - at91_nand_disable(host); +err_scan_tail: +err_scan_ident: +err_no_card: + atmel_nand_disable(host); platform_set_drvdata(pdev, NULL); + if (host->ecc) + iounmap(host->ecc); +err_ecc_ioremap: iounmap(host->io_base); +err_nand_ioremap: kfree(host); return res; } @@ -548,47 +604,47 @@ err_ecc_ioremap: /* * Remove a NAND device. */ -static int __devexit at91_nand_remove(struct platform_device *pdev) +static int __exit atmel_nand_remove(struct platform_device *pdev) { - struct at91_nand_host *host = platform_get_drvdata(pdev); + struct atmel_nand_host *host = platform_get_drvdata(pdev); struct mtd_info *mtd = &host->mtd; nand_release(mtd); - at91_nand_disable(host); + atmel_nand_disable(host); + if (host->ecc) + iounmap(host->ecc); iounmap(host->io_base); - iounmap(host->ecc); kfree(host); return 0; } -static struct platform_driver at91_nand_driver = { - .probe = at91_nand_probe, - .remove = at91_nand_remove, +static struct platform_driver atmel_nand_driver = { + .remove = __exit_p(atmel_nand_remove), .driver = { - .name = "at91_nand", + .name = "atmel_nand", .owner = THIS_MODULE, }, }; -static int __init at91_nand_init(void) +static int __init atmel_nand_init(void) { - return platform_driver_register(&at91_nand_driver); + return platform_driver_probe(&atmel_nand_driver, atmel_nand_probe); } -static void __exit at91_nand_exit(void) +static void __exit atmel_nand_exit(void) { - platform_driver_unregister(&at91_nand_driver); + platform_driver_unregister(&atmel_nand_driver); } -module_init(at91_nand_init); -module_exit(at91_nand_exit); +module_init(atmel_nand_init); +module_exit(atmel_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Rick Bronson"); -MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91RM9200 / AT91SAM9"); -MODULE_ALIAS("platform:at91_nand"); +MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91 / AVR32"); +MODULE_ALIAS("platform:atmel_nand"); diff --git a/drivers/mtd/nand/atmel_nand_ecc.h b/drivers/mtd/nand/atmel_nand_ecc.h new file mode 100644 index 000000000000..1ee7f993db1c --- /dev/null +++ b/drivers/mtd/nand/atmel_nand_ecc.h @@ -0,0 +1,36 @@ +/* + * Error Corrected Code Controller (ECC) - System peripherals regsters. + * Based on AT91SAM9260 datasheet revision B. + * + * 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. + */ + +#ifndef ATMEL_NAND_ECC_H +#define ATMEL_NAND_ECC_H + +#define ATMEL_ECC_CR 0x00 /* Control register */ +#define ATMEL_ECC_RST (1 << 0) /* Reset parity */ + +#define ATMEL_ECC_MR 0x04 /* Mode register */ +#define ATMEL_ECC_PAGESIZE (3 << 0) /* Page Size */ +#define ATMEL_ECC_PAGESIZE_528 (0) +#define ATMEL_ECC_PAGESIZE_1056 (1) +#define ATMEL_ECC_PAGESIZE_2112 (2) +#define ATMEL_ECC_PAGESIZE_4224 (3) + +#define ATMEL_ECC_SR 0x08 /* Status register */ +#define ATMEL_ECC_RECERR (1 << 0) /* Recoverable Error */ +#define ATMEL_ECC_ECCERR (1 << 1) /* ECC Single Bit Error */ +#define ATMEL_ECC_MULERR (1 << 2) /* Multiple Errors */ + +#define ATMEL_ECC_PR 0x0c /* Parity register */ +#define ATMEL_ECC_BITADDR (0xf << 0) /* Bit Error Address */ +#define ATMEL_ECC_WORDADDR (0xfff << 4) /* Word Error Address */ + +#define ATMEL_ECC_NPR 0x10 /* NParity register */ +#define ATMEL_ECC_NPARITY (0xffff << 0) /* NParity */ + +#endif diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 09e421a96893..761946ea45b1 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -3,8 +3,6 @@ * * Copyright (C) 2004 Embedded Edge, LLC * - * $Id: au1550nd.c,v 1.13 2005/11/07 11:14:30 gleixner Exp $ - * * 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. @@ -604,8 +602,6 @@ module_init(au1xxx_nand_init); */ static void __exit au1550_cleanup(void) { - struct nand_chip *this = (struct nand_chip *)&au1550_mtd[1]; - /* Release resources, unregister device */ nand_release(au1550_mtd); diff --git a/drivers/mtd/nand/autcpu12.c b/drivers/mtd/nand/autcpu12.c index dd38011ee0b7..553dd7e9b41c 100644 --- a/drivers/mtd/nand/autcpu12.c +++ b/drivers/mtd/nand/autcpu12.c @@ -6,8 +6,6 @@ * Derived from drivers/mtd/spia.c * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * - * $Id: autcpu12.c,v 1.23 2005/11/07 11:14:30 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index da6ceaa80ba1..95345d051579 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -626,10 +626,12 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, { struct mtd_info *mtd; struct cafe_priv *cafe; - struct mtd_partition *parts; uint32_t ctrl; - int nr_parts; int err = 0; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts; + int nr_parts; +#endif /* Very old versions shared the same PCI ident for all three functions on the chip. Verify the class too... */ diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 0e72153b3297..765d4f0f7c86 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -15,8 +15,6 @@ * converted to the generic Reed-Solomon library by Thomas Gleixner <tglx@linutronix.de> * * Interface to generic NAND code for M-Systems DiskOnChip devices - * - * $Id: diskonchip.c,v 1.55 2005/11/07 11:14:30 gleixner Exp $ */ #include <linux/kernel.h> @@ -54,8 +52,6 @@ static unsigned long __initdata doc_locations[] = { 0xe0000, 0xe2000, 0xe4000, 0xe6000, 0xe8000, 0xea000, 0xec000, 0xee000, #endif /* CONFIG_MTD_DOCPROBE_HIGH */ -#elif defined(__PPC__) - 0xe4000000, #else #warning Unknown architecture for DiskOnChip. No default probe locations defined #endif diff --git a/drivers/mtd/nand/edb7312.c b/drivers/mtd/nand/edb7312.c index ba67bbec20d3..387e4352903e 100644 --- a/drivers/mtd/nand/edb7312.c +++ b/drivers/mtd/nand/edb7312.c @@ -6,8 +6,6 @@ * Derived from drivers/mtd/nand/autcpu12.c * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * - * $Id: edb7312.c,v 1.12 2005/11/07 11:14:30 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/excite_nandflash.c b/drivers/mtd/nand/excite_nandflash.c index bed87290decc..ced14b5294d5 100644 --- a/drivers/mtd/nand/excite_nandflash.c +++ b/drivers/mtd/nand/excite_nandflash.c @@ -209,7 +209,7 @@ static int __init excite_nand_probe(struct device *dev) if (likely(!scan_res)) { DEBUG(MTD_DEBUG_LEVEL2, "%s: register partitions\n", module_id); add_mtd_partitions(&drvdata->board_mtd, partition_info, - sizeof partition_info / sizeof partition_info[0]); + ARRAY_SIZE(partition_info)); } else { iounmap(drvdata->regs); kfree(drvdata); diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 4b69aacdf5ca..9dff51351f4f 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -89,7 +89,6 @@ static struct nand_ecclayout fsl_elbc_oob_sp_eccm0 = { .eccbytes = 3, .eccpos = {6, 7, 8}, .oobfree = { {0, 5}, {9, 7} }, - .oobavail = 12, }; /* Small Page FLASH with FMR[ECCM] = 1 */ @@ -97,7 +96,6 @@ static struct nand_ecclayout fsl_elbc_oob_sp_eccm1 = { .eccbytes = 3, .eccpos = {8, 9, 10}, .oobfree = { {0, 5}, {6, 2}, {11, 5} }, - .oobavail = 12, }; /* Large Page FLASH with FMR[ECCM] = 0 */ @@ -105,7 +103,6 @@ static struct nand_ecclayout fsl_elbc_oob_lp_eccm0 = { .eccbytes = 12, .eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56}, .oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} }, - .oobavail = 48, }; /* Large Page FLASH with FMR[ECCM] = 1 */ @@ -113,7 +110,48 @@ static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = { .eccbytes = 12, .eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58}, .oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} }, - .oobavail = 48, +}; + +/* + * fsl_elbc_oob_lp_eccm* specify that LP NAND's OOB free area starts at offset + * 1, so we have to adjust bad block pattern. This pattern should be used for + * x8 chips only. So far hardware does not support x16 chips anyway. + */ +static u8 scan_ff_pattern[] = { 0xff, }; + +static struct nand_bbt_descr largepage_memorybased = { + .options = 0, + .offs = 0, + .len = 1, + .pattern = scan_ff_pattern, +}; + +/* + * ELBC may use HW ECC, so that OOB offsets, that NAND core uses for bbt, + * interfere with ECC positions, that's why we implement our own descriptors. + * OOB {11, 5}, works for both SP and LP chips, with ECCM = 1 and ECCM = 0. + */ +static u8 bbt_pattern[] = {'B', 'b', 't', '0' }; +static u8 mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 11, + .len = 4, + .veroffs = 15, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE | + NAND_BBT_2BIT | NAND_BBT_VERSION, + .offs = 11, + .len = 4, + .veroffs = 15, + .maxblocks = 4, + .pattern = mirror_pattern, }; /*=================================*/ @@ -687,8 +725,7 @@ static int fsl_elbc_chip_init_tail(struct mtd_info *mtd) chip->ecc.layout = (priv->fmr & FMR_ECCM) ? &fsl_elbc_oob_lp_eccm1 : &fsl_elbc_oob_lp_eccm0; - mtd->ecclayout = chip->ecc.layout; - mtd->oobavail = chip->ecc.layout->oobavail; + chip->badblock_pattern = &largepage_memorybased; } } else { dev_err(ctrl->dev, @@ -752,8 +789,12 @@ static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv) chip->cmdfunc = fsl_elbc_cmdfunc; chip->waitfunc = fsl_elbc_wait; + chip->bbt_td = &bbt_main_descr; + chip->bbt_md = &bbt_mirror_descr; + /* set up nand options */ - chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR; + chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR | + NAND_USE_FLASH_BBT; chip->controller = &ctrl->controller; chip->priv = priv; @@ -795,8 +836,8 @@ static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv) return 0; } -static int fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, - struct device_node *node) +static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, + struct device_node *node) { struct fsl_lbc_regs __iomem *lbc = ctrl->regs; struct fsl_elbc_mtd *priv; @@ -917,7 +958,7 @@ static int __devinit fsl_elbc_ctrl_init(struct fsl_elbc_ctrl *ctrl) return 0; } -static int __devexit fsl_elbc_ctrl_remove(struct of_device *ofdev) +static int fsl_elbc_ctrl_remove(struct of_device *ofdev) { struct fsl_elbc_ctrl *ctrl = dev_get_drvdata(&ofdev->dev); int i; @@ -1041,7 +1082,7 @@ static struct of_platform_driver fsl_elbc_ctrl_driver = { }, .match_table = fsl_elbc_match, .probe = fsl_elbc_ctrl_probe, - .remove = __devexit_p(fsl_elbc_ctrl_remove), + .remove = fsl_elbc_ctrl_remove, }; static int __init fsl_elbc_init(void) diff --git a/drivers/mtd/nand/h1910.c b/drivers/mtd/nand/h1910.c index 2d585d2d090c..9e59de501c2e 100644 --- a/drivers/mtd/nand/h1910.c +++ b/drivers/mtd/nand/h1910.c @@ -7,8 +7,6 @@ * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * - * $Id: h1910.c,v 1.6 2005/11/07 11:14:30 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index ba1bdf787323..d1129bae6c27 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -798,6 +798,87 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, } /** + * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function + * @mtd: mtd info structure + * @chip: nand chip info structure + * @dataofs offset of requested data within the page + * @readlen data length + * @buf: buffer to store read data + */ +static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi) +{ + int start_step, end_step, num_steps; + uint32_t *eccpos = chip->ecc.layout->eccpos; + uint8_t *p; + int data_col_addr, i, gaps = 0; + int datafrag_len, eccfrag_len, aligned_len, aligned_pos; + int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1; + + /* Column address wihin the page aligned to ECC size (256bytes). */ + start_step = data_offs / chip->ecc.size; + end_step = (data_offs + readlen - 1) / chip->ecc.size; + num_steps = end_step - start_step + 1; + + /* Data size aligned to ECC ecc.size*/ + datafrag_len = num_steps * chip->ecc.size; + eccfrag_len = num_steps * chip->ecc.bytes; + + data_col_addr = start_step * chip->ecc.size; + /* If we read not a page aligned data */ + if (data_col_addr != 0) + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1); + + p = bufpoi + data_col_addr; + chip->read_buf(mtd, p, datafrag_len); + + /* Calculate ECC */ + for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) + chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]); + + /* The performance is faster if to position offsets + according to ecc.pos. Let make sure here that + there are no gaps in ecc positions */ + for (i = 0; i < eccfrag_len - 1; i++) { + if (eccpos[i + start_step * chip->ecc.bytes] + 1 != + eccpos[i + start_step * chip->ecc.bytes + 1]) { + gaps = 1; + break; + } + } + if (gaps) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + } else { + /* send the command to read the particular ecc bytes */ + /* take care about buswidth alignment in read_buf */ + aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1); + aligned_len = eccfrag_len; + if (eccpos[start_step * chip->ecc.bytes] & (busw - 1)) + aligned_len++; + if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1)) + aligned_len++; + + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1); + chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); + } + + for (i = 0; i < eccfrag_len; i++) + chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]]; + + p = bufpoi + data_col_addr; + for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) { + int stat; + + stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]); + if (stat == -1) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + return 0; +} + +/** * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function * @mtd: mtd info structure * @chip: nand chip info structure @@ -994,6 +1075,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, /* Now read the page into the buffer */ if (unlikely(ops->mode == MTD_OOB_RAW)) ret = chip->ecc.read_page_raw(mtd, chip, bufpoi); + else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) + ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi); else ret = chip->ecc.read_page(mtd, chip, bufpoi); if (ret < 0) @@ -1001,7 +1084,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, /* Transfer not aligned data */ if (!aligned) { - chip->pagebuf = realpage; + if (!NAND_SUBPAGE_READ(chip) && !oob) + chip->pagebuf = realpage; memcpy(buf, chip->buffers->databuf + col, bytes); } @@ -2521,6 +2605,7 @@ int nand_scan_tail(struct mtd_info *mtd) chip->ecc.calculate = nand_calculate_ecc; chip->ecc.correct = nand_correct_data; 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_oob = nand_read_oob_std; chip->ecc.write_oob = nand_write_oob_std; diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 5e121ceaa598..0b1c48595f12 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -6,8 +6,6 @@ * * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) * - * $Id: nand_bbt.c,v 1.36 2005/11/07 11:14:30 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index 9003a135e050..918a806a8471 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -9,8 +9,6 @@ * * Copyright (C) 2006 Thomas Gleixner <tglx@linutronix.de> * - * $Id: nand_ecc.c,v 1.15 2005/11/07 11:14:30 gleixner Exp $ - * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 or (at your option) any diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index a3e3ab0185d5..69ee2c90eb0b 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -3,8 +3,6 @@ * * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) * - * $Id: nand_ids.c,v 1.16 2005/11/07 11:14:31 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index bb885d1fcab5..ecd70e2504f6 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -21,8 +21,6 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA - * - * $Id: nandsim.c,v 1.8 2005/03/19 15:33:56 dedekind Exp $ */ #include <linux/init.h> @@ -39,6 +37,7 @@ #include <linux/delay.h> #include <linux/list.h> #include <linux/random.h> +#include <asm/div64.h> /* Default simulator parameters values */ #if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ @@ -298,11 +297,11 @@ struct nandsim { /* NAND flash "geometry" */ struct nandsin_geometry { - uint32_t totsz; /* total flash size, bytes */ + uint64_t totsz; /* total flash size, bytes */ uint32_t secsz; /* flash sector (erase block) size, bytes */ uint pgsz; /* NAND flash page size, bytes */ uint oobsz; /* page OOB area size, bytes */ - uint32_t totszoob; /* total flash size including OOB, bytes */ + uint64_t totszoob; /* total flash size including OOB, bytes */ uint pgszoob; /* page size including OOB , bytes*/ uint secszoob; /* sector size including OOB, bytes */ uint pgnum; /* total number of pages */ @@ -459,6 +458,12 @@ static char *get_partition_name(int i) return kstrdup(buf, GFP_KERNEL); } +static u_int64_t divide(u_int64_t n, u_int32_t d) +{ + do_div(n, d); + return n; +} + /* * Initialize the nandsim structure. * @@ -469,8 +474,8 @@ static int init_nandsim(struct mtd_info *mtd) struct nand_chip *chip = (struct nand_chip *)mtd->priv; struct nandsim *ns = (struct nandsim *)(chip->priv); int i, ret = 0; - u_int32_t remains; - u_int32_t next_offset; + u_int64_t remains; + u_int64_t next_offset; if (NS_IS_INITIALIZED(ns)) { NS_ERR("init_nandsim: nandsim is already initialized\n"); @@ -487,8 +492,8 @@ static int init_nandsim(struct mtd_info *mtd) ns->geom.oobsz = mtd->oobsize; ns->geom.secsz = mtd->erasesize; ns->geom.pgszoob = ns->geom.pgsz + ns->geom.oobsz; - ns->geom.pgnum = ns->geom.totsz / ns->geom.pgsz; - ns->geom.totszoob = ns->geom.totsz + ns->geom.pgnum * ns->geom.oobsz; + ns->geom.pgnum = divide(ns->geom.totsz, ns->geom.pgsz); + ns->geom.totszoob = ns->geom.totsz + (uint64_t)ns->geom.pgnum * ns->geom.oobsz; ns->geom.secshift = ffs(ns->geom.secsz) - 1; ns->geom.pgshift = chip->page_shift; ns->geom.oobshift = ffs(ns->geom.oobsz) - 1; @@ -511,7 +516,7 @@ static int init_nandsim(struct mtd_info *mtd) } if (ns->options & OPT_SMALLPAGE) { - if (ns->geom.totsz < (32 << 20)) { + if (ns->geom.totsz <= (32 << 20)) { ns->geom.pgaddrbytes = 3; ns->geom.secaddrbytes = 2; } else { @@ -537,15 +542,16 @@ static int init_nandsim(struct mtd_info *mtd) remains = ns->geom.totsz; next_offset = 0; for (i = 0; i < parts_num; ++i) { - unsigned long part = parts[i]; - if (!part || part > remains / ns->geom.secsz) { + u_int64_t part_sz = (u_int64_t)parts[i] * ns->geom.secsz; + + if (!part_sz || part_sz > remains) { NS_ERR("bad partition size.\n"); ret = -EINVAL; goto error; } ns->partitions[i].name = get_partition_name(i); ns->partitions[i].offset = next_offset; - ns->partitions[i].size = part * ns->geom.secsz; + ns->partitions[i].size = part_sz; next_offset += ns->partitions[i].size; remains -= ns->partitions[i].size; } @@ -573,7 +579,7 @@ static int init_nandsim(struct mtd_info *mtd) if (ns->busw == 16) NS_WARN("16-bit flashes support wasn't tested\n"); - printk("flash size: %u MiB\n", ns->geom.totsz >> 20); + printk("flash size: %llu MiB\n", ns->geom.totsz >> 20); printk("page size: %u bytes\n", ns->geom.pgsz); printk("OOB area size: %u bytes\n", ns->geom.oobsz); printk("sector size: %u KiB\n", ns->geom.secsz >> 10); @@ -583,7 +589,7 @@ static int init_nandsim(struct mtd_info *mtd) printk("bits in sector size: %u\n", ns->geom.secshift); printk("bits in page size: %u\n", ns->geom.pgshift); printk("bits in OOB size: %u\n", ns->geom.oobshift); - printk("flash size with OOB: %u KiB\n", ns->geom.totszoob >> 10); + printk("flash size with OOB: %llu KiB\n", ns->geom.totszoob >> 10); printk("page address bytes: %u\n", ns->geom.pgaddrbytes); printk("sector address bytes: %u\n", ns->geom.secaddrbytes); printk("options: %#x\n", ns->options); @@ -825,7 +831,7 @@ static int setup_wear_reporting(struct mtd_info *mtd) if (!rptwear) return 0; - wear_eb_count = mtd->size / mtd->erasesize; + wear_eb_count = divide(mtd->size, mtd->erasesize); mem = wear_eb_count * sizeof(unsigned long); if (mem / sizeof(unsigned long) != wear_eb_count) { NS_ERR("Too many erase blocks for wear reporting\n"); @@ -2013,7 +2019,7 @@ static int __init ns_init_module(void) } if (overridesize) { - u_int32_t new_size = nsmtd->erasesize << overridesize; + u_int64_t new_size = (u_int64_t)nsmtd->erasesize << overridesize; if (new_size >> overridesize != nsmtd->erasesize) { NS_ERR("overridesize is too big\n"); goto err_exit; @@ -2021,7 +2027,8 @@ static int __init ns_init_module(void) /* N.B. This relies on nand_scan not doing anything with the size before we change it */ nsmtd->size = new_size; chip->chipsize = new_size; - chip->chip_shift = ffs(new_size) - 1; + chip->chip_shift = ffs(nsmtd->erasesize) + overridesize - 1; + chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; } if ((retval = setup_wear_reporting(nsmtd)) != 0) diff --git a/drivers/mtd/nand/ppchameleonevb.c b/drivers/mtd/nand/ppchameleonevb.c index 082073acf20f..cc8658431851 100644 --- a/drivers/mtd/nand/ppchameleonevb.c +++ b/drivers/mtd/nand/ppchameleonevb.c @@ -6,8 +6,6 @@ * Derived from drivers/mtd/nand/edb7312.c * * - * $Id: ppchameleonevb.c,v 1.7 2005/11/07 11:14:31 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c index 26f88215bc47..a033c4cd8e16 100644 --- a/drivers/mtd/nand/rtc_from4.c +++ b/drivers/mtd/nand/rtc_from4.c @@ -6,8 +6,6 @@ * Derived from drivers/mtd/nand/spia.c * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * - * $Id: rtc_from4.c,v 1.10 2005/11/07 11:14:31 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index b34a460ab679..556139ed1fdf 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -1,26 +1,10 @@ /* linux/drivers/mtd/nand/s3c2410.c * - * Copyright (c) 2004,2005 Simtec Electronics - * http://www.simtec.co.uk/products/SWLINUX/ + * Copyright © 2004-2008 Simtec Electronics + * http://armlinux.simtec.co.uk/ * Ben Dooks <ben@simtec.co.uk> * - * Samsung S3C2410/S3C240 NAND driver - * - * Changelog: - * 21-Sep-2004 BJD Initial version - * 23-Sep-2004 BJD Multiple device support - * 28-Sep-2004 BJD Fixed ECC placement for Hardware mode - * 12-Oct-2004 BJD Fixed errors in use of platform data - * 18-Feb-2005 BJD Fix sparse errors - * 14-Mar-2005 BJD Applied tglx's code reduction patch - * 02-May-2005 BJD Fixed s3c2440 support - * 02-May-2005 BJD Reduced hwcontrol decode - * 20-Jun-2005 BJD Updated s3c2440 support, fixed timing bug - * 08-Jul-2005 BJD Fix OOPS when no platform data supplied - * 20-Oct-2005 BJD Fix timing calculation bug - * 14-Jan-2006 BJD Allow clock to be stopped when idle - * - * $Id: s3c2410.c,v 1.23 2006/04/01 18:06:29 bjd Exp $ + * Samsung S3C2410/S3C2440/S3C2412 NAND driver * * 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 @@ -52,6 +36,7 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/clk.h> +#include <linux/cpufreq.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> @@ -120,8 +105,13 @@ struct s3c2410_nand_info { int sel_bit; int mtd_count; unsigned long save_sel; + unsigned long clk_rate; enum s3c_cpu_type cpu_type; + +#ifdef CONFIG_CPU_FREQ + struct notifier_block freq_transition; +#endif }; /* conversion functions */ @@ -179,17 +169,18 @@ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) /* controller setup */ -static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, - struct platform_device *pdev) +static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) { - struct s3c2410_platform_nand *plat = to_nand_plat(pdev); - unsigned long clkrate = clk_get_rate(info->clk); + struct s3c2410_platform_nand *plat = info->platform; int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4; int tacls, twrph0, twrph1; - unsigned long cfg = 0; + unsigned long clkrate = clk_get_rate(info->clk); + unsigned long set, cfg, mask; + unsigned long flags; /* calculate the timing information for the controller */ + info->clk_rate = clkrate; clkrate /= 1000; /* turn clock into kHz for ease of use */ if (plat != NULL) { @@ -211,28 +202,69 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n", tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate)); + switch (info->cpu_type) { + case TYPE_S3C2410: + mask = (S3C2410_NFCONF_TACLS(3) | + S3C2410_NFCONF_TWRPH0(7) | + S3C2410_NFCONF_TWRPH1(7)); + set = S3C2410_NFCONF_EN; + set |= S3C2410_NFCONF_TACLS(tacls - 1); + set |= S3C2410_NFCONF_TWRPH0(twrph0 - 1); + set |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); + break; + + case TYPE_S3C2440: + case TYPE_S3C2412: + mask = (S3C2410_NFCONF_TACLS(tacls_max - 1) | + S3C2410_NFCONF_TWRPH0(7) | + S3C2410_NFCONF_TWRPH1(7)); + + set = S3C2440_NFCONF_TACLS(tacls - 1); + set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); + set |= S3C2440_NFCONF_TWRPH1(twrph1 - 1); + break; + + default: + /* keep compiler happy */ + mask = 0; + set = 0; + BUG(); + } + + dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); + + local_irq_save(flags); + + cfg = readl(info->regs + S3C2410_NFCONF); + cfg &= ~mask; + cfg |= set; + writel(cfg, info->regs + S3C2410_NFCONF); + + local_irq_restore(flags); + + return 0; +} + +static int s3c2410_nand_inithw(struct s3c2410_nand_info *info) +{ + int ret; + + ret = s3c2410_nand_setrate(info); + if (ret < 0) + return ret; + switch (info->cpu_type) { case TYPE_S3C2410: - cfg = S3C2410_NFCONF_EN; - cfg |= S3C2410_NFCONF_TACLS(tacls - 1); - cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1); - cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1); + default: break; case TYPE_S3C2440: case TYPE_S3C2412: - cfg = S3C2440_NFCONF_TACLS(tacls - 1); - cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); - cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1); - /* enable the controller and de-assert nFCE */ writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT); } - dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); - - writel(cfg, info->regs + S3C2410_NFCONF); return 0; } @@ -513,6 +545,52 @@ static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int writesl(info->regs + S3C2440_NFDATA, buf, len / 4); } +/* cpufreq driver support */ + +#ifdef CONFIG_CPU_FREQ + +static int s3c2410_nand_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct s3c2410_nand_info *info; + unsigned long newclk; + + info = container_of(nb, struct s3c2410_nand_info, freq_transition); + newclk = clk_get_rate(info->clk); + + if ((val == CPUFREQ_POSTCHANGE && newclk < info->clk_rate) || + (val == CPUFREQ_PRECHANGE && newclk > info->clk_rate)) { + s3c2410_nand_setrate(info); + } + + return 0; +} + +static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info) +{ + info->freq_transition.notifier_call = s3c2410_nand_cpufreq_transition; + + return cpufreq_register_notifier(&info->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) +{ + cpufreq_unregister_notifier(&info->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +#else +static inline int s3c2410_nand_cpufreq_register(struct s3c2410_nand_info *info) +{ + return 0; +} + +static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *info) +{ +} +#endif + /* device management functions */ static int s3c2410_nand_remove(struct platform_device *pdev) @@ -524,9 +602,10 @@ static int s3c2410_nand_remove(struct platform_device *pdev) if (info == NULL) return 0; - /* first thing we need to do is release all our mtds - * and their partitions, then go through freeing the - * resources used + s3c2410_nand_cpufreq_deregister(info); + + /* Release all our mtds and their partitions, then go through + * freeing the resources used */ if (info->mtds != NULL) { @@ -691,7 +770,8 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, { struct nand_chip *chip = &nmtd->chip; - printk("%s: chip %p: %d\n", __func__, chip, chip->page_shift); + dev_dbg(info->device, "chip %p => page shift %d\n", + chip, chip->page_shift); if (hardware_ecc) { /* change the behaviour depending on wether we are using @@ -784,7 +864,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, /* initialise the hardware */ - err = s3c2410_nand_inithw(info, pdev); + err = s3c2410_nand_inithw(info); if (err != 0) goto exit_error; @@ -827,6 +907,12 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, sets++; } + err = s3c2410_nand_cpufreq_register(info); + if (err < 0) { + dev_err(&pdev->dev, "failed to init cpufreq support\n"); + goto exit_error; + } + if (allow_clk_stop(info)) { dev_info(&pdev->dev, "clock idle support enabled\n"); clk_disable(info->clk); @@ -874,7 +960,7 @@ static int s3c24xx_nand_resume(struct platform_device *dev) if (info) { clk_enable(info->clk); - s3c2410_nand_inithw(info, dev); + s3c2410_nand_inithw(info); /* Restore the state of the nFCE line. */ diff --git a/drivers/mtd/nand/sharpsl.c b/drivers/mtd/nand/sharpsl.c index 033f8800b1e6..6dba2fb66ae5 100644 --- a/drivers/mtd/nand/sharpsl.c +++ b/drivers/mtd/nand/sharpsl.c @@ -3,8 +3,6 @@ * * Copyright (C) 2004 Richard Purdie * - * $Id: sharpsl.c,v 1.7 2005/11/07 11:14:31 gleixner Exp $ - * * Based on Sharp's NAND driver sharp_sl.c * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/mtd/nand/spia.c b/drivers/mtd/nand/spia.c index 1f6d429b1583..0cc6d0acb8fe 100644 --- a/drivers/mtd/nand/spia.c +++ b/drivers/mtd/nand/spia.c @@ -8,8 +8,6 @@ * to controllines (due to change in nand.c) * page_cache added * - * $Id: spia.c,v 1.25 2005/11/07 11:14:31 gleixner Exp $ - * * 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. diff --git a/drivers/mtd/nand/toto.c b/drivers/mtd/nand/toto.c index f9e2d4a0ab8c..bbf492e6830d 100644 --- a/drivers/mtd/nand/toto.c +++ b/drivers/mtd/nand/toto.c @@ -14,8 +14,6 @@ * Overview: * This is a device driver for the NAND flash device found on the * TI fido board. It supports 32MiB and 64MiB cards - * - * $Id: toto.c,v 1.5 2005/11/07 11:14:31 gleixner Exp $ */ #include <linux/slab.h> diff --git a/drivers/mtd/nand/ts7250.c b/drivers/mtd/nand/ts7250.c index f40081069ab2..807a72752eeb 100644 --- a/drivers/mtd/nand/ts7250.c +++ b/drivers/mtd/nand/ts7250.c @@ -9,8 +9,6 @@ * Derived from drivers/mtd/nand/autcpu12.c * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * - * $Id: ts7250.c,v 1.4 2004/12/30 22:02:07 joff Exp $ - * * 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. |