summaryrefslogtreecommitdiffstats
path: root/drivers/mtd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/Kconfig30
-rw-r--r--drivers/mtd/chips/Kconfig12
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0001.c93
-rw-r--r--drivers/mtd/chips/fwh_lock.h3
-rw-r--r--drivers/mtd/devices/Kconfig26
-rw-r--r--drivers/mtd/devices/Makefile1
-rw-r--r--drivers/mtd/devices/at91_dataflash26.c485
-rw-r--r--drivers/mtd/devices/block2mtd.c67
-rw-r--r--drivers/mtd/maps/Kconfig47
-rw-r--r--drivers/mtd/maps/Makefile2
-rw-r--r--drivers/mtd/maps/alchemy-flash.c9
-rw-r--r--drivers/mtd/maps/plat-ram.c3
-rw-r--r--drivers/mtd/maps/pmcmsp-flash.c184
-rw-r--r--drivers/mtd/maps/pmcmsp-ramroot.c105
-rw-r--r--drivers/mtd/mtd_blkdevs.c43
-rw-r--r--drivers/mtd/mtdchar.c2
-rw-r--r--drivers/mtd/nand/Kconfig58
-rw-r--r--drivers/mtd/nand/cafe.c25
-rw-r--r--drivers/mtd/nand/nand_base.c6
-rw-r--r--drivers/mtd/nand/nand_ids.c4
-rw-r--r--drivers/mtd/nand/nandsim.c518
-rw-r--r--drivers/mtd/onenand/Kconfig15
-rw-r--r--drivers/mtd/onenand/onenand_base.c21
23 files changed, 1521 insertions, 238 deletions
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 26f75c29944..fedf9b7eae5 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -1,8 +1,6 @@
# $Id: Kconfig,v 1.11 2005/11/07 11:14:19 gleixner Exp $
-menu "Memory Technology Devices (MTD)"
-
-config MTD
+menuconfig MTD
tristate "Memory Technology Device (MTD) support"
help
Memory Technology Devices are flash, RAM and similar chips, often
@@ -13,9 +11,10 @@ config MTD
them. It will also allow you to select individual drivers for
particular hardware and users of MTD devices. If unsure, say N.
+if MTD
+
config MTD_DEBUG
bool "Debugging"
- depends on MTD
help
This turns on low-level debugging for the entire MTD sub-system.
Normally, you should say 'N'.
@@ -29,7 +28,6 @@ config MTD_DEBUG_VERBOSE
config MTD_CONCAT
tristate "MTD concatenating support"
- depends on MTD
help
Support for concatenating several MTD devices into a single
(virtual) one. This allows you to have -for example- a JFFS(2)
@@ -38,7 +36,6 @@ config MTD_CONCAT
config MTD_PARTITIONS
bool "MTD partitioning support"
- depends on MTD
help
If you have a device which needs to divide its flash chip(s) up
into multiple 'partitions', each of which appears to the user as
@@ -153,11 +150,9 @@ config MTD_AFS_PARTS
'armflash' map driver (CONFIG_MTD_ARMFLASH) does this, for example.
comment "User Modules And Translation Layers"
- depends on MTD
config MTD_CHAR
tristate "Direct char device access to MTD devices"
- depends on MTD
help
This provides a character device for each MTD device present in
the system, allowing the user to read and write directly to the
@@ -166,12 +161,12 @@ config MTD_CHAR
config MTD_BLKDEVS
tristate "Common interface to block layer for MTD 'translation layers'"
- depends on MTD && BLOCK
+ depends on BLOCK
default n
config MTD_BLOCK
tristate "Caching block device access to MTD devices"
- depends on MTD && BLOCK
+ depends on BLOCK
select MTD_BLKDEVS
---help---
Although most flash chips have an erase size too large to be useful
@@ -194,7 +189,7 @@ config MTD_BLOCK
config MTD_BLOCK_RO
tristate "Readonly block device access to MTD devices"
- depends on MTD_BLOCK!=y && MTD && BLOCK
+ depends on MTD_BLOCK!=y && BLOCK
select MTD_BLKDEVS
help
This allows you to mount read-only file systems (such as cramfs)
@@ -206,7 +201,7 @@ config MTD_BLOCK_RO
config FTL
tristate "FTL (Flash Translation Layer) support"
- depends on MTD && BLOCK
+ depends on BLOCK
select MTD_BLKDEVS
---help---
This provides support for the original Flash Translation Layer which
@@ -223,7 +218,7 @@ config FTL
config NFTL
tristate "NFTL (NAND Flash Translation Layer) support"
- depends on MTD && BLOCK
+ depends on BLOCK
select MTD_BLKDEVS
---help---
This provides support for the NAND Flash Translation Layer which is
@@ -247,7 +242,7 @@ config NFTL_RW
config INFTL
tristate "INFTL (Inverse NAND Flash Translation Layer) support"
- depends on MTD && BLOCK
+ depends on BLOCK
select MTD_BLKDEVS
---help---
This provides support for the Inverse NAND Flash Translation
@@ -265,7 +260,7 @@ config INFTL
config RFD_FTL
tristate "Resident Flash Disk (Flash Translation Layer) support"
- depends on MTD && BLOCK
+ depends on BLOCK
select MTD_BLKDEVS
---help---
This provides support for the flash translation layer known
@@ -276,7 +271,7 @@ config RFD_FTL
config SSFDC
tristate "NAND SSFDC (SmartMedia) read only translation layer"
- depends on MTD && BLOCK
+ depends on BLOCK
select MTD_BLKDEVS
help
This enables read only access to SmartMedia formatted NAND
@@ -292,5 +287,4 @@ source "drivers/mtd/nand/Kconfig"
source "drivers/mtd/onenand/Kconfig"
-endmenu
-
+endif # MTD
diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig
index 72e6d73beb4..d28e0fc85e1 100644
--- a/drivers/mtd/chips/Kconfig
+++ b/drivers/mtd/chips/Kconfig
@@ -6,7 +6,6 @@ menu "RAM/ROM/Flash chip drivers"
config MTD_CFI
tristate "Detect flash chips by Common Flash Interface (CFI) probe"
- depends on MTD
select MTD_GEN_PROBE
help
The Common Flash Interface specification was developed by Intel,
@@ -18,7 +17,6 @@ config MTD_CFI
config MTD_JEDECPROBE
tristate "Detect non-CFI AMD/JEDEC-compatible flash chips"
- depends on MTD
select MTD_GEN_PROBE
help
This option enables JEDEC-style probing of flash chips which are not
@@ -213,21 +211,18 @@ config MTD_CFI_UTIL
config MTD_RAM
tristate "Support for RAM chips in bus mapping"
- depends on MTD
help
This option enables basic support for RAM chips accessed through
a bus mapping driver.
config MTD_ROM
tristate "Support for ROM chips in bus mapping"
- depends on MTD
help
This option enables basic support for ROM chips accessed through
a bus mapping driver.
config MTD_ABSENT
tristate "Support for absent chips in bus mapping"
- depends on MTD
help
This option enables support for a dummy probing driver used to
allocated placeholder MTD devices on systems that have socketed
@@ -237,7 +232,6 @@ config MTD_ABSENT
with this driver will return -ENODEV upon access.
config MTD_OBSOLETE_CHIPS
- depends on MTD
bool "Older (theoretically obsoleted now) drivers for non-CFI chips"
help
This option does not enable any code directly, but will allow you to
@@ -250,7 +244,7 @@ config MTD_OBSOLETE_CHIPS
config MTD_AMDSTD
tristate "AMD compatible flash chip support (non-CFI)"
- depends on MTD && MTD_OBSOLETE_CHIPS && BROKEN
+ depends on MTD_OBSOLETE_CHIPS && BROKEN
help
This option enables support for flash chips using AMD-compatible
commands, including some which are not CFI-compatible and hence
@@ -260,7 +254,7 @@ config MTD_AMDSTD
config MTD_SHARP
tristate "pre-CFI Sharp chip support"
- depends on MTD && MTD_OBSOLETE_CHIPS
+ depends on MTD_OBSOLETE_CHIPS
help
This option enables support for flash chips using Sharp-compatible
commands, including some which are not CFI-compatible and hence
@@ -268,7 +262,7 @@ config MTD_SHARP
config MTD_JEDEC
tristate "JEDEC device support"
- depends on MTD && MTD_OBSOLETE_CHIPS && BROKEN
+ depends on MTD_OBSOLETE_CHIPS && BROKEN
help
Enable older JEDEC flash interface devices for self
programming flash. It is commonly used in older AMD chips. It is
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c
index f334959a335..2f19fa78d24 100644
--- a/drivers/mtd/chips/cfi_cmdset_0001.c
+++ b/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -15,6 +15,8 @@
* - optimized write buffer method
* 02/05/2002 Christopher Hoover <ch@hpl.hp.com>/<ch@murgatroid.com>
* - reworked lock/unlock/erase support for var size flash
+ * 21/03/2007 Rodolfo Giometti <giometti@linux.it>
+ * - auto unlock sectors on resume for auto locking flash on power up
*/
#include <linux/module.h>
@@ -30,6 +32,7 @@
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/reboot.h>
+#include <linux/bitmap.h>
#include <linux/mtd/xip.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
@@ -220,6 +223,15 @@ static void fixup_use_write_buffers(struct mtd_info *mtd, void *param)
}
}
+/*
+ * Some chips power-up with all sectors locked by default.
+ */
+static void fixup_use_powerup_lock(struct mtd_info *mtd, void *param)
+{
+ printk(KERN_INFO "Using auto-unlock on power-up/resume\n" );
+ mtd->flags |= MTD_STUPID_LOCK;
+}
+
static struct cfi_fixup cfi_fixup_table[] = {
#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL },
@@ -232,6 +244,7 @@ static struct cfi_fixup cfi_fixup_table[] = {
#endif
{ CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, NULL },
{ CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb, NULL },
+ { MANUFACTURER_INTEL, 0x891c, fixup_use_powerup_lock, NULL, },
{ 0, 0, NULL, NULL }
};
@@ -460,6 +473,7 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset;
mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize;
mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum;
+ mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].lockmap = kmalloc(ernum / 8 + 1, GFP_KERNEL);
}
offset += (ersize * ernum);
}
@@ -1825,8 +1839,7 @@ static void cfi_intelext_sync (struct mtd_info *mtd)
}
}
-#ifdef DEBUG_LOCK_BITS
-static int __xipram do_printlockstatus_oneblock(struct map_info *map,
+static int __xipram do_getlockstatus_oneblock(struct map_info *map,
struct flchip *chip,
unsigned long adr,
int len, void *thunk)
@@ -1840,8 +1853,17 @@ static int __xipram do_printlockstatus_oneblock(struct map_info *map,
chip->state = FL_JEDEC_QUERY;
status = cfi_read_query(map, adr+(2*ofs_factor));
xip_enable(map, chip, 0);
+ return status;
+}
+
+#ifdef DEBUG_LOCK_BITS
+static int __xipram do_printlockstatus_oneblock(struct map_info *map,
+ struct flchip *chip,
+ unsigned long adr,
+ int len, void *thunk)
+{
printk(KERN_DEBUG "block status register for 0x%08lx is %x\n",
- adr, status);
+ adr, do_getlockstatus_oneblock(map, chip, adr, len, thunk));
return 0;
}
#endif
@@ -2216,14 +2238,45 @@ static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd,
#endif
+static void cfi_intelext_save_locks(struct mtd_info *mtd)
+{
+ struct mtd_erase_region_info *region;
+ int block, status, i;
+ unsigned long adr;
+ size_t len;
+
+ for (i = 0; i < mtd->numeraseregions; i++) {
+ region = &mtd->eraseregions[i];
+ if (!region->lockmap)
+ continue;
+
+ for (block = 0; block < region->numblocks; block++){
+ len = region->erasesize;
+ adr = region->offset + block * len;
+
+ status = cfi_varsize_frob(mtd,
+ do_getlockstatus_oneblock, adr, len, 0);
+ if (status)
+ set_bit(block, region->lockmap);
+ else
+ clear_bit(block, region->lockmap);
+ }
+ }
+}
+
static int cfi_intelext_suspend(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
+ struct cfi_pri_intelext *extp = cfi->cmdset_priv;
int i;
struct flchip *chip;
int ret = 0;
+ if ((mtd->flags & MTD_STUPID_LOCK)
+ && extp && (extp->FeatureSupport & (1 << 5)))
+ cfi_intelext_save_locks(mtd);
+
for (i=0; !ret && i<cfi->numchips; i++) {
chip = &cfi->chips[i];
@@ -2285,10 +2338,33 @@ static int cfi_intelext_suspend(struct mtd_info *mtd)
return ret;
}
+static void cfi_intelext_restore_locks(struct mtd_info *mtd)
+{
+ struct mtd_erase_region_info *region;
+ int block, i;
+ unsigned long adr;
+ size_t len;
+
+ for (i = 0; i < mtd->numeraseregions; i++) {
+ region = &mtd->eraseregions[i];
+ if (!region->lockmap)
+ continue;
+
+ for (block = 0; block < region->numblocks; block++) {
+ len = region->erasesize;
+ adr = region->offset + block * len;
+
+ if (!test_bit(block, region->lockmap))
+ cfi_intelext_unlock(mtd, adr, len);
+ }
+ }
+}
+
static void cfi_intelext_resume(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
+ struct cfi_pri_intelext *extp = cfi->cmdset_priv;
int i;
struct flchip *chip;
@@ -2307,6 +2383,10 @@ static void cfi_intelext_resume(struct mtd_info *mtd)
spin_unlock(chip->mutex);
}
+
+ if ((mtd->flags & MTD_STUPID_LOCK)
+ && extp && (extp->FeatureSupport & (1 << 5)))
+ cfi_intelext_restore_locks(mtd);
}
static int cfi_intelext_reset(struct mtd_info *mtd)
@@ -2347,12 +2427,19 @@ static void cfi_intelext_destroy(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
+ struct mtd_erase_region_info *region;
+ int i;
cfi_intelext_reset(mtd);
unregister_reboot_notifier(&mtd->reboot_notifier);
kfree(cfi->cmdset_priv);
kfree(cfi->cfiq);
kfree(cfi->chips[0].priv);
kfree(cfi);
+ for (i = 0; i < mtd->numeraseregions; i++) {
+ region = &mtd->eraseregions[i];
+ if (region->lockmap)
+ kfree(region->lockmap);
+ }
kfree(mtd->eraseregions);
}
diff --git a/drivers/mtd/chips/fwh_lock.h b/drivers/mtd/chips/fwh_lock.h
index 77303ce5dcf..ab44f2b996f 100644
--- a/drivers/mtd/chips/fwh_lock.h
+++ b/drivers/mtd/chips/fwh_lock.h
@@ -65,11 +65,12 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip,
return ret;
}
+ chip->oldstate = chip->state;
chip->state = xxlt->state;
map_write(map, CMD(xxlt->val), adr);
/* Done and happy. */
- chip->state = FL_READY;
+ chip->state = chip->oldstate;
put_chip(map, chip, adr);
spin_unlock(chip->mutex);
return 0;
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 440f6851da6..690c94236d7 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -6,7 +6,7 @@ menu "Self-contained MTD device drivers"
config MTD_PMC551
tristate "Ramix PMC551 PCI Mezzanine RAM card support"
- depends on MTD && PCI
+ depends on PCI
---help---
This provides a MTD device driver for the Ramix PMC551 RAM PCI card
from Ramix Inc. <http://www.ramix.com/products/memory/pmc551.html>.
@@ -40,7 +40,7 @@ config MTD_PMC551_DEBUG
config MTD_MS02NV
tristate "DEC MS02-NV NVRAM module support"
- depends on MTD && MACH_DECSTATION
+ depends on MACH_DECSTATION
help
This is an MTD driver for the DEC's MS02-NV (54-20948-01) battery
backed-up NVRAM module. The module was originally meant as an NFS
@@ -54,15 +54,23 @@ config MTD_MS02NV
config MTD_DATAFLASH
tristate "Support for AT45xxx DataFlash"
- depends on MTD && SPI_MASTER && EXPERIMENTAL
+ depends on SPI_MASTER && EXPERIMENTAL
help
This enables access to AT45xxx DataFlash chips, using SPI.
Sometimes DataFlash chips are packaged inside MMC-format
cards; at this writing, the MMC stack won't handle those.
+config MTD_DATAFLASH26
+ tristate "AT91RM9200 DataFlash AT26xxx"
+ depends on MTD && ARCH_AT91RM9200 && AT91_SPI
+ help
+ This enables access to the DataFlash chip (AT26xxx) on an
+ AT91RM9200-based board.
+ If you have such a board and such a DataFlash, say 'Y'.
+
config MTD_M25P80
tristate "Support for M25 SPI Flash"
- depends on MTD && SPI_MASTER && EXPERIMENTAL
+ depends on SPI_MASTER && EXPERIMENTAL
help
This enables access to ST M25P80 and similar SPI flash chips,
used for program and data storage. Set up your spi devices
@@ -70,7 +78,6 @@ config MTD_M25P80
config MTD_SLRAM
tristate "Uncached system RAM"
- depends on MTD
help
If your CPU cannot cache all of the physical memory in your machine,
you can still use it for storage or swap by using this driver to
@@ -78,7 +85,6 @@ config MTD_SLRAM
config MTD_PHRAM
tristate "Physical system RAM"
- depends on MTD
help
This is a re-implementation of the slram driver above.
@@ -88,7 +94,7 @@ config MTD_PHRAM
config MTD_LART
tristate "28F160xx flash driver for LART"
- depends on SA1100_LART && MTD
+ depends on SA1100_LART
help
This enables the flash driver for LART. Please note that you do
not need any mapping/chip driver for LART. This one does it all
@@ -96,7 +102,6 @@ config MTD_LART
config MTD_MTDRAM
tristate "Test driver using RAM"
- depends on MTD
help
This enables a test MTD device driver which uses vmalloc() to
provide storage. You probably want to say 'N' unless you're
@@ -136,7 +141,7 @@ config MTDRAM_ABS_POS
config MTD_BLOCK2MTD
tristate "MTD using block device"
- depends on MTD && BLOCK
+ depends on BLOCK
help
This driver allows a block device to appear as an MTD. It would
generally be used in the following cases:
@@ -150,7 +155,6 @@ comment "Disk-On-Chip Device Drivers"
config MTD_DOC2000
tristate "M-Systems Disk-On-Chip 2000 and Millennium (DEPRECATED)"
- depends on MTD
select MTD_DOCPROBE
select MTD_NAND_IDS
---help---
@@ -173,7 +177,6 @@ config MTD_DOC2000
config MTD_DOC2001
tristate "M-Systems Disk-On-Chip Millennium-only alternative driver (DEPRECATED)"
- depends on MTD
select MTD_DOCPROBE
select MTD_NAND_IDS
---help---
@@ -195,7 +198,6 @@ config MTD_DOC2001
config MTD_DOC2001PLUS
tristate "M-Systems Disk-On-Chip Millennium Plus"
- depends on MTD
select MTD_DOCPROBE
select MTD_NAND_IDS
---help---
diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile
index 0f788d5c4bf..8ab568b3f53 100644
--- a/drivers/mtd/devices/Makefile
+++ b/drivers/mtd/devices/Makefile
@@ -16,4 +16,5 @@ obj-$(CONFIG_MTD_MTDRAM) += mtdram.o
obj-$(CONFIG_MTD_LART) += lart.o
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
+obj-$(CONFIG_MTD_DATAFLASH26) += at91_dataflash26.o
obj-$(CONFIG_MTD_M25P80) += m25p80.o
diff --git a/drivers/mtd/devices/at91_dataflash26.c b/drivers/mtd/devices/at91_dataflash26.c
new file mode 100644
index 00000000000..64ce37f986f
--- /dev/null
+++ b/drivers/mtd/devices/at91_dataflash26.c
@@ -0,0 +1,485 @@
+/*
+ * Atmel DataFlash driver for Atmel AT91RM9200 (Thunder)
+ * This is a largely modified version of at91_dataflash.c that
+ * supports AT26xxx dataflash chips. The original driver supports
+ * AT45xxx chips.
+ *
+ * Note: This driver was only tested with an AT26F004. It should be
+ * easy to make it work with other AT26xxx dataflash devices, though.
+ *
+ * Copyright (C) 2007 Hans J. Koch <hjk@linutronix.de>
+ * original Copyright (C) SAN People (Pty) Ltd
+ *
+ * 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/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+
+#include <asm/arch/at91_spi.h>
+
+#define DATAFLASH_MAX_DEVICES 4 /* max number of dataflash devices */
+
+#define MANUFACTURER_ID_ATMEL 0x1F
+
+/* command codes */
+
+#define AT26_OP_READ_STATUS 0x05
+#define AT26_OP_READ_DEV_ID 0x9F
+#define AT26_OP_ERASE_PAGE_4K 0x20
+#define AT26_OP_READ_ARRAY_FAST 0x0B
+#define AT26_OP_SEQUENTIAL_WRITE 0xAF
+#define AT26_OP_WRITE_ENABLE 0x06
+#define AT26_OP_WRITE_DISABLE 0x04
+#define AT26_OP_SECTOR_PROTECT 0x36
+#define AT26_OP_SECTOR_UNPROTECT 0x39
+
+/* status register bits */
+
+#define AT26_STATUS_BUSY 0x01
+#define AT26_STATUS_WRITE_ENABLE 0x02
+
+struct dataflash_local
+{
+ int spi; /* SPI chip-select number */
+ unsigned int page_size; /* number of bytes per page */
+};
+
+
+/* Detected DataFlash devices */
+static struct mtd_info* mtd_devices[DATAFLASH_MAX_DEVICES];
+static int nr_devices = 0;
+
+/* Allocate a single SPI transfer descriptor. We're assuming that if multiple
+ SPI transfers occur at the same time, spi_access_bus() will serialize them.
+ If this is not valid, then either (i) each dataflash 'priv' structure
+ needs it's own transfer descriptor, (ii) we lock this one, or (iii) use
+ another mechanism. */
+static struct spi_transfer_list* spi_transfer_desc;
+
+/*
+ * Perform a SPI transfer to access the DataFlash device.
+ */
+static int do_spi_transfer(int nr, char* tx, int tx_len, char* rx, int rx_len,
+ char* txnext, int txnext_len, char* rxnext, int rxnext_len)
+{
+ struct spi_transfer_list* list = spi_transfer_desc;
+
+ list->tx[0] = tx; list->txlen[0] = tx_len;
+ list->rx[0] = rx; list->rxlen[0] = rx_len;
+
+ list->tx[1] = txnext; list->txlen[1] = txnext_len;
+ list->rx[1] = rxnext; list->rxlen[1] = rxnext_len;
+
+ list->nr_transfers = nr;
+ /* Note: spi_transfer() always returns 0, there are no error checks */
+ return spi_transfer(list);
+}
+
+/*
+ * Return the status of the DataFlash device.
+ */
+static unsigned char at91_dataflash26_status(void)
+{
+ unsigned char command[2];
+
+ command[0] = AT26_OP_READ_STATUS;
+ command[1] = 0;
+
+ do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0);
+
+ return command[1];
+}
+
+/*
+ * Poll the DataFlash device until it is READY.
+ */
+static unsigned char at91_dataflash26_waitready(void)
+{
+ unsigned char status;
+
+ while (1) {
+ status = at91_dataflash26_status();
+ if (!(status & AT26_STATUS_BUSY))
+ return status;
+ }
+}
+
+/*
+ * Enable/disable write access
+ */
+ static void at91_dataflash26_write_enable(int enable)
+{
+ unsigned char cmd[2];
+
+ DEBUG(MTD_DEBUG_LEVEL3, "write_enable: enable=%i\n", enable);
+
+ if (enable)
+ cmd[0] = AT26_OP_WRITE_ENABLE;
+ else
+ cmd[0] = AT26_OP_WRITE_DISABLE;
+ cmd[1] = 0;
+
+ do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0);
+}
+
+/*
+ * Protect/unprotect sector
+ */
+ static void at91_dataflash26_sector_protect(loff_t addr, int protect)
+{
+ unsigned char cmd[4];
+
+ DEBUG(MTD_DEBUG_LEVEL3, "sector_protect: addr=0x%06x prot=%d\n",
+ addr, protect);
+
+ if (protect)
+ cmd[0] = AT26_OP_SECTOR_PROTECT;
+ else
+ cmd[0] = AT26_OP_SECTOR_UNPROTECT;
+ cmd[1] = (addr & 0x00FF0000) >> 16;
+ cmd[2] = (addr & 0x0000FF00) >> 8;
+ cmd[3] = (addr & 0x000000FF);
+
+ do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0);
+}
+
+/*
+ * Erase blocks of flash.
+ */
+static int at91_dataflash26_erase(struct mtd_info *mtd,
+ struct erase_info *instr)
+{
+ struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
+ unsigned char cmd[4];
+
+ DEBUG(MTD_DEBUG_LEVEL1, "dataflash_erase: addr=0x%06x len=%i\n",
+ instr->addr, instr->len);
+
+ /* Sanity checks */
+ if (priv->page_size != 4096)
+ return -EINVAL; /* Can't handle other sizes at the moment */
+
+ if ( ((instr->len % mtd->erasesize) != 0)
+ || ((instr->len % priv->page_size) != 0)
+ || ((instr->addr % priv->page_size) != 0)
+ || ((instr->addr + instr->len) > mtd->size))
+ return -EINVAL;
+
+ spi_access_bus(priv->spi);
+
+ while (instr->len > 0) {
+ at91_dataflash26_write_enable(1);
+ at91_dataflash26_sector_protect(instr->addr, 0);
+ at91_dataflash26_write_enable(1);
+ cmd[0] = AT26_OP_ERASE_PAGE_4K;
+ cmd[1] = (instr->addr & 0x00FF0000) >> 16;
+ cmd[2] = (instr->addr & 0x0000FF00) >> 8;
+ cmd[3] = (instr->addr & 0x000000FF);
+
+ DEBUG(MTD_DEBUG_LEVEL3, "ERASE: (0x%02x) 0x%02x 0x%02x"
+ "0x%02x\n",
+ cmd[0], cmd[1], cmd[2], cmd[3]);
+
+ do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0);
+ at91_dataflash26_waitready();
+
+ instr->addr += priv->page_size; /* next page */
+ instr->len -= priv->page_size;
+ }
+
+ at91_dataflash26_write_enable(0);
+ spi_release_bus(priv->spi);
+
+ /* Inform MTD subsystem that erase is complete */
+ instr->state = MTD_ERASE_DONE;
+ if (instr->callback)
+ instr->callback(instr);
+
+ return 0;
+}
+
+/*
+ * Read from the DataFlash device.
+ * from : Start offset in flash device
+ * len : Number of bytes to read
+ * retlen : Number of bytes actually read
+ * buf : Buffer that will receive data
+ */
+static int at91_dataflash26_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
+ unsigned char cmd[5];
+
+ DEBUG(MTD_DEBUG_LEVEL1, "dataflash_read: %lli .. %lli\n",
+ from, from+len);
+
+ *retlen = 0;
+
+ /* Sanity checks */
+ if (!len)
+ return 0;
+ if (from + len > mtd->size)
+ return -EINVAL;
+
+ cmd[0] = AT26_OP_READ_ARRAY_FAST;
+ cmd[1] = (from & 0x00FF0000) >> 16;
+ cmd[2] = (from & 0x0000FF00) >> 8;
+ cmd[3] = (from & 0x000000FF);
+ /* cmd[4] is a "Don't care" byte */
+
+ DEBUG(MTD_DEBUG_LEVEL3, "READ: (0x%02x) 0x%02x 0x%02x 0x%02x\n",
+ cmd[0], cmd[1], cmd[2], cmd[3]);
+
+ spi_access_bus(priv->spi);
+ do_spi_transfer(2, cmd, 5, cmd, 5, buf, len, buf, len);
+ spi_release_bus(priv->spi);
+
+ *retlen = len;
+ return 0;
+}
+
+/*
+ * Write to the DataFlash device.
+ * to : Start offset in flash device
+ * len : Number of bytes to write
+ * retlen : Number of bytes actually written
+ * buf : Buffer containing the data
+ */
+static int at91_dataflash26_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
+ unsigned int addr, buf_index = 0;
+ int ret = -EIO, sector, last_sector;
+ unsigned char status, cmd[5];
+
+ DEBUG(MTD_DEBUG_LEVEL1, "dataflash_write: %lli .. %lli\n", to, to+len);
+
+ *retlen = 0;
+
+ /* Sanity checks */
+ if (!len)
+ return 0;
+ if (to + len > mtd->size)
+ return -EINVAL;
+
+ spi_access_bus(priv->spi);
+
+ addr = to;
+ last_sector = -1;
+
+ while (buf_index < len) {
+ sector = addr / priv->page_size;
+ /* Write first byte if a new sector begins */
+ if (sector != last_sector) {
+ at91_dataflash26_write_enable(1);
+ at91_dataflash26_sector_protect(addr, 0);
+ at91_dataflash26_write_enable(1);
+
+ /* Program first byte of a new sector */
+ cmd[0] = AT26_OP_SEQUENTIAL_WRITE;
+ cmd[1] = (addr & 0x00FF0000) >> 16;
+ cmd[2] = (addr & 0x0000FF00) >> 8;
+ cmd[3] = (addr & 0x000000FF);
+ cmd[4] = buf[buf_index++];
+ do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0);
+ status = at91_dataflash26_waitready();
+ addr++;
+ /* On write errors, the chip resets the write enable
+ flag. This also happens after the last byte of a
+ sector is successfully programmed. */
+ if ( ( !(status & AT26_STATUS_WRITE_ENABLE))
+ && ((addr % priv->page_size) != 0) ) {
+ DEBUG(MTD_DEBUG_LEVEL1,
+ "write error1: addr=0x%06x, "
+ "status=0x%02x\n", addr, status);
+ goto write_err;
+ }
+ (*retlen)++;
+ last_sector = sector;
+ }
+
+ /* Write subsequent bytes in the same sector */
+ cmd[0] = AT26_OP_SEQUENTIAL_WRITE;
+ cmd[1] = buf[buf_index++];
+ do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0);
+ status = at91_dataflash26_waitready();
+ addr++;
+
+ if ( ( !(status & AT26_STATUS_WRITE_ENABLE))
+ && ((addr % priv->page_size) != 0) ) {
+ DEBUG(MTD_DEBUG_LEVEL1, "write error2: addr=0x%06x, "
+ "status=0x%02x\n", addr, status);
+ goto write_err;
+ }
+
+ (*retlen)++;
+ }
+
+ ret = 0;
+ at91_dataflash26_write_enable(0);
+write_err:
+ spi_release_bus(priv->spi);
+ return ret;
+}
+
+/*
+ * Initialize and register DataFlash device with MTD subsystem.
+ */
+static int __init add_dataflash(int channel, char *name, int nr_pages,
+ int pagesize)
+{
+ struct mtd_info *device;
+ struct dataflash_local *priv;
+
+ if (nr_devices >= DATAFLASH_MAX_DEVICES) {
+ printk(KERN_ERR "at91_dataflash26: Too many devices "
+ "detected\n");
+ return 0;
+ }
+
+ device = kzalloc(sizeof(struct mtd_info) + strlen(name) + 8,
+ GFP_KERNEL);
+ if (!device)
+ return -ENOMEM;
+
+ device->name = (char *)&device[1];
+ sprintf(device->name, "%s.spi%d", name, channel);
+ device->size = nr_pages * pagesize;
+ device->erasesize = pagesize;
+ device->owner = THIS_MODULE;
+ device->type = MTD_DATAFLASH;
+ device->flags = MTD_CAP_NORFLASH;
+ device->erase = at91_dataflash26_erase;
+ device->read = at91_dataflash26_read;
+ device->write = at91_dataflash26_write;
+
+ priv = (struct dataflash_local *)kzalloc(sizeof(struct dataflash_local),
+ GFP_KERNEL);
+ if (!priv) {
+ kfree(device);
+ return -ENOMEM;
+ }
+
+ priv->spi = channel;
+ priv->page_size = pagesize;
+ device->priv = priv;
+
+ mtd_devices[nr_devices] = device;
+ nr_devices++;
+ printk(KERN_INFO "at91_dataflash26: %s detected [spi%i] (%i bytes)\n",
+ name, channel, device->size);
+
+ return add_mtd_device(device);
+}
+
+/*
+ * Detect and initialize DataFlash device connected to specified SPI channel.
+ *
+ */
+
+struct dataflash26_types {
+ unsigned char id0;
+ unsigned char id1;
+ char *name;
+ int pagesize;
+ int nr_pages;
+};
+
+struct dataflash26_types df26_types[] = {
+ {
+ .id0 = 0x04,
+ .id1 = 0x00,
+ .name = "AT26F004",
+ .pagesize = 4096,
+ .nr_pages = 128,
+ },
+ {
+ .id0 = 0x45,
+ .id1 = 0x01,
+ .name = "AT26DF081A", /* Not tested ! */
+ .pagesize = 4096,
+ .nr_pages = 256,
+ },
+};
+
+static int __init at91_dataflash26_detect(int channel)
+{
+ unsigned char status, cmd[5];
+ int i;
+
+ spi_access_bus(channel);
+ status = at91_dataflash26_status();
+
+ if (status == 0 || status == 0xff) {
+ printk(KERN_ERR "at91_dataflash26_detect: status error %d\n",
+ status);
+ spi_release_bus(channel);
+ return -ENODEV;
+ }
+
+ cmd[0] = AT26_OP_READ_DEV_ID;
+ do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0);
+ spi_release_bus(channel);
+
+ if (cmd[1] != MANUFACTURER_ID_ATMEL)
+ return -ENODEV;
+
+ for (i = 0; i < ARRAY_SIZE(df26_types); i++) {
+ if ( cmd[2] == df26_types[i].id0
+ && cmd[3] == df26_types[i].id1)
+ return add_dataflash(channel,
+ df26_types[i].name,
+ df26_types[i].nr_pages,
+ df26_types[i].pagesize);
+ }
+
+ printk(KERN_ERR "at91_dataflash26_detect: Unsupported device "
+ "(0x%02x/0x%02x)\n", cmd[2], cmd[3]);
+ return -ENODEV;
+}
+
+static int __init at91_dataflash26_init(void)
+{
+ spi_transfer_desc = kmalloc(sizeof(struct spi_transfer_list),
+ GFP_KERNEL);
+ if (!spi_transfer_desc)
+ return -ENOMEM;
+
+ /* DataFlash (SPI chip select 0) */
+ at91_dataflash26_detect(0);
+
+#ifdef CONFIG_MTD_AT91_DATAFLASH_CARD
+ /* DataFlash card (SPI chip select 3) */
+ at91_dataflash26_detect(3);
+#endif
+ return 0;
+}
+
+static void __exit at91_dataflash26_exit(void)
+{
+ int i;
+
+ for (i = 0; i < DATAFLASH_MAX_DEVICES; i++) {
+ if (mtd_devices[i]) {
+ del_mtd_device(mtd_devices[i]);
+ kfree(mtd_devices[i]->priv);
+ kfree(mtd_devices[i]);
+ }
+ }
+ nr_devices = 0;
+ kfree(spi_transfer_desc);
+}
+
+module_init(at91_dataflash26_init);
+module_exit(at91_dataflash26_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Hans J. Koch");
+MODULE_DESCRIPTION("DataFlash AT26xxx driver for Atmel AT91RM9200");
diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c
index f9f2ce7806b..ce47544dc12 100644
--- a/drivers/mtd/devices/block2mtd.c
+++ b/drivers/mtd/devices/block2mtd.c
@@ -40,56 +40,9 @@ struct block2mtd_dev {
static LIST_HEAD(blkmtd_device_list);
-#define PAGE_READAHEAD 64
-static void cache_readahead(struct address_space *mapping, int index)
+static struct page* page_read(struct address_space *mapping, int index)
{
filler_t *filler = (filler_t*)mapping->a_ops->readpage;
- int i, pagei;
- unsigned ret = 0;
- unsigned long end_index;
- struct page *page;
- LIST_HEAD(page_pool);
- struct inode *inode = mapping->host;
- loff_t isize = i_size_read(inode);
-
- if (!isize) {
- INFO("iSize=0 in cache_readahead\n");
- return;
- }
-
- end_index = ((isize - 1) >> PAGE_CACHE_SHIFT);
-
- read_lock_irq(&mapping->tree_lock);
- for (i = 0; i < PAGE_READAHEAD; i++) {
- pagei = index + i;
- if (pagei > end_index) {
- INFO("Overrun end of disk in cache readahead\n");
- break;
- }
- page = radix_tree_lookup(&mapping->page_tree, pagei);
- if (page && (!i))
- break;
- if (page)
- continue;
- read_unlock_irq(&mapping->tree_lock);
- page = page_cache_alloc_cold(mapping);
- read_lock_irq(&mapping->tree_lock);
- if (!page)
- break;
- page->index = pagei;
- list_add(&page->lru, &page_pool);
- ret++;
- }
- read_unlock_irq(&mapping->tree_lock);
- if (ret)
- read_cache_pages(mapping, &page_pool, filler, NULL);
-}
-
-
-static struct page* page_readahead(struct address_space *mapping, int index)
-{
- filler_t *filler = (filler_t*)mapping->a_ops->readpage;
- cache_readahead(mapping, index);
return read_cache_page(mapping, index, filler, NULL);
}
@@ -105,14 +58,14 @@ static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len)
u_long *max;
while (pages) {
- page = page_readahead(mapping, index);
+ page = page_read(mapping, index);
if (!page)
return -ENOMEM;
if (IS_ERR(page))
return PTR_ERR(page);
- max = (u_long*)page_address(page) + PAGE_SIZE;
- for (p=(u_long*)page_address(page); p<max; p++)
+ max = page_address(page) + PAGE_SIZE;
+ for (p=page_address(page); p<max; p++)
if (*p != -1UL) {
lock_page(page);
memset(page_address(page), 0xff, PAGE_SIZE);
@@ -174,8 +127,7 @@ static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
cpylen = len; // this page
len = len - cpylen;
- // Get page
- page = page_readahead(dev->blkdev->bd_inode->i_mapping, index);
+ page = page_read(dev->blkdev->bd_inode->i_mapping, index);
if (!page)
return -ENOMEM;
if (IS_ERR(page))
@@ -213,8 +165,7 @@ static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf,
cpylen = len; // this page
len = len - cpylen;
- // Get page
- page = page_readahead(mapping, index);
+ page = page_read(mapping, index);
if (!page)
return -ENOMEM;
if (IS_ERR(page))
@@ -308,9 +259,9 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
/* We might not have rootfs mounted at this point. Try
to resolve the device name by other means. */
- dev_t dev = name_to_dev_t(devname);
- if (dev != 0) {
- bdev = open_by_devnum(dev, FMODE_WRITE | FMODE_READ);
+ dev_t devt = name_to_dev_t(devname);
+ if (devt) {
+ bdev = open_by_devnum(devt, FMODE_WRITE | FMODE_READ);
}
}
#endif
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index bbf0553bdb2..d990d8141ef 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -6,7 +6,6 @@ menu "Mapping drivers for chip access"
config MTD_COMPLEX_MAPPINGS
bool "Support non-linear mappings of flash chips"
- depends on MTD
help
This causes the chip drivers to allow for complicated
paged mappings of flash chips.
@@ -69,6 +68,39 @@ config MTD_PHYSMAP_OF
physically into the CPU's memory. The mapping description here is
taken from OF device tree.
+config MTD_PMC_MSP_EVM
+ tristate "CFI Flash device mapped on PMC-Sierra MSP"
+ depends on PMC_MSP && MTD_CFI
+ select MTD_PARTITIONS
+ help
+ This provides a 'mapping' driver which support the way
+ in which user-programmable flash chips are connected on the
+ PMC-Sierra MSP eval/demo boards
+
+choice
+ prompt "Maximum mappable memory avialable for flash IO"
+ depends on MTD_PMC_MSP_EVM
+ default MSP_FLASH_MAP_LIMIT_32M
+
+config MSP_FLASH_MAP_LIMIT_32M
+ bool "32M"
+
+endchoice
+
+config MSP_FLASH_MAP_LIMIT
+ hex
+ default "0x02000000"
+ depends on MSP_FLASH_MAP_LIMIT_32M
+
+config MTD_PMC_MSP_RAMROOT
+ tristate "Embedded RAM block device for root on PMC-Sierra MSP"
+ depends on PMC_MSP_EMBEDDED_ROOTFS && \
+ (MTD_BLOCK || MTD_BLOCK_RO) && \
+ MTD_RAM
+ help
+ This provides support for the embedded root file system
+ on PMC MSP devices. This memory is mapped as a MTD block device.
+
config MTD_SUN_UFLASH
tristate "Sun Microsystems userflash support"
depends on SPARC && MTD_CFI
@@ -240,13 +272,13 @@ config MTD_NETtel
config MTD_ALCHEMY
tristate "AMD Alchemy Pb1xxx/Db1xxx/RDK MTD support"
- depends on SOC_AU1X00
+ depends on SOC_AU1X00 && MTD_PARTITIONS && MTD_CFI
help
Flash memory access on AMD Alchemy Pb/Db/RDK Reference Boards
config MTD_MTX1
tristate "4G Systems MTX-1 Flash device"
- depends on MIPS && MIPS_MTX1
+ depends on MIPS_MTX1 && MTD_CFI
help
Flash memory access on 4G Systems MTX-1 Board. If you have one of
these boards and would like to use the flash chips on it, say 'Y'.
@@ -384,7 +416,7 @@ config MTD_TQM834x
config MTD_OCELOT
tristate "Momenco Ocelot boot flash device"
- depends on MIPS && MOMENCO_OCELOT
+ depends on MOMENCO_OCELOT
help
This enables access routines for the boot flash device and for the
NVRAM on the Momenco Ocelot board. If you have one of these boards
@@ -517,7 +549,7 @@ config MTD_OMAP_NOR
# This needs CFI or JEDEC, depending on the cards found.
config MTD_PCI
tristate "PCI MTD driver"
- depends on MTD && PCI && MTD_COMPLEX_MAPPINGS
+ depends on PCI && MTD_COMPLEX_MAPPINGS
help
Mapping for accessing flash devices on add-in cards like the Intel XScale
IQ80310 card, and the Intel EBSA285 card in blank ROM programming mode
@@ -527,7 +559,7 @@ config MTD_PCI
config MTD_PCMCIA
tristate "PCMCIA MTD driver"
- depends on MTD && PCMCIA && MTD_COMPLEX_MAPPINGS && BROKEN
+ depends on PCMCIA && MTD_COMPLEX_MAPPINGS && BROKEN
help
Map driver for accessing PCMCIA linear flash memory cards. These
cards are usually around 4-16MiB in size. This does not include
@@ -591,13 +623,12 @@ config MTD_BAST_MAXSIZE
config MTD_SHARP_SL
bool "ROM mapped on Sharp SL Series"
- depends on MTD && ARCH_PXA
+ depends on ARCH_PXA
help
This enables access to the flash chip on the Sharp SL Series of PDAs.
config MTD_PLATRAM
tristate "Map driver for platform device RAM (mtd-ram)"
- depends on MTD
select MTD_RAM
help
Map driver for RAM areas described via the platform device
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index 071d0bf922b..de036c5e613 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -27,6 +27,8 @@ obj-$(CONFIG_MTD_CEIVA) += ceiva.o
obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o
obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o
+obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o
+obj-$(CONFIG_MTD_PMC_MSP_RAMROOT)+= pmcmsp-ramroot.o
obj-$(CONFIG_MTD_PNC2000) += pnc2000.o
obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o
obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o
diff --git a/drivers/mtd/maps/alchemy-flash.c b/drivers/mtd/maps/alchemy-flash.c
index 7fc8097e41d..84fbe0e8c47 100644
--- a/drivers/mtd/maps/alchemy-flash.c
+++ b/drivers/mtd/maps/alchemy-flash.c
@@ -1,10 +1,7 @@
/*
* Flash memory access on AMD Alchemy evaluation boards
*
- * $Id: alchemy-flash.c,v 1.2 2005/11/07 11:14:26 gleixner Exp $
- *
* (C) 2003, 2004 Pete Popov <ppopov@embeddedalley.com>
- *
*/
#include <linux/init.h>
@@ -18,12 +15,6 @@
#include <asm/io.h>
-#ifdef DEBUG_RW
-#define DBG(x...) printk(x)
-#else
-#define DBG(x...)
-#endif
-
#ifdef CONFIG_MIPS_PB1000
#define BOARD_MAP_NAME "Pb1000 Flash"
#define BOARD_FLASH_SIZE 0x00800000 /* 8MB */
diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c
index 2b6504ecbbd..894c0b27128 100644
--- a/drivers/mtd/maps/plat-ram.c
+++ b/drivers/mtd/maps/plat-ram.c
@@ -169,7 +169,8 @@ static int platram_probe(struct platform_device *pdev)
goto exit_free;
}
- dev_dbg(&pdev->dev, "got platform resource %p (0x%lx)\n", res, res->start);
+ dev_dbg(&pdev->dev, "got platform resource %p (0x%llx)\n", res,
+ (unsigned long long)res->start);
/* setup map parameters */
diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c
new file mode 100644
index 00000000000..7e0377ec1c4
--- /dev/null
+++ b/drivers/mtd/maps/pmcmsp-flash.c
@@ -0,0 +1,184 @@
+/*
+ * Mapping of a custom board with both AMD CFI and JEDEC flash in partitions.
+ * Config with both CFI and JEDEC device support.
+ *
+ * Basically physmap.c with the addition of partitions and
+ * an array of mapping info to accomodate more than one flash type per board.
+ *
+ * Copyright 2005-2007 PMC-Sierra, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+
+#include <msp_prom.h>
+#include <msp_regs.h>
+
+
+static struct mtd_info **msp_flash;
+static struct mtd_partition **msp_parts;
+static struct map_info *msp_maps;
+static int fcnt;
+
+#define DEBUG_MARKER printk(KERN_NOTICE "%s[%d]\n",__FUNCTION__,__LINE__)
+
+int __init init_msp_flash(void)
+{
+ int i, j;
+ int offset, coff;
+ char *env;
+ int pcnt;
+ char flash_name[] = "flash0";
+ char part_name[] = "flash0_0";
+ unsigned addr, size;
+
+ /* If ELB is disabled by "ful-mux" mode, we can't get at flash */
+ if ((*DEV_ID_REG & DEV_ID_SINGLE_PC) &&
+ (*ELB_1PC_EN_REG & SINGLE_PCCARD)) {
+ printk(KERN_NOTICE "Single PC Card mode: no flash access\n");
+ return -ENXIO;
+ }
+
+ /* examine the prom environment for flash devices */
+ for (fcnt = 0; (env = prom_getenv(flash_name)); fcnt++)
+ flash_name[5] = '0' + fcnt + 1;
+
+ if (fcnt < 1)
+ return -ENXIO;
+
+ printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt);
+ msp_flash = (struct mtd_info **)kmalloc(
+ fcnt * sizeof(struct map_info *), GFP_KERNEL);
+ msp_parts = (struct mtd_partition **)kmalloc(
+ fcnt * sizeof(struct mtd_partition *), GFP_KERNEL);
+ msp_maps = (struct map_info *)kmalloc(
+ fcnt * sizeof(struct mtd_info), GFP_KERNEL);
+ memset(msp_maps, 0, fcnt * sizeof(struct mtd_info));
+
+ /* loop over the flash devices, initializing each */
+ for (i = 0; i < fcnt; i++) {
+ /* examine the prom environment for flash partititions */
+ part_name[5] = '0' + i;
+ part_name[7] = '0';
+ for (pcnt = 0; (env = prom_getenv(part_name)); pcnt++)
+ part_name[7] = '0' + pcnt + 1;
+
+ if (pcnt == 0) {
+ printk(KERN_NOTICE "Skipping flash device %d "
+ "(no partitions defined)\n", i);
+ continue;
+ }
+
+ msp_parts[i] = (struct mtd_partition *)kmalloc(
+ pcnt * sizeof(struct mtd_partition), GFP_KERNEL);
+ memset(msp_parts[i], 0, pcnt * sizeof(struct mtd_partition));
+
+ /* now initialize the devices proper */
+ flash_name[5] = '0' + i;
+ env = prom_getenv(flash_name);
+
+ if (sscanf(env, "%x:%x", &addr, &size) < 2)
+ return -ENXIO;
+ addr = CPHYSADDR(addr);
+
+ printk(KERN_NOTICE
+ "MSP flash device \"%s\": 0x%08x at 0x%08x\n",
+ flash_name, size, addr);
+ /* This must matchs the actual size of the flash chip */
+ msp_maps[i].size = size;
+ msp_maps[i].phys = addr;
+
+ /*
+ * Platforms have a specific limit of the size of memory
+ * which may be mapped for flash:
+ */
+ if (size > CONFIG_MSP_FLASH_MAP_LIMIT)
+ size = CONFIG_MSP_FLASH_MAP_LIMIT;
+ msp_maps[i].virt = ioremap(addr, size);
+ msp_maps[i].bankwidth = 1;
+ msp_maps[i].name = strncpy(kmalloc(7, GFP_KERNEL),
+ flash_name, 7);
+
+ if (msp_maps[i].virt == NULL)
+ return -ENXIO;
+
+ for (j = 0; j < pcnt; j++) {
+ part_name[5] = '0' + i;
+ part_name[7] = '0' + j;
+
+ env = prom_getenv(part_name);
+
+ if (sscanf(env, "%x:%x:%n", &offset, &size, &coff) < 2)
+ return -ENXIO;
+
+ msp_parts[i][j].size = size;
+ msp_parts[i][j].offset = offset;
+ msp_parts[i][j].name = env + coff;
+ }
+
+ /* now probe and add the device */
+ simple_map_init(&msp_maps[i]);
+ msp_flash[i] = do_map_probe("cfi_probe", &msp_maps[i]);
+ if (msp_flash[i]) {
+ msp_flash[i]->owner = THIS_MODULE;
+ add_mtd_partitions(msp_flash[i], msp_parts[i], pcnt);
+ } else {
+ printk(KERN_ERR "map probe failed for flash\n");
+ return -ENXIO;
+ }
+ }
+
+ return 0;
+}
+
+static void __exit cleanup_msp_flash(void)
+{
+ int i;
+
+ for (i = 0; i < sizeof(msp_flash) / sizeof(struct mtd_info **); i++) {
+ del_mtd_partitions(msp_flash[i]);
+ map_destroy(msp_flash[i]);
+ iounmap((void *)msp_maps[i].virt);
+
+ /* free the memory */
+ kfree(msp_maps[i].name);
+ kfree(msp_parts[i]);
+ }
+
+ kfree(msp_flash);
+ kfree(msp_parts);
+ kfree(msp_maps);
+}
+
+MODULE_AUTHOR("PMC-Sierra, Inc");
+MODULE_DESCRIPTION("MTD map driver for PMC-Sierra MSP boards");
+MODULE_LICENSE("GPL");
+
+module_init(init_msp_flash);
+module_exit(cleanup_msp_flash);
diff --git a/drivers/mtd/maps/pmcmsp-ramroot.c b/drivers/mtd/maps/pmcmsp-ramroot.c
new file mode 100644
index 00000000000..18049bceba8
--- /dev/null
+++ b/drivers/mtd/maps/pmcmsp-ramroot.c
@@ -0,0 +1,105 @@
+/*
+ * Mapping of the rootfs in a physical region of memory
+ *
+ * Copyright (C) 2005-2007 PMC-Sierra Inc.
+ * Author: Andrew Hughes, Andrew_Hughes@pmc-sierra.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/root_dev.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+
+#include <asm/io.h>
+
+#include <msp_prom.h>
+
+static struct mtd_info *rr_mtd;
+
+struct map_info rr_map = {
+ .name = "ramroot",
+ .bankwidth = 4,
+};
+
+static int __init init_rrmap(void)
+{
+ void *ramroot_start;
+ unsigned long ramroot_size;
+
+ /* Check for supported rootfs types */
+ if (get_ramroot(&ramroot_start, &ramroot_size)) {
+ rr_map.phys = CPHYSADDR(ramroot_start);
+ rr_map.size = ramroot_size;
+
+ printk(KERN_NOTICE
+ "PMC embedded root device: 0x%08lx @ 0x%08lx\n",
+ rr_map.size, (unsigned long)rr_map.phys);
+ } else {
+ printk(KERN_ERR
+ "init_rrmap: no supported embedded rootfs detected!\n");
+ return -ENXIO;
+ }
+
+ /* Map rootfs to I/O space for block device driver */
+ rr_map.virt = ioremap(rr_map.phys, rr_map.size);
+ if (!rr_map.virt) {
+ printk(KERN_ERR "Failed to ioremap\n");
+ return -EIO;
+ }
+
+ simple_map_init(&rr_map);
+
+ rr_mtd = do_map_probe("map_ram", &rr_map);
+ if (rr_mtd) {
+ rr_mtd->owner = THIS_MODULE;
+
+ add_mtd_device(rr_mtd);
+ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, rr_mtd->index);
+
+ return 0;
+ }
+
+ iounmap(rr_map.virt);
+ return -ENXIO;
+}
+
+static void __exit cleanup_rrmap(void)
+{
+ del_mtd_device(rr_mtd);
+ map_destroy(rr_mtd);
+
+ iounmap(rr_map.virt);
+ rr_map.virt = NULL;
+}
+
+MODULE_AUTHOR("PMC-Sierra, Inc");
+MODULE_DESCRIPTION("MTD map driver for embedded PMC-Sierra MSP filesystem");
+MODULE_LICENSE("GPL");
+
+module_init(init_rrmap);
+module_exit(cleanup_rrmap);
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index b879a66daa9..524b83b5ebf 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -20,6 +20,7 @@
#include <linux/hdreg.h>
#include <linux/init.h>
#include <linux/mutex.h>
+#include <linux/kthread.h>
#include <asm/uaccess.h>
static LIST_HEAD(blktrans_majors);
@@ -28,9 +29,7 @@ extern struct mutex mtd_table_mutex;
extern struct mtd_info *mtd_table[];
struct mtd_blkcore_priv {
- struct completion thread_dead;
- int exiting;
- wait_queue_head_t thread_wq;
+ struct task_struct *thread;
struct request_queue *rq;
spinlock_t queue_lock;
};
@@ -83,38 +82,19 @@ static int mtd_blktrans_thread(void *arg)
/* we might get involved when memory gets low, so use PF_MEMALLOC */
current->flags |= PF_MEMALLOC | PF_NOFREEZE;
- daemonize("%sd", tr->name);
-
- /* daemonize() doesn't do this for us since some kernel threads
- actually want to deal with signals. We can't just call
- exit_sighand() since that'll cause an oops when we finally
- do exit. */
- spin_lock_irq(&current->sighand->siglock);
- sigfillset(&current->blocked);
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
-
spin_lock_irq(rq->queue_lock);
-
- while (!tr->blkcore_priv->exiting) {
+ while (!kthread_should_stop()) {
struct request *req;
struct mtd_blktrans_dev *dev;
int res = 0;
- DECLARE_WAITQUEUE(wait, current);
req = elv_next_request(rq);
if (!req) {
- add_wait_queue(&tr->blkcore_priv->thread_wq, &wait);
set_current_state(TASK_INTERRUPTIBLE);
-
spin_unlock_irq(rq->queue_lock);
-
schedule();
- remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait);
-
spin_lock_irq(rq->queue_lock);
-
continue;
}
@@ -133,13 +113,13 @@ static int mtd_blktrans_thread(void *arg)
}
spin_unlock_irq(rq->queue_lock);
- complete_and_exit(&tr->blkcore_priv->thread_dead, 0);
+ return 0;
}
static void mtd_blktrans_request(struct request_queue *rq)
{
struct mtd_blktrans_ops *tr = rq->queuedata;
- wake_up(&tr->blkcore_priv->thread_wq);
+ wake_up_process(tr->blkcore_priv->thread);
}
@@ -388,8 +368,6 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
return ret;
}
spin_lock_init(&tr->blkcore_priv->queue_lock);
- init_completion(&tr->blkcore_priv->thread_dead);
- init_waitqueue_head(&tr->blkcore_priv->thread_wq);
tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
if (!tr->blkcore_priv->rq) {
@@ -403,13 +381,14 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
blk_queue_hardsect_size(tr->blkcore_priv->rq, tr->blksize);
tr->blkshift = ffs(tr->blksize) - 1;
- ret = kernel_thread(mtd_blktrans_thread, tr, CLONE_KERNEL);
- if (ret < 0) {
+ tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
+ "%sd", tr->name);
+ if (IS_ERR(tr->blkcore_priv->thread)) {
blk_cleanup_queue(tr->blkcore_priv->rq);
unregister_blkdev(tr->major, tr->name);
kfree(tr->blkcore_priv);
mutex_unlock(&mtd_table_mutex);
- return ret;
+ return PTR_ERR(tr->blkcore_priv->thread);
}
INIT_LIST_HEAD(&tr->devs);
@@ -432,9 +411,7 @@ int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
mutex_lock(&mtd_table_mutex);
/* Clean up the kernel thread */
- tr->blkcore_priv->exiting = 1;
- wake_up(&tr->blkcore_priv->thread_wq);
- wait_for_completion(&tr->blkcore_priv->thread_dead);
+ kthread_stop(tr->blkcore_priv->thread);
/* Remove it from the list of active majors */
list_del(&tr->list);
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 1592eac64e5..8c86b802f21 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -553,7 +553,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
ops.datbuf = NULL;
ops.mode = MTD_OOB_PLACE;
- if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs))
+ if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
return -EINVAL;
ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 2d12dcdd740..4e62afe0c0f 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -1,10 +1,7 @@
# drivers/mtd/nand/Kconfig
# $Id: Kconfig,v 1.35 2005/11/07 11:14:30 gleixner Exp $
-menu "NAND Flash Device Drivers"
- depends on MTD!=n
-
-config MTD_NAND
+menuconfig MTD_NAND
tristate "NAND Device Support"
depends on MTD
select MTD_NAND_IDS
@@ -13,9 +10,10 @@ config MTD_NAND
devices. For further information see
<http://www.linux-mtd.infradead.org/doc/nand.html>.
+if MTD_NAND
+
config MTD_NAND_VERIFY_WRITE
bool "Verify NAND page writes"
- depends on MTD_NAND
help
This adds an extra check when data is written to the flash. The
NAND flash device internally checks only bits transitioning
@@ -25,53 +23,61 @@ config MTD_NAND_VERIFY_WRITE
config MTD_NAND_ECC_SMC
bool "NAND ECC Smart Media byte order"
- depends on MTD_NAND
default n
help
Software ECC according to the Smart Media Specification.
The original Linux implementation had byte 0 and 1 swapped.
+config MTD_NAND_MUSEUM_IDS
+ bool "Enable chip ids for obsolete ancient NAND devices"
+ depends on MTD_NAND
+ default n
+ help
+ Enable this option only when your board has first generation
+ NAND chips (page size 256 byte, erase size 4-8KiB). The IDs
+ of these chips were reused by later, larger chips.
+
config MTD_NAND_AUTCPU12
tristate "SmartMediaCard on autronix autcpu12 board"
- depends on MTD_NAND && ARCH_AUTCPU12
+ depends on ARCH_AUTCPU12
help
This enables the driver for the autronix autcpu12 board to
access the SmartMediaCard.
config MTD_NAND_EDB7312
tristate "Support for Cirrus Logic EBD7312 evaluation board"
- depends on MTD_NAND && ARCH_EDB7312
+ depends on ARCH_EDB7312
help
This enables the driver for the Cirrus Logic EBD7312 evaluation
board to access the onboard NAND Flash.
config MTD_NAND_H1900
tristate "iPAQ H1900 flash"
- depends on MTD_NAND && ARCH_PXA && MTD_PARTITIONS
+ depends on ARCH_PXA && MTD_PARTITIONS
help
This enables the driver for the iPAQ h1900 flash.
config MTD_NAND_SPIA
tristate "NAND Flash device on SPIA board"
- depends on ARCH_P720T && MTD_NAND
+ depends on ARCH_P720T
help
If you had to ask, you don't have one. Say 'N'.
config MTD_NAND_AMS_DELTA
tristate "NAND Flash device on Amstrad E3"
- depends on MACH_AMS_DELTA && MTD_NAND
+ depends on MACH_AMS_DELTA
help
Support for NAND flash on Amstrad E3 (Delta).
config MTD_NAND_TOTO
tristate "NAND Flash device on TOTO board"
- depends on ARCH_OMAP && MTD_NAND && BROKEN
+ depends on ARCH_OMAP && BROKEN
help
Support for NAND flash on Texas Instruments Toto platform.
config MTD_NAND_TS7250
tristate "NAND Flash device on TS-7250 board"
- depends on MACH_TS72XX && MTD_NAND
+ depends on MACH_TS72XX
help
Support for NAND flash on Technologic Systems TS-7250 platform.
@@ -80,14 +86,14 @@ config MTD_NAND_IDS
config MTD_NAND_AU1550
tristate "Au1550/1200 NAND support"
- depends on (SOC_AU1200 || SOC_AU1550) && MTD_NAND
+ depends on SOC_AU1200 || SOC_AU1550
help
This enables the driver for the NAND flash controller on the
AMD/Alchemy 1550 SOC.
config MTD_NAND_RTC_FROM4
tristate "Renesas Flash ROM 4-slot interface board (FROM_BOARD4)"
- depends on MTD_NAND && SH_SOLUTION_ENGINE
+ depends on SH_SOLUTION_ENGINE
select REED_SOLOMON
select REED_SOLOMON_DEC8
select BITREVERSE
@@ -97,13 +103,13 @@ config MTD_NAND_RTC_FROM4
config MTD_NAND_PPCHAMELEONEVB
tristate "NAND Flash device on PPChameleonEVB board"
- depends on PPCHAMELEONEVB && MTD_NAND && BROKEN
+ depends on PPCHAMELEONEVB && BROKEN
help
This enables the NAND flash driver on the PPChameleon EVB Board.
config MTD_NAND_S3C2410
tristate "NAND Flash support for S3C2410/S3C2440 SoC"
- depends on ARCH_S3C2410 && MTD_NAND
+ depends on ARCH_S3C2410
help
This enables the NAND flash controller on the S3C2410 and S3C2440
SoCs
@@ -128,7 +134,7 @@ config MTD_NAND_S3C2410_HWECC
config MTD_NAND_NDFC
tristate "NDFC NanD Flash Controller"
- depends on MTD_NAND && 44x
+ depends on 44x
select MTD_NAND_ECC_SMC
help
NDFC Nand Flash Controllers are integrated in EP44x SoCs
@@ -145,7 +151,7 @@ config MTD_NAND_S3C2410_CLKSTOP
config MTD_NAND_DISKONCHIP
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)"
- depends on MTD_NAND && EXPERIMENTAL
+ depends on EXPERIMENTAL
select REED_SOLOMON
select REED_SOLOMON_DEC16
help
@@ -215,11 +221,11 @@ config MTD_NAND_DISKONCHIP_BBTWRITE
config MTD_NAND_SHARPSL
tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
- depends on MTD_NAND && ARCH_PXA
+ depends on ARCH_PXA
config MTD_NAND_BASLER_EXCITE
tristate "Support for NAND Flash on Basler eXcite"
- depends on MTD_NAND && BASLER_EXCITE
+ depends on BASLER_EXCITE
help
This enables the driver for the NAND flash device found on the
Basler eXcite Smart Camera. If built as a module, the driver
@@ -227,14 +233,14 @@ config MTD_NAND_BASLER_EXCITE
config MTD_NAND_CAFE
tristate "NAND support for OLPC CAFÉ chip"
- depends on MTD_NAND && PCI
+ depends on PCI
help
Use NAND flash attached to the CAFÉ chip designed for the $100
laptop.
config MTD_NAND_CS553X
tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
- depends on MTD_NAND && X86_32 && (X86_PC || X86_GENERICARCH)
+ depends on X86_32 && (X86_PC || X86_GENERICARCH)
help
The CS553x companion chips for the AMD Geode processor
include NAND flash controllers with built-in hardware ECC
@@ -247,16 +253,16 @@ config MTD_NAND_CS553X
config MTD_NAND_AT91
bool "Support for NAND Flash / SmartMedia on AT91"
- depends on MTD_NAND && ARCH_AT91
+ depends on ARCH_AT91
help
Enables support for NAND Flash / Smart Media Card interface
on Atmel AT91 processors.
config MTD_NAND_NANDSIM
tristate "Support for NAND Flash Simulator"
- depends on MTD_NAND && MTD_PARTITIONS
+ depends on MTD_PARTITIONS
help
The simulator may simulate various NAND flash chips for the
MTD nand layer.
-endmenu
+endif # MTD_NAND
diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c
index fd6bb3ed40d..c328a751451 100644
--- a/drivers/mtd/nand/cafe.c
+++ b/drivers/mtd/nand/cafe.c
@@ -530,7 +530,6 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
{
struct mtd_info *mtd;
struct cafe_priv *cafe;
- uint32_t timing1, timing2, timing3;
uint32_t ctrl;
int err = 0;
@@ -587,21 +586,19 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
}
if (numtimings == 3) {
- timing1 = timing[0];
- timing2 = timing[1];
- timing3 = timing[2];
cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n",
- timing1, timing2, timing3);
+ timing[0], timing[1], timing[2]);
} else {
- timing1 = cafe_readl(cafe, NAND_TIMING1);
- timing2 = cafe_readl(cafe, NAND_TIMING2);
- timing3 = cafe_readl(cafe, NAND_TIMING3);
+ timing[0] = cafe_readl(cafe, NAND_TIMING1);
+ timing[1] = cafe_readl(cafe, NAND_TIMING2);
+ timing[2] = cafe_readl(cafe, NAND_TIMING3);
- if (timing1 | timing2 | timing3) {
- cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", timing1, timing2, timing3);
+ if (timing[0] | timing[1] | timing[2]) {
+ cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n",
+ timing[0], timing[1], timing[2]);
} else {
dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n");
- timing1 = timing2 = timing3 = 0xffffffff;
+ timing[0] = timing[1] = timing[2] = 0xffffffff;
}
}
@@ -609,9 +606,9 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
cafe_writel(cafe, 1, NAND_RESET);
cafe_writel(cafe, 0, NAND_RESET);
- cafe_writel(cafe, timing1, NAND_TIMING1);
- cafe_writel(cafe, timing2, NAND_TIMING2);
- cafe_writel(cafe, timing3, NAND_TIMING3);
+ cafe_writel(cafe, timing[0], NAND_TIMING1);
+ cafe_writel(cafe, timing[1], NAND_TIMING2);
+ cafe_writel(cafe, timing[2], NAND_TIMING3);
cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK);
err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED,
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 6af37b8cff6..ab3b2d16cff 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -350,7 +350,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
int block, ret;
/* Get block number */
- block = ((int)ofs) >> chip->bbt_erase_shift;
+ block = (int)(ofs >> chip->bbt_erase_shift);
if (chip->bbt)
chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
@@ -771,7 +771,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *ecc_code = chip->buffers->ecccode;
int *eccpos = chip->ecc.layout->eccpos;
- nand_read_page_raw(mtd, chip, buf);
+ chip->ecc.read_page_raw(mtd, chip, buf);
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
@@ -1426,7 +1426,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
for (i = 0; i < chip->ecc.total; i++)
chip->oob_poi[eccpos[i]] = ecc_calc[i];
- nand_write_page_raw(mtd, chip, buf);
+ chip->ecc.write_page_raw(mtd, chip, buf);
}
/**
diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c
index 2e2cdf2fc91..2fc674a190c 100644
--- a/drivers/mtd/nand/nand_ids.c
+++ b/drivers/mtd/nand/nand_ids.c
@@ -24,6 +24,8 @@
* 512 512 Byte page size
*/
struct nand_flash_dev nand_flash_ids[] = {
+
+#ifdef CONFIG_MTD_NAND_MUSEUM_IDS
{"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0},
{"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0},
{"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0},
@@ -39,6 +41,7 @@ struct nand_flash_dev nand_flash_ids[] = {
{"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0},
{"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},
{"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16},
+#endif
{"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0},
{"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0},
@@ -137,6 +140,7 @@ struct nand_manufacturers nand_manuf_ids[] = {
{NAND_MFR_RENESAS, "Renesas"},
{NAND_MFR_STMICRO, "ST Micro"},
{NAND_MFR_HYNIX, "Hynix"},
+ {NAND_MFR_MICRON, "Micron"},
{0x0, "Unknown"}
};
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
index c3bca9590ad..205df0f771f 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/nandsim.c
@@ -37,6 +37,8 @@
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/random.h>
/* Default simulator parameters values */
#if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \
@@ -90,6 +92,15 @@ static uint bus_width = CONFIG_NANDSIM_BUS_WIDTH;
static uint do_delays = CONFIG_NANDSIM_DO_DELAYS;
static uint log = CONFIG_NANDSIM_LOG;
static uint dbg = CONFIG_NANDSIM_DBG;
+static unsigned long parts[MAX_MTD_DEVICES];
+static unsigned int parts_num;
+static char *badblocks = NULL;
+static char *weakblocks = NULL;
+static char *weakpages = NULL;
+static unsigned int bitflips = 0;
+static char *gravepages = NULL;
+static unsigned int rptwear = 0;
+static unsigned int overridesize = 0;
module_param(first_id_byte, uint, 0400);
module_param(second_id_byte, uint, 0400);
@@ -104,8 +115,16 @@ module_param(bus_width, uint, 0400);
module_param(do_delays, uint, 0400);
module_param(log, uint, 0400);
module_param(dbg, uint, 0400);
-
-MODULE_PARM_DESC(first_id_byte, "The fist byte returned by NAND Flash 'read ID' command (manufaturer ID)");
+module_param_array(parts, ulong, &parts_num, 0400);
+module_param(badblocks, charp, 0400);
+module_param(weakblocks, charp, 0400);
+module_param(weakpages, charp, 0400);
+module_param(bitflips, uint, 0400);
+module_param(gravepages, charp, 0400);
+module_param(rptwear, uint, 0400);
+module_param(overridesize, uint, 0400);
+
+MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)");
MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)");
MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command");
MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command");
@@ -118,6 +137,23 @@ MODULE_PARM_DESC(bus_width, "Chip's bus width (8- or 16-bit)");
MODULE_PARM_DESC(do_delays, "Simulate NAND delays using busy-waits if not zero");
MODULE_PARM_DESC(log, "Perform logging if not zero");
MODULE_PARM_DESC(dbg, "Output debug information if not zero");
+MODULE_PARM_DESC(parts, "Partition sizes (in erase blocks) separated by commas");
+/* Page and erase block positions for the following parameters are independent of any partitions */
+MODULE_PARM_DESC(badblocks, "Erase blocks that are initially marked bad, separated by commas");
+MODULE_PARM_DESC(weakblocks, "Weak erase blocks [: remaining erase cycles (defaults to 3)]"
+ " separated by commas e.g. 113:2 means eb 113"
+ " can be erased only twice before failing");
+MODULE_PARM_DESC(weakpages, "Weak pages [: maximum writes (defaults to 3)]"
+ " separated by commas e.g. 1401:2 means page 1401"
+ " can be written only twice before failing");
+MODULE_PARM_DESC(bitflips, "Maximum number of random bit flips per page (zero by default)");
+MODULE_PARM_DESC(gravepages, "Pages that lose data [: maximum reads (defaults to 3)]"
+ " separated by commas e.g. 1401:2 means page 1401"
+ " can be read only twice before failing");
+MODULE_PARM_DESC(rptwear, "Number of erases inbetween reporting wear, if not zero");
+MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the ID bytes. "
+ "The size is specified in erase blocks and as the exponent of a power of two"
+ " e.g. 5 means a size of 32 erase blocks");
/* The largest possible page size */
#define NS_LARGEST_PAGE_SIZE 2048
@@ -131,9 +167,11 @@ MODULE_PARM_DESC(dbg, "Output debug information if not zero");
#define NS_DBG(args...) \
do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0)
#define NS_WARN(args...) \
- do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warnig: " args); } while(0)
+ do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warning: " args); } while(0)
#define NS_ERR(args...) \
- do { printk(KERN_ERR NS_OUTPUT_PREFIX " errorr: " args); } while(0)
+ do { printk(KERN_ERR NS_OUTPUT_PREFIX " error: " args); } while(0)
+#define NS_INFO(args...) \
+ do { printk(KERN_INFO NS_OUTPUT_PREFIX " " args); } while(0)
/* Busy-wait delay macros (microseconds, milliseconds) */
#define NS_UDELAY(us) \
@@ -238,7 +276,8 @@ union ns_mem {
* The structure which describes all the internal simulator data.
*/
struct nandsim {
- struct mtd_partition part;
+ struct mtd_partition partitions[MAX_MTD_DEVICES];
+ unsigned int nbparts;
uint busw; /* flash chip bus width (8 or 16) */
u_char ids[4]; /* chip's ID bytes */
@@ -338,6 +377,38 @@ static struct nandsim_operations {
STATE_DATAOUT, STATE_READY}}
};
+struct weak_block {
+ struct list_head list;
+ unsigned int erase_block_no;
+ unsigned int max_erases;
+ unsigned int erases_done;
+};
+
+static LIST_HEAD(weak_blocks);
+
+struct weak_page {
+ struct list_head list;
+ unsigned int page_no;
+ unsigned int max_writes;
+ unsigned int writes_done;
+};
+
+static LIST_HEAD(weak_pages);
+
+struct grave_page {
+ struct list_head list;
+ unsigned int page_no;
+ unsigned int max_reads;
+ unsigned int reads_done;
+};
+
+static LIST_HEAD(grave_pages);
+
+static unsigned long *erase_block_wear = NULL;
+static unsigned int wear_eb_count = 0;
+static unsigned long total_wear = 0;
+static unsigned int rptwear_cnt = 0;
+
/* MTD structure for NAND controller */
static struct mtd_info *nsmtd;
@@ -381,6 +452,13 @@ static void free_device(struct nandsim *ns)
}
}
+static char *get_partition_name(int i)
+{
+ char buf[64];
+ sprintf(buf, "NAND simulator partition %d", i);
+ return kstrdup(buf, GFP_KERNEL);
+}
+
/*
* Initialize the nandsim structure.
*
@@ -390,7 +468,9 @@ 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;
+ int i, ret = 0;
+ u_int32_t remains;
+ u_int32_t next_offset;
if (NS_IS_INITIALIZED(ns)) {
NS_ERR("init_nandsim: nandsim is already initialized\n");
@@ -448,6 +528,40 @@ static int init_nandsim(struct mtd_info *mtd)
}
}
+ /* Fill the partition_info structure */
+ if (parts_num > ARRAY_SIZE(ns->partitions)) {
+ NS_ERR("too many partitions.\n");
+ ret = -EINVAL;
+ goto error;
+ }
+ 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) {
+ 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;
+ next_offset += ns->partitions[i].size;
+ remains -= ns->partitions[i].size;
+ }
+ ns->nbparts = parts_num;
+ if (remains) {
+ if (parts_num + 1 > ARRAY_SIZE(ns->partitions)) {
+ NS_ERR("too many partitions.\n");
+ ret = -EINVAL;
+ goto error;
+ }
+ ns->partitions[i].name = get_partition_name(i);
+ ns->partitions[i].offset = next_offset;
+ ns->partitions[i].size = remains;
+ ns->nbparts += 1;
+ }
+
/* Detect how many ID bytes the NAND chip outputs */
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
if (second_id_byte != nand_flash_ids[i].id)
@@ -474,7 +588,7 @@ static int init_nandsim(struct mtd_info *mtd)
printk("sector address bytes: %u\n", ns->geom.secaddrbytes);
printk("options: %#x\n", ns->options);
- if (alloc_device(ns) != 0)
+ if ((ret = alloc_device(ns)) != 0)
goto error;
/* Allocate / initialize the internal buffer */
@@ -482,21 +596,17 @@ static int init_nandsim(struct mtd_info *mtd)
if (!ns->buf.byte) {
NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n",
ns->geom.pgszoob);
+ ret = -ENOMEM;
goto error;
}
memset(ns->buf.byte, 0xFF, ns->geom.pgszoob);
- /* Fill the partition_info structure */
- ns->part.name = "NAND simulator partition";
- ns->part.offset = 0;
- ns->part.size = ns->geom.totsz;
-
return 0;
error:
free_device(ns);
- return -ENOMEM;
+ return ret;
}
/*
@@ -510,6 +620,287 @@ static void free_nandsim(struct nandsim *ns)
return;
}
+static int parse_badblocks(struct nandsim *ns, struct mtd_info *mtd)
+{
+ char *w;
+ int zero_ok;
+ unsigned int erase_block_no;
+ loff_t offset;
+
+ if (!badblocks)
+ return 0;
+ w = badblocks;
+ do {
+ zero_ok = (*w == '0' ? 1 : 0);
+ erase_block_no = simple_strtoul(w, &w, 0);
+ if (!zero_ok && !erase_block_no) {
+ NS_ERR("invalid badblocks.\n");
+ return -EINVAL;
+ }
+ offset = erase_block_no * ns->geom.secsz;
+ if (mtd->block_markbad(mtd, offset)) {
+ NS_ERR("invalid badblocks.\n");
+ return -EINVAL;
+ }
+ if (*w == ',')
+ w += 1;
+ } while (*w);
+ return 0;
+}
+
+static int parse_weakblocks(void)
+{
+ char *w;
+ int zero_ok;
+ unsigned int erase_block_no;
+ unsigned int max_erases;
+ struct weak_block *wb;
+
+ if (!weakblocks)
+ return 0;
+ w = weakblocks;
+ do {
+ zero_ok = (*w == '0' ? 1 : 0);
+ erase_block_no = simple_strtoul(w, &w, 0);
+ if (!zero_ok && !erase_block_no) {
+ NS_ERR("invalid weakblocks.\n");
+ return -EINVAL;
+ }
+ max_erases = 3;
+ if (*w == ':') {
+ w += 1;
+ max_erases = simple_strtoul(w, &w, 0);
+ }
+ if (*w == ',')
+ w += 1;
+ wb = kzalloc(sizeof(*wb), GFP_KERNEL);
+ if (!wb) {
+ NS_ERR("unable to allocate memory.\n");
+ return -ENOMEM;
+ }
+ wb->erase_block_no = erase_block_no;
+ wb->max_erases = max_erases;
+ list_add(&wb->list, &weak_blocks);
+ } while (*w);
+ return 0;
+}
+
+static int erase_error(unsigned int erase_block_no)
+{
+ struct weak_block *wb;
+
+ list_for_each_entry(wb, &weak_blocks, list)
+ if (wb->erase_block_no == erase_block_no) {
+ if (wb->erases_done >= wb->max_erases)
+ return 1;
+ wb->erases_done += 1;
+ return 0;
+ }
+ return 0;
+}
+
+static int parse_weakpages(void)
+{
+ char *w;
+ int zero_ok;
+ unsigned int page_no;
+ unsigned int max_writes;
+ struct weak_page *wp;
+
+ if (!weakpages)
+ return 0;
+ w = weakpages;
+ do {
+ zero_ok = (*w == '0' ? 1 : 0);
+ page_no = simple_strtoul(w, &w, 0);
+ if (!zero_ok && !page_no) {
+ NS_ERR("invalid weakpagess.\n");
+ return -EINVAL;
+ }
+ max_writes = 3;
+ if (*w == ':') {
+ w += 1;
+ max_writes = simple_strtoul(w, &w, 0);
+ }
+ if (*w == ',')
+ w += 1;
+ wp = kzalloc(sizeof(*wp), GFP_KERNEL);
+ if (!wp) {
+ NS_ERR("unable to allocate memory.\n");
+ return -ENOMEM;
+ }
+ wp->page_no = page_no;
+ wp->max_writes = max_writes;
+ list_add(&wp->list, &weak_pages);
+ } while (*w);
+ return 0;
+}
+
+static int write_error(unsigned int page_no)
+{
+ struct weak_page *wp;
+
+ list_for_each_entry(wp, &weak_pages, list)
+ if (wp->page_no == page_no) {
+ if (wp->writes_done >= wp->max_writes)
+ return 1;
+ wp->writes_done += 1;
+ return 0;
+ }
+ return 0;
+}
+
+static int parse_gravepages(void)
+{
+ char *g;
+ int zero_ok;
+ unsigned int page_no;
+ unsigned int max_reads;
+ struct grave_page *gp;
+
+ if (!gravepages)
+ return 0;
+ g = gravepages;
+ do {
+ zero_ok = (*g == '0' ? 1 : 0);
+ page_no = simple_strtoul(g, &g, 0);
+ if (!zero_ok && !page_no) {
+ NS_ERR("invalid gravepagess.\n");
+ return -EINVAL;
+ }
+ max_reads = 3;
+ if (*g == ':') {
+ g += 1;
+ max_reads = simple_strtoul(g, &g, 0);
+ }
+ if (*g == ',')
+ g += 1;
+ gp = kzalloc(sizeof(*gp), GFP_KERNEL);
+ if (!gp) {
+ NS_ERR("unable to allocate memory.\n");
+ return -ENOMEM;
+ }
+ gp->page_no = page_no;
+ gp->max_reads = max_reads;
+ list_add(&gp->list, &grave_pages);
+ } while (*g);
+ return 0;
+}
+
+static int read_error(unsigned int page_no)
+{
+ struct grave_page *gp;
+
+ list_for_each_entry(gp, &grave_pages, list)
+ if (gp->page_no == page_no) {
+ if (gp->reads_done >= gp->max_reads)
+ return 1;
+ gp->reads_done += 1;
+ return 0;
+ }
+ return 0;
+}
+
+static void free_lists(void)
+{
+ struct list_head *pos, *n;
+ list_for_each_safe(pos, n, &weak_blocks) {
+ list_del(pos);
+ kfree(list_entry(pos, struct weak_block, list));
+ }
+ list_for_each_safe(pos, n, &weak_pages) {
+ list_del(pos);
+ kfree(list_entry(pos, struct weak_page, list));
+ }
+ list_for_each_safe(pos, n, &grave_pages) {
+ list_del(pos);
+ kfree(list_entry(pos, struct grave_page, list));
+ }
+ kfree(erase_block_wear);
+}
+
+static int setup_wear_reporting(struct mtd_info *mtd)
+{
+ size_t mem;
+
+ if (!rptwear)
+ return 0;
+ wear_eb_count = 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");
+ return -ENOMEM;
+ }
+ erase_block_wear = kzalloc(mem, GFP_KERNEL);
+ if (!erase_block_wear) {
+ NS_ERR("Too many erase blocks for wear reporting\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void update_wear(unsigned int erase_block_no)
+{
+ unsigned long wmin = -1, wmax = 0, avg;
+ unsigned long deciles[10], decile_max[10], tot = 0;
+ unsigned int i;
+
+ if (!erase_block_wear)
+ return;
+ total_wear += 1;
+ if (total_wear == 0)
+ NS_ERR("Erase counter total overflow\n");
+ erase_block_wear[erase_block_no] += 1;
+ if (erase_block_wear[erase_block_no] == 0)
+ NS_ERR("Erase counter overflow for erase block %u\n", erase_block_no);
+ rptwear_cnt += 1;
+ if (rptwear_cnt < rptwear)
+ return;
+ rptwear_cnt = 0;
+ /* Calc wear stats */
+ for (i = 0; i < wear_eb_count; ++i) {
+ unsigned long wear = erase_block_wear[i];
+ if (wear < wmin)
+ wmin = wear;
+ if (wear > wmax)
+ wmax = wear;
+ tot += wear;
+ }
+ for (i = 0; i < 9; ++i) {
+ deciles[i] = 0;
+ decile_max[i] = (wmax * (i + 1) + 5) / 10;
+ }
+ deciles[9] = 0;
+ decile_max[9] = wmax;
+ for (i = 0; i < wear_eb_count; ++i) {
+ int d;
+ unsigned long wear = erase_block_wear[i];
+ for (d = 0; d < 10; ++d)
+ if (wear <= decile_max[d]) {
+ deciles[d] += 1;
+ break;
+ }
+ }
+ avg = tot / wear_eb_count;
+ /* Output wear report */
+ NS_INFO("*** Wear Report ***\n");
+ NS_INFO("Total numbers of erases: %lu\n", tot);
+ NS_INFO("Number of erase blocks: %u\n", wear_eb_count);
+ NS_INFO("Average number of erases: %lu\n", avg);
+ NS_INFO("Maximum number of erases: %lu\n", wmax);
+ NS_INFO("Minimum number of erases: %lu\n", wmin);
+ for (i = 0; i < 10; ++i) {
+ unsigned long from = (i ? decile_max[i - 1] + 1 : 0);
+ if (from > decile_max[i])
+ continue;
+ NS_INFO("Number of ebs with erase counts from %lu to %lu : %lu\n",
+ from,
+ decile_max[i],
+ deciles[i]);
+ }
+ NS_INFO("*** End of Wear Report ***\n");
+}
+
/*
* Returns the string representation of 'state' state.
*/
@@ -822,9 +1213,31 @@ static void read_page(struct nandsim *ns, int num)
NS_DBG("read_page: page %d not allocated\n", ns->regs.row);
memset(ns->buf.byte, 0xFF, num);
} else {
+ unsigned int page_no = ns->regs.row;
NS_DBG("read_page: page %d allocated, reading from %d\n",
ns->regs.row, ns->regs.column + ns->regs.off);
+ if (read_error(page_no)) {
+ int i;
+ memset(ns->buf.byte, 0xFF, num);
+ for (i = 0; i < num; ++i)
+ ns->buf.byte[i] = random32();
+ NS_WARN("simulating read error in page %u\n", page_no);
+ return;
+ }
memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num);
+ if (bitflips && random32() < (1 << 22)) {
+ int flips = 1;
+ if (bitflips > 1)
+ flips = (random32() % (int) bitflips) + 1;
+ while (flips--) {
+ int pos = random32() % (num * 8);
+ ns->buf.byte[pos / 8] ^= (1 << (pos % 8));
+ NS_WARN("read_page: flipping bit %d in page %d "
+ "reading from %d ecc: corrected=%u failed=%u\n",
+ pos, ns->regs.row, ns->regs.column + ns->regs.off,
+ nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed);
+ }
+ }
}
}
@@ -883,6 +1296,7 @@ static int do_state_action(struct nandsim *ns, uint32_t action)
{
int num;
int busdiv = ns->busw == 8 ? 1 : 2;
+ unsigned int erase_block_no, page_no;
action &= ACTION_MASK;
@@ -942,14 +1356,24 @@ static int do_state_action(struct nandsim *ns, uint32_t action)
8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column;
ns->regs.column = 0;
+ erase_block_no = ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift);
+
NS_DBG("do_state_action: erase sector at address %#x, off = %d\n",
ns->regs.row, NS_RAW_OFFSET(ns));
- NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift));
+ NS_LOG("erase sector %u\n", erase_block_no);
erase_sector(ns);
NS_MDELAY(erase_delay);
+ if (erase_block_wear)
+ update_wear(erase_block_no);
+
+ if (erase_error(erase_block_no)) {
+ NS_WARN("simulating erase failure in erase block %u\n", erase_block_no);
+ return -1;
+ }
+
break;
case ACTION_PRGPAGE:
@@ -972,6 +1396,8 @@ static int do_state_action(struct nandsim *ns, uint32_t action)
if (prog_page(ns, num) == -1)
return -1;
+ page_no = ns->regs.row;
+
NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n",
num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off);
NS_LOG("programm page %d\n", ns->regs.row);
@@ -979,6 +1405,11 @@ static int do_state_action(struct nandsim *ns, uint32_t action)
NS_UDELAY(programm_delay);
NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv);
+ if (write_error(page_no)) {
+ NS_WARN("simulating write failure in page %u\n", page_no);
+ return -1;
+ }
+
break;
case ACTION_ZEROOFF:
@@ -1503,7 +1934,7 @@ static int __init ns_init_module(void)
{
struct nand_chip *chip;
struct nandsim *nand;
- int retval = -ENOMEM;
+ int retval = -ENOMEM, i;
if (bus_width != 8 && bus_width != 16) {
NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width);
@@ -1533,6 +1964,8 @@ static int __init ns_init_module(void)
chip->verify_buf = ns_nand_verify_buf;
chip->read_word = ns_nand_read_word;
chip->ecc.mode = NAND_ECC_SOFT;
+ /* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */
+ /* and 'badblocks' parameters to work */
chip->options |= NAND_SKIP_BBTSCAN;
/*
@@ -1557,6 +1990,15 @@ static int __init ns_init_module(void)
nsmtd->owner = THIS_MODULE;
+ if ((retval = parse_weakblocks()) != 0)
+ goto error;
+
+ if ((retval = parse_weakpages()) != 0)
+ goto error;
+
+ if ((retval = parse_gravepages()) != 0)
+ goto error;
+
if ((retval = nand_scan(nsmtd, 1)) != 0) {
NS_ERR("can't register NAND Simulator\n");
if (retval > 0)
@@ -1564,23 +2006,44 @@ static int __init ns_init_module(void)
goto error;
}
- if ((retval = init_nandsim(nsmtd)) != 0) {
- NS_ERR("scan_bbt: can't initialize the nandsim structure\n");
- goto error;
+ if (overridesize) {
+ u_int32_t new_size = nsmtd->erasesize << overridesize;
+ if (new_size >> overridesize != nsmtd->erasesize) {
+ NS_ERR("overridesize is too big\n");
+ goto err_exit;
+ }
+ /* 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;
}
- if ((retval = nand_default_bbt(nsmtd)) != 0) {
- free_nandsim(nand);
- goto error;
- }
+ if ((retval = setup_wear_reporting(nsmtd)) != 0)
+ goto err_exit;
+
+ if ((retval = init_nandsim(nsmtd)) != 0)
+ goto err_exit;
- /* Register NAND as one big partition */
- add_mtd_partitions(nsmtd, &nand->part, 1);
+ if ((retval = parse_badblocks(nand, nsmtd)) != 0)
+ goto err_exit;
+
+ if ((retval = nand_default_bbt(nsmtd)) != 0)
+ goto err_exit;
+
+ /* Register NAND partitions */
+ if ((retval = add_mtd_partitions(nsmtd, &nand->partitions[0], nand->nbparts)) != 0)
+ goto err_exit;
return 0;
+err_exit:
+ free_nandsim(nand);
+ nand_release(nsmtd);
+ for (i = 0;i < ARRAY_SIZE(nand->partitions); ++i)
+ kfree(nand->partitions[i].name);
error:
kfree(nsmtd);
+ free_lists();
return retval;
}
@@ -1593,10 +2056,14 @@ module_init(ns_init_module);
static void __exit ns_cleanup_module(void)
{
struct nandsim *ns = (struct nandsim *)(((struct nand_chip *)nsmtd->priv)->priv);
+ int i;
free_nandsim(ns); /* Free nandsim private resources */
- nand_release(nsmtd); /* Unregisterd drived */
+ nand_release(nsmtd); /* Unregister driver */
+ for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i)
+ kfree(ns->partitions[i].name);
kfree(nsmtd); /* Free other structures */
+ free_lists();
}
module_exit(ns_cleanup_module);
@@ -1604,4 +2071,3 @@ module_exit(ns_cleanup_module);
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("Artem B. Bityuckiy");
MODULE_DESCRIPTION ("The NAND flash simulator");
-
diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig
index 373bddce8f1..c257d397d08 100644
--- a/drivers/mtd/onenand/Kconfig
+++ b/drivers/mtd/onenand/Kconfig
@@ -2,20 +2,18 @@
# linux/drivers/mtd/onenand/Kconfig
#
-menu "OneNAND Flash Device Drivers"
- depends on MTD != n
-
-config MTD_ONENAND
+menuconfig MTD_ONENAND
tristate "OneNAND Device Support"
depends on MTD
help
This enables support for accessing all type of OneNAND flash
devices. For further information see
- <http://www.samsung.com/Products/Semiconductor/Flash/OneNAND_TM/index.htm>.
+ <http://www.samsung.com/Products/Semiconductor/OneNAND/index.htm>
+
+if MTD_ONENAND
config MTD_ONENAND_VERIFY_WRITE
bool "Verify OneNAND page writes"
- depends on MTD_ONENAND
help
This adds an extra check when data is written to the flash. The
OneNAND flash device internally checks only bits transitioning
@@ -25,13 +23,12 @@ config MTD_ONENAND_VERIFY_WRITE
config MTD_ONENAND_GENERIC
tristate "OneNAND Flash device via platform device driver"
- depends on MTD_ONENAND && ARM
+ depends on ARM
help
Support for OneNAND flash via platform device driver.
config MTD_ONENAND_OTP
bool "OneNAND OTP Support"
- depends on MTD_ONENAND
help
One Block of the NAND Flash Array memory is reserved as
a One-Time Programmable Block memory area.
@@ -43,4 +40,4 @@ config MTD_ONENAND_OTP
OTP block is fully-guaranteed to be a valid block.
-endmenu
+endif # MTD_ONENAND
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 9e14a26ca4e..000794c6caf 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -836,9 +836,11 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col
int readcol = column;
int readend = column + thislen;
int lastgap = 0;
+ unsigned int i;
uint8_t *oob_buf = this->oob_buf;
- for (free = this->ecclayout->oobfree; free->length; ++free) {
+ free = this->ecclayout->oobfree;
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
if (readcol >= lastgap)
readcol += free->offset - lastgap;
if (readend >= lastgap)
@@ -846,7 +848,8 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col
lastgap = free->offset + free->length;
}
this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
- for (free = this->ecclayout->oobfree; free->length; ++free) {
+ free = this->ecclayout->oobfree;
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
int free_end = free->offset + free->length;
if (free->offset < readend && free_end > readcol) {
int st = max_t(int,free->offset,readcol);
@@ -854,7 +857,7 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col
int n = ed - st;
memcpy(buf, oob_buf + st, n);
buf += n;
- } else
+ } else if (column == 0)
break;
}
return 0;
@@ -1280,15 +1283,18 @@ static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
int writecol = column;
int writeend = column + thislen;
int lastgap = 0;
+ unsigned int i;
- for (free = this->ecclayout->oobfree; free->length; ++free) {
+ free = this->ecclayout->oobfree;
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
if (writecol >= lastgap)
writecol += free->offset - lastgap;
if (writeend >= lastgap)
writeend += free->offset - lastgap;
lastgap = free->offset + free->length;
}
- for (free = this->ecclayout->oobfree; free->length; ++free) {
+ free = this->ecclayout->oobfree;
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
int free_end = free->offset + free->length;
if (free->offset < writeend && free_end > writecol) {
int st = max_t(int,free->offset,writecol);
@@ -1296,7 +1302,7 @@ static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
int n = ed - st;
memcpy(oob_buf + st, buf, n);
buf += n;
- } else
+ } else if (column == 0)
break;
}
return 0;
@@ -2386,7 +2392,8 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
* the out of band area
*/
this->ecclayout->oobavail = 0;
- for (i = 0; this->ecclayout->oobfree[i].length; i++)
+ for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES &&
+ this->ecclayout->oobfree[i].length; i++)
this->ecclayout->oobavail +=
this->ecclayout->oobfree[i].length;
mtd->oobavail = this->ecclayout->oobavail;