aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2007-04-27 15:34:57 -0700
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-04-27 15:34:57 -0700
commitf00546363fff1576ceddc2690d47e5f9c1dd2e05 (patch)
treef6cb8965b6754fc6ce7570cf1471ebe9874e509a /drivers
parent50f732ee63b91eb08a29974b36bd63e1150bb642 (diff)
parent28b57cddb3ed4f7999e4b76ef36ebaaf6e2e0c37 (diff)
Merge git://git.infradead.org/mtd-2.6
* git://git.infradead.org/mtd-2.6: (46 commits) [MTD] [MAPS] drivers/mtd/maps/ck804xrom.c: convert pci_module_init() [MTD] [NAND] CM-x270 MTD driver [MTD] [NAND] Wrong calculation of page number in nand_block_bad() [MTD] [MAPS] fix plat-ram printk format [JFFS2] Fix compr_rubin.c build after include file elimination. [JFFS2] Handle inodes with only a single metadata node with non-zero isize [JFFS2] Tidy up licensing/copyright boilerplate. [MTD] [OneNAND] Exit loop only when column start with 0 [MTD] [OneNAND] Fix access the past of the real oobfree array [MTD] [OneNAND] Update Samsung OneNAND official URL [JFFS2] Better fix for all-zero node headers [JFFS2] Improve read_inode memory usage, v2. [JFFS2] Improve failure mode if inode checking leaves unchecked space. [JFFS2] Fix cross-endian build. [MTD] Finish conversion mtd_blkdevs to use the kthread API [JFFS2] Obsolete dirent nodes immediately on unlink, where possible. Use menuconfig objects: MTD [MTD] mtd_blkdevs: Convert to use the kthread API [MTD] Fix fwh_lock locking [JFFS2] Speed up mount for directly-mapped NOR flash ...
Diffstat (limited to 'drivers')
-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/ck804xrom.c2
-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/Kconfig63
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/cafe.c25
-rw-r--r--drivers/mtd/nand/cmx270_nand.c267
-rw-r--r--drivers/mtd/nand/nand_base.c8
-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
26 files changed, 1796 insertions, 240 deletions
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 6d1b91bf7ad..c1b47db29bd 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
@@ -294,5 +289,4 @@ source "drivers/mtd/onenand/Kconfig"
source "drivers/mtd/ubi/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\