aboutsummaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/Kconfig15
-rw-r--r--drivers/mtd/chips/Kconfig2
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0001.c2
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0002.c8
-rw-r--r--drivers/mtd/chips/jedec_probe.c17
-rw-r--r--drivers/mtd/devices/Kconfig2
-rw-r--r--drivers/mtd/maps/Kconfig9
-rw-r--r--drivers/mtd/maps/Makefile1
-rw-r--r--drivers/mtd/maps/amd76xrom.c34
-rw-r--r--drivers/mtd/maps/arctic-mtd.c6
-rw-r--r--drivers/mtd/maps/bast-flash.c2
-rw-r--r--drivers/mtd/maps/beech-mtd.c6
-rw-r--r--drivers/mtd/maps/cstm_mips_ixx.c129
-rw-r--r--drivers/mtd/maps/dmv182.c2
-rw-r--r--drivers/mtd/maps/esb2rom.c450
-rw-r--r--drivers/mtd/maps/nettel.c2
-rw-r--r--drivers/mtd/maps/physmap.c2
-rw-r--r--drivers/mtd/maps/redwood.c2
-rw-r--r--drivers/mtd/mtd_blkdevs.c4
-rw-r--r--drivers/mtd/mtdchar.c3
-rw-r--r--drivers/mtd/nand/Kconfig21
-rw-r--r--drivers/mtd/nand/Makefile1
-rw-r--r--drivers/mtd/nand/at91_nand.c220
-rw-r--r--drivers/mtd/nand/cs553x_nand.c2
-rw-r--r--drivers/mtd/nand/edb7312.c2
-rw-r--r--drivers/mtd/nand/nand_base.c27
-rw-r--r--drivers/mtd/nand/nandsim.c235
-rw-r--r--drivers/mtd/nftlcore.c4
-rw-r--r--drivers/mtd/onenand/Kconfig8
-rw-r--r--drivers/mtd/onenand/onenand_base.c154
30 files changed, 1079 insertions, 293 deletions
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index a03e862851d..291660abe3a 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -166,7 +166,7 @@ config MTD_CHAR
config MTD_BLOCK
tristate "Caching block device access to MTD devices"
- depends on MTD
+ depends on MTD && BLOCK
---help---
Although most flash chips have an erase size too large to be useful
as block devices, it is possible to use MTD devices which are based
@@ -188,7 +188,7 @@ config MTD_BLOCK
config MTD_BLOCK_RO
tristate "Readonly block device access to MTD devices"
- depends on MTD_BLOCK!=y && MTD
+ depends on MTD_BLOCK!=y && MTD && BLOCK
help
This allows you to mount read-only file systems (such as cramfs)
from an MTD device, without the overhead (and danger) of the caching
@@ -199,7 +199,7 @@ config MTD_BLOCK_RO
config FTL
tristate "FTL (Flash Translation Layer) support"
- depends on MTD
+ depends on MTD && BLOCK
---help---
This provides support for the original Flash Translation Layer which
is part of the PCMCIA specification. It uses a kind of pseudo-
@@ -215,7 +215,7 @@ config FTL
config NFTL
tristate "NFTL (NAND Flash Translation Layer) support"
- depends on MTD
+ depends on MTD && BLOCK
---help---
This provides support for the NAND Flash Translation Layer which is
used on M-Systems' DiskOnChip devices. It uses a kind of pseudo-
@@ -238,7 +238,7 @@ config NFTL_RW
config INFTL
tristate "INFTL (Inverse NAND Flash Translation Layer) support"
- depends on MTD
+ depends on MTD && BLOCK
---help---
This provides support for the Inverse NAND Flash Translation
Layer which is used on M-Systems' newer DiskOnChip devices. It
@@ -255,7 +255,7 @@ config INFTL
config RFD_FTL
tristate "Resident Flash Disk (Flash Translation Layer) support"
- depends on MTD
+ depends on MTD && BLOCK
---help---
This provides support for the flash translation layer known
as the Resident Flash Disk (RFD), as used by the Embedded BIOS
@@ -265,8 +265,7 @@ config RFD_FTL
config SSFDC
tristate "NAND SSFDC (SmartMedia) read only translation layer"
- depends on MTD
- default n
+ depends on MTD && BLOCK
help
This enables read only access to SmartMedia formatted NAND
flash. You can mount it with FAT file system.
diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig
index 6d8f30deb86..72e6d73beb4 100644
--- a/drivers/mtd/chips/Kconfig
+++ b/drivers/mtd/chips/Kconfig
@@ -270,7 +270,7 @@ config MTD_JEDEC
tristate "JEDEC device support"
depends on MTD && MTD_OBSOLETE_CHIPS && BROKEN
help
- Enable older older JEDEC flash interface devices for self
+ Enable older JEDEC flash interface devices for self
programming flash. It is commonly used in older AMD chips. It is
only called JEDEC because the JEDEC association
<http://www.jedec.org/> distributes the identification codes for the
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c
index 7ea49a0d5ec..e24973636e6 100644
--- a/drivers/mtd/chips/cfi_cmdset_0001.c
+++ b/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -2224,6 +2224,8 @@ static int cfi_intelext_suspend(struct mtd_info *mtd)
case FL_CFI_QUERY:
case FL_JEDEC_QUERY:
if (chip->oldstate == FL_READY) {
+ /* place the chip in a known state before suspend */
+ map_write(map, CMD(0xFF), cfi->chips[i].start);
chip->oldstate = chip->state;
chip->state = FL_PM_SUSPENDED;
/* No need to wake_up() on this state change -
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c
index 702ae4cd869..ca0882b5819 100644
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -48,6 +48,7 @@
#define MANUFACTURER_ATMEL 0x001F
#define MANUFACTURER_SST 0x00BF
#define SST49LF004B 0x0060
+#define SST49LF040B 0x0050
#define SST49LF008A 0x005a
#define AT49BV6416 0x00d6
@@ -233,6 +234,7 @@ static struct cfi_fixup cfi_fixup_table[] = {
};
static struct cfi_fixup jedec_fixup_table[] = {
{ MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, },
+ { MANUFACTURER_SST, SST49LF040B, fixup_use_fwh_lock, NULL, },
{ MANUFACTURER_SST, SST49LF008A, fixup_use_fwh_lock, NULL, },
{ 0, 0, NULL, NULL }
};
@@ -519,10 +521,12 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
if (mode == FL_WRITING) /* FIXME: Erase-suspend-program appears broken. */
goto sleep;
- if (!(mode == FL_READY || mode == FL_POINT
+ if (!( mode == FL_READY
+ || mode == FL_POINT
|| !cfip
|| (mode == FL_WRITING && (cfip->EraseSuspend & 0x2))
- || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1))))
+ || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1)
+ )))
goto sleep;
/* We could check to see if we're trying to access the sector
diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c
index 1154dac715a..58e561e8769 100644
--- a/drivers/mtd/chips/jedec_probe.c
+++ b/drivers/mtd/chips/jedec_probe.c
@@ -154,6 +154,7 @@
#define SST39SF010A 0x00B5
#define SST39SF020A 0x00B6
#define SST49LF004B 0x0060
+#define SST49LF040B 0x0050
#define SST49LF008A 0x005a
#define SST49LF030A 0x001C
#define SST49LF040A 0x0051
@@ -1401,6 +1402,20 @@ static const struct amd_flash_info jedec_table[] = {
}
}, {
.mfr_id = MANUFACTURER_SST,
+ .dev_id = SST49LF040B,
+ .name = "SST 49LF040B",
+ .uaddr = {
+ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+ },
+ .DevSize = SIZE_512KiB,
+ .CmdSet = P_ID_AMD_STD,
+ .NumEraseRegions= 1,
+ .regions = {
+ ERASEINFO(0x01000,128),
+ }
+ }, {
+
+ .mfr_id = MANUFACTURER_SST,
.dev_id = SST49LF004B,
.name = "SST 49LF004B",
.uaddr = {
@@ -1874,7 +1889,7 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index)
/*
- * There is a BIG problem properly ID'ing the JEDEC devic and guaranteeing
+ * There is a BIG problem properly ID'ing the JEDEC device and guaranteeing
* the mapped address, unlock addresses, and proper chip ID. This function
* attempts to minimize errors. It is doubtfull that this probe will ever
* be perfect - consequently there should be some module parameters that
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 16c02b5ccf7..440f6851da6 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -136,7 +136,7 @@ config MTDRAM_ABS_POS
config MTD_BLOCK2MTD
tristate "MTD using block device"
- depends on MTD
+ depends on MTD && BLOCK
help
This driver allows a block device to appear as an MTD. It would
generally be used in the following cases:
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 24747bdc3e1..7514a9bee01 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -184,6 +184,15 @@ config MTD_ICHXROM
BE VERY CAREFUL.
+config MTD_ESB2ROM
+ tristate "BIOS flash chip on Intel ESB Controller Hub 2"
+ depends on X86 && MTD_JEDECPROBE
+ help
+ Support for treating the BIOS flash chip on ESB2 motherboards
+ as an MTD device - with this you can reprogram your BIOS.
+
+ BE VERY CAREFUL.
+
config MTD_SCB2_FLASH
tristate "BIOS flash chip on Intel SCB2 boards"
depends on X86 && MTD_JEDECPROBE
diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
index 191c1928bbe..9061432c5e1 100644
--- a/drivers/mtd/maps/Makefile
+++ b/drivers/mtd/maps/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_MTD_DC21285) += dc21285.o
obj-$(CONFIG_MTD_DILNETPC) += dilnetpc.o
obj-$(CONFIG_MTD_L440GX) += l440gx.o
obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o
+obj-$(CONFIG_MTD_ESB2ROM) += esb2rom.o
obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o
obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o
obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o
diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c
index 797caffb20b..78b671172bb 100644
--- a/drivers/mtd/maps/amd76xrom.c
+++ b/drivers/mtd/maps/amd76xrom.c
@@ -7,6 +7,7 @@
#include <linux/module.h>
#include <linux/types.h>
+#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/io.h>
@@ -44,6 +45,23 @@ struct amd76xrom_map_info {
char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN];
};
+/* The 2 bits controlling the window size are often set to allow reading
+ * the BIOS, but too small to allow writing, since the lock registers are
+ * 4MiB lower in the address space than the data.
+ *
+ * This is intended to prevent flashing the bios, perhaps accidentally.
+ *
+ * This parameter allows the normal driver to over-ride the BIOS settings.
+ *
+ * The bits are 6 and 7. If both bits are set, it is a 5MiB window.
+ * If only the 7 Bit is set, it is a 4MiB window. Otherwise, a
+ * 64KiB window.
+ *
+ */
+static uint win_size_bits;
+module_param(win_size_bits, uint, 0);
+MODULE_PARM_DESC(win_size_bits, "ROM window size bits override for 0x43 byte, normally set by BIOS.");
+
static struct amd76xrom_window amd76xrom_window = {
.maps = LIST_HEAD_INIT(amd76xrom_window.maps),
};
@@ -95,6 +113,16 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev,
/* Remember the pci dev I find the window in - already have a ref */
window->pdev = pdev;
+ /* Enable the selected rom window. This is often incorrectly
+ * set up by the BIOS, and the 4MiB offset for the lock registers
+ * requires the full 5MiB of window space.
+ *
+ * This 'write, then read' approach leaves the bits for
+ * other uses of the hardware info.
+ */
+ pci_read_config_byte(pdev, 0x43, &byte);
+ pci_write_config_byte(pdev, 0x43, byte | win_size_bits );
+
/* Assume the rom window is properly setup, and find it's size */
pci_read_config_byte(pdev, 0x43, &byte);
if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6))) {
@@ -129,12 +157,6 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev,
(unsigned long long)window->rsrc.end);
}
-#if 0
-
- /* Enable the selected rom window */
- pci_read_config_byte(pdev, 0x43, &byte);
- pci_write_config_byte(pdev, 0x43, byte | rwindow->segen_bits);
-#endif
/* Enable writes through the rom window */
pci_read_config_byte(pdev, 0x40, &byte);
diff --git a/drivers/mtd/maps/arctic-mtd.c b/drivers/mtd/maps/arctic-mtd.c
index 642d96bc891..2cc90243627 100644
--- a/drivers/mtd/maps/arctic-mtd.c
+++ b/drivers/mtd/maps/arctic-mtd.c
@@ -96,7 +96,7 @@ static struct mtd_partition arctic_partitions[PARTITIONS] = {
static int __init
init_arctic_mtd(void)
{
- int err = 0;
+ int err;
printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR);
@@ -112,7 +112,7 @@ init_arctic_mtd(void)
arctic_mtd = do_map_probe("cfi_probe", &arctic_mtd_map);
if (!arctic_mtd) {
- iounmap((void *) arctic_mtd_map.virt);
+ iounmap(arctic_mtd_map.virt);
return -ENXIO;
}
@@ -121,7 +121,7 @@ init_arctic_mtd(void)
err = add_mtd_partitions(arctic_mtd, arctic_partitions, PARTITIONS);
if (err) {
printk("%s: add_mtd_partitions failed\n", NAME);
- iounmap((void *) arctic_mtd_map.virt);
+ iounmap(arctic_mtd_map.virt);
}
return err;
diff --git a/drivers/mtd/maps/bast-flash.c b/drivers/mtd/maps/bast-flash.c
index 51f962dd7e3..e074bb6787d 100644
--- a/drivers/mtd/maps/bast-flash.c
+++ b/drivers/mtd/maps/bast-flash.c
@@ -1,4 +1,4 @@
-/* linux/drivers/mtd/maps/bast_flash.c
+/* linux/drivers/mtd/maps/bast-flash.c
*
* Copyright (c) 2004-2005 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
diff --git a/drivers/mtd/maps/beech-mtd.c b/drivers/mtd/maps/beech-mtd.c
index a64b1a5ab31..d76d5981b86 100644
--- a/drivers/mtd/maps/beech-mtd.c
+++ b/drivers/mtd/maps/beech-mtd.c
@@ -72,7 +72,7 @@ static struct mtd_partition beech_partitions[2] = {
static int __init
init_beech_mtd(void)
{
- int err = 0;
+ int err;
printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR);
@@ -89,7 +89,7 @@ init_beech_mtd(void)
beech_mtd = do_map_probe("cfi_probe", &beech_mtd_map);
if (!beech_mtd) {
- iounmap((void *) beech_mtd_map.virt);
+ iounmap(beech_mtd_map.virt);
return -ENXIO;
}
@@ -98,7 +98,7 @@ init_beech_mtd(void)
err = add_mtd_partitions(beech_mtd, beech_partitions, 2);
if (err) {
printk("%s: add_mtd_partitions failed\n", NAME);
- iounmap((void *) beech_mtd_map.virt);
+ iounmap(beech_mtd_map.virt);
}
return err;
diff --git a/drivers/mtd/maps/cstm_mips_ixx.c b/drivers/mtd/maps/cstm_mips_ixx.c
index d6bef100d69..d57eba24c20 100644
--- a/drivers/mtd/maps/cstm_mips_ixx.c
+++ b/drivers/mtd/maps/cstm_mips_ixx.c
@@ -40,62 +40,6 @@
#include <linux/mtd/partitions.h>
#include <linux/delay.h>
-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
-#define CC_GCR 0xB4013818
-#define CC_GPBCR 0xB401380A
-#define CC_GPBDR 0xB4013808
-#define CC_M68K_DEVICE 1
-#define CC_M68K_FUNCTION 6
-#define CC_CONFADDR 0xB8004000
-#define CC_CONFDATA 0xB8004004
-#define CC_FC_FCR 0xB8002004
-#define CC_FC_DCR 0xB8002008
-#define CC_GPACR 0xB4013802
-#define CC_GPAICR 0xB4013804
-#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */
-
-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
-void cstm_mips_ixx_set_vpp(struct map_info *map,int vpp)
-{
- static DEFINE_SPINLOCK(vpp_lock);
- static int vpp_count = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&vpp_lock, flags);
-
- if (vpp) {
- if (!vpp_count++) {
- __u16 data;
- __u8 data1;
- static u8 first = 1;
-
- // Set GPIO port B pin3 to high
- data = *(__u16 *)(CC_GPBCR);
- data = (data & 0xff0f) | 0x0040;
- *(__u16 *)CC_GPBCR = data;
- *(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) | 0x08;
- if (first) {
- first = 0;
- /* need to have this delay for first
- enabling vpp after powerup */
- udelay(40);
- }
- }
- } else {
- if (!--vpp_count) {
- __u16 data;
-
- // Set GPIO port B pin3 to high
- data = *(__u16 *)(CC_GPBCR);
- data = (data & 0xff3f) | 0x0040;
- *(__u16 *)CC_GPBCR = data;
- *(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) & 0xf7;
- }
- }
- spin_unlock_irqrestore(&vpp_lock, flags);
-}
-#endif
-
/* board and partition description */
#define MAX_PHYSMAP_PARTITIONS 8
@@ -107,29 +51,6 @@ struct cstm_mips_ixx_info {
int num_partitions;
};
-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
-#define PHYSMAP_NUMBER 1 // number of board desc structs needed, one per contiguous flash type
-const struct cstm_mips_ixx_info cstm_mips_ixx_board_desc[PHYSMAP_NUMBER] =
-{
- { // 28F128J3A in 2x16 configuration
- "big flash", // name
- 0x08000000, // window_addr
- 0x02000000, // window_size
- 4, // bankwidth
- 1, // num_partitions
- }
-
-};
-static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = {
-{ // 28F128J3A in 2x16 configuration
- {
- .name = "main partition ",
- .size = 0x02000000, // 128 x 2 x 128k byte sectors
- .offset = 0,
- },
-},
-};
-#else /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */
#define PHYSMAP_NUMBER 1 // number of board desc structs needed, one per contiguous flash type
const struct cstm_mips_ixx_info cstm_mips_ixx_board_desc[PHYSMAP_NUMBER] =
{
@@ -151,7 +72,6 @@ static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP
},
},
};
-#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */
struct map_info cstm_mips_ixx_map[PHYSMAP_NUMBER];
@@ -175,8 +95,8 @@ int __init init_cstm_mips_ixx(void)
printk(KERN_WARNING "Failed to ioremap\n");
for (j = 0; j < i; j++) {
if (cstm_mips_ixx_map[j].virt) {
- iounmap((void *)cstm_mips_ixx_map[j].virt);
- cstm_mips_ixx_map[j].virt = 0;
+ iounmap(cstm_mips_ixx_map[j].virt);
+ cstm_mips_ixx_map[j].virt = NULL;
}
}
return -EIO;
@@ -184,17 +104,10 @@ int __init init_cstm_mips_ixx(void)
cstm_mips_ixx_map[i].name = cstm_mips_ixx_board_desc[i].name;
cstm_mips_ixx_map[i].size = cstm_mips_ixx_board_desc[i].window_size;
cstm_mips_ixx_map[i].bankwidth = cstm_mips_ixx_board_desc[i].bankwidth;
-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
- cstm_mips_ixx_map[i].set_vpp = cstm_mips_ixx_set_vpp;
-#endif
simple_map_init(&cstm_mips_ixx_map[i]);
//printk(KERN_NOTICE "cstm_mips_ixx: ioremap is %x\n",(unsigned int)(cstm_mips_ixx_map[i].virt));
}
-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
- setup_ITE_IVR_flash();
-#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */
-
for (i=0;i<PHYSMAP_NUMBER;i++) {
parts = &cstm_mips_ixx_partitions[i][0];
jedec = 0;
@@ -214,8 +127,8 @@ int __init init_cstm_mips_ixx(void)
else {
for (i = 0; i < PHYSMAP_NUMBER; i++) {
if (cstm_mips_ixx_map[i].virt) {
- iounmap((void *)cstm_mips_ixx_map[i].virt);
- cstm_mips_ixx_map[i].virt = 0;
+ iounmap(cstm_mips_ixx_map[i].virt);
+ cstm_mips_ixx_map[i].virt = NULL;
}
}
return -ENXIO;
@@ -241,38 +154,6 @@ static void __exit cleanup_cstm_mips_ixx(void)
}
}
}
-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
-void PCISetULongByOffset(__u32 DevNumber, __u32 FuncNumber, __u32 Offset, __u32 data)
-{
- __u32 offset;
-
- offset = ( unsigned long )( 0x80000000 | ( DevNumber << 11 ) + ( FuncNumber << 8 ) + Offset) ;
-
- *(__u32 *)CC_CONFADDR = offset;
- *(__u32 *)CC_CONFDATA = data;
-}
-void setup_ITE_IVR_flash()
-{
- __u32 size, base;
-
- size = 0x0e000000; // 32MiB
- base = (0x08000000) >> 8 >>1; // Bug: we must shift one more bit
-
- /* need to set ITE flash to 32 bits instead of default 8 */
-#ifdef CONFIG_MIPS_IVR
- *(__u32 *)CC_FC_FCR = 0x55;
- *(__u32 *)CC_GPACR = 0xfffc;
-#else
- *(__u32 *)CC_FC_FCR = 0x77;
-#endif
- /* turn bursting off */
- *(__u32 *)CC_FC_DCR = 0x0;
-
- /* setup for one chip 4 byte PCI access */
- PCISetULongByOffset(CC_M68K_DEVICE, CC_M68K_FUNCTION, 0x60, size | base);
- PCISetULongByOffset(CC_M68K_DEVICE, CC_M68K_FUNCTION, 0x64, 0x02);
-}
-#endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */
module_init(init_cstm_mips_ixx);
module_exit(cleanup_cstm_mips_ixx);
@@ -280,4 +161,4 @@ module_exit(cleanup_cstm_mips_ixx);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>");
-MODULE_DESCRIPTION("MTD map driver for ITE 8172G and Globespan IVR boards");
+MODULE_DESCRIPTION("MTD map driver for MIPS boards");
diff --git a/drivers/mtd/maps/dmv182.c b/drivers/mtd/maps/dmv182.c
index a43c49905ca..e0558b0b2fe 100644
--- a/drivers/mtd/maps/dmv182.c
+++ b/drivers/mtd/maps/dmv182.c
@@ -1,6 +1,6 @@
/*
- * drivers/mtd/maps/svme182.c
+ * drivers/mtd/maps/dmv182.c
*
* Flash map driver for the Dy4 SVME182 board
*
diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c
new file mode 100644
index 00000000000..a9d808a617c
--- /dev/null
+++ b/drivers/mtd/maps/esb2rom.c
@@ -0,0 +1,450 @@
+/*
+ * esb2rom.c
+ *
+ * Normal mappings of flash chips in physical memory
+ * through the Intel ESB2 Southbridge.
+ *
+ * This was derived from ichxrom.c in May 2006 by
+ * Lew Glendenning <lglendenning@lnxi.com>
+ *
+ * Eric Biederman, of course, was a major help in this effort.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/cfi.h>
+#include <linux/mtd/flashchip.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/list.h>
+
+#define MOD_NAME KBUILD_BASENAME
+
+#define ADDRESS_NAME_LEN 18
+
+#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */
+
+#define BIOS_CNTL 0xDC
+#define BIOS_LOCK_ENABLE 0x02
+#define BIOS_WRITE_ENABLE 0x01
+
+/* This became a 16-bit register, and EN2 has disappeared */
+#define FWH_DEC_EN1 0xD8
+#define FWH_F8_EN 0x8000
+#define FWH_F0_EN 0x4000
+#define FWH_E8_EN 0x2000
+#define FWH_E0_EN 0x1000
+#define FWH_D8_EN 0x0800
+#define FWH_D0_EN 0x0400
+#define FWH_C8_EN 0x0200
+#define FWH_C0_EN 0x0100
+#define FWH_LEGACY_F_EN 0x0080
+#define FWH_LEGACY_E_EN 0x0040
+/* reserved 0x0020 and 0x0010 */
+#define FWH_70_EN 0x0008
+#define FWH_60_EN 0x0004
+#define FWH_50_EN 0x0002
+#define FWH_40_EN 0x0001
+
+/* these are 32-bit values */
+#define FWH_SEL1 0xD0
+#define FWH_SEL2 0xD4
+
+#define FWH_8MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
+ FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \
+ FWH_70_EN | FWH_60_EN | FWH_50_EN | FWH_40_EN)
+
+#define FWH_7MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
+ FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \
+ FWH_70_EN | FWH_60_EN | FWH_50_EN)
+
+#define FWH_6MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
+ FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \
+ FWH_70_EN | FWH_60_EN)
+
+#define FWH_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
+ FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \
+ FWH_70_EN)
+
+#define FWH_4MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
+ FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN)
+
+#define FWH_3_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
+ FWH_D8_EN | FWH_D0_EN | FWH_C8_EN)
+
+#define FWH_3MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
+ FWH_D8_EN | FWH_D0_EN)
+
+#define FWH_2_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
+ FWH_D8_EN)
+
+#define FWH_2MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN)
+
+#define FWH_1_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN)
+
+#define FWH_1MiB (FWH_F8_EN | FWH_F0_EN)
+
+#define FWH_0_5MiB (FWH_F8_EN)
+
+
+struct esb2rom_window {
+ void __iomem* virt;
+ unsigned long phys;
+ unsigned long size;
+ struct list_head maps;
+ struct resource rsrc;
+ struct pci_dev *pdev;
+};
+
+struct esb2rom_map_info {
+ struct list_head list;
+ struct map_info map;
+ struct mtd_info *mtd;
+ struct resource rsrc;
+ char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN];
+};
+
+static struct esb2rom_window esb2rom_window = {
+ .maps = LIST_HEAD_INIT(esb2rom_window.maps),
+};
+
+static void esb2rom_cleanup(struct esb2rom_window *window)
+{
+ struct esb2rom_map_info *map, *scratch;
+ u8 byte;
+
+ /* Disable writes through the rom window */
+ pci_read_config_byte(window->pdev, BIOS_CNTL, &byte);
+ pci_write_config_byte(window->pdev, BIOS_CNTL,
+ byte & ~BIOS_WRITE_ENABLE);
+
+ /* Free all of the mtd devices */
+ list_for_each_entry_safe(map, scratch, &window->maps, list) {
+ if (map->rsrc.parent)
+ release_resource(&map->rsrc);
+ del_mtd_device(map->mtd);
+ map_destroy(map->mtd);
+ list_del(&map->list);
+ kfree(map);
+ }
+ if (window->rsrc.parent)
+ release_resource(&window->rsrc);
+ if (window->virt) {
+ iounmap(window->virt);
+ window->virt = NULL;
+ window->phys = 0;
+ window->size = 0;
+ }
+ pci_dev_put(window->pdev);
+}
+
+static int __devinit esb2rom_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
+ struct esb2rom_window *window = &esb2rom_window;
+ struct esb2rom_map_info *map = NULL;
+ unsigned long map_top;
+ u8 byte;
+ u16 word;
+
+ /* For now I just handle the ecb2 and I assume there
+ * are not a lot of resources up at the top of the address
+ * space. It is possible to handle other devices in the
+ * top 16MiB but it is very painful. Also since
+ * you can only really attach a FWH to an ICHX there
+ * a number of simplifications you can make.
+ *
+ * Also you can page firmware hubs if an 8MiB window isn't enough
+ * but don't currently handle that case either.
+ */
+ window->pdev = pci_dev_get(pdev);
+
+ /* RLG: experiment 2. Force the window registers to the widest values */
+
+/*
+ pci_read_config_word(pdev, FWH_DEC_EN1, &word);
+ printk(KERN_DEBUG "Original FWH_DEC_EN1 : %x\n", word);
+ pci_write_config_byte(pdev, FWH_DEC_EN1, 0xff);
+ pci_read_config_byte(pdev, FWH_DEC_EN1, &byte);
+ printk(KERN_DEBUG "New FWH_DEC_EN1 : %x\n", byte);
+
+ pci_read_config_byte(pdev, FWH_DEC_EN2, &byte);
+ printk(KERN_DEBUG "Original FWH_DEC_EN2 : %x\n", byte);
+ pci_write_config_byte(pdev, FWH_DEC_EN2, 0x0f);
+ pci_read_config_byte(pdev, FWH_DEC_EN2, &byte);
+ printk(KERN_DEBUG "New FWH_DEC_EN2 : %x\n", byte);
+*/
+
+ /* Find a region continuous to the end of the ROM window */
+ window->phys = 0;
+ pci_read_config_word(pdev, FWH_DEC_EN1, &word);
+ printk(KERN_DEBUG "pci_read_config_byte : %x\n", word);
+
+ if ((word & FWH_8MiB) == FWH_8MiB)
+ window->phys = 0xff400000;
+ else if ((word & FWH_7MiB) == FWH_7MiB)
+ window->phys = 0xff500000;
+ else if ((word & FWH_6MiB) == FWH_6MiB)
+ window->phys = 0xff600000;
+ else if ((word & FWH_5MiB) == FWH_5MiB)
+ window->phys = 0xFF700000;
+ else if ((word & FWH_4MiB) == FWH_4MiB)
+ window->phys = 0xffc00000;
+ else if ((word & FWH_3_5MiB) == FWH_3_5MiB)
+ window->phys = 0xffc80000;
+ else if ((word & FWH_3MiB) == FWH_3MiB)
+ window->phys = 0xffd00000;
+ else if ((word & FWH_2_5MiB) == FWH_2_5MiB)
+ window->phys = 0xffd80000;
+ else if ((word & FWH_2MiB) == FWH_2MiB)
+ window->phys = 0xffe00000;
+ else if ((word & FWH_1_5MiB) == FWH_1_5MiB)
+ window->phys = 0xffe80000;
+ else if ((word & FWH_1MiB) == FWH_1MiB)
+ window->phys = 0xfff00000;
+ else if ((word & FWH_0_5MiB) == FWH_0_5MiB)
+ window->phys = 0xfff80000;
+
+ /* reserved 0x0020 and 0x0010 */
+ window->phys -= 0x400000UL;
+ window->size = (0xffffffffUL - window->phys) + 1UL;
+
+ /* Enable writes through the rom window */
+ pci_read_config_byte(pdev, BIOS_CNTL, &byte);
+ if (!(byte & BIOS_WRITE_ENABLE) && (byte & (BIOS_LOCK_ENABLE))) {
+ /* The BIOS will generate an error if I enable
+ * this device, so don't even try.
+ */
+ printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n");
+ goto out;
+ }
+ pci_write_config_byte(pdev, BIOS_CNTL, byte | BIOS_WRITE_ENABLE);
+
+ /*
+ * Try to reserve the window mem region. If this fails then
+ * it is likely due to the window being "reseved" by the BIOS.
+ */
+ window->rsrc.name = MOD_NAME;
+ window->rsrc.start = window->phys;
+ window->rsrc.end = window->phys + window->size - 1;
+ window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+ if (request_resource(&iomem_resource, &window->rsrc)) {
+ window->rsrc.parent = NULL;
+ printk(KERN_DEBUG MOD_NAME
+ ": %s(): Unable to register resource"
+ " 0x%.08llx-0x%.08llx - kernel bug?\n",
+ __func__,
+ (unsigned long long)window->rsrc.start,
+ (unsigned long long)window->rsrc.end);
+ }
+
+ /* Map the firmware hub into my address space. */
+ window->virt = ioremap_nocache(window->phys, window->size);
+ if (!window->virt) {
+ printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n",
+ window->phys, window->size);
+ goto out;
+ }
+
+ /* Get the first address to look for an rom chip at */
+ map_top = window->phys;
+ if ((window->phys & 0x3fffff) != 0) {
+ /* if not aligned on 4MiB, look 4MiB lower in address space */
+ map_top = window->phys + 0x400000;
+ }
+#if 1
+ /* The probe sequence run over the firmware hub lock
+ * registers sets them to 0x7 (no access).
+ * (Insane hardware design, but most copied Intel's.)
+ * ==> Probe at most the last 4M of the address space.
+ */
+ if (map_top < 0xffc00000)
+ map_top = 0xffc00000;
+#endif
+ /* Loop through and look for rom chips */
+ while ((map_top - 1) < 0xffffffffUL) {
+ struct cfi_private *cfi;
+ unsigned long offset;
+ int i;
+
+ if (!map)
+ map = kmalloc(sizeof(*map), GFP_KERNEL);
+ if (!map) {
+ printk(KERN_ERR MOD_NAME ": kmalloc failed");
+ goto out;
+ }
+ memset(map, 0, sizeof(*map));
+ INIT_LIST_HEAD(&map->list);
+ map->map.name = map->map_name;
+ map->map.phys = map_top;
+ offset = map_top - window->phys;
+ map->map.virt = (void __iomem *)
+ (((unsigned long)(window->virt)) + offset);
+ map->map.size = 0xffffffffUL - map_top + 1UL;
+ /* Set the name of the map to the address I am trying */
+ sprintf(map->map_name, "%s @%08lx",
+ MOD_NAME, map->map.phys);
+
+ /* Firmware hubs only use vpp when being programmed
+ * in a factory setting. So in-place programming
+ * needs to use a different method.
+ */
+ for(map->map.bankwidth = 32; map->map.bankwidth;
+ map->map.bankwidth >>= 1) {
+ char **probe_type;
+ /* Skip bankwidths that are not supported */
+ if (!map_bankwidth_supported(map->map.bankwidth))
+ continue;
+
+ /* Setup the map methods */
+ simple_map_init(&map->map);
+
+ /* Try all of the probe methods */
+ probe_type = rom_probe_types;
+ for(; *probe_type; probe_type++) {
+ map->mtd = do_map_probe(*probe_type, &map->map);
+ if (map->mtd)
+ goto found;
+ }
+ }
+ map_top += ROM_PROBE_STEP_SIZE;
+ continue;
+ found:
+ /* Trim the size if we are larger than the map */
+ if (map->mtd->size > map->map.size) {
+ printk(KERN_WARNING MOD_NAME
+ " rom(%u) larger than window(%lu). fixing...\n",
+ map->mtd->size, map->map.size);
+ map->mtd->size = map->map.size;
+ }
+ if (window->rsrc.parent) {
+ /*
+ * Registering the MTD device in iomem may not be possible
+ * if there is a BIOS "reserved" and BUSY range. If this
+ * fails then continue anyway.
+ */
+ map->rsrc.name = map->map_name;
+ map->rsrc.start = map->map.phys;
+ map->rsrc.end = map->map.phys + map->mtd->size - 1;
+ map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+ if (request_resource(&window->rsrc, &map->rsrc)) {
+ printk(KERN_ERR MOD_NAME
+ ": cannot reserve MTD resource\n");
+ map->rsrc.parent = NULL;
+ }
+ }
+
+ /* Make the whole region visible in the map */
+ map->map.virt = window->virt;
+ map->map.phys = window->phys;
+ cfi = map->map.fldrv_priv;
+ for(i = 0; i < cfi->numchips; i++)
+ cfi->chips[i].start += offset;
+
+ /* Now that the mtd devices is complete claim and export it */
+ map->mtd->owner = THIS_MODULE;
+ if (add_mtd_device(map->mtd)) {
+ map_destroy(map->mtd);
+ map->mtd = NULL;
+ goto out;
+ }
+
+ /* Calculate the new value of map_top */
+ map_top += map->mtd->size;
+
+ /* File away the map structure */
+ list_add(&map->list, &window->maps);
+ map = NULL;
+ }
+
+ out:
+ /* Free any left over map structures */
+ kfree(map);
+
+ /* See if I have any map structures */
+ if (list_empty(&window->maps)) {
+ esb2rom_cleanup(window);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static void __devexit esb2rom_remove_one (struct pci_dev *pdev)
+{
+ struct esb2rom_window *window = &esb2rom_window;
+ esb2rom_cleanup(window);
+}
+
+static struct pci_device_id esb2rom_pci_tbl[] __devinitdata = {
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0,
+ PCI_ANY_ID, PCI_ANY_ID, },
+ { 0, },
+};
+
+#if 0
+MODULE_DEVICE_TABLE(pci, esb2rom_pci_tbl);
+
+static struct pci_driver esb2rom_driver = {
+ .name = MOD_NAME,
+ .id_table = esb2rom_pci_tbl,
+ .probe = esb2rom_init_one,
+ .remove = esb2rom_remove_one,
+};
+#endif
+
+static int __init init_esb2rom(void)
+{
+ struct pci_dev *pdev;
+ struct pci_device_id *id;
+ int retVal;
+
+ pdev = NULL;
+ for (id = esb2rom_pci_tbl; id->vendor; id++) {
+ printk(KERN_DEBUG "device id = %x\n", id->device);
+ pdev = pci_get_device(id->vendor, id->device, NULL);
+ if (pdev) {
+ printk(KERN_DEBUG "matched device = %x\n", id->device);
+ break;
+ }
+ }
+ if (pdev) {
+ printk(KERN_DEBUG "matched device id %x\n", id->device);
+ retVal = esb2rom_init_one(pdev, &esb2rom_pci_tbl[0]);
+ pci_dev_put(pdev);
+ printk(KERN_DEBUG "retVal = %d\n", retVal);
+ return retVal;
+ }
+ return -ENXIO;
+#if 0
+ return pci_register_driver(&esb2rom_driver);
+#endif
+}
+
+static void __exit cleanup_esb2rom(void)
+{
+ esb2rom_remove_one(esb2rom_window.pdev);
+}
+
+module_init(init_esb2rom);
+module_exit(cleanup_esb2rom);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Lew Glendenning <lglendenning@lnxi.com>");
+MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ESB2 southbridge");
diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c
index 198e840ff6d..f9e8e5bcbc3 100644
--- a/drivers/mtd/maps/nettel.c
+++ b/drivers/mtd/maps/nettel.c
@@ -463,7 +463,7 @@ int __init nettel_init(void)
#ifdef CONFIG_MTD_CFI_INTELEXT
out_unmap1:
- iounmap((void *) nettel_intel_map.virt);
+ iounmap(nettel_intel_map.virt);
#endif
out_unmap2:
diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c
index bc7cc71788b..d1717763f71 100644
--- a/drivers/mtd/maps/physmap.c
+++ b/drivers/mtd/maps/physmap.c
@@ -62,7 +62,7 @@ static int physmap_flash_remove(struct platform_device *dev)
}
if (info->map.virt != NULL)
- iounmap((void *)info->map.virt);
+ iounmap(info->map.virt);
if (info->res != NULL) {
release_resource(info->res);
diff --git a/drivers/mtd/maps/redwood.c b/drivers/mtd/maps/redwood.c
index 2257d2b500c..4d858b3d5f8 100644
--- a/drivers/mtd/maps/redwood.c
+++ b/drivers/mtd/maps/redwood.c
@@ -126,7 +126,7 @@ static struct mtd_info *redwood_mtd;
int __init init_redwood_flash(void)
{
- int err = 0;
+ int err;
printk(KERN_NOTICE "redwood: flash mapping: %x at %x\n",
WINDOW_SIZE, WINDOW_ADDR);
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 458d3c8ae1e..178b53b56be 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -46,7 +46,7 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
nsect = req->current_nr_sectors;
buf = req->buffer;
- if (!(req->flags & REQ_CMD))
+ if (!blk_fs_request(req))
return 0;
if (block + nsect > get_capacity(req->rq_disk))
@@ -69,7 +69,7 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
return 1;
default:
- printk(KERN_NOTICE "Unknown request %ld\n", rq_data_dir(req));
+ printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req));
return 0;
}
}
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 5b6acfcb2b8..866c8e0d57e 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -616,6 +616,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos));
memcpy(&oi.oobfree, mtd->ecclayout->oobfree,
sizeof(oi.oobfree));
+ oi.eccbytes = mtd->ecclayout->eccbytes;
if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo)))
return -EFAULT;
@@ -715,7 +716,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
if (!mtd->ecclayout)
return -EOPNOTSUPP;
- if (copy_to_user(argp, &mtd->ecclayout,
+ if (copy_to_user(argp, mtd->ecclayout,
sizeof(struct nand_ecclayout)))
return -EFAULT;
break;
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 5e97e63e127..564f79d1665 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -21,7 +21,7 @@ config MTD_NAND_VERIFY_WRITE
NAND flash device internally checks only bits transitioning
from 1 to 0. There is a rare possibility that even though the
device thinks the write was successful, a bit could have been
- flipped accidentaly due to device wear or something else.
+ flipped accidentally due to device wear or something else.
config MTD_NAND_ECC_SMC
bool "NAND ECC Smart Media byte order"
@@ -219,6 +219,13 @@ config MTD_NAND_SHARPSL
tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
depends on MTD_NAND && ARCH_PXA
+config MTD_NAND_CAFE
+ tristate "NAND support for OLPC CAFÉ chip"
+ 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)
@@ -232,12 +239,12 @@ config MTD_NAND_CS553X
If you say "m", the module will be called "cs553x_nand.ko".
-config MTD_NAND_CAFE
- tristate "NAND support for OLPC CAFÉ chip"
- depends on PCI
- help
- Use NAND flash attached to the CAFÉ chip designed for the $100
- laptop.
+config MTD_NAND_AT91
+ bool "Support for NAND Flash / SmartMedia on AT91"
+ depends on MTD_NAND && 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"
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 9346b831bc3..7cebc10c474 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -23,5 +23,6 @@ obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o
obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
+obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o
nand-objs = nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/at91_nand.c b/drivers/mtd/nand/at91_nand.c
new file mode 100644
index 00000000000..a58ed376308
--- /dev/null
+++ b/drivers/mtd/nand/at91_nand.c
@@ -0,0 +1,220 @@
+/*
+ * drivers/mtd/nand/at91_nand.c
+ *
+ * Copyright (C) 2003 Rick Bronson
+ *
+ * Derived from drivers/mtd/nand/autcpu12.c
+ * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ * Derived from drivers/mtd/spia.c
+ * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/sizes.h>
+
+#include <asm/hardware.h>
+#include <asm/arch/board.h>
+#include <asm/arch/gpio.h>
+
+struct at91_nand_host {
+ struct nand_chip nand_chip;
+ struct mtd_info mtd;
+ void __iomem *io_base;
+ struct at91_nand_data *board;
+};
+
+/*
+ * Hardware specific access to control-lines
+ */
+static void at91_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct at91_nand_host *host = nand_chip->priv;
+
+ if (cmd == NAND_CMD_NONE)
+ return;
+
+ if (ctrl & NAND_CLE)
+ writeb(cmd, host->io_base + (1 << host->board->cle));
+ else
+ writeb(cmd, host->io_base + (1 << host->board->ale));
+}
+
+/*
+ * Read the Device Ready pin.
+ */
+static int at91_nand_device_ready(struct mtd_info *mtd)
+{
+ struct nand_chip *nand_chip = mtd->priv;
+ struct at91_nand_host *host = nand_chip->priv;
+
+ return at91_get_gpio_value(host->board->rdy_pin);
+}
+
+/*
+ * Enable NAND.
+ */
+static void at91_nand_enable(struct at91_nand_host *host)
+{
+ if (host->board->enable_pin)
+ at91_set_gpio_value(host->board->enable_pin, 0);
+}
+
+/*
+ * Disable NAND.
+ */
+static void at91_nand_disable(struct at91_nand_host *host)
+{
+ if (host->board->enable_pin)
+ at91_set_gpio_value(host->board->enable_pin, 1);
+}
+
+/*
+ * Probe for the NAND device.
+ */
+static int __init at91_nand_probe(struct platform_device *pdev)
+{
+ struct at91_nand_host *host;
+ struct mtd_info *mtd;
+ struct nand_chip *nand_chip;
+ int res;
+
+#ifdef CONFIG_MTD_PARTITIONS
+ struct mtd_partition *partitions = NULL;
+ int num_partitions = 0;
+#endif
+
+ /* Allocate memory for the device structure (and zero it) */
+ host = kzalloc(sizeof(struct at91_nand_host), GFP_KERNEL);
+ if (!host) {
+ printk(KERN_ERR "at91_nand: failed to allocate device structure.\n");
+ return -ENOMEM;
+ }
+
+ host->io_base = ioremap(pdev->resource[0].start,
+ pdev->resource[0].end - pdev->resource[0].start + 1);
+ if (host->io_base == NULL) {
+ printk(KERN_ERR "at91_nand: ioremap failed\n");
+ kfree(host);
+ return -EIO;
+ }
+
+ mtd = &host->mtd;
+ nand_chip = &host->nand_chip;
+ host->board = pdev->dev.platform_data;
+
+ nand_chip->priv = host; /* link the private data structures */
+ mtd->priv = nand_chip;
+ mtd->owner = THIS_MODULE;
+
+ /* Set address of NAND IO lines */
+ nand_chip->IO_ADDR_R = host->io_base;
+ nand_chip->IO_ADDR_W = host->io_base;
+ nand_chip->cmd_ctrl = at91_nand_cmd_ctrl;
+ nand_chip->dev_ready = at91_nand_device_ready;
+ nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */
+ nand_chip->chip_delay = 20; /* 20us command delay time */
+
+ platform_set_drvdata(pdev, host);
+ at91_nand_enable(host);
+
+ if (host->board->det_pin) {
+ if (at91_get_gpio_value(host->board->det_pin)) {
+ printk ("No SmartMedia card inserted.\n");
+ res = ENXIO;
+ goto out;
+ }
+ }
+
+ /* Scan to find existance of the device */
+ if (nand_scan(mtd, 1)) {
+ res = -ENXIO;
+ goto out;
+ }
+
+#ifdef CONFIG_MTD_PARTITIONS
+ if (host->board->partition_info)
+ partitions = host->board->partition_info(mtd->size, &num_partitions);
+
+ if ((!partitions) || (num_partitions == 0)) {
+ printk(KERN_ERR "at91_nand: No parititions defined, or unsupported device.\n");
+ res = ENXIO;
+ goto release;
+ }
+
+ res = add_mtd_partitions(mtd, partitions, num_partitions);
+#else
+ res = add_mtd_device(mtd);
+#endif
+
+ if (!res)
+ return res;
+
+release:
+ nand_release(mtd);
+out:
+ at91_nand_disable(host);
+ platform_set_drvdata(pdev, NULL);
+ iounmap(host->io_base);
+ kfree(host);
+ return res;
+}
+
+/*
+ * Remove a NAND device.
+ */
+static int __devexit at91_nand_remove(struct platform_device *pdev)
+{
+ struct at91_nand_host *host = platform_get_drvdata(pdev);
+ struct mtd_info *mtd = &host->mtd;
+
+ nand_release(mtd);
+
+ at91_nand_disable(host);
+
+ iounmap(host->io_base);
+ kfree(host);
+
+ return 0;
+}
+
+static struct platform_driver at91_nand_driver = {
+ .probe = at91_nand_probe,
+ .remove = at91_nand_remove,
+ .driver = {
+ .name = "at91_nand",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init at91_nand_init(void)
+{
+ return platform_driver_register(&at91_nand_driver);
+}
+
+
+static void __exit at91_nand_exit(void)
+{
+ platform_driver_unregister(&at91_nand_driver);
+}
+
+
+module_init(at91_nand_init);
+module_exit(at91_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Rick Bronson");
+MODULE_DESCRIPTION("NAND/SmartMedia driver for AT91RM9200");
diff --git a/drivers/mtd/nand/cs553x_nand.c b/drivers/mtd/nand/cs553x_nand.c
index e0a1d386e58..94924d52a9b 100644
--- a/drivers/mtd/nand/cs553x_nand.c
+++ b/drivers/mtd/nand/cs553x_nand.c
@@ -249,7 +249,7 @@ static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
goto out;
out_ior:
- iounmap((void *)this->IO_ADDR_R);
+ iounmap(this->IO_ADDR_R);
out_mtd:
kfree(new_mtd);
out:
diff --git a/drivers/mtd/nand/edb7312.c b/drivers/mtd/nand/edb7312.c
index 12017f3c6bd..1daf8231aae 100644
--- a/drivers/mtd/nand/edb7312.c
+++ b/drivers/mtd/nand/edb7312.c
@@ -199,7 +199,7 @@ static void __exit ep7312_cleanup(void)
nand_release(ap7312_mtd);
/* Release io resource */
- iounmap((void *)this->IO_ADDR_R);
+ iounmap(this->IO_ADDR_R);
/* Free the MTD device structure */
kfree(ep7312_mtd);
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index baece61169f..f23ab2c7043 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -755,7 +755,7 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_read_page_swecc - {REPLACABLE] software ecc based page read function
+ * nand_read_page_swecc - [REPLACABLE] software ecc based page read function
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: buffer to store read data
@@ -795,7 +795,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_read_page_hwecc - {REPLACABLE] hardware ecc based page read function
+ * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: buffer to store read data
@@ -839,7 +839,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read
+ * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: buffer to store read data
@@ -971,7 +971,6 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
page = realpage & chip->pagemask;
col = (int)(from & (mtd->writesize - 1));
- chip->oob_poi = chip->buffers->oobrbuf;
buf = ops->datbuf;
oob = ops->oobbuf;
@@ -1270,8 +1269,6 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
realpage = (int)(from >> chip->page_shift);
page = realpage & chip->pagemask;
- chip->oob_poi = chip->buffers->oobrbuf;
-
while(1) {
sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
buf = nand_transfer_oob(chip, buf, ops);
@@ -1375,7 +1372,7 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_write_page_swecc - {REPLACABLE] software ecc based page write function
+ * nand_write_page_swecc - [REPLACABLE] software ecc based page write function
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: data buffer
@@ -1401,7 +1398,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_write_page_hwecc - {REPLACABLE] hardware ecc based page write function
+ * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: data buffer
@@ -1429,7 +1426,7 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
}
/**
- * nand_write_page_syndrome - {REPLACABLE] hardware ecc syndrom based page write
+ * nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write
* @mtd: mtd info structure
* @chip: nand chip info structure
* @buf: data buffer
@@ -1625,7 +1622,9 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
(chip->pagebuf << chip->page_shift) < (to + ops->len))
chip->pagebuf = -1;
- chip->oob_poi = chip->buffers->oobwbuf;
+ /* If we're not given explicit OOB data, let it be 0xFF */
+ if (likely(!oob))
+ memset(chip->oob_poi, 0xff, mtd->oobsize);
while(1) {
int cached = writelen > bytes && page != blockmask;
@@ -1654,9 +1653,6 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
}
}
- if (unlikely(oob))
- memset(chip->oob_poi, 0xff, mtd->oobsize);
-
ops->retlen = ops->len - writelen;
return ret;
}
@@ -1744,7 +1740,6 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
if (page == chip->pagebuf)
chip->pagebuf = -1;
- chip->oob_poi = chip->buffers->oobwbuf;
memset(chip->oob_poi, 0xff, mtd->oobsize);
nand_fill_oob(chip, ops->oobbuf, ops);
status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
@@ -2348,8 +2343,8 @@ int nand_scan_tail(struct mtd_info *mtd)
if (!chip->buffers)
return -ENOMEM;
- /* Preset the internal oob write buffer */
- memset(chip->buffers->oobwbuf, 0xff, mtd->oobsize);
+ /* Set the internal oob buffer location, just after the page data */
+ chip->oob_poi = chip->buffers->databuf + mtd->writesize;
/*
* If no default placement scheme is given, select an appropriate one
diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c
index 545ff252d81..abebcab4763 100644
--- a/drivers/mtd/nand/nandsim.c
+++ b/drivers/mtd/nand/nandsim.c
@@ -37,10 +37,6 @@
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/delay.h>
-#ifdef CONFIG_NS_ABS_POS
-#include <asm/io.h>
-#endif
-
/* Default simulator parameters values */
#if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \
@@ -231,6 +227,14 @@ MODULE_PARM_DESC(dbg, "Output debug information if not zero");
#define NS_MAX_PREVSTATES 1
/*
+ * A union to represent flash memory contents and flash buffer.
+ */
+union ns_mem {
+ u_char *byte; /* for byte access */
+ uint16_t *word; /* for 16-bit word access */
+};
+
+/*
* The structure which describes all the internal simulator data.
*/
struct nandsim {
@@ -247,17 +251,11 @@ struct nandsim {
uint16_t npstates; /* number of previous states saved */
uint16_t stateidx; /* current state index */
- /* The simulated NAND flash image */
- union flash_media {
- u_char *byte;
- uint16_t *word;
- } mem;
+ /* The simulated NAND flash pages array */
+ union ns_mem *pages;
/* Internal buffer of page + OOB size bytes */
- union internal_buffer {
- u_char *byte; /* for byte access */
- uint16_t *word; /* for 16-bit word access */
- } buf;
+ union ns_mem buf;
/* NAND flash "geometry" */
struct nandsin_geometry {
@@ -346,12 +344,49 @@ static struct mtd_info *nsmtd;
static u_char ns_verify_buf[NS_LARGEST_PAGE_SIZE];
/*
+ * Allocate array of page pointers and initialize the array to NULL
+ * pointers.
+ *
+ * RETURNS: 0 if success, -ENOMEM if memory alloc fails.
+ */
+static int alloc_device(struct nandsim *ns)
+{
+ int i;
+
+ ns->pages = vmalloc(ns->geom.pgnum * sizeof(union ns_mem));
+ if (!ns->pages) {
+ NS_ERR("alloc_map: unable to allocate page array\n");
+ return -ENOMEM;
+ }
+ for (i = 0; i < ns->geom.pgnum; i++) {
+ ns->pages[i].byte = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * Free any allocated pages, and free the array of page pointers.
+ */
+static void free_device(struct nandsim *ns)
+{
+ int i;
+
+ if (ns->pages) {
+ for (i = 0; i < ns->geom.pgnum; i++) {
+ if (ns->pages[i].byte)
+ kfree(ns->pages[i].byte);
+ }
+ vfree(ns->pages);
+ }
+}
+
+/*
* Initialize the nandsim structure.
*
* RETURNS: 0 if success, -ERRNO if failure.
*/
-static int
-init_nandsim(struct mtd_info *mtd)
+static int init_nandsim(struct mtd_info *mtd)
{
struct nand_chip *chip = (struct nand_chip *)mtd->priv;
struct nandsim *ns = (struct nandsim *)(chip->priv);
@@ -439,23 +474,8 @@ init_nandsim(struct mtd_info *mtd)
printk("sector address bytes: %u\n", ns->geom.secaddrbytes);
printk("options: %#x\n", ns->options);
- /* Map / allocate and initialize the flash image */
-#ifdef CONFIG_NS_ABS_POS
- ns->mem.byte = ioremap(CONFIG_NS_ABS_POS, ns->geom.totszoob);
- if (!ns->mem.byte) {
- NS_ERR("init_nandsim: failed to map the NAND flash image at address %p\n",
- (void *)CONFIG_NS_ABS_POS);
- return -ENOMEM;
- }
-#else
- ns->mem.byte = vmalloc(ns->geom.totszoob);
- if (!ns->mem.byte) {
- NS_ERR("init_nandsim: unable to allocate %u bytes for flash image\n",
- ns->geom.totszoob);
- return -ENOMEM;
- }
- memset(ns->mem.byte, 0xFF, ns->geom.totszoob);
-#endif
+ if (alloc_device(ns) != 0)
+ goto error;
/* Allocate / initialize the internal buffer */
ns->buf.byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
@@ -474,11 +494,7 @@ init_nandsim(struct mtd_info *mtd)
return 0;
error:
-#ifdef CONFIG_NS_ABS_POS
- iounmap(ns->mem.byte);
-#else
- vfree(ns->mem.byte);
-#endif
+ free_device(ns);
return -ENOMEM;
}
@@ -486,16 +502,10 @@ error:
/*
* Free the nandsim structure.
*/
-static void
-free_nandsim(struct nandsim *ns)
+static void free_nandsim(struct nandsim *ns)
{
kfree(ns->buf.byte);
-
-#ifdef CONFIG_NS_ABS_POS
- iounmap(ns->mem.byte);
-#else
- vfree(ns->mem.byte);
-#endif
+ free_device(ns);
return;
}
@@ -503,8 +513,7 @@ free_nandsim(struct nandsim *ns)
/*
* Returns the string representation of 'state' state.
*/
-static char *
-get_state_name(uint32_t state)
+static char *get_state_name(uint32_t state)
{
switch (NS_STATE(state)) {
case STATE_CMD_READ0:
@@ -562,8 +571,7 @@ get_state_name(uint32_t state)
*
* RETURNS: 1 if wrong command, 0 if right.
*/
-static int
-check_command(int cmd)
+static int check_command(int cmd)
{
switch (cmd) {
@@ -589,8 +597,7 @@ check_command(int cmd)
/*
* Returns state after command is accepted by command number.
*/
-static uint32_t
-get_state_by_command(unsigned command)
+static uint32_t get_state_by_command(unsigned command)
{
switch (command) {
case NAND_CMD_READ0:
@@ -626,8 +633,7 @@ get_state_by_command(unsigned command)
/*
* Move an address byte to the correspondent internal register.
*/
-static inline void
-accept_addr_byte(struct nandsim *ns, u_char bt)
+static inline void accept_addr_byte(struct nandsim *ns, u_char bt)
{
uint byte = (uint)bt;
@@ -645,8 +651,7 @@ accept_addr_byte(struct nandsim *ns, u_char bt)
/*
* Switch to STATE_READY state.
*/
-static inline void
-switch_to_ready_state(struct nandsim *ns, u_char status)
+static inline void switch_to_ready_state(struct nandsim *ns, u_char status)
{
NS_DBG("switch_to_ready_state: switch to %s state\n", get_state_name(STATE_READY));
@@ -705,8 +710,7 @@ switch_to_ready_state(struct nandsim *ns, u_char status)
* -1 - several matches.
* 0 - operation is found.
*/
-static int
-find_operation(struct nandsim *ns, uint32_t flag)
+static int find_operation(struct nandsim *ns, uint32_t flag)
{
int opsfound = 0;
int i, j, idx = 0;
@@ -791,14 +795,93 @@ find_operation(struct nandsim *ns, uint32_t flag)
}
/*
+ * Returns a pointer to the current page.
+ */
+static inline union ns_mem *NS_GET_PAGE(struct nandsim *ns)
+{
+ return &(ns->pages[ns->regs.row]);
+}
+
+/*
+ * Retuns a pointer to the current byte, within the current page.
+ */
+static inline u_char *NS_PAGE_BYTE_OFF(struct nandsim *ns)
+{
+ return NS_GET_PAGE(ns)->byte + ns->regs.column + ns->regs.off;
+}
+
+/*
+ * Fill the NAND buffer with data read from the specified page.
+ */
+static void read_page(struct nandsim *ns, int num)
+{
+ union ns_mem *mypage;
+
+ mypage = NS_GET_PAGE(ns);
+ if (mypage->byte == NULL) {
+ NS_DBG("read_page: page %d not allocated\n", ns->regs.row);
+ memset(ns->buf.byte, 0xFF, num);
+ } else {
+ NS_DBG("read_page: page %d allocated, reading from %d\n",
+ ns->regs.row, ns->regs.column + ns->regs.off);
+ memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num);
+ }
+}
+
+/*
+ * Erase all pages in the specified sector.
+ */
+static void erase_sector(struct nandsim *ns)
+{
+ union ns_mem *mypage;
+ int i;
+
+ mypage = NS_GET_PAGE(ns);
+ for (i = 0; i < ns->geom.pgsec; i++) {
+ if (mypage->byte != NULL) {
+ NS_DBG("erase_sector: freeing page %d\n", ns->regs.row+i);
+ kfree(mypage->byte);
+ mypage->byte = NULL;
+ }
+ mypage++;
+ }
+}
+
+/*
+ * Program the specified page with the contents from the NAND buffer.
+ */
+static int prog_page(struct nandsim *ns, int num)
+{
+ int i;
+ union ns_mem *mypage;
+ u_char *pg_off;
+
+ mypage = NS_GET_PAGE(ns);
+ if (mypage->byte == NULL) {
+ NS_DBG("prog_page: allocating page %d\n", ns->regs.row);
+ mypage->byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
+ if (mypage->byte == NULL) {
+ NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row);
+ return -1;
+ }
+ memset(mypage->byte, 0xFF, ns->geom.pgszoob);
+ }
+
+ pg_off = NS_PAGE_BYTE_OFF(ns);
+ for (i = 0; i < num; i++)
+ pg_off[i] &= ns->buf.byte[i];
+
+ return 0;
+}
+
+/*
* If state has any action bit, perform this action.
*
* RETURNS: 0 if success, -1 if error.
*/
-static int
-do_state_action(struct nandsim *ns, uint32_t action)
+static int do_state_action(struct nandsim *ns, uint32_t action)
{
- int i, num;
+ int num;
int busdiv = ns->busw == 8 ? 1 : 2;
action &= ACTION_MASK;
@@ -822,7 +905,7 @@ do_state_action(struct nandsim *ns, uint32_t action)
break;
}
num = ns->geom.pgszoob - ns->regs.off - ns->regs.column;
- memcpy(ns->buf.byte, ns->mem.byte + NS_RAW_OFFSET(ns) + ns->regs.off, num);
+ read_page(ns, num);
NS_DBG("do_state_action: (ACTION_CPY:) copy %d bytes to int buf, raw offset %d\n",
num, NS_RAW_OFFSET(ns) + ns->regs.off);
@@ -863,7 +946,7 @@ do_state_action(struct nandsim *ns, uint32_t action)
ns->regs.row, NS_RAW_OFFSET(ns));
NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift));
- memset(ns->mem.byte + NS_RAW_OFFSET(ns), 0xFF, ns->geom.secszoob);
+ erase_sector(ns);
NS_MDELAY(erase_delay);
@@ -886,8 +969,8 @@ do_state_action(struct nandsim *ns, uint32_t action)
return -1;
}
- for (i = 0; i < num; i++)
- ns->mem.byte[NS_RAW_OFFSET(ns) + ns->regs.off + i] &= ns->buf.byte[i];
+ if (prog_page(ns, num) == -1)
+ return -1;
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);
@@ -928,8 +1011,7 @@ do_state_action(struct nandsim *ns, uint32_t action)
/*
* Switch simulator's state.
*/
-static void
-switch_state(struct nandsim *ns)
+static void switch_state(struct nandsim *ns)
{
if (ns->op) {
/*
@@ -1070,8 +1152,7 @@ switch_state(struct nandsim *ns)
}
}
-static u_char
-ns_nand_read_byte(struct mtd_info *mtd)
+static u_char ns_nand_read_byte(struct mtd_info *mtd)
{
struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
u_char outb = 0x00;
@@ -1144,8 +1225,7 @@ ns_nand_read_byte(struct mtd_info *mtd)
return outb;
}
-static void
-ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
+static void ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
{
struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
@@ -1308,15 +1388,13 @@ static void ns_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int bitmask)
ns_nand_write_byte(mtd, cmd);
}
-static int
-ns_device_ready(struct mtd_info *mtd)
+static int ns_device_ready(struct mtd_info *mtd)
{
NS_DBG("device_ready\n");
return 1;
}
-static uint16_t
-ns_nand_read_word(struct mtd_info *mtd)
+static uint16_t ns_nand_read_word(struct mtd_info *mtd)
{
struct nand_chip *chip = (struct nand_chip *)mtd->priv;
@@ -1325,8 +1403,7 @@ ns_nand_read_word(struct mtd_info *mtd)
return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8);
}
-static void
-ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+static void ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
@@ -1353,8 +1430,7 @@ ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
}
}
-static void
-ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+static void ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
@@ -1407,8 +1483,7 @@ ns_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
return;
}
-static int
-ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+static int ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
ns_nand_read_buf(mtd, (u_char *)&ns_verify_buf[0], len);
diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c
index dd5cea8b4a7..b5a5f8da472 100644
--- a/drivers/mtd/nftlcore.c
+++ b/drivers/mtd/nftlcore.c
@@ -175,6 +175,8 @@ int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
return res;
}
+#ifdef CONFIG_NFTL_RW
+
/*
* Write data and oob to flash
*/
@@ -196,8 +198,6 @@ static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
return res;
}
-#ifdef CONFIG_NFTL_RW
-
/* Actual NFTL access routines */
/* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
* when the give Virtual Unit Chain
diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig
index 5930a03736d..373bddce8f1 100644
--- a/drivers/mtd/onenand/Kconfig
+++ b/drivers/mtd/onenand/Kconfig
@@ -21,7 +21,7 @@ config MTD_ONENAND_VERIFY_WRITE
OneNAND flash device internally checks only bits transitioning
from 1 to 0. There is a rare possibility that even though the
device thinks the write was successful, a bit could have been
- flipped accidentaly due to device wear or something else.
+ flipped accidentally due to device wear or something else.
config MTD_ONENAND_GENERIC
tristate "OneNAND Flash device via platform device driver"
@@ -43,10 +43,4 @@ config MTD_ONENAND_OTP
OTP block is fully-guaranteed to be a valid block.
-config MTD_ONENAND_SYNC_READ
- bool "OneNAND Sync. Burst Read Support"
- depends on ARCH_OMAP
- help
- This enables support for Sync. Burst Read.
-
endmenu
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 84ec40d2543..8ed68b28afe 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -1,7 +1,7 @@
/*
* linux/drivers/mtd/onenand/onenand_base.c
*
- * Copyright (C) 2005 Samsung Electronics
+ * Copyright (C) 2005-2006 Samsung Electronics
* Kyungmin Park <kyungmin.park@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -199,6 +199,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
case ONENAND_CMD_UNLOCK:
case ONENAND_CMD_LOCK:
case ONENAND_CMD_LOCK_TIGHT:
+ case ONENAND_CMD_UNLOCK_ALL:
block = -1;
page = -1;
break;
@@ -1211,11 +1212,11 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
end = len >> this->erase_shift;
/* Continuous lock scheme */
- if (this->options & ONENAND_CONT_LOCK) {
+ if (this->options & ONENAND_HAS_CONT_LOCK) {
/* Set start block address */
this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
/* Set end block address */
- this->write_word(end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS);
+ this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS);
/* Write unlock command */
this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
@@ -1236,7 +1237,7 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
}
/* Block lock scheme */
- for (block = start; block < end; block++) {
+ for (block = start; block < start + end; block++) {
/* Set block address */
value = onenand_block_address(this, block);
this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
@@ -1265,6 +1266,79 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
return 0;
}
+/**
+ * onenand_check_lock_status - [OneNAND Interface] Check lock status
+ * @param this onenand chip data structure
+ *
+ * Check lock status
+ */
+static void onenand_check_lock_status(struct onenand_chip *this)
+{
+ unsigned int value, block, status;
+ unsigned int end;
+
+ end = this->chipsize >> this->erase_shift;
+ for (block = 0; block < end; block++) {
+ /* Set block address */
+ value = onenand_block_address(this, block);
+ this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
+ /* Select DataRAM for DDP */
+ value = onenand_bufferram_address(this, block);
+ this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
+ /* Set start block address */
+ this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
+
+ /* Check lock status */
+ status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
+ if (!(status & ONENAND_WP_US))
+ printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status);
+ }
+}
+
+/**
+ * onenand_unlock_all - [OneNAND Interface] unlock all blocks
+ * @param mtd MTD device structure
+ *
+ * Unlock all blocks
+ */
+static int onenand_unlock_all(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+
+ if (this->options & ONENAND_HAS_UNLOCK_ALL) {
+ /* Write unlock command */
+ this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
+
+ /* There's no return value */
+ this->wait(mtd, FL_UNLOCKING);
+
+ /* Sanity check */
+ while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
+ & ONENAND_CTRL_ONGO)
+ continue;
+
+ /* Workaround for all block unlock in DDP */
+ if (this->device_id & ONENAND_DEVICE_IS_DDP) {
+ loff_t ofs;
+ size_t len;
+
+ /* 1st block on another chip */
+ ofs = this->chipsize >> 1;
+ len = 1 << this->erase_shift;
+
+ onenand_unlock(mtd, ofs, len);
+ }
+
+ onenand_check_lock_status(this);
+
+ return 0;
+ }
+
+ mtd->unlock(mtd, 0x0, this->chipsize);
+
+ return 0;
+}
+
#ifdef CONFIG_MTD_ONENAND_OTP
/* Interal OTP operation */
@@ -1564,12 +1638,43 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
#endif /* CONFIG_MTD_ONENAND_OTP */
/**
+ * onenand_lock_scheme - Check and set OneNAND lock scheme
+ * @param mtd MTD data structure
+ *
+ * Check and set OneNAND lock scheme
+ */
+static void onenand_lock_scheme(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+ unsigned int density, process;
+
+ /* Lock scheme depends on density and process */
+ density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+ process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
+
+ /* Lock scheme */
+ if (density >= ONENAND_DEVICE_DENSITY_1Gb) {
+ /* A-Die has all block unlock */
+ if (process) {
+ printk(KERN_DEBUG "Chip support all block unlock\n");
+ this->options |= ONENAND_HAS_UNLOCK_ALL;
+ }
+ } else {
+ /* Some OneNAND has continues lock scheme */
+ if (!process) {
+ printk(KERN_DEBUG "Lock scheme is Continues Lock\n");
+ this->options |= ONENAND_HAS_CONT_LOCK;
+ }
+ }
+}
+
+/**
* onenand_print_device_info - Print device ID
* @param device device ID
*
* Print device ID
*/
-static void onenand_print_device_info(int device)
+static void onenand_print_device_info(int device, int version)
{
int vcc, demuxed, ddp, density;
@@ -1583,6 +1688,7 @@ static void onenand_print_device_info(int device)
(16 << density),
vcc ? "2.65/3.3" : "1.8",
device);
+ printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version);
}
static const struct onenand_manufacturers onenand_manuf_ids[] = {
@@ -1625,9 +1731,14 @@ static int onenand_check_maf(int manuf)
static int onenand_probe(struct mtd_info *mtd)
{
struct onenand_chip *this = mtd->priv;
- int bram_maf_id, bram_dev_id, maf_id, dev_id;
- int version_id;
+ int bram_maf_id, bram_dev_id, maf_id, dev_id, ver_id;
int density;
+ int syscfg;
+
+ /* Save system configuration 1 */
+ syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
+ /* Clear Sync. Burst Read mode to read BootRAM */
+ this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ), this->base + ONENAND_REG_SYS_CFG1);
/* Send the command for reading device ID from BootRAM */
this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM);
@@ -1636,24 +1747,31 @@ static int onenand_probe(struct mtd_info *mtd)
bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0);
bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2);
+ /* Reset OneNAND to read default register values */
+ this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM);
+ /* Wait reset */
+ this->wait(mtd, FL_RESETING);
+
+ /* Restore system configuration 1 */
+ this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
+
/* Check manufacturer ID */
if (onenand_check_maf(bram_maf_id))
return -ENXIO;
- /* Reset OneNAND to read default register values */
- this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM);
-
/* Read manufacturer and device IDs from Register */
maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
+ ver_id= this->read_word(this->base + ONENAND_REG_VERSION_ID);
/* Check OneNAND device */
if (maf_id != bram_maf_id || dev_id != bram_dev_id)
return -ENXIO;
/* Flash device information */
- onenand_print_device_info(dev_id);
+ onenand_print_device_info(dev_id, ver_id);
this->device_id = dev_id;
+ this->version_id = ver_id;
density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
this->chipsize = (16 << density) << 20;
@@ -1676,16 +1794,8 @@ static int onenand_probe(struct mtd_info *mtd)
mtd->size = this->chipsize;
- /* Version ID */
- version_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
- printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version_id);
-
- /* Lock scheme */
- if (density <= ONENAND_DEVICE_DENSITY_512Mb &&
- !(version_id >> ONENAND_VERSION_PROCESS_SHIFT)) {
- printk(KERN_INFO "Lock scheme is Continues Lock\n");
- this->options |= ONENAND_CONT_LOCK;
- }
+ /* Check OneNAND lock scheme */
+ onenand_lock_scheme(mtd);
return 0;
}
@@ -1821,7 +1931,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
mtd->owner = THIS_MODULE;
/* Unlock whole block */
- mtd->unlock(mtd, 0x0, this->chipsize);
+ onenand_unlock_all(mtd);
return this->scan_bbt(mtd);
}