You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge git://git.infradead.org/mtd-2.6
* git://git.infradead.org/mtd-2.6: (91 commits) [MTD] [NAND] Blackfin on-chip NAND Flash Controller driver [MTD] [NOR] fix ctrl-alt-del can't reboot for intel flash bug [MTD] [NAND] Fix compiler warning in Alauda driver [JFFS2] Remove stray debugging printk [JFFS2] Handle dirents on the flash with embedded zero bytes in names. [JFFS2] Check for creation of dirents with embedded zero bytes in name. [JFFS2] Don't count all 'very dirty' blocks except in debug mode [JFFS2] Check whether garbage-collection actually obsoleted its victim. [JFFS2] Relax threshold for triggering GC due to dirty blocks. [MTD] [OneNAND] Fix typo related with recent commit [JFFS2] Trigger garbage collection when very_dirty_list size becomes excessive [MTD] [NAND] Avoid deadlock in erase callback; release chip lock first. [MTD] [NAND] Resume method for CAFÉ NAND controller [MTD] [NAND] Fix PCI ident table for CAFÉ NAND controller. [MTD] [NAND] s3c2410: fix arch moves [MTD] [OneNAND] fix numerous races [MTD] map driver for NOR flash on the Intel Vermilion Range chipset [JFFS2] Fix unpoint length [MTD] fix CFI point method for discontiguous maps [MTD] MAPS: Merge Lubbock and Mainstone drivers into common PXA2xx driver ...
This commit is contained in:
@@ -275,16 +275,13 @@ int __init board_init (void)
|
||||
int err = 0;
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
board_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL);
|
||||
board_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
|
||||
if (!board_mtd) {
|
||||
printk ("Unable to allocate NAND MTD device structure.\n");
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Initialize structures */
|
||||
memset ((char *) board_mtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip));
|
||||
|
||||
/* map physical adress */
|
||||
baseaddr = (unsigned long)ioremap(CHIP_PHYSICAL_ADDRESS, 1024);
|
||||
if(!baseaddr){
|
||||
|
||||
@@ -278,6 +278,14 @@ config SSFDC
|
||||
This enables read only access to SmartMedia formatted NAND
|
||||
flash. You can mount it with FAT file system.
|
||||
|
||||
config MTD_OOPS
|
||||
tristate "Log panic/oops to an MTD buffer"
|
||||
depends on MTD
|
||||
help
|
||||
This enables panic and oops messages to be logged to a circular
|
||||
buffer in a flash partition where it can be read back at some
|
||||
later point.
|
||||
|
||||
source "drivers/mtd/chips/Kconfig"
|
||||
|
||||
source "drivers/mtd/maps/Kconfig"
|
||||
|
||||
@@ -22,6 +22,7 @@ obj-$(CONFIG_NFTL) += nftl.o
|
||||
obj-$(CONFIG_INFTL) += inftl.o
|
||||
obj-$(CONFIG_RFD_FTL) += rfd_ftl.o
|
||||
obj-$(CONFIG_SSFDC) += ssfdc.o
|
||||
obj-$(CONFIG_MTD_OOPS) += mtdoops.o
|
||||
|
||||
nftl-objs := nftlcore.o nftlmount.o
|
||||
inftl-objs := inftlcore.o inftlmount.o
|
||||
|
||||
@@ -526,7 +526,7 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd,
|
||||
struct cfi_pri_intelext *extp = cfi->cmdset_priv;
|
||||
|
||||
/*
|
||||
* Probing of multi-partition flash ships.
|
||||
* Probing of multi-partition flash chips.
|
||||
*
|
||||
* To support multiple partitions when available, we simply arrange
|
||||
* for each of them to have their own flchip structure even if they
|
||||
@@ -653,7 +653,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
|
||||
resettime:
|
||||
timeo = jiffies + HZ;
|
||||
retry:
|
||||
if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE)) {
|
||||
if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE || mode == FL_SHUTDOWN)) {
|
||||
/*
|
||||
* OK. We have possibility for contension on the write/erase
|
||||
* operations which are global to the real chip and not per
|
||||
@@ -798,6 +798,9 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
|
||||
if (mode == FL_READY && chip->oldstate == FL_READY)
|
||||
return 0;
|
||||
|
||||
case FL_SHUTDOWN:
|
||||
/* The machine is rebooting now,so no one can get chip anymore */
|
||||
return -EIO;
|
||||
default:
|
||||
sleep:
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
@@ -1166,28 +1169,34 @@ static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, si
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
unsigned long ofs;
|
||||
unsigned long ofs, last_end = 0;
|
||||
int chipnum;
|
||||
int ret = 0;
|
||||
|
||||
if (!map->virt || (from + len > mtd->size))
|
||||
return -EINVAL;
|
||||
|
||||
*mtdbuf = (void *)map->virt + from;
|
||||
*retlen = 0;
|
||||
|
||||
/* Now lock the chip(s) to POINT state */
|
||||
|
||||
/* ofs: offset within the first chip that the first read should start */
|
||||
chipnum = (from >> cfi->chipshift);
|
||||
ofs = from - (chipnum << cfi->chipshift);
|
||||
|
||||
*mtdbuf = (void *)map->virt + cfi->chips[chipnum].start + ofs;
|
||||
*retlen = 0;
|
||||
|
||||
while (len) {
|
||||
unsigned long thislen;
|
||||
|
||||
if (chipnum >= cfi->numchips)
|
||||
break;
|
||||
|
||||
/* We cannot point across chips that are virtually disjoint */
|
||||
if (!last_end)
|
||||
last_end = cfi->chips[chipnum].start;
|
||||
else if (cfi->chips[chipnum].start != last_end)
|
||||
break;
|
||||
|
||||
if ((len + ofs -1) >> cfi->chipshift)
|
||||
thislen = (1<<cfi->chipshift) - ofs;
|
||||
else
|
||||
@@ -1201,6 +1210,7 @@ static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, si
|
||||
len -= thislen;
|
||||
|
||||
ofs = 0;
|
||||
last_end += 1 << cfi->chipshift;
|
||||
chipnum++;
|
||||
}
|
||||
return 0;
|
||||
@@ -1780,7 +1790,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
|
||||
static int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
|
||||
{
|
||||
unsigned long ofs, len;
|
||||
int ret;
|
||||
@@ -1930,7 +1940,7 @@ static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
|
||||
printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
|
||||
__FUNCTION__, ofs, len);
|
||||
cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
|
||||
ofs, len, 0);
|
||||
ofs, len, NULL);
|
||||
#endif
|
||||
|
||||
ret = cfi_varsize_frob(mtd, do_xxlock_oneblock,
|
||||
@@ -1940,7 +1950,7 @@ static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
|
||||
printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
|
||||
__FUNCTION__, ret);
|
||||
cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
|
||||
ofs, len, 0);
|
||||
ofs, len, NULL);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
@@ -1954,7 +1964,7 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
|
||||
printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
|
||||
__FUNCTION__, ofs, len);
|
||||
cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
|
||||
ofs, len, 0);
|
||||
ofs, len, NULL);
|
||||
#endif
|
||||
|
||||
ret = cfi_varsize_frob(mtd, do_xxlock_oneblock,
|
||||
@@ -1964,7 +1974,7 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
|
||||
printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
|
||||
__FUNCTION__, ret);
|
||||
cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
|
||||
ofs, len, 0);
|
||||
ofs, len, NULL);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
@@ -2255,7 +2265,7 @@ static void cfi_intelext_save_locks(struct mtd_info *mtd)
|
||||
adr = region->offset + block * len;
|
||||
|
||||
status = cfi_varsize_frob(mtd,
|
||||
do_getlockstatus_oneblock, adr, len, 0);
|
||||
do_getlockstatus_oneblock, adr, len, NULL);
|
||||
if (status)
|
||||
set_bit(block, region->lockmap);
|
||||
else
|
||||
@@ -2402,10 +2412,10 @@ static int cfi_intelext_reset(struct mtd_info *mtd)
|
||||
and switch to array mode so any bootloader in
|
||||
flash is accessible for soft reboot. */
|
||||
spin_lock(chip->mutex);
|
||||
ret = get_chip(map, chip, chip->start, FL_SYNCING);
|
||||
ret = get_chip(map, chip, chip->start, FL_SHUTDOWN);
|
||||
if (!ret) {
|
||||
map_write(map, CMD(0xff), chip->start);
|
||||
chip->state = FL_READY;
|
||||
chip->state = FL_SHUTDOWN;
|
||||
}
|
||||
spin_unlock(chip->mutex);
|
||||
}
|
||||
|
||||
@@ -1609,7 +1609,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
|
||||
}
|
||||
|
||||
|
||||
int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
|
||||
static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
|
||||
{
|
||||
unsigned long ofs, len;
|
||||
int ret;
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
@@ -70,6 +69,7 @@
|
||||
|
||||
/* Fujitsu */
|
||||
#define MBM29F040C 0x00A4
|
||||
#define MBM29F800BA 0x2258
|
||||
#define MBM29LV650UE 0x22D7
|
||||
#define MBM29LV320TE 0x22F6
|
||||
#define MBM29LV320BE 0x22F9
|
||||
@@ -129,6 +129,7 @@
|
||||
#define LH28F640BF 0x00b0
|
||||
|
||||
/* ST - www.st.com */
|
||||
#define M29F800AB 0x0058
|
||||
#define M29W800DT 0x00D7
|
||||
#define M29W800DB 0x005B
|
||||
#define M29W160DT 0x22C4
|
||||
@@ -644,6 +645,23 @@ static const struct amd_flash_info jedec_table[] = {
|
||||
.regions = {
|
||||
ERASEINFO(0x10000,8)
|
||||
}
|
||||
}, {
|
||||
.mfr_id = MANUFACTURER_FUJITSU,
|
||||
.dev_id = MBM29F800BA,
|
||||
.name = "Fujitsu MBM29F800BA",
|
||||
.uaddr = {
|
||||
[0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
|
||||
[1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
|
||||
},
|
||||
.DevSize = SIZE_1MiB,
|
||||
.CmdSet = P_ID_AMD_STD,
|
||||
.NumEraseRegions= 4,
|
||||
.regions = {
|
||||
ERASEINFO(0x04000,1),
|
||||
ERASEINFO(0x02000,2),
|
||||
ERASEINFO(0x08000,1),
|
||||
ERASEINFO(0x10000,15),
|
||||
}
|
||||
}, {
|
||||
.mfr_id = MANUFACTURER_FUJITSU,
|
||||
.dev_id = MBM29LV650UE,
|
||||
@@ -1510,6 +1528,23 @@ static const struct amd_flash_info jedec_table[] = {
|
||||
ERASEINFO(0x1000,256)
|
||||
}
|
||||
|
||||
}, {
|
||||
.mfr_id = MANUFACTURER_ST,
|
||||
.dev_id = M29F800AB,
|
||||
.name = "ST M29F800AB",
|
||||
.uaddr = {
|
||||
[0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
|
||||
[1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
|
||||
},
|
||||
.DevSize = SIZE_1MiB,
|
||||
.CmdSet = P_ID_AMD_STD,
|
||||
.NumEraseRegions= 4,
|
||||
.regions = {
|
||||
ERASEINFO(0x04000,1),
|
||||
ERASEINFO(0x02000,2),
|
||||
ERASEINFO(0x08000,1),
|
||||
ERASEINFO(0x10000,15),
|
||||
}
|
||||
}, {
|
||||
.mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */
|
||||
.dev_id = M29W800DT,
|
||||
|
||||
+13
-12
@@ -60,21 +60,22 @@ config MTD_DATAFLASH
|
||||
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"
|
||||
tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
|
||||
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
|
||||
with the right board-specific platform data.
|
||||
This enables access to most modern SPI flash chips, used for
|
||||
program and data storage. Series supported include Atmel AT26DF,
|
||||
Spansion S25SL, SST 25VF, ST M25P, and Winbond W25X. Other chips
|
||||
are supported as well. See the driver source for the current list,
|
||||
or to add other chips.
|
||||
|
||||
Note that the original DataFlash chips (AT45 series, not AT26DF),
|
||||
need an entirely different driver.
|
||||
|
||||
Set up your spi devices with the right board-specific platform data,
|
||||
if you want to specify device partitioning or to use a device which
|
||||
doesn't support the JEDEC ID instruction.
|
||||
|
||||
config MTD_SLRAM
|
||||
tristate "Uncached system RAM"
|
||||
|
||||
@@ -16,5 +16,4 @@ 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
|
||||
|
||||
@@ -1,485 +0,0 @@
|
||||
/*
|
||||
* Atmel DataFlash driver for Atmel AT91RM9200 (Thunder)
|
||||
* This is a largely modified version of at91_dataflash.c that
|
||||
* supports AT26xxx dataflash chips. The original driver supports
|
||||
* AT45xxx chips.
|
||||
*
|
||||
* Note: This driver was only tested with an AT26F004. It should be
|
||||
* easy to make it work with other AT26xxx dataflash devices, though.
|
||||
*
|
||||
* Copyright (C) 2007 Hans J. Koch <hjk@linutronix.de>
|
||||
* original Copyright (C) SAN People (Pty) Ltd
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
||||
#include <asm/arch/at91_spi.h>
|
||||
|
||||
#define DATAFLASH_MAX_DEVICES 4 /* max number of dataflash devices */
|
||||
|
||||
#define MANUFACTURER_ID_ATMEL 0x1F
|
||||
|
||||
/* command codes */
|
||||
|
||||
#define AT26_OP_READ_STATUS 0x05
|
||||
#define AT26_OP_READ_DEV_ID 0x9F
|
||||
#define AT26_OP_ERASE_PAGE_4K 0x20
|
||||
#define AT26_OP_READ_ARRAY_FAST 0x0B
|
||||
#define AT26_OP_SEQUENTIAL_WRITE 0xAF
|
||||
#define AT26_OP_WRITE_ENABLE 0x06
|
||||
#define AT26_OP_WRITE_DISABLE 0x04
|
||||
#define AT26_OP_SECTOR_PROTECT 0x36
|
||||
#define AT26_OP_SECTOR_UNPROTECT 0x39
|
||||
|
||||
/* status register bits */
|
||||
|
||||
#define AT26_STATUS_BUSY 0x01
|
||||
#define AT26_STATUS_WRITE_ENABLE 0x02
|
||||
|
||||
struct dataflash_local
|
||||
{
|
||||
int spi; /* SPI chip-select number */
|
||||
unsigned int page_size; /* number of bytes per page */
|
||||
};
|
||||
|
||||
|
||||
/* Detected DataFlash devices */
|
||||
static struct mtd_info* mtd_devices[DATAFLASH_MAX_DEVICES];
|
||||
static int nr_devices = 0;
|
||||
|
||||
/* Allocate a single SPI transfer descriptor. We're assuming that if multiple
|
||||
SPI transfers occur at the same time, spi_access_bus() will serialize them.
|
||||
If this is not valid, then either (i) each dataflash 'priv' structure
|
||||
needs it's own transfer descriptor, (ii) we lock this one, or (iii) use
|
||||
another mechanism. */
|
||||
static struct spi_transfer_list* spi_transfer_desc;
|
||||
|
||||
/*
|
||||
* Perform a SPI transfer to access the DataFlash device.
|
||||
*/
|
||||
static int do_spi_transfer(int nr, char* tx, int tx_len, char* rx, int rx_len,
|
||||
char* txnext, int txnext_len, char* rxnext, int rxnext_len)
|
||||
{
|
||||
struct spi_transfer_list* list = spi_transfer_desc;
|
||||
|
||||
list->tx[0] = tx; list->txlen[0] = tx_len;
|
||||
list->rx[0] = rx; list->rxlen[0] = rx_len;
|
||||
|
||||
list->tx[1] = txnext; list->txlen[1] = txnext_len;
|
||||
list->rx[1] = rxnext; list->rxlen[1] = rxnext_len;
|
||||
|
||||
list->nr_transfers = nr;
|
||||
/* Note: spi_transfer() always returns 0, there are no error checks */
|
||||
return spi_transfer(list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the status of the DataFlash device.
|
||||
*/
|
||||
static unsigned char at91_dataflash26_status(void)
|
||||
{
|
||||
unsigned char command[2];
|
||||
|
||||
command[0] = AT26_OP_READ_STATUS;
|
||||
command[1] = 0;
|
||||
|
||||
do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0);
|
||||
|
||||
return command[1];
|
||||
}
|
||||
|
||||
/*
|
||||
* Poll the DataFlash device until it is READY.
|
||||
*/
|
||||
static unsigned char at91_dataflash26_waitready(void)
|
||||
{
|
||||
unsigned char status;
|
||||
|
||||
while (1) {
|
||||
status = at91_dataflash26_status();
|
||||
if (!(status & AT26_STATUS_BUSY))
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable/disable write access
|
||||
*/
|
||||
static void at91_dataflash26_write_enable(int enable)
|
||||
{
|
||||
unsigned char cmd[2];
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "write_enable: enable=%i\n", enable);
|
||||
|
||||
if (enable)
|
||||
cmd[0] = AT26_OP_WRITE_ENABLE;
|
||||
else
|
||||
cmd[0] = AT26_OP_WRITE_DISABLE;
|
||||
cmd[1] = 0;
|
||||
|
||||
do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Protect/unprotect sector
|
||||
*/
|
||||
static void at91_dataflash26_sector_protect(loff_t addr, int protect)
|
||||
{
|
||||
unsigned char cmd[4];
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "sector_protect: addr=0x%06x prot=%d\n",
|
||||
addr, protect);
|
||||
|
||||
if (protect)
|
||||
cmd[0] = AT26_OP_SECTOR_PROTECT;
|
||||
else
|
||||
cmd[0] = AT26_OP_SECTOR_UNPROTECT;
|
||||
cmd[1] = (addr & 0x00FF0000) >> 16;
|
||||
cmd[2] = (addr & 0x0000FF00) >> 8;
|
||||
cmd[3] = (addr & 0x000000FF);
|
||||
|
||||
do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Erase blocks of flash.
|
||||
*/
|
||||
static int at91_dataflash26_erase(struct mtd_info *mtd,
|
||||
struct erase_info *instr)
|
||||
{
|
||||
struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
|
||||
unsigned char cmd[4];
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL1, "dataflash_erase: addr=0x%06x len=%i\n",
|
||||
instr->addr, instr->len);
|
||||
|
||||
/* Sanity checks */
|
||||
if (priv->page_size != 4096)
|
||||
return -EINVAL; /* Can't handle other sizes at the moment */
|
||||
|
||||
if ( ((instr->len % mtd->erasesize) != 0)
|
||||
|| ((instr->len % priv->page_size) != 0)
|
||||
|| ((instr->addr % priv->page_size) != 0)
|
||||
|| ((instr->addr + instr->len) > mtd->size))
|
||||
return -EINVAL;
|
||||
|
||||
spi_access_bus(priv->spi);
|
||||
|
||||
while (instr->len > 0) {
|
||||
at91_dataflash26_write_enable(1);
|
||||
at91_dataflash26_sector_protect(instr->addr, 0);
|
||||
at91_dataflash26_write_enable(1);
|
||||
cmd[0] = AT26_OP_ERASE_PAGE_4K;
|
||||
cmd[1] = (instr->addr & 0x00FF0000) >> 16;
|
||||
cmd[2] = (instr->addr & 0x0000FF00) >> 8;
|
||||
cmd[3] = (instr->addr & 0x000000FF);
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "ERASE: (0x%02x) 0x%02x 0x%02x"
|
||||
"0x%02x\n",
|
||||
cmd[0], cmd[1], cmd[2], cmd[3]);
|
||||
|
||||
do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0);
|
||||
at91_dataflash26_waitready();
|
||||
|
||||
instr->addr += priv->page_size; /* next page */
|
||||
instr->len -= priv->page_size;
|
||||
}
|
||||
|
||||
at91_dataflash26_write_enable(0);
|
||||
spi_release_bus(priv->spi);
|
||||
|
||||
/* Inform MTD subsystem that erase is complete */
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
if (instr->callback)
|
||||
instr->callback(instr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read from the DataFlash device.
|
||||
* from : Start offset in flash device
|
||||
* len : Number of bytes to read
|
||||
* retlen : Number of bytes actually read
|
||||
* buf : Buffer that will receive data
|
||||
*/
|
||||
static int at91_dataflash26_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
|
||||
unsigned char cmd[5];
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL1, "dataflash_read: %lli .. %lli\n",
|
||||
from, from+len);
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
/* Sanity checks */
|
||||
if (!len)
|
||||
return 0;
|
||||
if (from + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
cmd[0] = AT26_OP_READ_ARRAY_FAST;
|
||||
cmd[1] = (from & 0x00FF0000) >> 16;
|
||||
cmd[2] = (from & 0x0000FF00) >> 8;
|
||||
cmd[3] = (from & 0x000000FF);
|
||||
/* cmd[4] is a "Don't care" byte */
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "READ: (0x%02x) 0x%02x 0x%02x 0x%02x\n",
|
||||
cmd[0], cmd[1], cmd[2], cmd[3]);
|
||||
|
||||
spi_access_bus(priv->spi);
|
||||
do_spi_transfer(2, cmd, 5, cmd, 5, buf, len, buf, len);
|
||||
spi_release_bus(priv->spi);
|
||||
|
||||
*retlen = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write to the DataFlash device.
|
||||
* to : Start offset in flash device
|
||||
* len : Number of bytes to write
|
||||
* retlen : Number of bytes actually written
|
||||
* buf : Buffer containing the data
|
||||
*/
|
||||
static int at91_dataflash26_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
|
||||
unsigned int addr, buf_index = 0;
|
||||
int ret = -EIO, sector, last_sector;
|
||||
unsigned char status, cmd[5];
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL1, "dataflash_write: %lli .. %lli\n", to, to+len);
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
/* Sanity checks */
|
||||
if (!len)
|
||||
return 0;
|
||||
if (to + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
spi_access_bus(priv->spi);
|
||||
|
||||
addr = to;
|
||||
last_sector = -1;
|
||||
|
||||
while (buf_index < len) {
|
||||
sector = addr / priv->page_size;
|
||||
/* Write first byte if a new sector begins */
|
||||
if (sector != last_sector) {
|
||||
at91_dataflash26_write_enable(1);
|
||||
at91_dataflash26_sector_protect(addr, 0);
|
||||
at91_dataflash26_write_enable(1);
|
||||
|
||||
/* Program first byte of a new sector */
|
||||
cmd[0] = AT26_OP_SEQUENTIAL_WRITE;
|
||||
cmd[1] = (addr & 0x00FF0000) >> 16;
|
||||
cmd[2] = (addr & 0x0000FF00) >> 8;
|
||||
cmd[3] = (addr & 0x000000FF);
|
||||
cmd[4] = buf[buf_index++];
|
||||
do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0);
|
||||
status = at91_dataflash26_waitready();
|
||||
addr++;
|
||||
/* On write errors, the chip resets the write enable
|
||||
flag. This also happens after the last byte of a
|
||||
sector is successfully programmed. */
|
||||
if ( ( !(status & AT26_STATUS_WRITE_ENABLE))
|
||||
&& ((addr % priv->page_size) != 0) ) {
|
||||
DEBUG(MTD_DEBUG_LEVEL1,
|
||||
"write error1: addr=0x%06x, "
|
||||
"status=0x%02x\n", addr, status);
|
||||
goto write_err;
|
||||
}
|
||||
(*retlen)++;
|
||||
last_sector = sector;
|
||||
}
|
||||
|
||||
/* Write subsequent bytes in the same sector */
|
||||
cmd[0] = AT26_OP_SEQUENTIAL_WRITE;
|
||||
cmd[1] = buf[buf_index++];
|
||||
do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0);
|
||||
status = at91_dataflash26_waitready();
|
||||
addr++;
|
||||
|
||||
if ( ( !(status & AT26_STATUS_WRITE_ENABLE))
|
||||
&& ((addr % priv->page_size) != 0) ) {
|
||||
DEBUG(MTD_DEBUG_LEVEL1, "write error2: addr=0x%06x, "
|
||||
"status=0x%02x\n", addr, status);
|
||||
goto write_err;
|
||||
}
|
||||
|
||||
(*retlen)++;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
at91_dataflash26_write_enable(0);
|
||||
write_err:
|
||||
spi_release_bus(priv->spi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize and register DataFlash device with MTD subsystem.
|
||||
*/
|
||||
static int __init add_dataflash(int channel, char *name, int nr_pages,
|
||||
int pagesize)
|
||||
{
|
||||
struct mtd_info *device;
|
||||
struct dataflash_local *priv;
|
||||
|
||||
if (nr_devices >= DATAFLASH_MAX_DEVICES) {
|
||||
printk(KERN_ERR "at91_dataflash26: Too many devices "
|
||||
"detected\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
device = kzalloc(sizeof(struct mtd_info) + strlen(name) + 8,
|
||||
GFP_KERNEL);
|
||||
if (!device)
|
||||
return -ENOMEM;
|
||||
|
||||
device->name = (char *)&device[1];
|
||||
sprintf(device->name, "%s.spi%d", name, channel);
|
||||
device->size = nr_pages * pagesize;
|
||||
device->erasesize = pagesize;
|
||||
device->owner = THIS_MODULE;
|
||||
device->type = MTD_DATAFLASH;
|
||||
device->flags = MTD_CAP_NORFLASH;
|
||||
device->erase = at91_dataflash26_erase;
|
||||
device->read = at91_dataflash26_read;
|
||||
device->write = at91_dataflash26_write;
|
||||
|
||||
priv = (struct dataflash_local *)kzalloc(sizeof(struct dataflash_local),
|
||||
GFP_KERNEL);
|
||||
if (!priv) {
|
||||
kfree(device);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->spi = channel;
|
||||
priv->page_size = pagesize;
|
||||
device->priv = priv;
|
||||
|
||||
mtd_devices[nr_devices] = device;
|
||||
nr_devices++;
|
||||
printk(KERN_INFO "at91_dataflash26: %s detected [spi%i] (%i bytes)\n",
|
||||
name, channel, device->size);
|
||||
|
||||
return add_mtd_device(device);
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect and initialize DataFlash device connected to specified SPI channel.
|
||||
*
|
||||
*/
|
||||
|
||||
struct dataflash26_types {
|
||||
unsigned char id0;
|
||||
unsigned char id1;
|
||||
char *name;
|
||||
int pagesize;
|
||||
int nr_pages;
|
||||
};
|
||||
|
||||
struct dataflash26_types df26_types[] = {
|
||||
{
|
||||
.id0 = 0x04,
|
||||
.id1 = 0x00,
|
||||
.name = "AT26F004",
|
||||
.pagesize = 4096,
|
||||
.nr_pages = 128,
|
||||
},
|
||||
{
|
||||
.id0 = 0x45,
|
||||
.id1 = 0x01,
|
||||
.name = "AT26DF081A", /* Not tested ! */
|
||||
.pagesize = 4096,
|
||||
.nr_pages = 256,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init at91_dataflash26_detect(int channel)
|
||||
{
|
||||
unsigned char status, cmd[5];
|
||||
int i;
|
||||
|
||||
spi_access_bus(channel);
|
||||
status = at91_dataflash26_status();
|
||||
|
||||
if (status == 0 || status == 0xff) {
|
||||
printk(KERN_ERR "at91_dataflash26_detect: status error %d\n",
|
||||
status);
|
||||
spi_release_bus(channel);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
cmd[0] = AT26_OP_READ_DEV_ID;
|
||||
do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0);
|
||||
spi_release_bus(channel);
|
||||
|
||||
if (cmd[1] != MANUFACTURER_ID_ATMEL)
|
||||
return -ENODEV;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(df26_types); i++) {
|
||||
if ( cmd[2] == df26_types[i].id0
|
||||
&& cmd[3] == df26_types[i].id1)
|
||||
return add_dataflash(channel,
|
||||
df26_types[i].name,
|
||||
df26_types[i].nr_pages,
|
||||
df26_types[i].pagesize);
|
||||
}
|
||||
|
||||
printk(KERN_ERR "at91_dataflash26_detect: Unsupported device "
|
||||
"(0x%02x/0x%02x)\n", cmd[2], cmd[3]);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int __init at91_dataflash26_init(void)
|
||||
{
|
||||
spi_transfer_desc = kmalloc(sizeof(struct spi_transfer_list),
|
||||
GFP_KERNEL);
|
||||
if (!spi_transfer_desc)
|
||||
return -ENOMEM;
|
||||
|
||||
/* DataFlash (SPI chip select 0) */
|
||||
at91_dataflash26_detect(0);
|
||||
|
||||
#ifdef CONFIG_MTD_AT91_DATAFLASH_CARD
|
||||
/* DataFlash card (SPI chip select 3) */
|
||||
at91_dataflash26_detect(3);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit at91_dataflash26_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DATAFLASH_MAX_DEVICES; i++) {
|
||||
if (mtd_devices[i]) {
|
||||
del_mtd_device(mtd_devices[i]);
|
||||
kfree(mtd_devices[i]->priv);
|
||||
kfree(mtd_devices[i]);
|
||||
}
|
||||
}
|
||||
nr_devices = 0;
|
||||
kfree(spi_transfer_desc);
|
||||
}
|
||||
|
||||
module_init(at91_dataflash26_init);
|
||||
module_exit(at91_dataflash26_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Hans J. Koch");
|
||||
MODULE_DESCRIPTION("DataFlash AT26xxx driver for Atmel AT91RM9200");
|
||||
@@ -81,9 +81,7 @@ static unsigned long __initdata doc_locations[] = {
|
||||
#endif /* CONFIG_MTD_DOCPROBE_HIGH */
|
||||
#elif defined(__PPC__)
|
||||
0xe4000000,
|
||||
#elif defined(CONFIG_MOMENCO_OCELOT_G)
|
||||
0xff000000,
|
||||
##else
|
||||
#else
|
||||
#warning Unknown architecture for DiskOnChip. No default probe locations defined
|
||||
#endif
|
||||
0xffffffff };
|
||||
|
||||
+200
-71
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* MTD SPI driver for ST M25Pxx flash chips
|
||||
* MTD SPI driver for ST M25Pxx (and similar) serial flash chips
|
||||
*
|
||||
* Author: Mike Lavender, mike@steroidmicros.com
|
||||
*
|
||||
@@ -19,33 +19,32 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/flash.h>
|
||||
|
||||
#include <asm/semaphore.h>
|
||||
|
||||
|
||||
/* NOTE: AT 25F and SST 25LF series are very similar,
|
||||
* but commands for sector erase and chip id differ...
|
||||
*/
|
||||
|
||||
#define FLASH_PAGESIZE 256
|
||||
|
||||
/* Flash opcodes. */
|
||||
#define OPCODE_WREN 6 /* Write enable */
|
||||
#define OPCODE_RDSR 5 /* Read status register */
|
||||
#define OPCODE_READ 3 /* Read data bytes */
|
||||
#define OPCODE_PP 2 /* Page program */
|
||||
#define OPCODE_SE 0xd8 /* Sector erase */
|
||||
#define OPCODE_RES 0xab /* Read Electronic Signature */
|
||||
#define OPCODE_WREN 0x06 /* Write enable */
|
||||
#define OPCODE_RDSR 0x05 /* Read status register */
|
||||
#define OPCODE_READ 0x03 /* Read data bytes (low frequency) */
|
||||
#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */
|
||||
#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */
|
||||
#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */
|
||||
#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */
|
||||
#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */
|
||||
#define OPCODE_RDID 0x9f /* Read JEDEC ID */
|
||||
|
||||
/* Status Register bits. */
|
||||
#define SR_WIP 1 /* Write in progress */
|
||||
#define SR_WEL 2 /* Write enable latch */
|
||||
/* meaning of other SR_* bits may differ between vendors */
|
||||
#define SR_BP0 4 /* Block protect 0 */
|
||||
#define SR_BP1 8 /* Block protect 1 */
|
||||
#define SR_BP2 0x10 /* Block protect 2 */
|
||||
@@ -65,9 +64,10 @@
|
||||
|
||||
struct m25p {
|
||||
struct spi_device *spi;
|
||||
struct semaphore lock;
|
||||
struct mutex lock;
|
||||
struct mtd_info mtd;
|
||||
unsigned partitioned;
|
||||
unsigned partitioned:1;
|
||||
u8 erase_opcode;
|
||||
u8 command[4];
|
||||
};
|
||||
|
||||
@@ -150,8 +150,9 @@ static int wait_till_ready(struct m25p *flash)
|
||||
*/
|
||||
static int erase_sector(struct m25p *flash, u32 offset)
|
||||
{
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "%s: %s at 0x%08x\n", flash->spi->dev.bus_id,
|
||||
__FUNCTION__, offset);
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n",
|
||||
flash->spi->dev.bus_id, __FUNCTION__,
|
||||
flash->mtd.erasesize / 1024, offset);
|
||||
|
||||
/* Wait until finished previous write command. */
|
||||
if (wait_till_ready(flash))
|
||||
@@ -161,7 +162,7 @@ static int erase_sector(struct m25p *flash, u32 offset)
|
||||
write_enable(flash);
|
||||
|
||||
/* Set up command buffer. */
|
||||
flash->command[0] = OPCODE_SE;
|
||||
flash->command[0] = flash->erase_opcode;
|
||||
flash->command[1] = offset >> 16;
|
||||
flash->command[2] = offset >> 8;
|
||||
flash->command[3] = offset;
|
||||
@@ -201,13 +202,17 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
addr = instr->addr;
|
||||
len = instr->len;
|
||||
|
||||
down(&flash->lock);
|
||||
mutex_lock(&flash->lock);
|
||||
|
||||
/* REVISIT in some cases we could speed up erasing large regions
|
||||
* by using OPCODE_SE instead of OPCODE_BE_4K
|
||||
*/
|
||||
|
||||
/* now erase those sectors */
|
||||
while (len) {
|
||||
if (erase_sector(flash, addr)) {
|
||||
instr->state = MTD_ERASE_FAILED;
|
||||
up(&flash->lock);
|
||||
mutex_unlock(&flash->lock);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@@ -215,7 +220,7 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
len -= mtd->erasesize;
|
||||
}
|
||||
|
||||
up(&flash->lock);
|
||||
mutex_unlock(&flash->lock);
|
||||
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
mtd_erase_callback(instr);
|
||||
@@ -260,16 +265,19 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
if (retlen)
|
||||
*retlen = 0;
|
||||
|
||||
down(&flash->lock);
|
||||
mutex_lock(&flash->lock);
|
||||
|
||||
/* Wait till previous write/erase is done. */
|
||||
if (wait_till_ready(flash)) {
|
||||
/* REVISIT status return?? */
|
||||
up(&flash->lock);
|
||||
mutex_unlock(&flash->lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* NOTE: OPCODE_FAST_READ (if available) is faster... */
|
||||
/* FIXME switch to OPCODE_FAST_READ. It's required for higher
|
||||
* clocks; and at this writing, every chip this driver handles
|
||||
* supports that opcode.
|
||||
*/
|
||||
|
||||
/* Set up the write data buffer. */
|
||||
flash->command[0] = OPCODE_READ;
|
||||
@@ -281,7 +289,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
|
||||
*retlen = m.actual_length - sizeof(flash->command);
|
||||
|
||||
up(&flash->lock);
|
||||
mutex_unlock(&flash->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -323,7 +331,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
t[1].tx_buf = buf;
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
down(&flash->lock);
|
||||
mutex_lock(&flash->lock);
|
||||
|
||||
/* Wait until finished previous write command. */
|
||||
if (wait_till_ready(flash))
|
||||
@@ -381,10 +389,10 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
if (retlen)
|
||||
*retlen += m.actual_length
|
||||
- sizeof(flash->command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
up(&flash->lock);
|
||||
mutex_unlock(&flash->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -398,24 +406,118 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
|
||||
struct flash_info {
|
||||
char *name;
|
||||
u8 id;
|
||||
u16 jedec_id;
|
||||
|
||||
/* JEDEC id zero means "no ID" (most older chips); otherwise it has
|
||||
* a high byte of zero plus three data bytes: the manufacturer id,
|
||||
* then a two byte device id.
|
||||
*/
|
||||
u32 jedec_id;
|
||||
|
||||
/* The size listed here is what works with OPCODE_SE, which isn't
|
||||
* necessarily called a "sector" by the vendor.
|
||||
*/
|
||||
unsigned sector_size;
|
||||
unsigned n_sectors;
|
||||
u16 n_sectors;
|
||||
|
||||
u16 flags;
|
||||
#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */
|
||||
};
|
||||
|
||||
|
||||
/* NOTE: double check command sets and memory organization when you add
|
||||
* more flash chips. This current list focusses on newer chips, which
|
||||
* have been converging on command sets which including JEDEC ID.
|
||||
*/
|
||||
static struct flash_info __devinitdata m25p_data [] = {
|
||||
/* REVISIT: fill in JEDEC ids, for parts that have them */
|
||||
{ "m25p05", 0x05, 0x2010, 32 * 1024, 2 },
|
||||
{ "m25p10", 0x10, 0x2011, 32 * 1024, 4 },
|
||||
{ "m25p20", 0x11, 0x2012, 64 * 1024, 4 },
|
||||
{ "m25p40", 0x12, 0x2013, 64 * 1024, 8 },
|
||||
{ "m25p80", 0x13, 0x0000, 64 * 1024, 16 },
|
||||
{ "m25p16", 0x14, 0x2015, 64 * 1024, 32 },
|
||||
{ "m25p32", 0x15, 0x2016, 64 * 1024, 64 },
|
||||
{ "m25p64", 0x16, 0x2017, 64 * 1024, 128 },
|
||||
|
||||
/* Atmel -- some are (confusingly) marketed as "DataFlash" */
|
||||
{ "at25fs010", 0x1f6601, 32 * 1024, 4, SECT_4K, },
|
||||
{ "at25fs040", 0x1f6604, 64 * 1024, 8, SECT_4K, },
|
||||
|
||||
{ "at25df041a", 0x1f4401, 64 * 1024, 8, SECT_4K, },
|
||||
|
||||
{ "at26f004", 0x1f0400, 64 * 1024, 8, SECT_4K, },
|
||||
{ "at26df081a", 0x1f4501, 64 * 1024, 16, SECT_4K, },
|
||||
{ "at26df161a", 0x1f4601, 64 * 1024, 32, SECT_4K, },
|
||||
{ "at26df321", 0x1f4701, 64 * 1024, 64, SECT_4K, },
|
||||
|
||||
/* Spansion -- single (large) sector size only, at least
|
||||
* for the chips listed here (without boot sectors).
|
||||
*/
|
||||
{ "s25sl004a", 0x010212, 64 * 1024, 8, },
|
||||
{ "s25sl008a", 0x010213, 64 * 1024, 16, },
|
||||
{ "s25sl016a", 0x010214, 64 * 1024, 32, },
|
||||
{ "s25sl032a", 0x010215, 64 * 1024, 64, },
|
||||
{ "s25sl064a", 0x010216, 64 * 1024, 128, },
|
||||
|
||||
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
|
||||
{ "sst25vf040b", 0xbf258d, 64 * 1024, 8, SECT_4K, },
|
||||
{ "sst25vf080b", 0xbf258e, 64 * 1024, 16, SECT_4K, },
|
||||
{ "sst25vf016b", 0xbf2541, 64 * 1024, 32, SECT_4K, },
|
||||
{ "sst25vf032b", 0xbf254a, 64 * 1024, 64, SECT_4K, },
|
||||
|
||||
/* ST Microelectronics -- newer production may have feature updates */
|
||||
{ "m25p05", 0x202010, 32 * 1024, 2, },
|
||||
{ "m25p10", 0x202011, 32 * 1024, 4, },
|
||||
{ "m25p20", 0x202012, 64 * 1024, 4, },
|
||||
{ "m25p40", 0x202013, 64 * 1024, 8, },
|
||||
{ "m25p80", 0, 64 * 1024, 16, },
|
||||
{ "m25p16", 0x202015, 64 * 1024, 32, },
|
||||
{ "m25p32", 0x202016, 64 * 1024, 64, },
|
||||
{ "m25p64", 0x202017, 64 * 1024, 128, },
|
||||
{ "m25p128", 0x202018, 256 * 1024, 64, },
|
||||
|
||||
{ "m45pe80", 0x204014, 64 * 1024, 16, },
|
||||
{ "m45pe16", 0x204015, 64 * 1024, 32, },
|
||||
|
||||
{ "m25pe80", 0x208014, 64 * 1024, 16, },
|
||||
{ "m25pe16", 0x208015, 64 * 1024, 32, SECT_4K, },
|
||||
|
||||
/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
|
||||
{ "w25x10", 0xef3011, 64 * 1024, 2, SECT_4K, },
|
||||
{ "w25x20", 0xef3012, 64 * 1024, 4, SECT_4K, },
|
||||
{ "w25x40", 0xef3013, 64 * 1024, 8, SECT_4K, },
|
||||
{ "w25x80", 0xef3014, 64 * 1024, 16, SECT_4K, },
|
||||
{ "w25x16", 0xef3015, 64 * 1024, 32, SECT_4K, },
|
||||
{ "w25x32", 0xef3016, 64 * 1024, 64, SECT_4K, },
|
||||
{ "w25x64", 0xef3017, 64 * 1024, 128, SECT_4K, },
|
||||
};
|
||||
|
||||
static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
|
||||
{
|
||||
int tmp;
|
||||
u8 code = OPCODE_RDID;
|
||||
u8 id[3];
|
||||
u32 jedec;
|
||||
struct flash_info *info;
|
||||
|
||||
/* JEDEC also defines an optional "extended device information"
|
||||
* string for after vendor-specific data, after the three bytes
|
||||
* we use here. Supporting some chips might require using it.
|
||||
*/
|
||||
tmp = spi_write_then_read(spi, &code, 1, id, 3);
|
||||
if (tmp < 0) {
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n",
|
||||
spi->dev.bus_id, tmp);
|
||||
return NULL;
|
||||
}
|
||||
jedec = id[0];
|
||||
jedec = jedec << 8;
|
||||
jedec |= id[1];
|
||||
jedec = jedec << 8;
|
||||
jedec |= id[2];
|
||||
|
||||
for (tmp = 0, info = m25p_data;
|
||||
tmp < ARRAY_SIZE(m25p_data);
|
||||
tmp++, info++) {
|
||||
if (info->jedec_id == jedec)
|
||||
return info;
|
||||
}
|
||||
dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* board specific setup should have ensured the SPI clock used here
|
||||
* matches what the READ command supports, at least until this driver
|
||||
@@ -429,37 +531,51 @@ static int __devinit m25p_probe(struct spi_device *spi)
|
||||
unsigned i;
|
||||
|
||||
/* Platform data helps sort out which chip type we have, as
|
||||
* well as how this board partitions it.
|
||||
* well as how this board partitions it. If we don't have
|
||||
* a chip ID, try the JEDEC id commands; they'll work for most
|
||||
* newer chips, even if we don't recognize the particular chip.
|
||||
*/
|
||||
data = spi->dev.platform_data;
|
||||
if (!data || !data->type) {
|
||||
/* FIXME some chips can identify themselves with RES
|
||||
* or JEDEC get-id commands. Try them ...
|
||||
*/
|
||||
DEBUG(MTD_DEBUG_LEVEL1, "%s: no chip id\n",
|
||||
spi->dev.bus_id);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (data && data->type) {
|
||||
for (i = 0, info = m25p_data;
|
||||
i < ARRAY_SIZE(m25p_data);
|
||||
i++, info++) {
|
||||
if (strcmp(data->type, info->name) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0, info = m25p_data; i < ARRAY_SIZE(m25p_data); i++, info++) {
|
||||
if (strcmp(data->type, info->name) == 0)
|
||||
break;
|
||||
}
|
||||
if (i == ARRAY_SIZE(m25p_data)) {
|
||||
DEBUG(MTD_DEBUG_LEVEL1, "%s: unrecognized id %s\n",
|
||||
spi->dev.bus_id, data->type);
|
||||
/* unrecognized chip? */
|
||||
if (i == ARRAY_SIZE(m25p_data)) {
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n",
|
||||
spi->dev.bus_id, data->type);
|
||||
info = NULL;
|
||||
|
||||
/* recognized; is that chip really what's there? */
|
||||
} else if (info->jedec_id) {
|
||||
struct flash_info *chip = jedec_probe(spi);
|
||||
|
||||
if (!chip || chip != info) {
|
||||
dev_warn(&spi->dev, "found %s, expected %s\n",
|
||||
chip ? chip->name : "UNKNOWN",
|
||||
info->name);
|
||||
info = NULL;
|
||||
}
|
||||
}
|
||||
} else
|
||||
info = jedec_probe(spi);
|
||||
|
||||
if (!info)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
flash = kzalloc(sizeof *flash, GFP_KERNEL);
|
||||
if (!flash)
|
||||
return -ENOMEM;
|
||||
|
||||
flash->spi = spi;
|
||||
init_MUTEX(&flash->lock);
|
||||
mutex_init(&flash->lock);
|
||||
dev_set_drvdata(&spi->dev, flash);
|
||||
|
||||
if (data->name)
|
||||
if (data && data->name)
|
||||
flash->mtd.name = data->name;
|
||||
else
|
||||
flash->mtd.name = spi->dev.bus_id;
|
||||
@@ -468,17 +584,25 @@ static int __devinit m25p_probe(struct spi_device *spi)
|
||||
flash->mtd.writesize = 1;
|
||||
flash->mtd.flags = MTD_CAP_NORFLASH;
|
||||
flash->mtd.size = info->sector_size * info->n_sectors;
|
||||
flash->mtd.erasesize = info->sector_size;
|
||||
flash->mtd.erase = m25p80_erase;
|
||||
flash->mtd.read = m25p80_read;
|
||||
flash->mtd.write = m25p80_write;
|
||||
|
||||
/* prefer "small sector" erase if possible */
|
||||
if (info->flags & SECT_4K) {
|
||||
flash->erase_opcode = OPCODE_BE_4K;
|
||||
flash->mtd.erasesize = 4096;
|
||||
} else {
|
||||
flash->erase_opcode = OPCODE_SE;
|
||||
flash->mtd.erasesize = info->sector_size;
|
||||
}
|
||||
|
||||
dev_info(&spi->dev, "%s (%d Kbytes)\n", info->name,
|
||||
flash->mtd.size / 1024);
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL2,
|
||||
"mtd .name = %s, .size = 0x%.8x (%uM) "
|
||||
".erasesize = 0x%.8x (%uK) .numeraseregions = %d\n",
|
||||
"mtd .name = %s, .size = 0x%.8x (%uMiB) "
|
||||
".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
|
||||
flash->mtd.name,
|
||||
flash->mtd.size, flash->mtd.size / (1024*1024),
|
||||
flash->mtd.erasesize, flash->mtd.erasesize / 1024,
|
||||
@@ -488,7 +612,7 @@ static int __devinit m25p_probe(struct spi_device *spi)
|
||||
for (i = 0; i < flash->mtd.numeraseregions; i++)
|
||||
DEBUG(MTD_DEBUG_LEVEL2,
|
||||
"mtd.eraseregions[%d] = { .offset = 0x%.8x, "
|
||||
".erasesize = 0x%.8x (%uK), "
|
||||
".erasesize = 0x%.8x (%uKiB), "
|
||||
".numblocks = %d }\n",
|
||||
i, flash->mtd.eraseregions[i].offset,
|
||||
flash->mtd.eraseregions[i].erasesize,
|
||||
@@ -516,14 +640,14 @@ static int __devinit m25p_probe(struct spi_device *spi)
|
||||
}
|
||||
|
||||
if (nr_parts > 0) {
|
||||
for (i = 0; i < data->nr_parts; i++) {
|
||||
for (i = 0; i < nr_parts; i++) {
|
||||
DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = "
|
||||
"{.name = %s, .offset = 0x%.8x, "
|
||||
".size = 0x%.8x (%uK) }\n",
|
||||
i, data->parts[i].name,
|
||||
data->parts[i].offset,
|
||||
data->parts[i].size,
|
||||
data->parts[i].size / 1024);
|
||||
".size = 0x%.8x (%uKiB) }\n",
|
||||
i, parts[i].name,
|
||||
parts[i].offset,
|
||||
parts[i].size,
|
||||
parts[i].size / 1024);
|
||||
}
|
||||
flash->partitioned = 1;
|
||||
return add_mtd_partitions(&flash->mtd, parts, nr_parts);
|
||||
@@ -560,6 +684,11 @@ static struct spi_driver m25p80_driver = {
|
||||
},
|
||||
.probe = m25p_probe,
|
||||
.remove = __devexit_p(m25p_remove),
|
||||
|
||||
/* REVISIT: many of these chips have deep power-down modes, which
|
||||
* should clearly be entered on suspend() to minimize power use.
|
||||
* And also when they're otherwise idle...
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/flash.h>
|
||||
|
||||
@@ -89,7 +90,7 @@ struct dataflash {
|
||||
unsigned short page_offset; /* offset in flash address */
|
||||
unsigned int page_size; /* of bytes per page */
|
||||
|
||||
struct semaphore lock;
|
||||
struct mutex lock;
|
||||
struct spi_device *spi;
|
||||
|
||||
struct mtd_info mtd;
|
||||
@@ -167,7 +168,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
x.len = 4;
|
||||
spi_message_add_tail(&x, &msg);
|
||||
|
||||
down(&priv->lock);
|
||||
mutex_lock(&priv->lock);
|
||||
while (instr->len > 0) {
|
||||
unsigned int pageaddr;
|
||||
int status;
|
||||
@@ -210,7 +211,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
instr->len -= priv->page_size;
|
||||
}
|
||||
}
|
||||
up(&priv->lock);
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
/* Inform MTD subsystem that erase is complete */
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
@@ -266,7 +267,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
x[1].len = len;
|
||||
spi_message_add_tail(&x[1], &msg);
|
||||
|
||||
down(&priv->lock);
|
||||
mutex_lock(&priv->lock);
|
||||
|
||||
/* Continuous read, max clock = f(car) which may be less than
|
||||
* the peak rate available. Some chips support commands with
|
||||
@@ -279,7 +280,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
/* plus 4 "don't care" bytes */
|
||||
|
||||
status = spi_sync(priv->spi, &msg);
|
||||
up(&priv->lock);
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
if (status >= 0) {
|
||||
*retlen = msg.actual_length - 8;
|
||||
@@ -336,7 +337,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
else
|
||||
writelen = len;
|
||||
|
||||
down(&priv->lock);
|
||||
mutex_lock(&priv->lock);
|
||||
while (remaining > 0) {
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "write @ %i:%i len=%i\n",
|
||||
pageaddr, offset, writelen);
|
||||
@@ -441,7 +442,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
else
|
||||
writelen = remaining;
|
||||
}
|
||||
up(&priv->lock);
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -463,7 +464,7 @@ add_dataflash(struct spi_device *spi, char *name,
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
init_MUTEX(&priv->lock);
|
||||
mutex_init(&priv->lock);
|
||||
priv->spi = spi;
|
||||
priv->page_size = pagesize;
|
||||
priv->page_offset = pageoffset;
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
*
|
||||
* Notes:
|
||||
* Due to what I assume is more buggy SROM, the 64M PMC551 I
|
||||
* have available claims that all 4 of it's DRAM banks have 64M
|
||||
* of ram configured (making a grand total of 256M onboard).
|
||||
* have available claims that all 4 of its DRAM banks have 64MiB
|
||||
* of ram configured (making a grand total of 256MiB onboard).
|
||||
* This is slightly annoying since the BAR0 size reflects the
|
||||
* aperture size, not the dram size, and the V370PDC supplies no
|
||||
* other method for memory size discovery. This problem is
|
||||
@@ -70,7 +70,7 @@
|
||||
* made the memory unusable, added a fix to code to touch up
|
||||
* the DRAM some.
|
||||
*
|
||||
* Bugs/FIXME's:
|
||||
* Bugs/FIXMEs:
|
||||
* * MUST fix the init function to not spin on a register
|
||||
* waiting for it to set .. this does not safely handle busted
|
||||
* devices that never reset the register correctly which will
|
||||
@@ -562,10 +562,10 @@ static u32 fixup_pmc551(struct pci_dev *dev)
|
||||
/*
|
||||
* Some screen fun
|
||||
*/
|
||||
printk(KERN_DEBUG "pmc551: %d%c (0x%x) of %sprefetchable memory at "
|
||||
printk(KERN_DEBUG "pmc551: %d%sB (0x%x) of %sprefetchable memory at "
|
||||
"0x%llx\n", (size < 1024) ? size : (size < 1048576) ?
|
||||
size >> 10 : size >> 20,
|
||||
(size < 1024) ? 'B' : (size < 1048576) ? 'K' : 'M', size,
|
||||
(size < 1024) ? "" : (size < 1048576) ? "Ki" : "Mi", size,
|
||||
((dcmd & (0x1 << 3)) == 0) ? "non-" : "",
|
||||
(unsigned long long)pci_resource_start(dev, 0));
|
||||
|
||||
@@ -649,14 +649,10 @@ MODULE_DESCRIPTION(PMC551_VERSION);
|
||||
* Stuff these outside the ifdef so as to not bust compiled in driver support
|
||||
*/
|
||||
static int msize = 0;
|
||||
#if defined(CONFIG_MTD_PMC551_APERTURE_SIZE)
|
||||
static int asize = CONFIG_MTD_PMC551_APERTURE_SIZE;
|
||||
#else
|
||||
static int asize = 0;
|
||||
#endif
|
||||
|
||||
module_param(msize, int, 0);
|
||||
MODULE_PARM_DESC(msize, "memory size in Megabytes [1 - 1024]");
|
||||
MODULE_PARM_DESC(msize, "memory size in MiB [1 - 1024]");
|
||||
module_param(asize, int, 0);
|
||||
MODULE_PARM_DESC(asize, "aperture size, must be <= memsize [1-1024]");
|
||||
|
||||
@@ -799,8 +795,7 @@ static int __init init_pmc551(void)
|
||||
mtd->owner = THIS_MODULE;
|
||||
|
||||
if (add_mtd_device(mtd)) {
|
||||
printk(KERN_NOTICE "pmc551: Failed to register new "
|
||||
"device\n");
|
||||
printk(KERN_NOTICE "pmc551: Failed to register new device\n");
|
||||
pci_iounmap(PCI_Device, priv->start);
|
||||
kfree(mtd->priv);
|
||||
kfree(mtd);
|
||||
@@ -811,13 +806,13 @@ static int __init init_pmc551(void)
|
||||
pci_dev_get(PCI_Device);
|
||||
|
||||
printk(KERN_NOTICE "Registered pmc551 memory device.\n");
|
||||
printk(KERN_NOTICE "Mapped %dM of memory from 0x%p to 0x%p\n",
|
||||
printk(KERN_NOTICE "Mapped %dMiB of memory from 0x%p to 0x%p\n",
|
||||
priv->asize >> 20,
|
||||
priv->start, priv->start + priv->asize);
|
||||
printk(KERN_NOTICE "Total memory is %d%c\n",
|
||||
printk(KERN_NOTICE "Total memory is %d%sB\n",
|
||||
(length < 1024) ? length :
|
||||
(length < 1048576) ? length >> 10 : length >> 20,
|
||||
(length < 1024) ? 'B' : (length < 1048576) ? 'K' : 'M');
|
||||
(length < 1024) ? "" : (length < 1048576) ? "Ki" : "Mi");
|
||||
priv->nextpmc551 = pmc551list;
|
||||
pmc551list = mtd;
|
||||
found++;
|
||||
@@ -850,7 +845,7 @@ static void __exit cleanup_pmc551(void)
|
||||
pmc551list = priv->nextpmc551;
|
||||
|
||||
if (priv->start) {
|
||||
printk(KERN_DEBUG "pmc551: unmapping %dM starting at "
|
||||
printk(KERN_DEBUG "pmc551: unmapping %dMiB starting at "
|
||||
"0x%p\n", priv->asize >> 20, priv->start);
|
||||
pci_iounmap(priv->dev, priv->start);
|
||||
}
|
||||
|
||||
@@ -580,14 +580,13 @@ int INFTL_mount(struct INFTLrecord *s)
|
||||
logical_block = block = BLOCK_NIL;
|
||||
|
||||
/* Temporary buffer to store ANAC numbers. */
|
||||
ANACtable = kmalloc(s->nb_blocks * sizeof(u8), GFP_KERNEL);
|
||||
ANACtable = kcalloc(s->nb_blocks, sizeof(u8), GFP_KERNEL);
|
||||
if (!ANACtable) {
|
||||
printk(KERN_WARNING "INFTL: allocation of ANACtable "
|
||||
"failed (%zd bytes)\n",
|
||||
s->nb_blocks * sizeof(u8));
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(ANACtable, 0, s->nb_blocks);
|
||||
|
||||
/*
|
||||
* First pass is to explore each physical unit, and construct the
|
||||
|
||||
+13
-30
@@ -163,20 +163,12 @@ config MTD_SBC_GXX
|
||||
More info at
|
||||
<http://www.arcomcontrols.com/products/icp/pc104/processors/SBC_GX1.htm>.
|
||||
|
||||
config MTD_LUBBOCK
|
||||
tristate "CFI Flash device mapped on Intel Lubbock XScale eval board"
|
||||
depends on ARCH_LUBBOCK && MTD_CFI_INTELEXT && MTD_PARTITIONS
|
||||
help
|
||||
This provides a driver for the on-board flash of the Intel
|
||||
'Lubbock' XScale evaluation board.
|
||||
|
||||
config MTD_MAINSTONE
|
||||
tristate "CFI Flash device mapped on Intel Mainstone XScale eval board"
|
||||
depends on MACH_MAINSTONE && MTD_CFI_INTELEXT
|
||||
config MTD_PXA2XX
|
||||
tristate "CFI Flash device mapped on Intel XScale PXA2xx based boards"
|
||||
depends on (PXA25x || PXA27x) && MTD_CFI_INTELEXT
|
||||
select MTD_PARTITIONS
|
||||
help
|
||||
This provides a driver for the on-board flash of the Intel
|
||||
'Mainstone PXA27x evaluation board.
|
||||
This provides a driver for the NOR flash attached to a PXA2xx chip.
|
||||
|
||||
config MTD_OCTAGON
|
||||
tristate "JEDEC Flash device mapped on Octagon 5066 SBC"
|
||||
@@ -354,7 +346,7 @@ config MTD_CFI_FLAGADM
|
||||
|
||||
config MTD_WALNUT
|
||||
tristate "Flash device mapped on IBM 405GP Walnut"
|
||||
depends on MTD_JEDECPROBE && WALNUT
|
||||
depends on MTD_JEDECPROBE && WALNUT && !PPC_MERGE
|
||||
help
|
||||
This enables access routines for the flash chips on the IBM 405GP
|
||||
Walnut board. If you have one of these boards and would like to
|
||||
@@ -370,7 +362,7 @@ config MTD_EBONY
|
||||
|
||||
config MTD_OCOTEA
|
||||
tristate "Flash devices mapped on IBM 440GX Ocotea"
|
||||
depends on MTD_CFI && OCOTEA
|
||||
depends on MTD_CFI && OCOTEA && !PPC_MERGE
|
||||
help
|
||||
This enables access routines for the flash chips on the IBM 440GX
|
||||
Ocotea board. If you have one of these boards and would like to
|
||||
@@ -384,22 +376,6 @@ config MTD_REDWOOD
|
||||
Redwood board. If you have one of these boards and would like to
|
||||
use the flash chips on it, say 'Y'.
|
||||
|
||||
config MTD_TQM834x
|
||||
tristate "Flash device mapped on TQ Components TQM834x Boards"
|
||||
depends on MTD_CFI && TQM834x
|
||||
help
|
||||
This enables access routines for the flash chips on the
|
||||
TQ Components TQM834x boards. If you have one of these boards
|
||||
and would like to use the flash chips on it, say 'Y'.
|
||||
|
||||
config MTD_OCELOT
|
||||
tristate "Momenco Ocelot boot flash device"
|
||||
depends on MOMENCO_OCELOT
|
||||
help
|
||||
This enables access routines for the boot flash device and for the
|
||||
NVRAM on the Momenco Ocelot board. If you have one of these boards
|
||||
and would like access to either of these, say 'Y'.
|
||||
|
||||
config MTD_SOLUTIONENGINE
|
||||
tristate "CFI Flash device mapped on Hitachi SolutionEngine"
|
||||
depends on SUPERH && MTD_CFI && MTD_REDBOOT_PARTS
|
||||
@@ -605,6 +581,13 @@ config MTD_SHARP_SL
|
||||
help
|
||||
This enables access to the flash chip on the Sharp SL Series of PDAs.
|
||||
|
||||
config MTD_INTEL_VR_NOR
|
||||
tristate "NOR flash on Intel Vermilion Range Expansion Bus CS0"
|
||||
depends on PCI
|
||||
help
|
||||
Map driver for a NOR flash bank located on the Expansion Bus of the
|
||||
Intel Vermilion Range chipset.
|
||||
|
||||
config MTD_PLATRAM
|
||||
tristate "Map driver for platform device RAM (mtd-ram)"
|
||||
select MTD_RAM
|
||||
|
||||
@@ -20,8 +20,7 @@ obj-$(CONFIG_MTD_ESB2ROM) += esb2rom.o
|
||||
obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o
|
||||
obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o
|
||||
obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o
|
||||
obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o
|
||||
obj-$(CONFIG_MTD_MAINSTONE) += mainstone-flash.o
|
||||
obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o
|
||||
obj-$(CONFIG_MTD_MBX860) += mbx860.o
|
||||
obj-$(CONFIG_MTD_CEIVA) += ceiva.o
|
||||
obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o
|
||||
@@ -43,7 +42,6 @@ obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o
|
||||
obj-$(CONFIG_MTD_VMAX) += vmax301.o
|
||||
obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
|
||||
obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o
|
||||
obj-$(CONFIG_MTD_OCELOT) += ocelot.o
|
||||
obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
|
||||
obj-$(CONFIG_MTD_PCI) += pci.o
|
||||
obj-$(CONFIG_MTD_ALCHEMY) += alchemy-flash.o
|
||||
@@ -70,4 +68,4 @@ obj-$(CONFIG_MTD_SHARP_SL) += sharpsl-flash.o
|
||||
obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
|
||||
obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o
|
||||
obj-$(CONFIG_MTD_MTX1) += mtx-1_flash.o
|
||||
obj-$(CONFIG_MTD_TQM834x) += tqm834x.o
|
||||
obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
|
||||
|
||||
@@ -75,13 +75,6 @@
|
||||
#define BOARD_FLASH_WIDTH 2 /* 16-bits */
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MIPS_HYDROGEN3
|
||||
#define BOARD_MAP_NAME "Hydrogen3 Flash"
|
||||
#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */
|
||||
#define BOARD_FLASH_WIDTH 4 /* 32-bits */
|
||||
#define USE_LOCAL_ACCESSORS /* why? */
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MIPS_BOSPORUS
|
||||
#define BOARD_MAP_NAME "Bosporus Flash"
|
||||
#define BOARD_FLASH_SIZE 0x01000000 /* 16MB */
|
||||
@@ -130,13 +123,6 @@ int __init alchemy_mtd_init(void)
|
||||
|
||||
window_addr = 0x20000000 - BOARD_FLASH_SIZE;
|
||||
window_size = BOARD_FLASH_SIZE;
|
||||
#ifdef CONFIG_MIPS_MIRAGE_WHY
|
||||
/* Boot ROM flash bank only; no user bank */
|
||||
window_addr = 0x1C000000;
|
||||
window_size = 0x04000000;
|
||||
/* USERFS from 0x1C00 0000 to 0x1FC00000 */
|
||||
alchemy_partitions[0].size = 0x03C00000;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Static partition definition selection
|
||||
|
||||
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* drivers/mtd/maps/intel_vr_nor.c
|
||||
*
|
||||
* An MTD map driver for a NOR flash bank on the Expansion Bus of the Intel
|
||||
* Vermilion Range chipset.
|
||||
*
|
||||
* The Vermilion Range Expansion Bus supports four chip selects, each of which
|
||||
* has 64MiB of address space. The 2nd BAR of the Expansion Bus PCI Device
|
||||
* is a 256MiB memory region containing the address spaces for all four of the
|
||||
* chip selects, with start addresses hardcoded on 64MiB boundaries.
|
||||
*
|
||||
* This map driver only supports NOR flash on chip select 0. The buswidth
|
||||
* (either 8 bits or 16 bits) is determined by reading the Expansion Bus Timing
|
||||
* and Control Register for Chip Select 0 (EXP_TIMING_CS0). This driver does
|
||||
* not modify the value in the EXP_TIMING_CS0 register except to enable writing
|
||||
* and disable boot acceleration. The timing parameters in the register are
|
||||
* assumed to have been properly initialized by the BIOS. The reset default
|
||||
* timing parameters are maximally conservative (slow), so access to the flash
|
||||
* will be slower than it should be if the BIOS has not initialized the timing
|
||||
* parameters.
|
||||
*
|
||||
* Author: Andy Lowe <alowe@mvista.com>
|
||||
*
|
||||
* 2006 (c) MontaVista Software, Inc. This file is licensed under
|
||||
* the terms of the GNU General Public License version 2. This program
|
||||
* is licensed "as is" without any warranty of any kind, whether express
|
||||
* or implied.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/cfi.h>
|
||||
#include <linux/mtd/flashchip.h>
|
||||
|
||||
#define DRV_NAME "vr_nor"
|
||||
|
||||
struct vr_nor_mtd {
|
||||
void __iomem *csr_base;
|
||||
struct map_info map;
|
||||
struct mtd_info *info;
|
||||
int nr_parts;
|
||||
struct pci_dev *dev;
|
||||
};
|
||||
|
||||
/* Expansion Bus Configuration and Status Registers are in BAR 0 */
|
||||
#define EXP_CSR_MBAR 0
|
||||
/* Expansion Bus Memory Window is BAR 1 */
|
||||
#define EXP_WIN_MBAR 1
|
||||
/* Maximum address space for Chip Select 0 is 64MiB */
|
||||
#define CS0_SIZE 0x04000000
|
||||
/* Chip Select 0 is at offset 0 in the Memory Window */
|
||||
#define CS0_START 0x0
|
||||
/* Chip Select 0 Timing Register is at offset 0 in CSR */
|
||||
#define EXP_TIMING_CS0 0x00
|
||||
#define TIMING_CS_EN (1 << 31) /* Chip Select Enable */
|
||||
#define TIMING_BOOT_ACCEL_DIS (1 << 8) /* Boot Acceleration Disable */
|
||||
#define TIMING_WR_EN (1 << 1) /* Write Enable */
|
||||
#define TIMING_BYTE_EN (1 << 0) /* 8-bit vs 16-bit bus */
|
||||
#define TIMING_MASK 0x3FFF0000
|
||||
|
||||
static void __devexit vr_nor_destroy_partitions(struct vr_nor_mtd *p)
|
||||
{
|
||||
if (p->nr_parts > 0) {
|
||||
#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
|
||||
del_mtd_partitions(p->info);
|
||||
#endif
|
||||
} else
|
||||
del_mtd_device(p->info);
|
||||
}
|
||||
|
||||
static int __devinit vr_nor_init_partitions(struct vr_nor_mtd *p)
|
||||
{
|
||||
int err = 0;
|
||||
#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
|
||||
struct mtd_partition *parts;
|
||||
static const char *part_probes[] = { "cmdlinepart", NULL };
|
||||
#endif
|
||||
|
||||
/* register the flash bank */
|
||||
#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
|
||||
/* partition the flash bank */
|
||||
p->nr_parts = parse_mtd_partitions(p->info, part_probes, &parts, 0);
|
||||
if (p->nr_parts > 0)
|
||||
err = add_mtd_partitions(p->info, parts, p->nr_parts);
|
||||
#endif
|
||||
if (p->nr_parts <= 0)
|
||||
err = add_mtd_device(p->info);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __devexit vr_nor_destroy_mtd_setup(struct vr_nor_mtd *p)
|
||||
{
|
||||
map_destroy(p->info);
|
||||
}
|
||||
|
||||
static int __devinit vr_nor_mtd_setup(struct vr_nor_mtd *p)
|
||||
{
|
||||
static const char *probe_types[] =
|
||||
{ "cfi_probe", "jedec_probe", NULL };
|
||||
const char **type;
|
||||
|
||||
for (type = probe_types; !p->info && *type; type++)
|
||||
p->info = do_map_probe(*type, &p->map);
|
||||
if (!p->info)
|
||||
return -ENODEV;
|
||||
|
||||
p->info->owner = THIS_MODULE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __devexit vr_nor_destroy_maps(struct vr_nor_mtd *p)
|
||||
{
|
||||
unsigned int exp_timing_cs0;
|
||||
|
||||
/* write-protect the flash bank */
|
||||
exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
|
||||
exp_timing_cs0 &= ~TIMING_WR_EN;
|
||||
writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
|
||||
|
||||
/* unmap the flash window */
|
||||
iounmap(p->map.virt);
|
||||
|
||||
/* unmap the csr window */
|
||||
iounmap(p->csr_base);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the map_info structure and map the flash.
|
||||
* Returns 0 on success, nonzero otherwise.
|
||||
*/
|
||||
static int __devinit vr_nor_init_maps(struct vr_nor_mtd *p)
|
||||
{
|
||||
unsigned long csr_phys, csr_len;
|
||||
unsigned long win_phys, win_len;
|
||||
unsigned int exp_timing_cs0;
|
||||
int err;
|
||||
|
||||
csr_phys = pci_resource_start(p->dev, EXP_CSR_MBAR);
|
||||
csr_len = pci_resource_len(p->dev, EXP_CSR_MBAR);
|
||||
win_phys = pci_resource_start(p->dev, EXP_WIN_MBAR);
|
||||
win_len = pci_resource_len(p->dev, EXP_WIN_MBAR);
|
||||
|
||||
if (!csr_phys || !csr_len || !win_phys || !win_len)
|
||||
return -ENODEV;
|
||||
|
||||
if (win_len < (CS0_START + CS0_SIZE))
|
||||
return -ENXIO;
|
||||
|
||||
p->csr_base = ioremap_nocache(csr_phys, csr_len);
|
||||
if (!p->csr_base)
|
||||
return -ENOMEM;
|
||||
|
||||
exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
|
||||
if (!(exp_timing_cs0 & TIMING_CS_EN)) {
|
||||
dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 "
|
||||
"is disabled.\n");
|
||||
err = -ENODEV;
|
||||
goto release;
|
||||
}
|
||||
if ((exp_timing_cs0 & TIMING_MASK) == TIMING_MASK) {
|
||||
dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 "
|
||||
"is configured for maximally slow access times.\n");
|
||||
}
|
||||
p->map.name = DRV_NAME;
|
||||
p->map.bankwidth = (exp_timing_cs0 & TIMING_BYTE_EN) ? 1 : 2;
|
||||
p->map.phys = win_phys + CS0_START;
|
||||
p->map.size = CS0_SIZE;
|
||||
p->map.virt = ioremap_nocache(p->map.phys, p->map.size);
|
||||
if (!p->map.virt) {
|
||||
err = -ENOMEM;
|
||||
goto release;
|
||||
}
|
||||
simple_map_init(&p->map);
|
||||
|
||||
/* Enable writes to flash bank */
|
||||
exp_timing_cs0 |= TIMING_BOOT_ACCEL_DIS | TIMING_WR_EN;
|
||||
writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
|
||||
|
||||
return 0;
|
||||
|
||||
release:
|
||||
iounmap(p->csr_base);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct pci_device_id vr_nor_pci_ids[] = {
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x500D)},
|
||||
{0,}
|
||||
};
|
||||
|
||||
static void __devexit vr_nor_pci_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct vr_nor_mtd *p = pci_get_drvdata(dev);
|
||||
|
||||
pci_set_drvdata(dev, NULL);
|
||||
vr_nor_destroy_partitions(p);
|
||||
vr_nor_destroy_mtd_setup(p);
|
||||
vr_nor_destroy_maps(p);
|
||||
kfree(p);
|
||||
pci_release_regions(dev);
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
|
||||
static int __devinit
|
||||
vr_nor_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
struct vr_nor_mtd *p = NULL;
|
||||
unsigned int exp_timing_cs0;
|
||||
int err;
|
||||
|
||||
err = pci_enable_device(dev);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = pci_request_regions(dev, DRV_NAME);
|
||||
if (err)
|
||||
goto disable_dev;
|
||||
|
||||
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||
err = -ENOMEM;
|
||||
if (!p)
|
||||
goto release;
|
||||
|
||||
p->dev = dev;
|
||||
|
||||
err = vr_nor_init_maps(p);
|
||||
if (err)
|
||||
goto release;
|
||||
|
||||
err = vr_nor_mtd_setup(p);
|
||||
if (err)
|
||||
goto destroy_maps;
|
||||
|
||||
err = vr_nor_init_partitions(p);
|
||||
if (err)
|
||||
goto destroy_mtd_setup;
|
||||
|
||||
pci_set_drvdata(dev, p);
|
||||
|
||||
return 0;
|
||||
|
||||
destroy_mtd_setup:
|
||||
map_destroy(p->info);
|
||||
|
||||
destroy_maps:
|
||||
/* write-protect the flash bank */
|
||||
exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
|
||||
exp_timing_cs0 &= ~TIMING_WR_EN;
|
||||
writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
|
||||
|
||||
/* unmap the flash window */
|
||||
iounmap(p->map.virt);
|
||||
|
||||
/* unmap the csr window */
|
||||
iounmap(p->csr_base);
|
||||
|
||||
release:
|
||||
kfree(p);
|
||||
pci_release_regions(dev);
|
||||
|
||||
disable_dev:
|
||||
pci_disable_device(dev);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct pci_driver vr_nor_pci_driver = {
|
||||
.name = DRV_NAME,
|
||||
.probe = vr_nor_pci_probe,
|
||||
.remove = __devexit_p(vr_nor_pci_remove),
|
||||
.id_table = vr_nor_pci_ids,
|
||||
};
|
||||
|
||||
static int __init vr_nor_mtd_init(void)
|
||||
{
|
||||
return pci_register_driver(&vr_nor_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit vr_nor_mtd_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&vr_nor_pci_driver);
|
||||
}
|
||||
|
||||
module_init(vr_nor_mtd_init);
|
||||
module_exit(vr_nor_mtd_exit);
|
||||
|
||||
MODULE_AUTHOR("Andy Lowe");
|
||||
MODULE_DESCRIPTION("MTD map driver for NOR flash on Intel Vermilion Range");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DEVICE_TABLE(pci, vr_nor_pci_ids);
|
||||
@@ -1,168 +0,0 @@
|
||||
/*
|
||||
* $Id: lubbock-flash.c,v 1.21 2005/11/07 11:14:27 gleixner Exp $
|
||||
*
|
||||
* Map driver for the Lubbock developer platform.
|
||||
*
|
||||
* Author: Nicolas Pitre
|
||||
* Copyright: (C) 2001 MontaVista Software Inc.
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/arch/pxa-regs.h>
|
||||
#include <asm/arch/lubbock.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#define ROM_ADDR 0x00000000
|
||||
#define FLASH_ADDR 0x04000000
|
||||
|
||||
#define WINDOW_SIZE 64*1024*1024
|
||||
|
||||
static void lubbock_map_inval_cache(struct map_info *map, unsigned long from, ssize_t len)
|
||||
{
|
||||
flush_ioremap_region(map->phys, map->cached, from, len);
|
||||
}
|
||||
|
||||
static struct map_info lubbock_maps[2] = { {
|
||||
.size = WINDOW_SIZE,
|
||||
.phys = 0x00000000,
|
||||
.inval_cache = lubbock_map_inval_cache,
|
||||
}, {
|
||||
.size = WINDOW_SIZE,
|
||||
.phys = 0x04000000,
|
||||
.inval_cache = lubbock_map_inval_cache,
|
||||
} };
|
||||
|
||||
static struct mtd_partition lubbock_partitions[] = {
|
||||
{
|
||||
.name = "Bootloader",
|
||||
.size = 0x00040000,
|
||||
.offset = 0,
|
||||
.mask_flags = MTD_WRITEABLE /* force read-only */
|
||||
},{
|
||||
.name = "Kernel",
|
||||
.size = 0x00100000,
|
||||
.offset = 0x00040000,
|
||||
},{
|
||||
.name = "Filesystem",
|
||||
.size = MTDPART_SIZ_FULL,
|
||||
.offset = 0x00140000
|
||||
}
|
||||
};
|
||||
|
||||
static struct mtd_info *mymtds[2];
|
||||
static struct mtd_partition *parsed_parts[2];
|
||||
static int nr_parsed_parts[2];
|
||||
|
||||
static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||
|
||||
static int __init init_lubbock(void)
|
||||
{
|
||||
int flashboot = (LUB_CONF_SWITCHES & 1);
|
||||
int ret = 0, i;
|
||||
|
||||
lubbock_maps[0].bankwidth = lubbock_maps[1].bankwidth =
|
||||
(BOOT_DEF & 1) ? 2 : 4;
|
||||
|
||||
/* Compensate for the nROMBT switch which swaps the flash banks */
|
||||
printk(KERN_NOTICE "Lubbock configured to boot from %s (bank %d)\n",
|
||||
flashboot?"Flash":"ROM", flashboot);
|
||||
|
||||
lubbock_maps[flashboot^1].name = "Lubbock Application Flash";
|
||||
lubbock_maps[flashboot].name = "Lubbock Boot ROM";
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
lubbock_maps[i].virt = ioremap(lubbock_maps[i].phys, WINDOW_SIZE);
|
||||
if (!lubbock_maps[i].virt) {
|
||||
printk(KERN_WARNING "Failed to ioremap %s\n", lubbock_maps[i].name);
|
||||
if (!ret)
|
||||
ret = -ENOMEM;
|
||||
continue;
|
||||
}
|
||||
lubbock_maps[i].cached = ioremap_cached(lubbock_maps[i].phys, WINDOW_SIZE);
|
||||
if (!lubbock_maps[i].cached)
|
||||
printk(KERN_WARNING "Failed to ioremap cached %s\n", lubbock_maps[i].name);
|
||||
simple_map_init(&lubbock_maps[i]);
|
||||
|
||||
printk(KERN_NOTICE "Probing %s at physical address 0x%08lx (%d-bit bankwidth)\n",
|
||||
lubbock_maps[i].name, lubbock_maps[i].phys,
|
||||
lubbock_maps[i].bankwidth * 8);
|
||||
|
||||
mymtds[i] = do_map_probe("cfi_probe", &lubbock_maps[i]);
|
||||
|
||||
if (!mymtds[i]) {
|
||||
iounmap((void *)lubbock_maps[i].virt);
|
||||
if (lubbock_maps[i].cached)
|
||||
iounmap(lubbock_maps[i].cached);
|
||||
if (!ret)
|
||||
ret = -EIO;
|
||||
continue;
|
||||
}
|
||||
mymtds[i]->owner = THIS_MODULE;
|
||||
|
||||
ret = parse_mtd_partitions(mymtds[i], probes,
|
||||
&parsed_parts[i], 0);
|
||||
|
||||
if (ret > 0)
|
||||
nr_parsed_parts[i] = ret;
|
||||
}
|
||||
|
||||
if (!mymtds[0] && !mymtds[1])
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!mymtds[i]) {
|
||||
printk(KERN_WARNING "%s is absent. Skipping\n", lubbock_maps[i].name);
|
||||
} else if (nr_parsed_parts[i]) {
|
||||
add_mtd_partitions(mymtds[i], parsed_parts[i], nr_parsed_parts[i]);
|
||||
} else if (!i) {
|
||||
printk("Using static partitions on %s\n", lubbock_maps[i].name);
|
||||
add_mtd_partitions(mymtds[i], lubbock_partitions, ARRAY_SIZE(lubbock_partitions));
|
||||
} else {
|
||||
printk("Registering %s as whole device\n", lubbock_maps[i].name);
|
||||
add_mtd_device(mymtds[i]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cleanup_lubbock(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!mymtds[i])
|
||||
continue;
|
||||
|
||||
if (nr_parsed_parts[i] || !i)
|
||||
del_mtd_partitions(mymtds[i]);
|
||||
else
|
||||
del_mtd_device(mymtds[i]);
|
||||
|
||||
map_destroy(mymtds[i]);
|
||||
iounmap((void *)lubbock_maps[i].virt);
|
||||
if (lubbock_maps[i].cached)
|
||||
iounmap(lubbock_maps[i].cached);
|
||||
|
||||
kfree(parsed_parts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
module_init(init_lubbock);
|
||||
module_exit(cleanup_lubbock);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
|
||||
MODULE_DESCRIPTION("MTD map driver for Intel Lubbock");
|
||||
@@ -1,180 +0,0 @@
|
||||
/*
|
||||
* $Id: $
|
||||
*
|
||||
* Map driver for the Mainstone developer platform.
|
||||
*
|
||||
* Author: Nicolas Pitre
|
||||
* Copyright: (C) 2001 MontaVista Software Inc.
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/arch/pxa-regs.h>
|
||||
#include <asm/arch/mainstone.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
|
||||
#define ROM_ADDR 0x00000000
|
||||
#define FLASH_ADDR 0x04000000
|
||||
|
||||
#define WINDOW_SIZE 0x04000000
|
||||
|
||||
static void mainstone_map_inval_cache(struct map_info *map, unsigned long from,
|
||||
ssize_t len)
|
||||
{
|
||||
flush_ioremap_region(map->phys, map->cached, from, len);
|
||||
}
|
||||
|
||||
static struct map_info mainstone_maps[2] = { {
|
||||
.size = WINDOW_SIZE,
|
||||
.phys = PXA_CS0_PHYS,
|
||||
.inval_cache = mainstone_map_inval_cache,
|
||||
}, {
|
||||
.size = WINDOW_SIZE,
|
||||
.phys = PXA_CS1_PHYS,
|
||||
.inval_cache = mainstone_map_inval_cache,
|
||||
} };
|
||||
|
||||
static struct mtd_partition mainstone_partitions[] = {
|
||||
{
|
||||
.name = "Bootloader",
|
||||
.size = 0x00040000,
|
||||
.offset = 0,
|
||||
.mask_flags = MTD_WRITEABLE /* force read-only */
|
||||
},{
|
||||
.name = "Kernel",
|
||||
.size = 0x00400000,
|
||||
.offset = 0x00040000,
|
||||
},{
|
||||
.name = "Filesystem",
|
||||
.size = MTDPART_SIZ_FULL,
|
||||
.offset = 0x00440000
|
||||
}
|
||||
};
|
||||
|
||||
static struct mtd_info *mymtds[2];
|
||||
static struct mtd_partition *parsed_parts[2];
|
||||
static int nr_parsed_parts[2];
|
||||
|
||||
static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||
|
||||
static int __init init_mainstone(void)
|
||||
{
|
||||
int SW7 = 0; /* FIXME: get from SCR (Mst doc section 3.2.1.1) */
|
||||
int ret = 0, i;
|
||||
|
||||
mainstone_maps[0].bankwidth = (BOOT_DEF & 1) ? 2 : 4;
|
||||
mainstone_maps[1].bankwidth = 4;
|
||||
|
||||
/* Compensate for SW7 which swaps the flash banks */
|
||||
mainstone_maps[SW7].name = "processor flash";
|
||||
mainstone_maps[SW7 ^ 1].name = "main board flash";
|
||||
|
||||
printk(KERN_NOTICE "Mainstone configured to boot from %s\n",
|
||||
mainstone_maps[0].name);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
mainstone_maps[i].virt = ioremap(mainstone_maps[i].phys,
|
||||
WINDOW_SIZE);
|
||||
if (!mainstone_maps[i].virt) {
|
||||
printk(KERN_WARNING "Failed to ioremap %s\n",
|
||||
mainstone_maps[i].name);
|
||||
if (!ret)
|
||||
ret = -ENOMEM;
|
||||
continue;
|
||||
}
|
||||
mainstone_maps[i].cached =
|
||||
ioremap_cached(mainstone_maps[i].phys, WINDOW_SIZE);
|
||||
if (!mainstone_maps[i].cached)
|
||||
printk(KERN_WARNING "Failed to ioremap cached %s\n",
|
||||
mainstone_maps[i].name);
|
||||
simple_map_init(&mainstone_maps[i]);
|
||||
|
||||
printk(KERN_NOTICE
|
||||
"Probing %s at physical address 0x%08lx"
|
||||
" (%d-bit bankwidth)\n",
|
||||
mainstone_maps[i].name, mainstone_maps[i].phys,
|
||||
mainstone_maps[i].bankwidth * 8);
|
||||
|
||||
mymtds[i] = do_map_probe("cfi_probe", &mainstone_maps[i]);
|
||||
|
||||
if (!mymtds[i]) {
|
||||
iounmap((void *)mainstone_maps[i].virt);
|
||||
if (mainstone_maps[i].cached)
|
||||
iounmap(mainstone_maps[i].cached);
|
||||
if (!ret)
|
||||
ret = -EIO;
|
||||
continue;
|
||||
}
|
||||
mymtds[i]->owner = THIS_MODULE;
|
||||
|
||||
ret = parse_mtd_partitions(mymtds[i], probes,
|
||||
&parsed_parts[i], 0);
|
||||
|
||||
if (ret > 0)
|
||||
nr_parsed_parts[i] = ret;
|
||||
}
|
||||
|
||||
if (!mymtds[0] && !mymtds[1])
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!mymtds[i]) {
|
||||
printk(KERN_WARNING "%s is absent. Skipping\n",
|
||||
mainstone_maps[i].name);
|
||||
} else if (nr_parsed_parts[i]) {
|
||||
add_mtd_partitions(mymtds[i], parsed_parts[i],
|
||||
nr_parsed_parts[i]);
|
||||
} else if (!i) {
|
||||
printk("Using static partitions on %s\n",
|
||||
mainstone_maps[i].name);
|
||||
add_mtd_partitions(mymtds[i], mainstone_partitions,
|
||||
ARRAY_SIZE(mainstone_partitions));
|
||||
} else {
|
||||
printk("Registering %s as whole device\n",
|
||||
mainstone_maps[i].name);
|
||||
add_mtd_device(mymtds[i]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cleanup_mainstone(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!mymtds[i])
|
||||
continue;
|
||||
|
||||
if (nr_parsed_parts[i] || !i)
|
||||
del_mtd_partitions(mymtds[i]);
|
||||
else
|
||||
del_mtd_device(mymtds[i]);
|
||||
|
||||
map_destroy(mymtds[i]);
|
||||
iounmap((void *)mainstone_maps[i].virt);
|
||||
if (mainstone_maps[i].cached)
|
||||
iounmap(mainstone_maps[i].cached);
|
||||
kfree(parsed_parts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
module_init(init_mainstone);
|
||||
module_exit(cleanup_mainstone);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
|
||||
MODULE_DESCRIPTION("MTD map driver for Intel Mainstone");
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user