aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd/nand
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--drivers/mtd/nand/Kconfig28
-rw-r--r--drivers/mtd/nand/Makefile3
-rw-r--r--drivers/mtd/nand/at91_nand.c594
-rw-r--r--drivers/mtd/nand/atmel_nand.c650
-rw-r--r--drivers/mtd/nand/atmel_nand_ecc.h36
-rw-r--r--drivers/mtd/nand/au1550nd.c4
-rw-r--r--drivers/mtd/nand/autcpu12.c2
-rw-r--r--drivers/mtd/nand/cafe_nand.c6
-rw-r--r--drivers/mtd/nand/cmx270_nand.c79
-rw-r--r--drivers/mtd/nand/diskonchip.c4
-rw-r--r--drivers/mtd/nand/edb7312.c2
-rw-r--r--drivers/mtd/nand/excite_nandflash.c2
-rw-r--r--drivers/mtd/nand/fsl_elbc_nand.c63
-rw-r--r--drivers/mtd/nand/h1910.c2
-rw-r--r--drivers/mtd/nand/nand_base.c87
-rw-r--r--drivers/mtd/nand/nand_bbt.c2
-rw-r--r--drivers/mtd/nand/nand_ecc.c2
-rw-r--r--drivers/mtd/nand/nand_ids.c2
-rw-r--r--drivers/mtd/nand/nandsim.c41
-rw-r--r--drivers/mtd/nand/orion_nand.c3
-rw-r--r--drivers/mtd/nand/ppchameleonevb.c2
-rw-r--r--drivers/mtd/nand/rtc_from4.c2
-rw-r--r--drivers/mtd/nand/s3c2410.c168
-rw-r--r--drivers/mtd/nand/sharpsl.c2
-rw-r--r--drivers/mtd/nand/spia.c2
-rw-r--r--drivers/mtd/nand/toto.c2
-rw-r--r--drivers/mtd/nand/ts7250.c2
27 files changed, 1045 insertions, 747 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/at91_nand.c
deleted file mode 100644
index 0adb287027a2..000000000000
--- a/drivers/mtd/nand/at91_nand.c
+++ /dev/null
@@ -1,594 +0,0 @@
-/*
- * drivers/mtd/nand/at91_nand.c
- *
- * Copyright (C) 2003 Rick Bronson
- *
- * Derived from drivers/mtd/nand/autcpu12.c
- * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
- *
- * Derived from drivers/mtd/spia.c
- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
- *
- *
- * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
- * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright (C) 2007
- *
- * Derived from Das U-Boot source code
- * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
- * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
-#include <linux/mtd/partitions.h>
-
-#include <asm/io.h>
-#include <asm/sizes.h>
-
-#include <asm/hardware.h>
-#include <asm/arch/board.h>
-#include <asm/arch/gpio.h>
-
-#ifdef CONFIG_MTD_NAND_AT91_ECC_HW
-#define hard_ecc 1
-#else
-#define hard_ecc 0
-#endif
-
-#ifdef CONFIG_MTD_NAND_AT91_ECC_NONE
-#define no_ecc 1
-#else
-#define no_ecc 0
-#endif
-
-/* Register access macros */
-#define ecc_readl(add, reg) \
- __raw_readl(add + AT91_ECC_##reg)
-#define ecc_writel(add, reg, value) \
- __raw_writel((value), add + AT91_ECC_##reg)
-
-#include <asm/arch/at91_ecc.h> /* AT91SAM9260/3 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 = {
- .eccbytes = 4,
- .eccpos = {60, 61, 62, 63},
- .oobfree = {
- {2, 58}
- },
-};
-
-/* oob layout for small page size
- * bad block info is on bytes 4 and 5
- * the bytes have to be consecutives to avoid
- * several NAND_CMD_RNDOUT during read
- */
-static struct nand_ecclayout at91_oobinfo_small = {
- .eccbytes = 4,
- .eccpos = {0, 1, 2, 3},
- .oobfree = {
- {6, 10}
- },
-};
-
-struct at91_nand_host {
- struct nand_chip nand_chip;
- struct mtd_info mtd;
- void __iomem *io_base;
- struct at91_nand_data *board;
- struct device *dev;
- void __iomem *ecc;
-};
-
-/*
- * Enable NAND.
- */
-static void at91_nand_enable(struct at91_nand_host *host)
-{
- if (host->board->enable_pin)
- at91_set_gpio_value(host->board->enable_pin, 0);
-}
-
-/*
- * Disable NAND.
- */
-static void at91_nand_disable(struct at91_nand_host *host)
-{
- if (host->board->enable_pin)
- at91_set_gpio_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)
-{
- struct nand_chip *nand_chip = mtd->priv;
- struct at91_nand_host *host = nand_chip->priv;
-
- if (ctrl & NAND_CTRL_CHANGE) {
- if (ctrl & NAND_NCE)
- at91_nand_enable(host);
- else
- at91_nand_disable(host);
- }
- if (cmd == NAND_CMD_NONE)
- return;
-
- if (ctrl & NAND_CLE)
- writeb(cmd, host->io_base + (1 << host->board->cle));
- else
- writeb(cmd, host->io_base + (1 << host->board->ale));
-}
-
-/*
- * Read the Device Ready pin.
- */
-static int at91_nand_device_ready(struct mtd_info *mtd)
-{
- struct nand_chip *nand_chip = mtd->priv;
- struct at91_nand_host *host = nand_chip->priv;
-
- return at91_get_gpio_value(host->board->rdy_pin);
-}
-
-/*
- * write oob for small pages
- */
-static int at91_nand_write_oob_512(struct mtd_info *mtd,
- struct nand_chip *chip, int page)
-{
- int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
- int eccsize = chip->ecc.size, length = mtd->oobsize;
- int len, pos, status = 0;
- const uint8_t *bufpoi = chip->oob_poi;
-
- pos = eccsize + chunk;
-
- chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page);
- len = min_t(int, length, chunk);
- chip->write_buf(mtd, bufpoi, len);
- bufpoi += len;
- length -= len;
- if (length > 0)
- chip->write_buf(mtd, bufpoi, length);
-
- chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
- status = chip->waitfunc(mtd, chip);
-
- return status & NAND_STATUS_FAIL ? -EIO : 0;
-
-}
-
-/*
- * read oob for small pages
- */
-static int at91_nand_read_oob_512(struct mtd_info *mtd,
- struct nand_chip *chip, int page, int sndcmd)
-{
- if (sndcmd) {
- chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
- sndcmd = 0;
- }
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
- return sndcmd;
-}
-
-/*
- * Calculate HW ECC
- *
- * function called after a write
- *
- * mtd: MTD block structure
- * dat: raw data (unused)
- * ecc_code: buffer for ECC
- */
-static int at91_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;
- uint32_t *eccpos = nand_chip->ecc.layout->eccpos;
- unsigned int ecc_value;
-
- /* get the first 2 ECC bytes */
- ecc_value = ecc_readl(host->ecc, PR);
-
- ecc_code[eccpos[0]] = ecc_value & 0xFF;
- ecc_code[eccpos[1]] = (ecc_value >> 8) & 0xFF;
-
- /* get the last 2 ECC bytes */
- ecc_value = ecc_readl(host->ecc, NPR) & AT91_ECC_NPARITY;
-
- ecc_code[eccpos[2]] = ecc_value & 0xFF;
- ecc_code[eccpos[3]] = (ecc_value >> 8) & 0xFF;
-
- return 0;
-}
-
-/*
- * HW ECC read page function
- *
- * mtd: mtd info structure
- * chip: nand chip info structure
- * buf: buffer to store read data
- */
-static int at91_nand_read_page(struct mtd_info *mtd,
- struct nand_chip *chip, uint8_t *buf)
-{
- int eccsize = chip->ecc.size;
- int eccbytes = chip->ecc.bytes;
- uint32_t *eccpos = chip->ecc.layout->eccpos;
- uint8_t *p = buf;
- uint8_t *oob = chip->oob_poi;
- uint8_t *ecc_pos;
- int stat;
-
- /* read the page */
- chip->read_buf(mtd, p, eccsize);
-
- /* move to ECC position if needed */
- if (eccpos[0] != 0) {
- /* This only works on large pages
- * because the ECC controller waits for
- * NAND_CMD_RNDOUTSTART after the
- * NAND_CMD_RNDOUT.
- * anyway, for small pages, the eccpos[0] == 0
- */
- chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
- mtd->writesize + eccpos[0], -1);
- }
-
- /* the ECC controller needs to read the ECC just after the data */
- ecc_pos = oob + eccpos[0];
- chip->read_buf(mtd, ecc_pos, eccbytes);
-
- /* check if there's an error */
- stat = chip->ecc.correct(mtd, p, oob, NULL);
-
- if (stat < 0)
- mtd->ecc_stats.failed++;
- else
- mtd->ecc_stats.corrected += stat;
-
- /* get back to oob start (end of page) */
- chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
-
- /* read the oob */
- chip->read_buf(mtd, oob, mtd->oobsize);
-
- return 0;
-}
-
-/*
- * HW ECC Correction
- *
- * function called after a read
- *
- * mtd: MTD block structure
- * dat: raw data read from the chip
- * read_ecc: ECC from the chip (unused)
- * isnull: unused
- *
- * Detect and correct a 1 bit error for a page
- */
-static int at91_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;
- unsigned int ecc_status;
- unsigned int ecc_word, ecc_bit;
-
- /* get the status from the Status Register */
- ecc_status = ecc_readl(host->ecc, SR);
-
- /* if there's no error */
- if (likely(!(ecc_status & AT91_ECC_RECERR)))
- return 0;
-
- /* get error bit offset (4 bits) */
- ecc_bit = ecc_readl(host->ecc, PR) & AT91_ECC_BITADDR;
- /* get word address (12 bits) */
- ecc_word = ecc_readl(host->ecc, PR) & AT91_ECC_WORDADDR;
- ecc_word >>= 4;
-
- /* if there are multiple errors */
- if (ecc_status & AT91_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))) {
- /* 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."
- " Unable to correct.\n");
- return -EIO;
- }
-
- /* if there's a single bit error : we can correct it */
- if (ecc_status & AT91_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."
- " Nothing to correct\n");
- return 0;
- }
-
- dev_dbg(host->dev, "at91_nand : one bit error on data."
- " (word offset in the page :"
- " 0x%x bit offset : 0x%x)\n",
- ecc_word, ecc_bit);
- /* correct the error */
- if (nand_chip->options & NAND_BUSWIDTH_16) {
- /* 16 bits words */
- ((unsigned short *) dat)[ecc_word] ^= (1 << ecc_bit);
- } else {
- /* 8 bits words */
- dat[ecc_word] ^= (1 << ecc_bit);
- }
- dev_dbg(host->dev, "at91_nand : error corrected\n");
- return 1;
-}
-
-/*
- * Enable HW ECC : unsused
- */
-static void at91_nand_hwctl(struct mtd_info *mtd, int mode) { ; }
-
-#ifdef CONFIG_MTD_PARTITIONS
-static const char *part_probes[] = { "cmdlinepart", NULL };
-#endif
-
-/*
- * Probe for the NAND device.
- */
-static int __init at91_nand_probe(struct platform_device *pdev)
-{
- struct at91_nand_host *host;
- struct mtd_info *mtd;
- struct nand_chip *nand_chip;
- struct resource *regs;
- struct resource *mem;
- 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 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");
- return -ENXIO;
- }
-
- 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;
- }
-
- mtd = &host->mtd;
- nand_chip = &host->nand_chip;
- host->board = pdev->dev.platform_data;
- host->dev = &pdev->dev;
-
- nand_chip->priv = host; /* link the private data structures */
- mtd->priv = nand_chip;
- mtd->owner = THIS_MODULE;
-
- /* 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;
-
- if (host->board->rdy_pin)
- nand_chip->dev_ready = at91_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 "
- "regs\nFalling back on software ECC\n");
- }
-
- nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */
- if (no_ecc)
- nand_chip->ecc.mode = NAND_ECC_NONE;
- 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");
- 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.bytes = 4;
- nand_chip->ecc.prepad = 0;
- nand_chip->ecc.postpad = 0;
- }
-
- nand_chip->chip_delay = 20; /* 20us command delay time */
-
- if (host->board->bus_width_16) /* 16-bit bus width */
- nand_chip->options |= NAND_BUSWIDTH_16;
-
- platform_set_drvdata(pdev, host);
- at91_nand_enable(host);
-
- if (host->board->det_pin) {
- if (at91_get_gpio_value(host->board->det_pin)) {
- printk ("No SmartMedia card inserted.\n");
- res = ENXIO;
- goto out;
- }
- }
-
- /* first scan to find the device and get the page size */
- if (nand_scan_ident(mtd, 1)) {
- res = -ENXIO;
- goto out;
- }
-
- if (nand_chip->ecc.mode == NAND_ECC_HW_SYNDROME) {
- /* ECC is calculated for the whole page (1 step) */
- nand_chip->ecc.size = mtd->writesize;
-
- /* 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);
- break;
- case 1024:
- nand_chip->ecc.layout = &at91_oobinfo_large;
- ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_1056);
- break;
- case 2048:
- nand_chip->ecc.layout = &at91_oobinfo_large;
- ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_2112);
- break;
- case 4096:
- nand_chip->ecc.layout = &at91_oobinfo_large;
- ecc_writel(host->ecc, MR, AT91_ECC_PAGESIZE_4224);
- break;
- default:
- /* page size not handled by HW ECC */
- /* switching back to soft ECC */
- nand_chip->ecc.mode = NAND_ECC_SOFT;
- nand_chip->ecc.calculate = NULL;
- nand_chip->ecc.correct = NULL;
- nand_chip->ecc.hwctl = NULL;
- nand_chip->ecc.read_page = NULL;
- nand_chip->ecc.postpad = 0;
- nand_chip->ecc.prepad = 0;
- nand_chip->ecc.bytes = 0;
- break;
- }
- }
-
- /* second phase scan */
- if (nand_scan_tail(mtd)) {
- res = -ENXIO;
- goto out;
- }
-
-#ifdef CONFIG_MTD_PARTITIONS
-#ifdef CONFIG_MTD_CMDLINE_PARTS
- mtd->name = "at91_nand";
- num_partitions = parse_mtd_partitions(mtd, part_probes,
- &partitions, 0);
-#endif
- if (num_partitions <= 0 && host->board->partition_info)
- partitions = host->board->partition_info(mtd->size,
- &num_partitions);
-
- if ((!partitions) || (num_partitions == 0)) {
- printk(KERN_ERR "at91_nand: No parititions defined, or unsupported device.\n");
- res = ENXIO;
- goto release;
- }
-
- res = add_mtd_partitions(mtd, partitions, num_partitions);
-#else
- res = add_mtd_device(mtd);
-#endif
-
- if (!res)
- return res;
-
-#ifdef CONFIG_MTD_PARTITIONS
-release:
-#endif
- nand_release(mtd);
-
-out:
- iounmap(host->ecc);
-
-err_ecc_ioremap:
- at91_nand_disable(host);
- platform_set_drvdata(pdev, NULL);
- iounmap(host->io_base);
- kfree(host);
- return res;
-}
-
-/*
- * Remove a NAND device.
- */
-static int __devexit at91_nand_remove(struct platform_device *pdev)
-{
- struct at91_nand_host *host = platform_get_drvdata(pdev);
- struct mtd_info *mtd = &host->mtd;
-
- nand_release(mtd);
-
- at91_nand_disable(host);
-
- 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,
- .driver = {
- .name = "at91_nand",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init at91_nand_init(void)
-{
- return platform_driver_register(&at91_nand_driver);
-}
-
-
-static void __exit at91_nand_exit(void)
-{
- platform_driver_unregister(&at91_nand_driver);
-}
-
-
-module_init(at91_nand_init);
-module_exit(at91_nand_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Rick Bronson");
-MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91RM9200 / AT91SAM9");
-MODULE_ALIAS("platform:at91_nand");
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
new file mode 100644
index 000000000000..99aec46e2145
--- /dev/null
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -0,0 +1,650 @@
+/*
+ * Copyright (C) 2003 Rick Bronson
+ *
+ * Derived from drivers/mtd/nand/autcpu12.c
+ * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ * Derived from drivers/mtd/spia.c
+ * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
+ *
+ *
+ * Add Hardware ECC support for AT91SAM9260 / AT91SAM9263
+ * Richard Genoud (richard.genoud@gmail.com), Adeneo Copyright (C) 2007
+ *
+ * Derived from Das U-Boot source code
+ * (u-boot-1.1.5/board/atmel/at91sam9263ek/nand.c)
+ * (C) Copyright 2006 ATMEL Rousset, Lacressonniere Nicolas
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+#include <linux/gpio.h>
+#include <linux/io.h>
+
+#include <asm/arch/board.h>
+#include <asm/arch/cpu.h>
+
+#ifdef CONFIG_MTD_NAND_ATMEL_ECC_HW
+#define hard_ecc 1
+#else
+#define hard_ecc 0
+#endif
+
+#ifdef CONFIG_MTD_NAND_ATMEL_ECC_NONE
+#define no_ecc 1
+#else
+#define no_ecc 0
+#endif
+
+/* Register access macros */
+#define ecc_readl(add, reg) \
+ __raw_readl(add + ATMEL_ECC_##reg)
+#define ecc_writel(add, reg, value) \
+ __raw_writel((value), add + ATMEL_ECC_##reg)
+
+#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 atmel_oobinfo_large = {
+ .eccbytes = 4,
+ .eccpos = {60, 61, 62, 63},
+ .oobfree = {
+ {2, 58}
+ },
+};
+
+/* oob layout for small page size
+ * bad block info is on bytes 4 and 5
+ * the bytes have to be consecutives to avoid
+ * several NAND_CMD_RNDOUT during read
+ */
+static struct nand_ecclayout atmel_oobinfo_small = {
+ .eccbytes = 4,
+ .eccpos = {0, 1, 2, 3},
+ .oobfree = {
+ {6, 10}
+ },
+};
+
+struct atmel_nand_host {
+ struct nand_chip nand_chip;
+ struct mtd_info mtd;
+ void __iomem *io_base;
+ struct atmel_nand_data *board;
+ struct device *dev;
+ void __iomem *ecc;
+};
+
+/*
+ * Enable NAND.
+ */
+static void atmel_nand_enable(struct atmel_nand_host *host)
+{
+ if (host->board->enable_pin)
+ gpio_set_value(host->board->enable_pin, 0);
+}
+
+/*
+ * Disable NAND.
+ */
+static void atmel_nand_disable(struct atmel_nand_host *host)
+{
+ if (host->board->enable_pin)
+ gpio_set_value(host->board->enable_pin, 1);
+}
+
+/*
+ * Hardware specific access to control-lines
+ */
+static void atmel_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct atmel_nand_host *host = nand_chip->priv;
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ if (ctrl & NAND_NCE)
+ atmel_nand_enable(host);
+ else
+ atmel_nand_disable(host);
+ }
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ if (ctrl & NAND_CLE)
+ writeb(cmd, host->io_base + (1 << host->board->cle));
+ else
+ writeb(cmd, host->io_base + (1 << host->board->ale));
+}
+
+/*
+ * Read the Device Ready pin.
+ */
+static int atmel_nand_device_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct atmel_nand_host *host = nand_chip->priv;
+
+ 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 atmel_nand_write_oob_512(struct mtd_info *mtd,
+ struct nand_chip *chip, int page)
+{
+ int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
+ int eccsize = chip->ecc.size, length = mtd->oobsize;
+ int len, pos, status = 0;
+ const uint8_t *bufpoi = chip->oob_poi;
+
+ pos = eccsize + chunk;
+
+ chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page);
+ len = min_t(int, length, chunk);
+ chip->write_buf(mtd, bufpoi, len);
+ bufpoi += len;
+ length -= len;
+ if (length > 0)
+ chip->write_buf(mtd, bufpoi, length);
+
+ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+ status = chip->waitfunc(mtd, chip);
+
+ return status & NAND_STATUS_FAIL ? -EIO : 0;
+
+}
+
+/*
+ * read oob for small pages
+ */
+static int atmel_nand_read_oob_512(struct mtd_info *mtd,
+ struct nand_chip *chip, int page, int sndcmd)
+{
+ if (sndcmd) {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+ sndcmd = 0;
+ }
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ return sndcmd;
+}
+
+/*
+ * Calculate HW ECC
+ *
+ * function called after a write
+ *
+ * mtd: MTD block structure
+ * dat: raw data (unused)
+ * ecc_code: buffer for ECC
+ */
+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 atmel_nand_host *host = nand_chip->priv;
+ uint32_t *eccpos = nand_chip->ecc.layout->eccpos;
+ unsigned int ecc_value;
+
+ /* get the first 2 ECC bytes */
+ ecc_value = ecc_readl(host->ecc, PR);
+
+ ecc_code[eccpos[0]] = ecc_value & 0xFF;
+ ecc_code[eccpos[1]] = (ecc_value >> 8) & 0xFF;
+
+ /* get the last 2 ECC bytes */
+ ecc_value = ecc_readl(host->ecc, NPR) & ATMEL_ECC_NPARITY;
+
+ ecc_code[eccpos[2]] = ecc_value & 0xFF;
+ ecc_code[eccpos[3]] = (ecc_value >> 8) & 0xFF;
+
+ return 0;
+}
+
+/*
+ * HW ECC read page function
+ *
+ * mtd: mtd info structure
+ * chip: nand chip info structure
+ * buf: buffer to store read data
+ */
+static int atmel_nand_read_page(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf)
+{
+ int eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint8_t *p = buf;
+ uint8_t *oob = chip->oob_poi;
+ 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);
+
+ /* move to ECC position if needed */
+ if (eccpos[0] != 0) {
+ /* This only works on large pages
+ * because the ECC controller waits for
+ * NAND_CMD_RNDOUTSTART after the
+ * NAND_CMD_RNDOUT.
+ * anyway, for small pages, the eccpos[0] == 0
+ */
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT,
+ mtd->writesize + eccpos[0], -1);
+ }
+
+ /* the ECC controller needs to read the ECC just after the data */
+ ecc_pos = oob + eccpos[0];
+ chip->read_buf(mtd, ecc_pos, eccbytes);
+
+ /* check if there's an error */
+ stat = chip->ecc.correct(mtd, p, oob, NULL);
+
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+
+ /* get back to oob start (end of page) */
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
+
+ /* read the oob */
+ chip->read_buf(mtd, oob, mtd->oobsize);
+
+ return 0;
+}
+
+/*
+ * HW ECC Correction
+ *
+ * function called after a read
+ *
+ * mtd: MTD block structure
+ * dat: raw data read from the chip
+ * read_ecc: ECC from the chip (unused)
+ * isnull: unused
+ *
+ * Detect and correct a 1 bit error for a page
+ */
+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 atmel_nand_host *host = nand_chip->priv;
+ unsigned int ecc_status;
+ unsigned int ecc_word, ecc_bit;
+
+ /* get the status from the Status Register */
+ ecc_status = ecc_readl(host->ecc, SR);
+
+ /* if there's no error */
+ if (likely(!(ecc_status & ATMEL_ECC_RECERR)))
+ return 0;
+
+ /* get error bit offset (4 bits) */
+ ecc_bit = ecc_readl(host->ecc, PR) & ATMEL_ECC_BITADDR;
+ /* get word address (12 bits) */
+ ecc_word = ecc_readl(host->ecc, PR) & ATMEL_ECC_WORDADDR;
+ ecc_word >>= 4;
+
+ /* if there are multiple errors */
+ if (ecc_status & ATMEL_ECC_MULERR) {
+ /* check if it is a freshly erased block
+ * (filled with 0xff) */
+ 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, "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 & ATMEL_ECC_ECCERR) {
+ /* there's nothing much to do here.
+ * the bit error is on the ECC itself.
+ */
+ dev_dbg(host->dev, "atmel_nand : one bit error on ECC code."
+ " Nothing to correct\n");
+ return 0;
+ }
+
+ 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);
+ /* correct the error */
+ if (nand_chip->options & NAND_BUSWIDTH_16) {
+ /* 16 bits words */
+ ((unsigned short *) dat)[ecc_word] ^= (1 << ecc_bit);
+ } else {
+ /* 8 bits words */
+ dat[ecc_word] ^= (1 << ecc_bit);
+ }
+ dev_dbg(host->dev, "atmel_nand : error corrected\n");
+ return 1;
+}
+
+/*
+ * Enable HW ECC : unused on most chips
+ */
+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 };
+#endif
+
+/*
+ * Probe for the NAND device.
+ */
+static int __init atmel_nand_probe(struct platform_device *pdev)
+{
+ struct atmel_nand_host *host;
+ struct mtd_info *mtd;
+ struct nand_chip *nand_chip;
+ struct resource *regs;
+ struct resource *mem;
+ int res;
+
+#ifdef CONFIG_MTD_PARTITIONS
+ struct mtd_partition *partitions = NULL;
+ int num_partitions = 0;
+#endif
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ 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 "atmel_nand: ioremap failed\n");
+ res = -EIO;
+ goto err_nand_ioremap;
+ }
+
+ mtd = &host->mtd;
+ nand_chip = &host->nand_chip;
+ host->board = pdev->dev.platform_data;
+ host->dev = &pdev->dev;
+
+ nand_chip->priv = host; /* link the private data structures */
+ mtd->priv = nand_chip;
+ mtd->owner = THIS_MODULE;
+
+ /* 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 = atmel_nand_cmd_ctrl;
+
+ if (host->board->rdy_pin)
+ nand_chip->dev_ready = atmel_nand_device_ready;
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!regs && hard_ecc) {
+ printk(KERN_ERR "atmel_nand: can't get I/O resource "
+ "regs\nFalling back on software ECC\n");
+ }
+
+ nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */
+ if (no_ecc)
+ nand_chip->ecc.mode = NAND_ECC_NONE;
+ if (hard_ecc && regs) {
+ host->ecc = ioremap(regs->start, regs->end - regs->start + 1);
+ if (host->ecc == NULL) {
+ 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 = atmel_nand_calculate;
+ nand_chip->ecc.correct = atmel_nand_correct;
+ nand_chip->ecc.hwctl = atmel_nand_hwctl;
+ nand_chip->ecc.read_page = atmel_nand_read_page;
+ nand_chip->ecc.bytes = 4;
+ nand_chip->ecc.prepad = 0;
+ nand_chip->ecc.postpad = 0;
+ }
+
+ nand_chip->chip_delay = 20; /* 20us command delay time */
+
+ 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);
+ atmel_nand_enable(host);
+
+ if (host->board->det_pin) {
+ if (gpio_get_value(host->board->det_pin)) {
+ printk("No SmartMedia card inserted.\n");
+ res = ENXIO;
+ goto err_no_card;
+ }
+ }
+
+ /* first scan to find the device and get the page size */
+ if (nand_scan_ident(mtd, 1)) {
+ res = -ENXIO;
+ goto err_scan_ident;
+ }
+
+ if (nand_chip->ecc.mode == NAND_ECC_HW_SYNDROME) {
+ /* ECC is calculated for the whole page (1 step) */
+ nand_chip->ecc.size = mtd->writesize;
+
+ /* set ECC page size and oob layout */
+ switch (mtd->writesize) {
+ case 512:
+ nand_chip->ecc.layout = &atmel_oobinfo_small;
+ nand_chip->ecc.read_oob = atmel_nand_read_oob_512;
+ nand_chip->ecc.write_oob = atmel_nand_write_oob_512;
+ ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_528);
+ break;
+ case 1024:
+ nand_chip->ecc.layout = &atmel_oobinfo_large;
+ ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_1056);
+ break;
+ case 2048:
+ nand_chip->ecc.layout = &atmel_oobinfo_large;
+ ecc_writel(host->ecc, MR, ATMEL_ECC_PAGESIZE_2112);
+ break;
+ case 4096:
+ 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 */
+ /* switching back to soft ECC */
+ nand_chip->ecc.mode = NAND_ECC_SOFT;
+ nand_chip->ecc.calculate = NULL;
+ nand_chip->ecc.correct = NULL;
+ nand_chip->ecc.hwctl = NULL;
+ nand_chip->ecc.read_page = NULL;
+ nand_chip->ecc.postpad = 0;
+ nand_chip->ecc.prepad = 0;
+ nand_chip->ecc.bytes = 0;
+ break;
+ }
+ }
+
+ /* second phase scan */
+ if (nand_scan_tail(mtd)) {
+ res = -ENXIO;
+ goto err_scan_tail;
+ }
+
+#ifdef CONFIG_MTD_PARTITIONS
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+ mtd->name = "atmel_nand";
+ num_partitions = parse_mtd_partitions(mtd, part_probes,
+ &partitions, 0);
+#endif
+ if (num_partitions <= 0 && host->board->partition_info)
+ partitions = host->board->partition_info(mtd->size,
+ &num_partitions);
+
+ if ((!partitions) || (num_partitions == 0)) {
+ printk(KERN_ERR "atmel_nand: No parititions defined, or unsupported device.\n");
+ res = ENXIO;
+ goto err_no_partitions;
+ }
+
+ res = add_mtd_partitions(mtd, partitions, num_partitions);
+#else
+ res = add_mtd_device(mtd);
+#endif
+
+ if (!res)
+ return res;
+
+#ifdef CONFIG_MTD_PARTITIONS
+err_no_partitions:
+#endif
+ nand_release(mtd);
+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;
+}
+
+/*
+ * Remove a NAND device.
+ */
+static int __exit atmel_nand_remove(struct platform_device *pdev)
+{
+ struct atmel_nand_host *host = platform_get_drvdata(pdev);
+ struct mtd_info *mtd = &host->mtd;
+
+ nand_release(mtd);
+
+ atmel_nand_disable(host);
+
+ if (host->ecc)
+ iounmap(host->ecc);
+ iounmap(host->io_base);
+ kfree(host);
+
+ return 0;
+}
+
+static struct platform_driver atmel_nand_driver = {
+ .remove = __exit_p(atmel_nand_remove),
+ .driver = {
+ .name = "atmel_nand",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init atmel_nand_init(void)
+{
+ return platform_driver_probe(&atmel_nand_driver, atmel_nand_probe);
+}
+
+
+static void __exit atmel_nand_exit(void)
+{
+ platform_driver_unregister(&atmel_nand_driver);
+}
+
+
+module_init(atmel_nand_init);
+module_exit(atmel_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Rick Bronson");
+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/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c
index cb663ef245d5..fc8529bedfdf 100644
--- a/drivers/mtd/nand/cmx270_nand.c
+++ b/drivers/mtd/nand/cmx270_nand.c
@@ -20,9 +20,11 @@
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
+#include <linux/gpio.h>
#include <asm/io.h>
#include <asm/irq.h>
+#include <asm/mach-types.h>
#include <asm/arch/hardware.h>
#include <asm/arch/pxa-regs.h>
@@ -30,20 +32,6 @@
#define GPIO_NAND_CS (11)
#define GPIO_NAND_RB (89)
-/* This macro needed to ensure in-order operation of GPIO and local
- * bus. Without both asm command and dummy uncached read there're
- * states when NAND access is broken. I've looked for such macro(s) in
- * include/asm-arm but found nothing approptiate.
- * dmac_clean_range is close, but is makes cache invalidation
- * unnecessary here and it cannot be used in module
- */
-#define DRAIN_WB() \
- do { \
- unsigned char dummy; \
- asm volatile ("mcr p15, 0, r0, c7, c10, 4":::"r0"); \
- dummy=*((unsigned char*)UNCACHED_ADDR); \
- } while(0)
-
/* MTD structure for CM-X270 board */
static struct mtd_info *cmx270_nand_mtd;
@@ -103,14 +91,14 @@ static int cmx270_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
static inline void nand_cs_on(void)
{
- GPCR(GPIO_NAND_CS) = GPIO_bit(GPIO_NAND_CS);
+ gpio_set_value(GPIO_NAND_CS, 0);
}
static void nand_cs_off(void)
{
- DRAIN_WB();
+ dsb();
- GPSR(GPIO_NAND_CS) = GPIO_bit(GPIO_NAND_CS);
+ gpio_set_value(GPIO_NAND_CS, 1);
}
/*
@@ -122,7 +110,7 @@ static void cmx270_hwcontrol(struct mtd_info *mtd, int dat,
struct nand_chip* this = mtd->priv;
unsigned int nandaddr = (unsigned int)this->IO_ADDR_W;
- DRAIN_WB();
+ dsb();
if (ctrl & NAND_CTRL_CHANGE) {
if ( ctrl & NAND_ALE )
@@ -139,12 +127,12 @@ static void cmx270_hwcontrol(struct mtd_info *mtd, int dat,
nand_cs_off();
}
- DRAIN_WB();
+ dsb();
this->IO_ADDR_W = (void __iomem*)nandaddr;
if (dat != NAND_CMD_NONE)
writel((dat << 16), this->IO_ADDR_W);
- DRAIN_WB();
+ dsb();
}
/*
@@ -152,9 +140,9 @@ static void cmx270_hwcontrol(struct mtd_info *mtd, int dat,
*/
static int cmx270_device_ready(struct mtd_info *mtd)
{
- DRAIN_WB();
+ dsb();
- return (GPLR(GPIO_NAND_RB) & GPIO_bit(GPIO_NAND_RB));
+ return (gpio_get_value(GPIO_NAND_RB));
}
/*
@@ -168,20 +156,40 @@ static int cmx270_init(void)
int mtd_parts_nb = 0;
int ret;
+ if (!machine_is_armcore())
+ return -ENODEV;
+
+ ret = gpio_request(GPIO_NAND_CS, "NAND CS");
+ if (ret) {
+ pr_warning("CM-X270: failed to request NAND CS gpio\n");
+ return ret;
+ }
+
+ gpio_direction_output(GPIO_NAND_CS, 1);
+
+ ret = gpio_request(GPIO_NAND_RB, "NAND R/B");
+ if (ret) {
+ pr_warning("CM-X270: failed to request NAND R/B gpio\n");
+ goto err_gpio_request;
+ }
+
+ gpio_direction_input(GPIO_NAND_RB);
+
/* Allocate memory for MTD device structure and private data */
cmx270_nand_mtd = kzalloc(sizeof(struct mtd_info) +
sizeof(struct nand_chip),
GFP_KERNEL);
if (!cmx270_nand_mtd) {
- printk("Unable to allocate CM-X270 NAND MTD device structure.\n");
- return -ENOMEM;
+ pr_debug("Unable to allocate CM-X270 NAND MTD device structure.\n");
+ ret = -ENOMEM;
+ goto err_kzalloc;
}
cmx270_nand_io = ioremap(PXA_CS1_PHYS, 12);
if (!cmx270_nand_io) {
- printk("Unable to ioremap NAND device\n");
+ pr_debug("Unable to ioremap NAND device\n");
ret = -EINVAL;
- goto err1;
+ goto err_ioremap;
}
/* Get pointer to private data */
@@ -209,9 +217,9 @@ static int cmx270_init(void)
/* Scan to find existence of the device */
if (nand_scan (cmx270_nand_mtd, 1)) {
- printk(KERN_NOTICE "No NAND device\n");
+ pr_notice("No NAND device\n");
ret = -ENXIO;
- goto err2;
+ goto err_scan;
}
#ifdef CONFIG_MTD_CMDLINE_PARTS
@@ -229,18 +237,22 @@ static int cmx270_init(void)
}
/* Register the partitions */
- printk(KERN_NOTICE "Using %s partition definition\n", part_type);
+ pr_notice("Using %s partition definition\n", part_type);
ret = add_mtd_partitions(cmx270_nand_mtd, mtd_parts, mtd_parts_nb);
if (ret)
- goto err2;
+ goto err_scan;
/* Return happy */
return 0;
-err2:
+err_scan:
iounmap(cmx270_nand_io);
-err1:
+err_ioremap:
kfree(cmx270_nand_mtd);
+err_kzalloc:
+ gpio_free(GPIO_NAND_RB);
+err_gpio_request:
+ gpio_free(GPIO_NAND_CS);
return ret;
@@ -255,6 +267,9 @@ static void cmx270_cleanup(void)
/* Release resources, unregister device */
nand_release(cmx270_nand_mtd);
+ gpio_free(GPIO_NAND_RB);
+ gpio_free(GPIO_NAND_CS);
+
iounmap(cmx270_nand_io);
/* Free the MTD device structure */
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/orion_nand.c b/drivers/mtd/nand/orion_nand.c
index 59e05a1c50cf..ee2ac3948cd8 100644
--- a/drivers/mtd/nand/orion_nand.c
+++ b/drivers/mtd/nand/orion_nand.c
@@ -85,6 +85,9 @@ static int __init orion_nand_probe(struct platform_device *pdev)
nc->cmd_ctrl = orion_nand_cmd_ctrl;
nc->ecc.mode = NAND_ECC_SOFT;
+ if (board->chip_delay)
+ nc->chip_delay = board->chip_delay;
+
if (board->width == 16)
nc->options |= NAND_BUSWIDTH_16;
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.