提交 c8c55bcb 编写于 作者: L Linus Torvalds

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
  ...
......@@ -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,
......
......@@ -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
/*
* 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 };
......
/*
* 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
......
......@@ -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
......
/*
* 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);
/*
* $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");
/*
* $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");
......@@ -158,68 +158,11 @@ static struct notifier_block nettel_notifier_block = {
nettel_reboot_notifier, NULL, 0
};
/*
* Erase the configuration file system.
* Used to support the software reset button.
*/
static void nettel_erasecallback(struct erase_info *done)
{
wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
wake_up(wait_q);
}
static struct erase_info nettel_erase;
int nettel_eraseconfig(void)
{
struct mtd_info *mtd;
DECLARE_WAITQUEUE(wait, current);
wait_queue_head_t wait_q;
int ret;
init_waitqueue_head(&wait_q);
mtd = get_mtd_device(NULL, 2);
if (!IS_ERR(mtd)) {
nettel_erase.mtd = mtd;
nettel_erase.callback = nettel_erasecallback;
nettel_erase.callback = NULL;
nettel_erase.addr = 0;
nettel_erase.len = mtd->size;
nettel_erase.priv = (u_long) &wait_q;
nettel_erase.priv = 0;
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&wait_q, &wait);
ret = mtd->erase(mtd, &nettel_erase);
if (ret) {
set_current_state(TASK_RUNNING);
remove_wait_queue(&wait_q, &wait);
put_mtd_device(mtd);
return(ret);
}
schedule(); /* Wait for erase to finish. */
remove_wait_queue(&wait_q, &wait);
put_mtd_device(mtd);
}
return(0);
}
#else
int nettel_eraseconfig(void)
{
return(0);
}
#endif
/****************************************************************************/
int __init nettel_init(void)
static int __init nettel_init(void)
{
volatile unsigned long *amdpar;
unsigned long amdaddr, maxsize;
......@@ -421,10 +364,6 @@ int __init nettel_init(void)
intel_mtd->owner = THIS_MODULE;
#ifndef CONFIG_BLK_DEV_INITRD
ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 1);
#endif
num_intel_partitions = sizeof(nettel_intel_partitions) /
sizeof(nettel_intel_partitions[0]);
......@@ -477,7 +416,7 @@ int __init nettel_init(void)
/****************************************************************************/
void __exit nettel_cleanup(void)
static void __exit nettel_cleanup(void)
{
#ifdef CONFIG_MTD_CFI_INTELEXT
unregister_reboot_notifier(&nettel_notifier_block);
......
/*
* $Id: ocelot.c,v 1.17 2005/11/07 11:14:27 gleixner Exp $
*
* Flash on Momenco Ocelot
*/
#include <linux/module.h>
#include <linux/types.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/partitions.h>
#define OCELOT_PLD 0x2c000000
#define FLASH_WINDOW_ADDR 0x2fc00000
#define FLASH_WINDOW_SIZE 0x00080000
#define FLASH_BUSWIDTH 1
#define NVRAM_WINDOW_ADDR 0x2c800000
#define NVRAM_WINDOW_SIZE 0x00007FF0
#define NVRAM_BUSWIDTH 1
static unsigned int cacheflush = 0;
static struct mtd_info *flash_mtd;
static struct mtd_info *nvram_mtd;
static void ocelot_ram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
{
struct map_info *map = mtd->priv;
size_t done = 0;
/* If we use memcpy, it does word-wide writes. Even though we told the
GT64120A that it's an 8-bit wide region, word-wide writes don't work.
We end up just writing the first byte of the four to all four bytes.
So we have this loop instead */
*retlen = len;
while(len) {
__raw_writeb(*(unsigned char *) from, map->virt + to);
from++;
to++;
len--;
}
}
static struct mtd_partition *parsed_parts;
struct map_info ocelot_flash_map = {
.name = "Ocelot boot flash",
.size = FLASH_WINDOW_SIZE,
.bankwidth = FLASH_BUSWIDTH,
.phys = FLASH_WINDOW_ADDR,
};
struct map_info ocelot_nvram_map = {
.name = "Ocelot NVRAM",
.size = NVRAM_WINDOW_SIZE,
.bankwidth = NVRAM_BUSWIDTH,
.phys = NVRAM_WINDOW_ADDR,
};
static const char *probes[] = { "RedBoot", NULL };
static int __init init_ocelot_maps(void)
{
void *pld;
int nr_parts;
unsigned char brd_status;
printk(KERN_INFO "Momenco Ocelot MTD mappings: Flash 0x%x at 0x%x, NVRAM 0x%x at 0x%x\n",
FLASH_WINDOW_SIZE, FLASH_WINDOW_ADDR, NVRAM_WINDOW_SIZE, NVRAM_WINDOW_ADDR);
/* First check whether the flash jumper is present */
pld = ioremap(OCELOT_PLD, 0x10);
if (!pld) {
printk(KERN_NOTICE "Failed to ioremap Ocelot PLD\n");
return -EIO;
}
brd_status = readb(pld+4);
iounmap(pld);
/* Now ioremap the NVRAM space */
ocelot_nvram_map.virt = ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE);
if (!ocelot_nvram_map.virt) {
printk(KERN_NOTICE "Failed to ioremap Ocelot NVRAM space\n");
return -EIO;
}
simple_map_init(&ocelot_nvram_map);
/* And do the RAM probe on it to get an MTD device */
nvram_mtd = do_map_probe("map_ram", &ocelot_nvram_map);
if (!nvram_mtd) {
printk("NVRAM probe failed\n");
goto fail_1;
}
nvram_mtd->owner = THIS_MODULE;
nvram_mtd->erasesize = 16;
/* Override the write() method */
nvram_mtd->write = ocelot_ram_write;
/* Now map the flash space */
ocelot_flash_map.virt = ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE);
if (!ocelot_flash_map.virt) {
printk(KERN_NOTICE "Failed to ioremap Ocelot flash space\n");
goto fail_2;
}
/* Now the cached version */
ocelot_flash_map.cached = (unsigned long)__ioremap(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE, 0);
simple_map_init(&ocelot_flash_map);
/* Only probe for flash if the write jumper is present */
if (brd_status & 0x40) {
flash_mtd = do_map_probe("jedec", &ocelot_flash_map);
} else {
printk(KERN_NOTICE "Ocelot flash write jumper not present. Treating as ROM\n");
}
/* If that failed or the jumper's absent, pretend it's ROM */
if (!flash_mtd) {
flash_mtd = do_map_probe("map_rom", &ocelot_flash_map);
/* If we're treating it as ROM, set the erase size */
if (flash_mtd)
flash_mtd->erasesize = 0x10000;
}
if (!flash_mtd)
goto fail3;
add_mtd_device(nvram_mtd);
flash_mtd->owner = THIS_MODULE;
nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0);
if (nr_parts > 0)
add_mtd_partitions(flash_mtd, parsed_parts, nr_parts);
else
add_mtd_device(flash_mtd);
return 0;
fail3:
iounmap((void *)ocelot_flash_map.virt);
if (ocelot_flash_map.cached)
iounmap((void *)ocelot_flash_map.cached);
fail_2:
map_destroy(nvram_mtd);
fail_1:
iounmap((void *)ocelot_nvram_map.virt);
return -ENXIO;
}
static void __exit cleanup_ocelot_maps(void)
{
del_mtd_device(nvram_mtd);
map_destroy(nvram_mtd);
iounmap((void *)ocelot_nvram_map.virt);
if (parsed_parts)
del_mtd_partitions(flash_mtd);
else
del_mtd_device(flash_mtd);
map_destroy(flash_mtd);
iounmap((void *)ocelot_flash_map.virt);
if (ocelot_flash_map.cached)
iounmap((void *)ocelot_flash_map.cached);
}
module_init(init_ocelot_maps);
module_exit(cleanup_ocelot_maps);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com>");
MODULE_DESCRIPTION("MTD map driver for Momenco Ocelot board");
......@@ -232,7 +232,6 @@ static int __devinit of_flash_probe(struct of_device *dev,
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info)
goto err_out;
memset(info, 0, sizeof(*info));
dev_set_drvdata(&dev->dev, info);
......
......@@ -73,13 +73,16 @@ int __init init_msp_flash(void)
return -ENXIO;
printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt);
msp_flash = (struct mtd_info **)kmalloc(
fcnt * sizeof(struct map_info *), GFP_KERNEL);
msp_parts = (struct mtd_partition **)kmalloc(
fcnt * sizeof(struct mtd_partition *), GFP_KERNEL);
msp_maps = (struct map_info *)kmalloc(
fcnt * sizeof(struct mtd_info), GFP_KERNEL);
memset(msp_maps, 0, fcnt * sizeof(struct mtd_info));
msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL);
msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL);
msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL);
if (!msp_flash || !msp_parts || !msp_maps) {
kfree(msp_maps);
kfree(msp_parts);
kfree(msp_flash);
return -ENOMEM;
}
/* loop over the flash devices, initializing each */
for (i = 0; i < fcnt; i++) {
......@@ -95,9 +98,8 @@ int __init init_msp_flash(void)
continue;
}
msp_parts[i] = (struct mtd_partition *)kmalloc(
pcnt * sizeof(struct mtd_partition), GFP_KERNEL);
memset(msp_parts[i], 0, pcnt * sizeof(struct mtd_partition));
msp_parts[i] = kcalloc(pcnt, sizeof(struct mtd_partition),
GFP_KERNEL);
/* now initialize the devices proper */
flash_name[5] = '0' + i;
......
......@@ -79,7 +79,6 @@ static int __init init_rrmap(void)
rr_mtd->owner = THIS_MODULE;
add_mtd_device(rr_mtd);
ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, rr_mtd->index);
return 0;
}
......
/*
* drivers/mtd/maps/pq2fads.c
*
* Mapping for the flash SIMM on 8272ADS and PQ2FADS board
*
* Author: Vitaly Bordug <vbordug@ru.mvista.com>
*
* 2005 (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/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/io.h>
#include <asm/ppcboot.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/physmap.h>
/*
NOTE: bank width and interleave relative to the installed flash
should have been chosen within MTD_CFI_GEOMETRY options.
*/
#define PQ2FADS_BANK_WIDTH 4
static struct mtd_partition pq2fads_partitions[] = {
{
#ifdef CONFIG_ADS8272
.name = "HRCW",
.size = 0x40000,
.offset = 0,
.mask_flags = MTD_WRITEABLE, /* force read-only */
}, {
.name = "User FS",
.size = 0x5c0000,
.offset = 0x40000,
#else
.name = "User FS",
.size = 0x600000,
.offset = 0,
#endif
}, {
.name = "uImage",
.size = 0x100000,
.offset = 0x600000,
.mask_flags = MTD_WRITEABLE, /* force read-only */
}, {
.name = "bootloader",
.size = 0x40000,
.offset = 0x700000,
.mask_flags = MTD_WRITEABLE, /* force read-only */
}, {
.name = "bootloader env",
.size = 0x40000,
.offset = 0x740000,
.mask_flags = MTD_WRITEABLE, /* force read-only */
}
};
/* pointer to MPC885ADS board info data */
extern unsigned char __res[];
static int __init init_pq2fads_mtd(void)
{
bd_t *bd = (bd_t *)__res;
physmap_configure(bd->bi_flashstart, bd->bi_flashsize, PQ2FADS_BANK_WIDTH, NULL);
physmap_set_partitions(pq2fads_partitions,
sizeof (pq2fads_partitions) /
sizeof (pq2fads_partitions[0]));
return 0;
}
static void __exit cleanup_pq2fads_mtd(void)
{
}
module_init(init_pq2fads_mtd);
module_exit(cleanup_pq2fads_mtd);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MTD map and partitions for MPC8272ADS boards");
/*
* Map driver for Intel XScale PXA2xx platforms.
*
* 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/platform_device.h>
#include <linux/dma-mapping.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/mach/flash.h>
static void pxa2xx_map_inval_cache(struct map_info *map, unsigned long from,
ssize_t len)
{
consistent_sync((char *)map->cached + from, len, DMA_FROM_DEVICE);
}
struct pxa2xx_flash_info {
struct mtd_partition *parts;
int nr_parts;
struct mtd_info *mtd;
struct map_info map;
};
static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
static int __init pxa2xx_flash_probe(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct flash_platform_data *flash = pdev->dev.platform_data;
struct pxa2xx_flash_info *info;
struct mtd_partition *parts;
struct resource *res;
int ret = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
info = kmalloc(sizeof(struct pxa2xx_flash_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
memset(info, 0, sizeof(struct pxa2xx_flash_info));
info->map.name = (char *) flash->name;
info->map.bankwidth = flash->width;
info->map.phys = res->start;
info->map.size = res->end - res->start + 1;
info->parts = flash->parts;
info->nr_parts = flash->nr_parts;
info->map.virt = ioremap(info->map.phys, info->map.size);
if (!info->map.virt) {
printk(KERN_WARNING "Failed to ioremap %s\n",
info->map.name);
return -ENOMEM;
}
info->map.cached =
ioremap_cached(info->map.phys, info->map.size);
if (!info->map.cached)
printk(KERN_WARNING "Failed to ioremap cached %s\n",
info->map.name);
info->map.inval_cache = pxa2xx_map_inval_cache;
simple_map_init(&info->map);
printk(KERN_NOTICE
"Probing %s at physical address 0x%08lx"
" (%d-bit bankwidth)\n",
info->map.name, (unsigned long)info->map.phys,
info->map.bankwidth * 8);
info->mtd = do_map_probe(flash->map_name, &info->map);
if (!info->mtd) {
iounmap((void *)info->map.virt);
if (info->map.cached)
iounmap(info->map.cached);
return -EIO;
}
info->mtd->owner = THIS_MODULE;
#ifdef CONFIG_MTD_PARTITIONS
ret = parse_mtd_partitions(info->mtd, probes, &parts, 0);
if (ret > 0) {
info->nr_parts = ret;
info->parts = parts;
}
#endif
if (info->nr_parts) {
add_mtd_partitions(info->mtd, info->parts,
info->nr_parts);
} else {
printk("Registering %s as whole device\n",
info->map.name);
add_mtd_device(info->mtd);
}
dev_set_drvdata(dev, info);
return 0;
}
static int __exit pxa2xx_flash_remove(struct device *dev)
{
struct pxa2xx_flash_info *info = dev_get_drvdata(dev);
dev_set_drvdata(dev, NULL);
#ifdef CONFIG_MTD_PARTITIONS
if (info->nr_parts)
del_mtd_partitions(info->mtd);
else
#endif
del_mtd_device(info->mtd);
map_destroy(info->mtd);
iounmap(info->map.virt);
if (info->map.cached)
iounmap(info->map.cached);
kfree(info->parts);
kfree(info);
return 0;
}
#ifdef CONFIG_PM
static int pxa2xx_flash_suspend(struct device *dev, pm_message_t state)
{
struct pxa2xx_flash_info *info = dev_get_drvdata(dev);
int ret = 0;
if (info->mtd && info->mtd->suspend)
ret = info->mtd->suspend(info->mtd);
return ret;
}
static int pxa2xx_flash_resume(struct device *dev)
{
struct pxa2xx_flash_info *info = dev_get_drvdata(dev);
if (info->mtd && info->mtd->resume)
info->mtd->resume(info->mtd);
return 0;
}
static void pxa2xx_flash_shutdown(struct device *dev)
{
struct pxa2xx_flash_info *info = dev_get_drvdata(dev);
if (info && info->mtd->suspend(info->mtd) == 0)
info->mtd->resume(info->mtd);
}
#else
#define pxa2xx_flash_suspend NULL
#define pxa2xx_flash_resume NULL
#define pxa2xx_flash_shutdown NULL
#endif
static struct device_driver pxa2xx_flash_driver = {
.name = "pxa2xx-flash",
.bus = &platform_bus_type,
.probe = pxa2xx_flash_probe,
.remove = __exit_p(pxa2xx_flash_remove),
.suspend = pxa2xx_flash_suspend,
.resume = pxa2xx_flash_resume,
.shutdown = pxa2xx_flash_shutdown,
};
static int __init init_pxa2xx_flash(void)
{
return driver_register(&pxa2xx_flash_driver);
}
static void __exit cleanup_pxa2xx_flash(void)
{
driver_unregister(&pxa2xx_flash_driver);
}
module_init(init_pxa2xx_flash);
module_exit(cleanup_pxa2xx_flash);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
MODULE_DESCRIPTION("MTD map driver for Intel XScale PXA2xx");
/*
* drivers/mtd/maps/tqm834x.c
*
* MTD mapping driver for TQM834x boards
*
* Copyright 2005 Wolfgang Denk, DENX Software Engineering, <wd@denx.de>.
*
* 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/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <asm/ppcboot.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#define FLASH_BANK_MAX 2
extern unsigned char __res[];
/* trivial struct to describe partition information */
struct mtd_part_def
{
int nums;
unsigned char *type;
struct mtd_partition* mtd_part;
};
static struct mtd_info* mtd_banks[FLASH_BANK_MAX];
static struct map_info* map_banks[FLASH_BANK_MAX];
static struct mtd_part_def part_banks[FLASH_BANK_MAX];
static unsigned long num_banks;
static unsigned long start_scan_addr;
#ifdef CONFIG_MTD_PARTITIONS
/*
* The following defines the partition layout of TQM834x boards.
*
* See include/linux/mtd/partitions.h for definition of the
* mtd_partition structure.
*
* Assume minimal initial size of 4 MiB per bank, will be updated
* later in init_tqm834x_mtd() routine.
*/
/* Partition definition for the first flash bank which is always present. */
static struct mtd_partition tqm834x_partitions_bank1[] = {
{
.name = "u-boot", /* u-boot firmware */
.offset = 0x00000000,
.size = 0x00040000, /* 256 KiB */
/*mask_flags: MTD_WRITEABLE, * force read-only */
},
{
.name = "env", /* u-boot environment */
.offset = 0x00040000,
.size = 0x00020000, /* 128 KiB */
/*mask_flags: MTD_WRITEABLE, * force read-only */
},
{
.name = "kernel", /* linux kernel image */
.offset = 0x00060000,
.size = 0x00100000, /* 1 MiB */
/*mask_flags: MTD_WRITEABLE, * force read-only */
},
{
.name = "initrd", /* ramdisk image */
.offset = 0x00160000,
.size = 0x00200000, /* 2 MiB */
},
{
.name = "user", /* user data */
.offset = 0x00360000,
.size = 0x000a0000, /* remaining space */
/* NOTE: this parttion size is re-calcated in */
/* init_tqm834x_mtd() to cover actual remaining space. */
},
};
/* Partition definition for the second flash bank which may be present on some
* TQM834x boards.
*/
static struct mtd_partition tqm834x_partitions_bank2[] = {
{
.name = "jffs2", /* jffs2 filesystem */
.offset = 0x00000000,
.size = 0x00400000, /* whole device */
/* NOTE: this parttion size is re-calcated in */
/* init_tqm834x_mtd() to cover actual device size. */
},
};
#endif /* CONFIG_MTD_PARTITIONS */
static int __init init_tqm834x_mtd(void)
{
int idx = 0, ret = 0;
unsigned long flash_addr, flash_size, mtd_size = 0;
/* pointer to TQM834x board info data */
bd_t *bd = (bd_t *)__res;
#ifdef CONFIG_MTD_CMDLINE_PARTS
int n;
char mtdid[4];
const char *part_probes[] = { "cmdlinepart", NULL };
#endif
flash_addr = bd->bi_flashstart;
flash_size = bd->bi_flashsize;
/* request maximum flash size address space */
start_scan_addr = (unsigned long)ioremap(flash_addr, flash_size);
if (!start_scan_addr) {
printk("%s: Failed to ioremap address: 0x%lx\n",
__FUNCTION__, flash_addr);
return -EIO;
}
for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) {
if (mtd_size >= flash_size)
break;
pr_debug("%s: chip probing count %d\n", __FUNCTION__, idx);
map_banks[idx] = kzalloc(sizeof(struct map_info), GFP_KERNEL);
if (map_banks[idx] == NULL) {
ret = -ENOMEM;
goto error_mem;
}
map_banks[idx]->name = kzalloc(16, GFP_KERNEL);
if (map_banks[idx]->name == NULL) {
ret = -ENOMEM;
goto error_mem;
}
sprintf(map_banks[idx]->name, "TQM834x-%d", idx);
map_banks[idx]->size = flash_size;
map_banks[idx]->bankwidth = 4;
simple_map_init(map_banks[idx]);
map_banks[idx]->virt = (void __iomem *)
(start_scan_addr + ((idx > 0) ?
(mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0));
map_banks[idx]->phys =
flash_addr + ((idx > 0) ?
(mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0);
/* start to probe flash chips */
mtd_banks[idx] = do_map_probe("cfi_probe", map_banks[idx]);
if (mtd_banks[idx]) {
mtd_banks[idx]->owner = THIS_MODULE;
mtd_size += mtd_banks[idx]->size;
num_banks++;
pr_debug("%s: bank %ld, name: %s, size: %d bytes \n",
__FUNCTION__, num_banks,
mtd_banks[idx]->name, mtd_banks[idx]->size);
}
}
/* no supported flash chips found */
if (!num_banks) {
printk("TQM834x: No supported flash chips found!\n");
ret = -ENXIO;
goto error_mem;
}
#ifdef CONFIG_MTD_PARTITIONS
/*
* Select static partition definitions
*/
n = ARRAY_SIZE(tqm834x_partitions_bank1);
part_banks[0].mtd_part = tqm834x_partitions_bank1;
part_banks[0].type = "static image bank1";
part_banks[0].nums = n;
/* update last partition size to cover actual remaining space */
tqm834x_partitions_bank1[n - 1].size =
mtd_banks[0]->size -
tqm834x_partitions_bank1[n - 1].offset;
/* check if we have second bank? */
if (num_banks == 2) {
n = ARRAY_SIZE(tqm834x_partitions_bank2);
part_banks[1].mtd_part = tqm834x_partitions_bank2;
part_banks[1].type = "static image bank2";
part_banks[1].nums = n;
/* update last partition size to cover actual remaining space */
tqm834x_partitions_bank2[n - 1].size =
mtd_banks[1]->size -
tqm834x_partitions_bank2[n - 1].offset;
}
for(idx = 0; idx < num_banks ; idx++) {
#ifdef CONFIG_MTD_CMDLINE_PARTS
sprintf(mtdid, "%d", idx);
n = parse_mtd_partitions(mtd_banks[idx],
part_probes,
&part_banks[idx].mtd_part,
0);
pr_debug("%s: %d command line partitions on bank %s\n",
__FUNCTION__, n, mtdid);
if (n > 0) {
part_banks[idx].type = "command line";
part_banks[idx].nums = n;
}
#endif /* CONFIG_MTD_CMDLINE_PARTS */
if (part_banks[idx].nums == 0) {
printk(KERN_NOTICE
"TQM834x flash bank %d: no partition info "
"available, registering whole device\n", idx);
add_mtd_device(mtd_banks[idx]);
} else {
printk(KERN_NOTICE
"TQM834x flash bank %d: Using %s partition "
"definition\n", idx, part_banks[idx].type);
add_mtd_partitions(mtd_banks[idx],
part_banks[idx].mtd_part,
part_banks[idx].nums);
}
}
#else /* ! CONFIG_MTD_PARTITIONS */
printk(KERN_NOTICE "TQM834x flash: registering %d flash banks "
"at once\n", num_banks);
for(idx = 0 ; idx < num_banks ; idx++)
add_mtd_device(mtd_banks[idx]);
#endif /* CONFIG_MTD_PARTITIONS */
return 0;
error_mem:
for (idx = 0 ; idx < FLASH_BANK_MAX ; idx++) {
if (map_banks[idx] != NULL) {
if (map_banks[idx]->name != NULL) {
kfree(map_banks[idx]->name);
map_banks[idx]->name = NULL;
}
kfree(map_banks[idx]);
map_banks[idx] = NULL;
}
}
iounmap((void *)start_scan_addr);
return ret;
}
static void __exit cleanup_tqm834x_mtd(void)
{
unsigned int idx = 0;
for(idx = 0 ; idx < num_banks ; idx++) {
/* destroy mtd_info previously allocated */
if (mtd_banks[idx]) {
del_mtd_partitions(mtd_banks[idx]);
map_destroy(mtd_banks[idx]);
}
/* release map_info not used anymore */
kfree(map_banks[idx]->name);
kfree(map_banks[idx]);
}
if (start_scan_addr) {
iounmap((void *)start_scan_addr);
start_scan_addr = 0;
}
}
module_init(init_tqm834x_mtd);
module_exit(cleanup_tqm834x_mtd);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Wolfgang Denk <wd@denx.de>");
MODULE_DESCRIPTION("MTD map driver for TQM834x boards");
......@@ -24,10 +24,9 @@
#include <linux/kthread.h>
#include <asm/uaccess.h>
static LIST_HEAD(blktrans_majors);
#include "mtdcore.h"
extern struct mutex mtd_table_mutex;
extern struct mtd_info *mtd_table[];
static LIST_HEAD(blktrans_majors);
struct mtd_blkcore_priv {
struct task_struct *thread;
......@@ -202,7 +201,7 @@ static int blktrans_ioctl(struct inode *inode, struct file *file,
}
}
struct block_device_operations mtd_blktrans_ops = {
static struct block_device_operations mtd_blktrans_ops = {
.owner = THIS_MODULE,
.open = blktrans_open,
.release = blktrans_release,
......
......@@ -136,7 +136,8 @@ static int mtd_close(struct inode *inode, struct file *file)
DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
if (mtd->sync)
/* Only sync if opened RW */
if ((file->f_mode & 2) && mtd->sync)
mtd->sync(mtd);
put_mtd_device(mtd);
......
......@@ -178,7 +178,7 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs,
/* Check alignment */
if (mtd->writesize > 1) {
loff_t __to = to;
uint64_t __to = to;
if (do_div(__to, mtd->writesize) || (total_len % mtd->writesize))
return -EINVAL;
}
......
......@@ -22,6 +22,8 @@
#include <linux/mtd/mtd.h>
#include "mtdcore.h"
/* These are exported solely for the purpose of mtd_blkdevs.c. You
should not use them for _anything_ else */
DEFINE_MUTEX(mtd_table_mutex);
......
/* linux/drivers/mtd/mtdcore.h
*
* Header file for driver private mtdcore exports
*
*/
/* These are exported solely for the purpose of mtd_blkdevs.c. You
should not use them for _anything_ else */
extern struct mutex mtd_table_mutex;
extern struct mtd_info *mtd_table[MAX_MTD_DEVICES];
/*
* MTD Oops/Panic logger
*
* Copyright (C) 2007 Nokia Corporation. All rights reserved.
*
* Author: Richard Purdie <rpurdie@openedhand.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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/console.h>
#include <linux/vmalloc.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/mtd/mtd.h>
#define OOPS_PAGE_SIZE 4096
static struct mtdoops_context {
int mtd_index;
struct work_struct work;
struct mtd_info *mtd;
int oops_pages;
int nextpage;
int nextcount;
void *oops_buf;
int ready;
int writecount;
} oops_cxt;
static void mtdoops_erase_callback(struct erase_info *done)
{
wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv;
wake_up(wait_q);
}
static int mtdoops_erase_block(struct mtd_info *mtd, int offset)
{
struct erase_info erase;
DECLARE_WAITQUEUE(wait, current);
wait_queue_head_t wait_q;
int ret;
init_waitqueue_head(&wait_q);
erase.mtd = mtd;
erase.callback = mtdoops_erase_callback;
erase.addr = offset;
if (mtd->erasesize < OOPS_PAGE_SIZE)
erase.len = OOPS_PAGE_SIZE;
else
erase.len = mtd->erasesize;
erase.priv = (u_long)&wait_q;
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&wait_q, &wait);
ret = mtd->erase(mtd, &erase);
if (ret) {
set_current_state(TASK_RUNNING);
remove_wait_queue(&wait_q, &wait);
printk (KERN_WARNING "mtdoops: erase of region [0x%x, 0x%x] "
"on \"%s\" failed\n",
erase.addr, erase.len, mtd->name);
return ret;
}
schedule(); /* Wait for erase to finish. */
remove_wait_queue(&wait_q, &wait);
return 0;
}
static int mtdoops_inc_counter(struct mtdoops_context *cxt)
{
struct mtd_info *mtd = cxt->mtd;
size_t retlen;
u32 count;
int ret;
cxt->nextpage++;
if (cxt->nextpage > cxt->oops_pages)
cxt->nextpage = 0;
cxt->nextcount++;
if (cxt->nextcount == 0xffffffff)
cxt->nextcount = 0;
ret = mtd->read(mtd, cxt->nextpage * OOPS_PAGE_SIZE, 4,
&retlen, (u_char *) &count);
if ((retlen != 4) || (ret < 0)) {
printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)"
", err %d.\n", cxt->nextpage * OOPS_PAGE_SIZE,
retlen, ret);
return 1;
}
/* See if we need to erase the next block */
if (count != 0xffffffff)
return 1;
printk(KERN_DEBUG "mtdoops: Ready %d, %d (no erase)\n",
cxt->nextpage, cxt->nextcount);
cxt->ready = 1;
return 0;
}
static void mtdoops_prepare(struct mtdoops_context *cxt)
{
struct mtd_info *mtd = cxt->mtd;
int i = 0, j, ret, mod;
/* We were unregistered */
if (!mtd)
return;
mod = (cxt->nextpage * OOPS_PAGE_SIZE) % mtd->erasesize;
if (mod != 0) {
cxt->nextpage = cxt->nextpage + ((mtd->erasesize - mod) / OOPS_PAGE_SIZE);
if (cxt->nextpage > cxt->oops_pages)
cxt->nextpage = 0;
}
while (mtd->block_isbad &&
mtd->block_isbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE)) {
badblock:
printk(KERN_WARNING "mtdoops: Bad block at %08x\n",
cxt->nextpage * OOPS_PAGE_SIZE);
i++;
cxt->nextpage = cxt->nextpage + (mtd->erasesize / OOPS_PAGE_SIZE);
if (cxt->nextpage > cxt->oops_pages)
cxt->nextpage = 0;
if (i == (cxt->oops_pages / (mtd->erasesize / OOPS_PAGE_SIZE))) {
printk(KERN_ERR "mtdoops: All blocks bad!\n");
return;
}
}
for (j = 0, ret = -1; (j < 3) && (ret < 0); j++)
ret = mtdoops_erase_block(mtd, cxt->nextpage * OOPS_PAGE_SIZE);
if (ret < 0) {
if (mtd->block_markbad)
mtd->block_markbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE);
goto badblock;
}
printk(KERN_DEBUG "mtdoops: Ready %d, %d \n", cxt->nextpage, cxt->nextcount);
cxt->ready = 1;
}
static void mtdoops_workfunc(struct work_struct *work)
{
struct mtdoops_context *cxt =
container_of(work, struct mtdoops_context, work);
mtdoops_prepare(cxt);
}
static int find_next_position(struct mtdoops_context *cxt)
{
struct mtd_info *mtd = cxt->mtd;
int page, maxpos = 0;
u32 count, maxcount = 0xffffffff;
size_t retlen;
for (page = 0; page < cxt->oops_pages; page++) {
mtd->read(mtd, page * OOPS_PAGE_SIZE, 4, &retlen, (u_char *) &count);
if (count == 0xffffffff)
continue;
if (maxcount == 0xffffffff) {
maxcount = count;
maxpos = page;
} else if ((count < 0x40000000) && (maxcount > 0xc0000000)) {
maxcount = count;
maxpos = page;
} else if ((count > maxcount) && (count < 0xc0000000)) {
maxcount = count;
maxpos = page;
} else if ((count > maxcount) && (count > 0xc0000000)
&& (maxcount > 0x80000000)) {
maxcount = count;
maxpos = page;
}
}
if (maxcount == 0xffffffff) {
cxt->nextpage = 0;
cxt->nextcount = 1;
cxt->ready = 1;
printk(KERN_DEBUG "mtdoops: Ready %d, %d (first init)\n",
cxt->nextpage, cxt->nextcount);
return 0;
}
cxt->nextpage = maxpos;
cxt->nextcount = maxcount;
return mtdoops_inc_counter(cxt);
}
static void mtdoops_notify_add(struct mtd_info *mtd)
{
struct mtdoops_context *cxt = &oops_cxt;
int ret;
if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0)
return;
if (mtd->size < (mtd->erasesize * 2)) {
printk(KERN_ERR "MTD partition %d not big enough for mtdoops\n",
mtd->index);
return;
}
cxt->mtd = mtd;
cxt->oops_pages = mtd->size / OOPS_PAGE_SIZE;
ret = find_next_position(cxt);
if (ret == 1)
mtdoops_prepare(cxt);
printk(KERN_DEBUG "mtdoops: Attached to MTD device %d\n", mtd->index);
}
static void mtdoops_notify_remove(struct mtd_info *mtd)
{
struct mtdoops_context *cxt = &oops_cxt;
if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0)
return;
cxt->mtd = NULL;
flush_scheduled_work();
}
static void mtdoops_console_sync(void)
{
struct mtdoops_context *cxt = &oops_cxt;
struct mtd_info *mtd = cxt->mtd;
size_t retlen;
int ret;
if (!cxt->ready || !mtd)
return;
if (cxt->writecount == 0)
return;
if (cxt->writecount < OOPS_PAGE_SIZE)
memset(cxt->oops_buf + cxt->writecount, 0xff,
OOPS_PAGE_SIZE - cxt->writecount);
ret = mtd->write(mtd, cxt->nextpage * OOPS_PAGE_SIZE,
OOPS_PAGE_SIZE, &retlen, cxt->oops_buf);
cxt->ready = 0;
cxt->writecount = 0;
if ((retlen != OOPS_PAGE_SIZE) || (ret < 0))
printk(KERN_ERR "mtdoops: Write failure at %d (%td of %d written), err %d.\n",
cxt->nextpage * OOPS_PAGE_SIZE, retlen, OOPS_PAGE_SIZE, ret);
ret = mtdoops_inc_counter(cxt);
if (ret == 1)
schedule_work(&cxt->work);
}
static void
mtdoops_console_write(struct console *co, const char *s, unsigned int count)
{
struct mtdoops_context *cxt = co->data;
struct mtd_info *mtd = cxt->mtd;
int i;
if (!oops_in_progress) {
mtdoops_console_sync();
return;
}
if (!cxt->ready || !mtd)
return;
if (cxt->writecount == 0) {
u32 *stamp = cxt->oops_buf;
*stamp = cxt->nextcount;
cxt->writecount = 4;
}
if ((count + cxt->writecount) > OOPS_PAGE_SIZE)
count = OOPS_PAGE_SIZE - cxt->writecount;
for (i = 0; i < count; i++, s++)
*((char *)(cxt->oops_buf) + cxt->writecount + i) = *s;
cxt->writecount = cxt->writecount + count;
}
static int __init mtdoops_console_setup(struct console *co, char *options)
{
struct mtdoops_context *cxt = co->data;
if (cxt->mtd_index != -1)
return -EBUSY;
if (co->index == -1)
return -EINVAL;
cxt->mtd_index = co->index;
return 0;
}
static struct mtd_notifier mtdoops_notifier = {
.add = mtdoops_notify_add,
.remove = mtdoops_notify_remove,
};
static struct console mtdoops_console = {
.name = "ttyMTD",
.write = mtdoops_console_write,
.setup = mtdoops_console_setup,
.unblank = mtdoops_console_sync,
.flags = CON_PRINTBUFFER,
.index = -1,
.data = &oops_cxt,
};
static int __init mtdoops_console_init(void)
{
struct mtdoops_context *cxt = &oops_cxt;
cxt->mtd_index = -1;
cxt->oops_buf = vmalloc(OOPS_PAGE_SIZE);
if (!cxt->oops_buf) {
printk(KERN_ERR "Failed to allocate oops buffer workspace\n");
return -ENOMEM;
}
INIT_WORK(&cxt->work, mtdoops_workfunc);
register_console(&mtdoops_console);
register_mtd_user(&mtdoops_notifier);
return 0;
}
static void __exit mtdoops_console_exit(void)
{
struct mtdoops_context *cxt = &oops_cxt;
unregister_mtd_user(&mtdoops_notifier);
unregister_console(&mtdoops_console);
vfree(cxt->oops_buf);
}
subsys_initcall(mtdoops_console_init);
module_exit(mtdoops_console_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
MODULE_DESCRIPTION("MTD Oops/Panic console logger/driver");
......@@ -91,6 +91,25 @@ config MTD_NAND_AU1550
This enables the driver for the NAND flash controller on the
AMD/Alchemy 1550 SOC.
config MTD_NAND_BF5XX
tristate "Blackfin on-chip NAND Flash Controller driver"
depends on BF54x && MTD_NAND
help
This enables the Blackfin on-chip NAND flash controller
No board specific support is done by this driver, each board
must advertise a platform_device for the driver to attach.
This driver can also be built as a module. If so, the module
will be called bf5xx-nand.
config MTD_NAND_BF5XX_HWECC
bool "BF5XX NAND Hardware ECC"
depends on MTD_NAND_BF5XX
help
Enable the use of the BF5XX's internal ECC generator when
using NAND.
config MTD_NAND_RTC_FROM4
tristate "Renesas Flash ROM 4-slot interface board (FROM_BOARD4)"
depends on SH_SOLUTION_ENGINE
......@@ -134,10 +153,10 @@ config MTD_NAND_S3C2410_HWECC
config MTD_NAND_NDFC
tristate "NDFC NanD Flash Controller"
depends on 44x
depends on 4xx && !PPC_MERGE
select MTD_NAND_ECC_SMC
help
NDFC Nand Flash Controllers are integrated in EP44x SoCs
NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
config MTD_NAND_S3C2410_CLKSTOP
bool "S3C2410 NAND IDLE clock stop"
......@@ -237,7 +256,7 @@ config MTD_NAND_CAFE
select REED_SOLOMON
select REED_SOLOMON_DEC16
help
Use NAND flash attached to the CAFÉ chip designed for the $100
Use NAND flash attached to the CAFÉ chip designed for the OLPC
laptop.
config MTD_NAND_CS553X
......@@ -280,5 +299,11 @@ config MTD_NAND_PLATFORM
devices. You will need to provide platform-specific functions
via platform_data.
config MTD_ALAUDA
tristate "MTD driver for Olympus MAUSB-10 and Fijufilm DPC-R1"
depends on MTD_NAND && USB
help
These two (and possibly other) Alauda-based cardreaders for
SmartMedia and xD allow raw flash access.
endif # MTD_NAND
......@@ -13,6 +13,7 @@ obj-$(CONFIG_MTD_NAND_TOTO) += toto.o
obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o
obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o
obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o
obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o
obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
......@@ -27,5 +28,6 @@ obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o
obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
obj-$(CONFIG_MTD_ALAUDA) += alauda.o
nand-objs := nand_base.o nand_bbt.o
/*
* MTD driver for Alauda chips
*
* Copyright (C) 2007 Joern Engel <joern@logfs.org>
*
* Based on drivers/usb/usb-skeleton.c which is:
* Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
* and on drivers/usb/storage/alauda.c, which is:
* (c) 2005 Daniel Drake <dsd@gentoo.org>
*
* Idea and initial work by Arnd Bergmann <arnd@arndb.de>
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kref.h>
#include <linux/usb.h>
#include <linux/mutex.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand_ecc.h>
/* Control commands */
#define ALAUDA_GET_XD_MEDIA_STATUS 0x08
#define ALAUDA_ACK_XD_MEDIA_CHANGE 0x0a
#define ALAUDA_GET_XD_MEDIA_SIG 0x86
/* Common prefix */
#define ALAUDA_BULK_CMD 0x40
/* The two ports */
#define ALAUDA_PORT_XD 0x00
#define ALAUDA_PORT_SM 0x01
/* Bulk commands */
#define ALAUDA_BULK_READ_PAGE 0x84
#define ALAUDA_BULK_READ_OOB 0x85 /* don't use, there's a chip bug */
#define ALAUDA_BULK_READ_BLOCK 0x94
#define ALAUDA_BULK_ERASE_BLOCK 0xa3
#define ALAUDA_BULK_WRITE_PAGE 0xa4
#define ALAUDA_BULK_WRITE_BLOCK 0xb4
#define ALAUDA_BULK_RESET_MEDIA 0xe0
/* Address shifting */
#define PBA_LO(pba) ((pba & 0xF) << 5)
#define PBA_HI(pba) (pba >> 3)
#define PBA_ZONE(pba) (pba >> 11)
#define TIMEOUT HZ
static struct usb_device_id alauda_table [] = {
{ USB_DEVICE(0x0584, 0x0008) }, /* Fujifilm DPC-R1 */
{ USB_DEVICE(0x07b4, 0x010a) }, /* Olympus MAUSB-10 */
{ }
};
MODULE_DEVICE_TABLE(usb, alauda_table);
struct alauda_card {
u8 id; /* id byte */
u8 chipshift; /* 1<<chipshift total size */
u8 pageshift; /* 1<<pageshift page size */
u8 blockshift; /* 1<<blockshift block size */
};
struct alauda {
struct usb_device *dev;
struct usb_interface *interface;
struct mtd_info *mtd;
struct alauda_card *card;
struct mutex card_mutex;
u32 pagemask;
u32 bytemask;
u32 blockmask;
unsigned int write_out;
unsigned int bulk_in;
unsigned int bulk_out;
u8 port;
struct kref kref;
};
static struct alauda_card alauda_card_ids[] = {
/* NAND flash */
{ 0x6e, 20, 8, 12}, /* 1 MB */
{ 0xe8, 20, 8, 12}, /* 1 MB */
{ 0xec, 20, 8, 12}, /* 1 MB */
{ 0x64, 21, 8, 12}, /* 2 MB */
{ 0xea, 21, 8, 12}, /* 2 MB */
{ 0x6b, 22, 9, 13}, /* 4 MB */
{ 0xe3, 22, 9, 13}, /* 4 MB */
{ 0xe5, 22, 9, 13}, /* 4 MB */
{ 0xe6, 23, 9, 13}, /* 8 MB */
{ 0x73, 24, 9, 14}, /* 16 MB */
{ 0x75, 25, 9, 14}, /* 32 MB */
{ 0x76, 26, 9, 14}, /* 64 MB */
{ 0x79, 27, 9, 14}, /* 128 MB */
{ 0x71, 28, 9, 14}, /* 256 MB */
/* MASK ROM */
{ 0x5d, 21, 9, 13}, /* 2 MB */
{ 0xd5, 22, 9, 13}, /* 4 MB */
{ 0xd6, 23, 9, 13}, /* 8 MB */
{ 0x57, 24, 9, 13}, /* 16 MB */
{ 0x58, 25, 9, 13}, /* 32 MB */
{ }
};
static struct alauda_card *get_card(u8 id)
{
struct alauda_card *card;
for (card = alauda_card_ids; card->id; card++)
if (card->id == id)
return card;
return NULL;
}
static void alauda_delete(struct kref *kref)
{
struct alauda *al = container_of(kref, struct alauda, kref);
if (al->mtd) {
del_mtd_device(al->mtd);
kfree(al->mtd);
}
usb_put_dev(al->dev);
kfree(al);
}
static int alauda_get_media_status(struct alauda *al, void *buf)
{
int ret;
mutex_lock(&al->card_mutex);
ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0),
ALAUDA_GET_XD_MEDIA_STATUS, 0xc0, 0, 1, buf, 2, HZ);
mutex_unlock(&al->card_mutex);
return ret;
}
static int alauda_ack_media(struct alauda *al)
{
int ret;
mutex_lock(&al->card_mutex);
ret = usb_control_msg(al->dev, usb_sndctrlpipe(al->dev, 0),
ALAUDA_ACK_XD_MEDIA_CHANGE, 0x40, 0, 1, NULL, 0, HZ);
mutex_unlock(&al->card_mutex);
return ret;
}
static int alauda_get_media_signatures(struct alauda *al, void *buf)
{
int ret;
mutex_lock(&al->card_mutex);
ret = usb_control_msg(al->dev, usb_rcvctrlpipe(al->dev, 0),
ALAUDA_GET_XD_MEDIA_SIG, 0xc0, 0, 0, buf, 4, HZ);
mutex_unlock(&al->card_mutex);
return ret;
}
static void alauda_reset(struct alauda *al)
{
u8 command[] = {
ALAUDA_BULK_CMD, ALAUDA_BULK_RESET_MEDIA, 0, 0,
0, 0, 0, 0, al->port
};
mutex_lock(&al->card_mutex);
usb_bulk_msg(al->dev, al->bulk_out, command, 9, NULL, HZ);
mutex_unlock(&al->card_mutex);
}
static void correct_data(void *buf, void *read_ecc,
int *corrected, int *uncorrected)
{
u8 calc_ecc[3];
int err;
nand_calculate_ecc(NULL, buf, calc_ecc);
err = nand_correct_data(NULL, buf, read_ecc, calc_ecc);
if (err) {
if (err > 0)
(*corrected)++;
else
(*uncorrected)++;
}
}
struct alauda_sg_request {
struct urb *urb[3];
struct completion comp;
};
static void alauda_complete(struct urb *urb)
{
struct completion *comp = urb->context;
if (comp)
complete(comp);
}
static int __alauda_read_page(struct mtd_info *mtd, loff_t from, void *buf,
void *oob)
{
struct alauda_sg_request sg;
struct alauda *al = mtd->priv;
u32 pba = from >> al->card->blockshift;
u32 page = (from >> al->card->pageshift) & al->pagemask;
u8 command[] = {
ALAUDA_BULK_CMD, ALAUDA_BULK_READ_PAGE, PBA_HI(pba),
PBA_ZONE(pba), 0, PBA_LO(pba) + page, 1, 0, al->port
};
int i, err;
for (i=0; i<3; i++)
sg.urb[i] = NULL;
err = -ENOMEM;
for (i=0; i<3; i++) {
sg.urb[i] = usb_alloc_urb(0, GFP_NOIO);
if (!sg.urb[i])
goto out;
}
init_completion(&sg.comp);
usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9,
alauda_complete, NULL);
usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, mtd->writesize,
alauda_complete, NULL);
usb_fill_bulk_urb(sg.urb[2], al->dev, al->bulk_in, oob, 16,
alauda_complete, &sg.comp);
mutex_lock(&al->card_mutex);
for (i=0; i<3; i++) {
err = usb_submit_urb(sg.urb[i], GFP_NOIO);
if (err)
goto cancel;
}
if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) {
err = -ETIMEDOUT;
cancel:
for (i=0; i<3; i++) {
usb_kill_urb(sg.urb[i]);
}
}
mutex_unlock(&al->card_mutex);
out:
usb_free_urb(sg.urb[0]);
usb_free_urb(sg.urb[1]);
usb_free_urb(sg.urb[2]);
return err;
}
static int alauda_read_page(struct mtd_info *mtd, loff_t from,
void *buf, u8 *oob, int *corrected, int *uncorrected)
{
int err;
err = __alauda_read_page(mtd, from, buf, oob);
if (err)
return err;
correct_data(buf, oob+13, corrected, uncorrected);
correct_data(buf+256, oob+8, corrected, uncorrected);
return 0;
}
static int alauda_write_page(struct mtd_info *mtd, loff_t to, void *buf,
void *oob)
{
struct alauda_sg_request sg;
struct alauda *al = mtd->priv;
u32 pba = to >> al->card->blockshift;
u32 page = (to >> al->card->pageshift) & al->pagemask;
u8 command[] = {
ALAUDA_BULK_CMD, ALAUDA_BULK_WRITE_PAGE, PBA_HI(pba),
PBA_ZONE(pba), 0, PBA_LO(pba) + page, 32, 0, al->port
};
int i, err;
for (i=0; i<3; i++)
sg.urb[i] = NULL;
err = -ENOMEM;
for (i=0; i<3; i++) {
sg.urb[i] = usb_alloc_urb(0, GFP_NOIO);
if (!sg.urb[i])
goto out;
}
init_completion(&sg.comp);
usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9,
alauda_complete, NULL);
usb_fill_bulk_urb(sg.urb[1], al->dev, al->write_out, buf,mtd->writesize,
alauda_complete, NULL);
usb_fill_bulk_urb(sg.urb[2], al->dev, al->write_out, oob, 16,
alauda_complete, &sg.comp);
mutex_lock(&al->card_mutex);
for (i=0; i<3; i++) {
err = usb_submit_urb(sg.urb[i], GFP_NOIO);
if (err)
goto cancel;
}
if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) {
err = -ETIMEDOUT;
cancel:
for (i=0; i<3; i++) {
usb_kill_urb(sg.urb[i]);
}
}
mutex_unlock(&al->card_mutex);
out:
usb_free_urb(sg.urb[0]);
usb_free_urb(sg.urb[1]);
usb_free_urb(sg.urb[2]);
return err;
}
static int alauda_erase_block(struct mtd_info *mtd, loff_t ofs)
{
struct alauda_sg_request sg;
struct alauda *al = mtd->priv;
u32 pba = ofs >> al->card->blockshift;
u8 command[] = {
ALAUDA_BULK_CMD, ALAUDA_BULK_ERASE_BLOCK, PBA_HI(pba),
PBA_ZONE(pba), 0, PBA_LO(pba), 0x02, 0, al->port
};
u8 buf[2];
int i, err;
for (i=0; i<2; i++)
sg.urb[i] = NULL;
err = -ENOMEM;
for (i=0; i<2; i++) {
sg.urb[i] = usb_alloc_urb(0, GFP_NOIO);
if (!sg.urb[i])
goto out;
}
init_completion(&sg.comp);
usb_fill_bulk_urb(sg.urb[0], al->dev, al->bulk_out, command, 9,
alauda_complete, NULL);
usb_fill_bulk_urb(sg.urb[1], al->dev, al->bulk_in, buf, 2,
alauda_complete, &sg.comp);
mutex_lock(&al->card_mutex);
for (i=0; i<2; i++) {
err = usb_submit_urb(sg.urb[i], GFP_NOIO);
if (err)
goto cancel;
}
if (!wait_for_completion_timeout(&sg.comp, TIMEOUT)) {
err = -ETIMEDOUT;
cancel:
for (i=0; i<2; i++) {
usb_kill_urb(sg.urb[i]);
}
}
mutex_unlock(&al->card_mutex);
out:
usb_free_urb(sg.urb[0]);
usb_free_urb(sg.urb[1]);
return err;
}
static int alauda_read_oob(struct mtd_info *mtd, loff_t from, void *oob)
{
static u8 ignore_buf[512]; /* write only */
return __alauda_read_page(mtd, from, ignore_buf, oob);
}
static int popcount8(u8 c)
{
int ret = 0;
for ( ; c; c>>=1)
ret += c & 1;
return ret;
}
static int alauda_isbad(struct mtd_info *mtd, loff_t ofs)
{
u8 oob[16];
int err;
err = alauda_read_oob(mtd, ofs, oob);
if (err)
return err;
/* A block is marked bad if two or more bits are zero */
return popcount8(oob[5]) >= 7 ? 0 : 1;
}
static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct alauda *al = mtd->priv;
void *bounce_buf;
int err, corrected=0, uncorrected=0;
bounce_buf = kmalloc(mtd->writesize, GFP_KERNEL);
if (!bounce_buf)
return -ENOMEM;
*retlen = len;
while (len) {
u8 oob[16];
size_t byte = from & al->bytemask;
size_t cplen = min(len, mtd->writesize - byte);
err = alauda_read_page(mtd, from, bounce_buf, oob,
&corrected, &uncorrected);
if (err)
goto out;
memcpy(buf, bounce_buf + byte, cplen);
buf += cplen;
from += cplen;
len -= cplen;
}
err = 0;
if (corrected)
err = -EUCLEAN;
if (uncorrected)
err = -EBADMSG;
out:
kfree(bounce_buf);
return err;
}
static int alauda_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct alauda *al = mtd->priv;
int err, corrected=0, uncorrected=0;
if ((from & al->bytemask) || (len & al->bytemask))
return alauda_bounce_read(mtd, from, len, retlen, buf);
*retlen = len;
while (len) {
u8 oob[16];
err = alauda_read_page(mtd, from, buf, oob,
&corrected, &uncorrected);
if (err)
return err;
buf += mtd->writesize;
from += mtd->writesize;
len -= mtd->writesize;
}
err = 0;
if (corrected)
err = -EUCLEAN;
if (uncorrected)
err = -EBADMSG;
return err;
}
static int alauda_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct alauda *al = mtd->priv;
int err;
if ((to & al->bytemask) || (len & al->bytemask))
return -EINVAL;
*retlen = len;
while (len) {
u32 page = (to >> al->card->pageshift) & al->pagemask;
u8 oob[16] = { 'h', 'e', 'l', 'l', 'o', 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
/* don't write to bad blocks */
if (page == 0) {
err = alauda_isbad(mtd, to);
if (err) {
return -EIO;
}
}
nand_calculate_ecc(mtd, buf, &oob[13]);
nand_calculate_ecc(mtd, buf+256, &oob[8]);
err = alauda_write_page(mtd, to, (void*)buf, oob);
if (err)
return err;
buf += mtd->writesize;
to += mtd->writesize;
len -= mtd->writesize;
}
return 0;
}
static int __alauda_erase(struct mtd_info *mtd, struct erase_info *instr)
{
struct alauda *al = mtd->priv;
u32 ofs = instr->addr;
u32 len = instr->len;
int err;
if ((ofs & al->blockmask) || (len & al->blockmask))
return -EINVAL;
while (len) {
/* don't erase bad blocks */
err = alauda_isbad(mtd, ofs);
if (err > 0)
err = -EIO;
if (err < 0)
return err;
err = alauda_erase_block(mtd, ofs);
if (err < 0)
return err;
ofs += mtd->erasesize;
len -= mtd->erasesize;
}
return 0;
}
static int alauda_erase(struct mtd_info *mtd, struct erase_info *instr)
{
int err;
err = __alauda_erase(mtd, instr);
instr->state = err ? MTD_ERASE_FAILED : MTD_ERASE_DONE;
mtd_erase_callback(instr);
return err;
}
static int alauda_init_media(struct alauda *al)
{
u8 buf[4], *b0=buf, *b1=buf+1;
struct alauda_card *card;
struct mtd_info *mtd;
int err;
mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
if (!mtd)
return -ENOMEM;
for (;;) {
err = alauda_get_media_status(al, buf);
if (err < 0)
goto error;
if (*b0 & 0x10)
break;
msleep(20);
}
err = alauda_ack_media(al);
if (err)
goto error;
msleep(10);
err = alauda_get_media_status(al, buf);
if (err < 0)
goto error;
if (*b0 != 0x14) {
/* media not ready */
err = -EIO;
goto error;
}
err = alauda_get_media_signatures(al, buf);
if (err < 0)
goto error;
card = get_card(*b1);
if (!card) {
printk(KERN_ERR"Alauda: unknown card id %02x\n", *b1);
err = -EIO;
goto error;
}
printk(KERN_INFO"pagesize=%x\nerasesize=%x\nsize=%xMiB\n",
1<<card->pageshift, 1<<card->blockshift,
1<<(card->chipshift-20));
al->card = card;
al->pagemask = (1 << (card->blockshift - card->pageshift)) - 1;
al->bytemask = (1 << card->pageshift) - 1;
al->blockmask = (1 << card->blockshift) - 1;
mtd->name = "alauda";
mtd->size = 1<<card->chipshift;
mtd->erasesize = 1<<card->blockshift;
mtd->writesize = 1<<card->pageshift;
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH;
mtd->read = alauda_read;
mtd->write = alauda_write;
mtd->erase = alauda_erase;
mtd->block_isbad = alauda_isbad;
mtd->priv = al;
mtd->owner = THIS_MODULE;
err = add_mtd_device(mtd);
if (err) {
err = -ENFILE;
goto error;
}
al->mtd = mtd;
alauda_reset(al); /* no clue whether this is necessary */
return 0;
error:
kfree(mtd);
return err;
}
static int alauda_check_media(struct alauda *al)
{
u8 buf[2], *b0 = buf, *b1 = buf+1;
int err;
err = alauda_get_media_status(al, buf);
if (err < 0)
return err;
if ((*b1 & 0x01) == 0) {
/* door open */
return -EIO;
}
if ((*b0 & 0x80) || ((*b0 & 0x1F) == 0x10)) {
/* no media ? */
return -EIO;
}
if (*b0 & 0x08) {
/* media change ? */
return alauda_init_media(al);
}
return 0;
}
static int alauda_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct alauda *al;
struct usb_host_interface *iface;
struct usb_endpoint_descriptor *ep,
*ep_in=NULL, *ep_out=NULL, *ep_wr=NULL;
int i, err = -ENOMEM;
al = kzalloc(2*sizeof(*al), GFP_KERNEL);
if (!al)
goto error;
kref_init(&al->kref);
usb_set_intfdata(interface, al);
al->dev = usb_get_dev(interface_to_usbdev(interface));
al->interface = interface;
iface = interface->cur_altsetting;
for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
ep = &iface->endpoint[i].desc;
if (usb_endpoint_is_bulk_in(ep)) {
ep_in = ep;
} else if (usb_endpoint_is_bulk_out(ep)) {
if (i==0)
ep_wr = ep;
else
ep_out = ep;
}
}
err = -EIO;
if (!ep_wr || !ep_in || !ep_out)
goto error;
al->write_out = usb_sndbulkpipe(al->dev,
ep_wr->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
al->bulk_in = usb_rcvbulkpipe(al->dev,
ep_in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
al->bulk_out = usb_sndbulkpipe(al->dev,
ep_out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
/* second device is identical up to now */
memcpy(al+1, al, sizeof(*al));
mutex_init(&al[0].card_mutex);
mutex_init(&al[1].card_mutex);
al[0].port = ALAUDA_PORT_XD;
al[1].port = ALAUDA_PORT_SM;
info("alauda probed");
alauda_check_media(al);
alauda_check_media(al+1);
return 0;
error:
if (al)
kref_put(&al->kref, alauda_delete);
return err;
}
static void alauda_disconnect(struct usb_interface *interface)
{
struct alauda *al;
al = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
/* FIXME: prevent more I/O from starting */
/* decrement our usage count */
if (al)
kref_put(&al->kref, alauda_delete);
info("alauda gone");
}
static struct usb_driver alauda_driver = {
.name = "alauda",
.probe = alauda_probe,
.disconnect = alauda_disconnect,
.id_table = alauda_table,
};
static int __init alauda_init(void)
{
return usb_register(&alauda_driver);
}
static void __exit alauda_exit(void)
{
usb_deregister(&alauda_driver);
}
module_init(alauda_init);
module_exit(alauda_exit);
MODULE_LICENSE("GPL");
此差异已折叠。
......@@ -623,6 +623,11 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
uint32_t ctrl;
int err = 0;
/* Very old versions shared the same PCI ident for all three
functions on the chip. Verify the class too... */
if ((pdev->class >> 8) != PCI_CLASS_MEMORY_FLASH)
return -ENODEV;
err = pci_enable_device(pdev);
if (err)
return err;
......@@ -816,21 +821,57 @@ static void __devexit cafe_nand_remove(struct pci_dev *pdev)
}
static struct pci_device_id cafe_nand_tbl[] = {
{ 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MEMORY_FLASH << 8, 0xFFFF0 },
{ 0, }
{ 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID },
{ }
};
MODULE_DEVICE_TABLE(pci, cafe_nand_tbl);
static int cafe_nand_resume(struct pci_dev *pdev)
{
uint32_t ctrl;
struct mtd_info *mtd = pci_get_drvdata(pdev);
struct cafe_priv *cafe = mtd->priv;
/* Start off by resetting the NAND controller completely */
cafe_writel(cafe, 1, NAND_RESET);
cafe_writel(cafe, 0, NAND_RESET);
cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK);
/* Restore timing configuration */
cafe_writel(cafe, timing[0], NAND_TIMING1);
cafe_writel(cafe, timing[1], NAND_TIMING2);
cafe_writel(cafe, timing[2], NAND_TIMING3);
/* Disable master reset, enable NAND clock */
ctrl = cafe_readl(cafe, GLOBAL_CTRL);
ctrl &= 0xffffeff0;
ctrl |= 0x00007000;
cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL);
cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL);
cafe_writel(cafe, 0, NAND_DMA_CTRL);
cafe_writel(cafe, 0x7006, GLOBAL_CTRL);
cafe_writel(cafe, 0x700a, GLOBAL_CTRL);
/* Set up DMA address */
cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0);
if (sizeof(cafe->dmaaddr) > 4)
/* Shift in two parts to shut the compiler up */
cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1);
else
cafe_writel(cafe, 0, NAND_DMA_ADDR1);
/* Enable NAND IRQ in global IRQ mask register */
cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
return 0;
}
static struct pci_driver cafe_nand_pci_driver = {
.name = "CAFÉ NAND",
.id_table = cafe_nand_tbl,
.probe = cafe_nand_probe,
.remove = __devexit_p(cafe_nand_remove),
#ifdef CONFIG_PMx
.suspend = cafe_nand_suspend,
.resume = cafe_nand_resume,
#endif
};
static int cafe_nand_init(void)
......
......@@ -56,8 +56,6 @@ static unsigned long __initdata doc_locations[] = {
#endif /* CONFIG_MTD_DOCPROBE_HIGH */
#elif defined(__PPC__)
0xe4000000,
#elif defined(CONFIG_MOMENCO_OCELOT_G)
0xff000000,
#else
#warning Unknown architecture for DiskOnChip. No default probe locations defined
#endif
......
......@@ -27,7 +27,6 @@
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
......
......@@ -7,7 +7,7 @@
* Basic support for AG-AND chips is provided.
*
* Additional technical information is available on
* http://www.linux-mtd.infradead.org/tech/nand.html
* http://www.linux-mtd.infradead.org/doc/nand.html
*
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
* 2002-2006 Thomas Gleixner (tglx@linutronix.de)
......@@ -2069,13 +2069,14 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
erase_exit:
ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
/* Do call back function */
if (!ret)
mtd_erase_callback(instr);
/* Deselect and wake up anyone waiting on the device */
nand_release_device(mtd);
/* Do call back function */
if (!ret)
mtd_erase_callback(instr);
/*
* If BBT requires refresh and erase was successful, rewrite any
* selected bad block tables
......
......@@ -141,6 +141,7 @@ struct nand_manufacturers nand_manuf_ids[] = {
{NAND_MFR_STMICRO, "ST Micro"},
{NAND_MFR_HYNIX, "Hynix"},
{NAND_MFR_MICRON, "Micron"},
{NAND_MFR_AMD, "AMD"},
{0x0, "Unknown"}
};
......
......@@ -1272,7 +1272,13 @@ static int prog_page(struct nandsim *ns, int num)
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);
/*
* We allocate memory with GFP_NOFS because a flash FS may
* utilize this. If it is holding an FS lock, then gets here,
* then kmalloc runs writeback which goes to the FS again
* and deadlocks. This was seen in practice.
*/
mypage->byte = kmalloc(ns->geom.pgszoob, GFP_NOFS);
if (mypage->byte == NULL) {
NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row);
return -1;
......
......@@ -24,7 +24,11 @@
#include <linux/platform_device.h>
#include <asm/io.h>
#ifdef CONFIG_40x
#include <asm/ibm405.h>
#else
#include <asm/ibm44x.h>
#endif
struct ndfc_nand_mtd {
struct mtd_info mtd;
......@@ -230,7 +234,11 @@ static int ndfc_nand_probe(struct platform_device *pdev)
struct ndfc_controller *ndfc = &ndfc_ctrl;
unsigned long long phys = settings->ndfc_erpn | res->start;
#ifndef CONFIG_PHYS_64BIT
ndfc->ndfcbase = ioremap((phys_addr_t)phys, res->end - res->start + 1);
#else
ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1);
#endif
if (!ndfc->ndfcbase) {
printk(KERN_ERR "NDFC: ioremap failed\n");
return -EIO;
......
......@@ -60,8 +60,8 @@
#include <asm/io.h>
#include <asm/arch/regs-nand.h>
#include <asm/arch/nand.h>
#include <asm/plat-s3c/regs-nand.h>
#include <asm/plat-s3c/nand.h>
#ifdef CONFIG_MTD_NAND_S3C2410_HWECC
static int hardware_ecc = 1;
......
......@@ -40,4 +40,27 @@ config MTD_ONENAND_OTP
OTP block is fully-guaranteed to be a valid block.
config MTD_ONENAND_2X_PROGRAM
bool "OneNAND 2X program support"
help
The 2X Program is an extension of Program Operation.
Since the device is equipped with two DataRAMs, and two-plane NAND
Flash memory array, these two component enables simultaneous program
of 4KiB. Plane1 has only even blocks such as block0, block2, block4
while Plane2 has only odd blocks such as block1, block3, block5.
So MTD regards it as 4KiB page size and 256KiB block size
Now the following chips support it. (KFXXX16Q2M)
Demux: KFG2G16Q2M, KFH4G16Q2M, KFW8G16Q2M,
Mux: KFM2G16Q2M, KFN4G16Q2M,
And more recent chips
config MTD_ONENAND_SIM
tristate "OneNAND simulator support"
depends on MTD_PARTITIONS
help
The simulator may simulate various OneNAND flash chips for the
OneNAND MTD layer.
endif # MTD_ONENAND
......@@ -8,4 +8,7 @@ obj-$(CONFIG_MTD_ONENAND) += onenand.o
# Board specific.
obj-$(CONFIG_MTD_ONENAND_GENERIC) += generic.o
# Simulator
obj-$(CONFIG_MTD_ONENAND_SIM) += onenand_sim.o
onenand-objs = onenand_base.o onenand_bbt.o
此差异已折叠。
/*
* linux/drivers/mtd/onenand/onenand_sim.c
*
* The OneNAND simulator
*
* Copyright © 2005-2007 Samsung Electronics
* Kyungmin Park <kyungmin.park@samsung.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/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/vmalloc.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/onenand.h>
#include <linux/io.h>
#ifndef CONFIG_ONENAND_SIM_MANUFACTURER
#define CONFIG_ONENAND_SIM_MANUFACTURER 0xec
#endif
#ifndef CONFIG_ONENAND_SIM_DEVICE_ID
#define CONFIG_ONENAND_SIM_DEVICE_ID 0x04
#endif
#ifndef CONFIG_ONENAND_SIM_VERSION_ID
#define CONFIG_ONENAND_SIM_VERSION_ID 0x1e
#endif
static int manuf_id = CONFIG_ONENAND_SIM_MANUFACTURER;
static int device_id = CONFIG_ONENAND_SIM_DEVICE_ID;
static int version_id = CONFIG_ONENAND_SIM_VERSION_ID;
struct onenand_flash {
void __iomem *base;
void __iomem *data;
};
#define ONENAND_CORE(flash) (flash->data)
#define ONENAND_CORE_SPARE(flash, this, offset) \
((flash->data) + (this->chipsize) + (offset >> 5))
#define ONENAND_MAIN_AREA(this, offset) \
(this->base + ONENAND_DATARAM + offset)
#define ONENAND_SPARE_AREA(this, offset) \
(this->base + ONENAND_SPARERAM + offset)
#define ONENAND_GET_WP_STATUS(this) \
(readw(this->base + ONENAND_REG_WP_STATUS))
#define ONENAND_SET_WP_STATUS(v, this) \
(writew(v, this->base + ONENAND_REG_WP_STATUS))
/* It has all 0xff chars */
#define MAX_ONENAND_PAGESIZE (2048 + 64)
static unsigned char *ffchars;
static struct mtd_partition os_partitions[] = {
{
.name = "OneNAND simulator partition",
.offset = 0,
.size = MTDPART_SIZ_FULL,
},
};
/*
* OneNAND simulator mtd
*/
struct onenand_info {
struct mtd_info mtd;
struct mtd_partition *parts;
struct onenand_chip onenand;
struct onenand_flash flash;
};
static struct onenand_info *info;
#define DPRINTK(format, args...) \
do { \
printk(KERN_DEBUG "%s[%d]: " format "\n", __func__, \
__LINE__, ##args); \
} while (0)
/**
* onenand_lock_handle - Handle Lock scheme
* @param this OneNAND device structure
* @param cmd The command to be sent
*
* Send lock command to OneNAND device.
* The lock scheme is depends on chip type.
*/
static void onenand_lock_handle(struct onenand_chip *this, int cmd)
{
int block_lock_scheme;
int status;
status = ONENAND_GET_WP_STATUS(this);
block_lock_scheme = !(this->options & ONENAND_HAS_CONT_LOCK);
switch (cmd) {
case ONENAND_CMD_UNLOCK:
if (block_lock_scheme)
ONENAND_SET_WP_STATUS(ONENAND_WP_US, this);
else
ONENAND_SET_WP_STATUS(status | ONENAND_WP_US, this);
break;
case ONENAND_CMD_LOCK:
if (block_lock_scheme)
ONENAND_SET_WP_STATUS(ONENAND_WP_LS, this);
else
ONENAND_SET_WP_STATUS(status | ONENAND_WP_LS, this);
break;
case ONENAND_CMD_LOCK_TIGHT:
if (block_lock_scheme)
ONENAND_SET_WP_STATUS(ONENAND_WP_LTS, this);
else
ONENAND_SET_WP_STATUS(status | ONENAND_WP_LTS, this);
break;
default:
break;
}
}
/**
* onenand_bootram_handle - Handle BootRAM area
* @param this OneNAND device structure
* @param cmd The command to be sent
*
* Emulate BootRAM area. It is possible to do basic operation using BootRAM.
*/
static void onenand_bootram_handle(struct onenand_chip *this, int cmd)
{
switch (cmd) {
case ONENAND_CMD_READID:
writew(manuf_id, this->base);
writew(device_id, this->base + 2);
writew(version_id, this->base + 4);
break;
default:
/* REVIST: Handle other commands */
break;
}
}
/**
* onenand_update_interrupt - Set interrupt register
* @param this OneNAND device structure
* @param cmd The command to be sent
*
* Update interrupt register. The status is depends on command.
*/
static void onenand_update_interrupt(struct onenand_chip *this, int cmd)
{
int interrupt = ONENAND_INT_MASTER;
switch (cmd) {
case ONENAND_CMD_READ:
case ONENAND_CMD_READOOB:
interrupt |= ONENAND_INT_READ;
break;
case ONENAND_CMD_PROG:
case ONENAND_CMD_PROGOOB:
interrupt |= ONENAND_INT_WRITE;
break;
case ONENAND_CMD_ERASE:
interrupt |= ONENAND_INT_ERASE;
break;
case ONENAND_CMD_RESET:
interrupt |= ONENAND_INT_RESET;
break;
default:
break;
}
writew(interrupt, this->base + ONENAND_REG_INTERRUPT);
}
/**
* onenand_check_overwrite - Check over-write if happend
* @param dest The destination pointer
* @param src The source pointer
* @param count The length to be check
* @return 0 on same, otherwise 1
*
* Compare the source with destination
*/
static int onenand_check_overwrite(void *dest, void *src, size_t count)
{
unsigned int *s = (unsigned int *) src;
unsigned int *d = (unsigned int *) dest;
int i;
count >>= 2;
for (i = 0; i < count; i++)
if ((*s++ ^ *d++) != 0)
return 1;
return 0;
}
/**
* onenand_data_handle - Handle OneNAND Core and DataRAM
* @param this OneNAND device structure
* @param cmd The command to be sent
* @param dataram Which dataram used
* @param offset The offset to OneNAND Core
*
* Copy data from OneNAND Core to DataRAM (read)
* Copy data from DataRAM to OneNAND Core (write)
* Erase the OneNAND Core (erase)
*/
static void onenand_data_handle(struct onenand_chip *this, int cmd,
int dataram, unsigned int offset)
{
struct mtd_info *mtd = &info->mtd;
struct onenand_flash *flash = this->priv;
int main_offset, spare_offset;
void __iomem *src;
void __iomem *dest;
unsigned int i;
if (dataram) {
main_offset = mtd->writesize;
spare_offset = mtd->oobsize;
} else {
main_offset = 0;
spare_offset = 0;
}
switch (cmd) {
case ONENAND_CMD_READ:
src = ONENAND_CORE(flash) + offset;
dest = ONENAND_MAIN_AREA(this, main_offset);
memcpy(dest, src, mtd->writesize);
/* Fall through */
case ONENAND_CMD_READOOB:
src = ONENAND_CORE_SPARE(flash, this, offset);
dest = ONENAND_SPARE_AREA(this, spare_offset);
memcpy(dest, src, mtd->oobsize);
break;
case ONENAND_CMD_PROG:
src = ONENAND_MAIN_AREA(this, main_offset);
dest = ONENAND_CORE(flash) + offset;
/* To handle partial write */
for (i = 0; i < (1 << mtd->subpage_sft); i++) {
int off = i * this->subpagesize;
if (!memcmp(src + off, ffchars, this->subpagesize))
continue;
if (memcmp(dest + off, ffchars, this->subpagesize) &&
onenand_check_overwrite(dest + off, src + off, this->subpagesize))
printk(KERN_ERR "over-write happend at 0x%08x\n", offset);
memcpy(dest + off, src + off, this->subpagesize);
}
/* Fall through */
case ONENAND_CMD_PROGOOB:
src = ONENAND_SPARE_AREA(this, spare_offset);
/* Check all data is 0xff chars */
if (!memcmp(src, ffchars, mtd->oobsize))
break;
dest = ONENAND_CORE_SPARE(flash, this, offset);
if (memcmp(dest, ffchars, mtd->oobsize) &&
onenand_check_overwrite(dest, src, mtd->oobsize))
printk(KERN_ERR "OOB: over-write happend at 0x%08x\n",
offset);
memcpy(dest, src, mtd->oobsize);
break;
case ONENAND_CMD_ERASE:
memset(ONENAND_CORE(flash) + offset, 0xff, mtd->erasesize);
memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff,
(mtd->erasesize >> 5));
break;
default:
break;
}
}
/**
* onenand_command_handle - Handle command
* @param this OneNAND device structure
* @param cmd The command to be sent
*
* Emulate OneNAND command.
*/
static void onenand_command_handle(struct onenand_chip *this, int cmd)
{
unsigned long offset = 0;
int block = -1, page = -1, bufferram = -1;
int dataram = 0;
switch (cmd) {
case ONENAND_CMD_UNLOCK:
case ONENAND_CMD_LOCK:
case ONENAND_CMD_LOCK_TIGHT:
case ONENAND_CMD_UNLOCK_ALL:
onenand_lock_handle(this, cmd);
break;
case ONENAND_CMD_BUFFERRAM:
/* Do nothing */
return;
default:
block = (int) readw(this->base + ONENAND_REG_START_ADDRESS1);
if (block & (1 << ONENAND_DDP_SHIFT)) {
block &= ~(1 << ONENAND_DDP_SHIFT);
/* The half of chip block */
block += this->chipsize >> (this->erase_shift + 1);
}
if (cmd == ONENAND_CMD_ERASE)
break;
page = (int) readw(this->base + ONENAND_REG_START_ADDRESS8);
page = (page >> ONENAND_FPA_SHIFT);
bufferram = (int) readw(this->base + ONENAND_REG_START_BUFFER);
bufferram >>= ONENAND_BSA_SHIFT;
bufferram &= ONENAND_BSA_DATARAM1;
dataram = (bufferram == ONENAND_BSA_DATARAM1) ? 1 : 0;
break;
}
if (block != -1)
offset += block << this->erase_shift;
if (page != -1)
offset += page << this->page_shift;
onenand_data_handle(this, cmd, dataram, offset);
onenand_update_interrupt(this, cmd);
}
/**
* onenand_writew - [OneNAND Interface] Emulate write operation
* @param value value to write
* @param addr address to write
*
* Write OneNAND register with value
*/
static void onenand_writew(unsigned short value, void __iomem * addr)
{
struct onenand_chip *this = info->mtd.priv;
/* BootRAM handling */
if (addr < this->base + ONENAND_DATARAM) {
onenand_bootram_handle(this, value);
return;
}
/* Command handling */
if (addr == this->base + ONENAND_REG_COMMAND)
onenand_command_handle(this, value);
writew(value, addr);
}
/**
* flash_init - Initialize OneNAND simulator
* @param flash OneNAND simulaotr data strucutres
*
* Initialize OneNAND simulator.
*/
static int __init flash_init(struct onenand_flash *flash)
{
int density, size;
int buffer_size;
flash->base = kzalloc(131072, GFP_KERNEL);
if (!flash->base) {
printk(KERN_ERR "Unable to allocate base address.\n");
return -ENOMEM;
}
density = device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
size = ((16 << 20) << density);
ONENAND_CORE(flash) = vmalloc(size + (size >> 5));
if (!ONENAND_CORE(flash)) {
printk(KERN_ERR "Unable to allocate nand core address.\n");
kfree(flash->base);
return -ENOMEM;
}
memset(ONENAND_CORE(flash), 0xff, size + (size >> 5));
/* Setup registers */
writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID);
writew(device_id, flash->base + ONENAND_REG_DEVICE_ID);
writew(version_id, flash->base + ONENAND_REG_VERSION_ID);
if (density < 2)
buffer_size = 0x0400; /* 1KiB page */
else
buffer_size = 0x0800; /* 2KiB page */
writew(buffer_size, flash->base + ONENAND_REG_DATA_BUFFER_SIZE);
return 0;
}
/**
* flash_exit - Clean up OneNAND simulator
* @param flash OneNAND simulaotr data strucutres
*
* Clean up OneNAND simulator.
*/
static void flash_exit(struct onenand_flash *flash)
{
vfree(ONENAND_CORE(flash));
kfree(flash->base);
kfree(flash);
}
static int __init onenand_sim_init(void)
{
/* Allocate all 0xff chars pointer */
ffchars = kmalloc(MAX_ONENAND_PAGESIZE, GFP_KERNEL);
if (!ffchars) {
printk(KERN_ERR "Unable to allocate ff chars.\n");
return -ENOMEM;
}
memset(ffchars, 0xff, MAX_ONENAND_PAGESIZE);
/* Allocate OneNAND simulator mtd pointer */
info = kzalloc(sizeof(struct onenand_info), GFP_KERNEL);
if (!info) {
printk(KERN_ERR "Unable to allocate core structures.\n");
kfree(ffchars);
return -ENOMEM;
}
/* Override write_word function */
info->onenand.write_word = onenand_writew;
if (flash_init(&info->flash)) {
printk(KERN_ERR "Unable to allocat flash.\n");
kfree(ffchars);
kfree(info);
return -ENOMEM;
}
info->parts = os_partitions;
info->onenand.base = info->flash.base;
info->onenand.priv = &info->flash;
info->mtd.name = "OneNAND simulator";
info->mtd.priv = &info->onenand;
info->mtd.owner = THIS_MODULE;
if (onenand_scan(&info->mtd, 1)) {
flash_exit(&info->flash);
kfree(ffchars);
kfree(info);
return -ENXIO;
}
add_mtd_partitions(&info->mtd, info->parts, ARRAY_SIZE(os_partitions));
return 0;
}
static void __exit onenand_sim_exit(void)
{
struct onenand_chip *this = info->mtd.priv;
struct onenand_flash *flash = this->priv;
onenand_release(&info->mtd);
flash_exit(flash);
kfree(ffchars);
kfree(info);
}
module_init(onenand_sim_init);
module_exit(onenand_sim_exit);
MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
MODULE_DESCRIPTION("The OneNAND flash simulator");
MODULE_LICENSE("GPL");
......@@ -779,10 +779,8 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
else {
if (!mtd->erasesize) {
printk(KERN_WARNING PREFIX "please provide block_size");
kfree(part);
return;
}
else
goto out;
} else
part->block_size = mtd->erasesize;
}
......@@ -804,7 +802,7 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
if (!add_mtd_blktrans_dev((void*)part))
return;
}
out:
kfree(part);
}
......
此差异已折叠。
此差异已折叠。
......@@ -17,4 +17,5 @@ jffs2-$(CONFIG_JFFS2_FS_POSIX_ACL) += acl.o
jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o
jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o
jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o
jffs2-$(CONFIG_JFFS2_LZO) += compr_lzo.o
jffs2-$(CONFIG_JFFS2_SUMMARY) += summary.o
此差异已折叠。
此差异已折叠。
......@@ -23,8 +23,8 @@ static int jffs2_garbage_collect_thread(void *);
void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c)
{
spin_lock(&c->erase_completion_lock);
if (c->gc_task && jffs2_thread_should_wake(c))
send_sig(SIGHUP, c->gc_task, 1);
if (c->gc_task && jffs2_thread_should_wake(c))
send_sig(SIGHUP, c->gc_task, 1);
spin_unlock(&c->erase_completion_lock);
}
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -55,6 +55,7 @@
#define CH_SPORT3_RX 20
#define CH_SPORT3_TX 21
#define CH_SDH 22
#define CH_NFC 22
#define CH_SPI2 23
#define CH_MEM_STREAM0_DEST 24
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册