diff options
Diffstat (limited to 'drivers/mtd')
36 files changed, 1354 insertions, 754 deletions
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index fbec8cd55e3..8848e8ac705 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -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" diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 6d958a4566f..7f0b04b4caa 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -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 diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 2f19fa78d24..39eff9ff575 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -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 @@ -1780,7 +1780,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 +1930,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 +1940,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 +1954,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 +1964,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 +2255,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 diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 1f644584046..389acc600f5 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -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; diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 58e561e8769..a67b23b87fc 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -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 @@ -646,6 +647,23 @@ static const struct amd_flash_info jedec_table[] = { } }, { .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, .name = "Fujitsu MBM29LV650UE", .uaddr = { @@ -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, diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index ff642f8fbee..811d56fd890 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -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" diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 8ab568b3f53..0f788d5c4bf 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -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 diff --git a/drivers/mtd/devices/at91_dataflash26.c b/drivers/mtd/devices/at91_dataflash26.c deleted file mode 100644 index 64ce37f986f..00000000000 --- a/drivers/mtd/devices/at91_dataflash26.c +++ /dev/null @@ -1,485 +0,0 @@ -/* - * Atmel DataFlash driver for Atmel AT91RM9200 (Thunder) - * This is a largely modified version of at91_dataflash.c that - * supports AT26xxx dataflash chips. The original driver supports - * AT45xxx chips. - * - * Note: This driver was only tested with an AT26F004. It should be - * easy to make it work with other AT26xxx dataflash devices, though. - * - * Copyright (C) 2007 Hans J. Koch <hjk@linutronix.de> - * original Copyright (C) SAN People (Pty) Ltd - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. -*/ - -#include <linux/config.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/mtd/mtd.h> - -#include <asm/arch/at91_spi.h> - -#define DATAFLASH_MAX_DEVICES 4 /* max number of dataflash devices */ - -#define MANUFACTURER_ID_ATMEL 0x1F - -/* command codes */ - -#define AT26_OP_READ_STATUS 0x05 -#define AT26_OP_READ_DEV_ID 0x9F -#define AT26_OP_ERASE_PAGE_4K 0x20 -#define AT26_OP_READ_ARRAY_FAST 0x0B -#define AT26_OP_SEQUENTIAL_WRITE 0xAF -#define AT26_OP_WRITE_ENABLE 0x06 -#define AT26_OP_WRITE_DISABLE 0x04 -#define AT26_OP_SECTOR_PROTECT 0x36 -#define AT26_OP_SECTOR_UNPROTECT 0x39 - -/* status register bits */ - -#define AT26_STATUS_BUSY 0x01 -#define AT26_STATUS_WRITE_ENABLE 0x02 - -struct dataflash_local -{ - int spi; /* SPI chip-select number */ - unsigned int page_size; /* number of bytes per page */ -}; - - -/* Detected DataFlash devices */ -static struct mtd_info* mtd_devices[DATAFLASH_MAX_DEVICES]; -static int nr_devices = 0; - -/* Allocate a single SPI transfer descriptor. We're assuming that if multiple - SPI transfers occur at the same time, spi_access_bus() will serialize them. - If this is not valid, then either (i) each dataflash 'priv' structure - needs it's own transfer descriptor, (ii) we lock this one, or (iii) use - another mechanism. */ -static struct spi_transfer_list* spi_transfer_desc; - -/* - * Perform a SPI transfer to access the DataFlash device. - */ -static int do_spi_transfer(int nr, char* tx, int tx_len, char* rx, int rx_len, - char* txnext, int txnext_len, char* rxnext, int rxnext_len) -{ - struct spi_transfer_list* list = spi_transfer_desc; - - list->tx[0] = tx; list->txlen[0] = tx_len; - list->rx[0] = rx; list->rxlen[0] = rx_len; - - list->tx[1] = txnext; list->txlen[1] = txnext_len; - list->rx[1] = rxnext; list->rxlen[1] = rxnext_len; - - list->nr_transfers = nr; - /* Note: spi_transfer() always returns 0, there are no error checks */ - return spi_transfer(list); -} - -/* - * Return the status of the DataFlash device. - */ -static unsigned char at91_dataflash26_status(void) -{ - unsigned char command[2]; - - command[0] = AT26_OP_READ_STATUS; - command[1] = 0; - - do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0); - - return command[1]; -} - -/* - * Poll the DataFlash device until it is READY. - */ -static unsigned char at91_dataflash26_waitready(void) -{ - unsigned char status; - - while (1) { - status = at91_dataflash26_status(); - if (!(status & AT26_STATUS_BUSY)) - return status; - } -} - -/* - * Enable/disable write access - */ - static void at91_dataflash26_write_enable(int enable) -{ - unsigned char cmd[2]; - - DEBUG(MTD_DEBUG_LEVEL3, "write_enable: enable=%i\n", enable); - - if (enable) - cmd[0] = AT26_OP_WRITE_ENABLE; - else - cmd[0] = AT26_OP_WRITE_DISABLE; - cmd[1] = 0; - - do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0); -} - -/* - * Protect/unprotect sector - */ - static void at91_dataflash26_sector_protect(loff_t addr, int protect) -{ - unsigned char cmd[4]; - - DEBUG(MTD_DEBUG_LEVEL3, "sector_protect: addr=0x%06x prot=%d\n", - addr, protect); - - if (protect) - cmd[0] = AT26_OP_SECTOR_PROTECT; - else - cmd[0] = AT26_OP_SECTOR_UNPROTECT; - cmd[1] = (addr & 0x00FF0000) >> 16; - cmd[2] = (addr & 0x0000FF00) >> 8; - cmd[3] = (addr & 0x000000FF); - - do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0); -} - -/* - * Erase blocks of flash. - */ -static int at91_dataflash26_erase(struct mtd_info *mtd, - struct erase_info *instr) -{ - struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; - unsigned char cmd[4]; - - DEBUG(MTD_DEBUG_LEVEL1, "dataflash_erase: addr=0x%06x len=%i\n", - instr->addr, instr->len); - - /* Sanity checks */ - if (priv->page_size != 4096) - return -EINVAL; /* Can't handle other sizes at the moment */ - - if ( ((instr->len % mtd->erasesize) != 0) - || ((instr->len % priv->page_size) != 0) - || ((instr->addr % priv->page_size) != 0) - || ((instr->addr + instr->len) > mtd->size)) - return -EINVAL; - - spi_access_bus(priv->spi); - - while (instr->len > 0) { - at91_dataflash26_write_enable(1); - at91_dataflash26_sector_protect(instr->addr, 0); - at91_dataflash26_write_enable(1); - cmd[0] = AT26_OP_ERASE_PAGE_4K; - cmd[1] = (instr->addr & 0x00FF0000) >> 16; - cmd[2] = (instr->addr & 0x0000FF00) >> 8; - cmd[3] = (instr->addr & 0x000000FF); - - DEBUG(MTD_DEBUG_LEVEL3, "ERASE: (0x%02x) 0x%02x 0x%02x" - "0x%02x\n", - cmd[0], cmd[1], cmd[2], cmd[3]); - - do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0); - at91_dataflash26_waitready(); - - instr->addr += priv->page_size; /* next page */ - instr->len -= priv->page_size; - } - - at91_dataflash26_write_enable(0); - spi_release_bus(priv->spi); - - /* Inform MTD subsystem that erase is complete */ - instr->state = MTD_ERASE_DONE; - if (instr->callback) - instr->callback(instr); - - return 0; -} - -/* - * Read from the DataFlash device. - * from : Start offset in flash device - * len : Number of bytes to read - * retlen : Number of bytes actually read - * buf : Buffer that will receive data - */ -static int at91_dataflash26_read(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) -{ - struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; - unsigned char cmd[5]; - - DEBUG(MTD_DEBUG_LEVEL1, "dataflash_read: %lli .. %lli\n", - from, from+len); - - *retlen = 0; - - /* Sanity checks */ - if (!len) - return 0; - if (from + len > mtd->size) - return -EINVAL; - - cmd[0] = AT26_OP_READ_ARRAY_FAST; - cmd[1] = (from & 0x00FF0000) >> 16; - cmd[2] = (from & 0x0000FF00) >> 8; - cmd[3] = (from & 0x000000FF); - /* cmd[4] is a "Don't care" byte */ - - DEBUG(MTD_DEBUG_LEVEL3, "READ: (0x%02x) 0x%02x 0x%02x 0x%02x\n", - cmd[0], cmd[1], cmd[2], cmd[3]); - - spi_access_bus(priv->spi); - do_spi_transfer(2, cmd, 5, cmd, 5, buf, len, buf, len); - spi_release_bus(priv->spi); - - *retlen = len; - return 0; -} - -/* - * Write to the DataFlash device. - * to : Start offset in flash device - * len : Number of bytes to write - * retlen : Number of bytes actually written - * buf : Buffer containing the data - */ -static int at91_dataflash26_write(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) -{ - struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; - unsigned int addr, buf_index = 0; - int ret = -EIO, sector, last_sector; - unsigned char status, cmd[5]; - - DEBUG(MTD_DEBUG_LEVEL1, "dataflash_write: %lli .. %lli\n", to, to+len); - - *retlen = 0; - - /* Sanity checks */ - if (!len) - return 0; - if (to + len > mtd->size) - return -EINVAL; - - spi_access_bus(priv->spi); - - addr = to; - last_sector = -1; - - while (buf_index < len) { - sector = addr / priv->page_size; - /* Write first byte if a new sector begins */ - if (sector != last_sector) { - at91_dataflash26_write_enable(1); - at91_dataflash26_sector_protect(addr, 0); - at91_dataflash26_write_enable(1); - - /* Program first byte of a new sector */ - cmd[0] = AT26_OP_SEQUENTIAL_WRITE; - cmd[1] = (addr & 0x00FF0000) >> 16; - cmd[2] = (addr & 0x0000FF00) >> 8; - cmd[3] = (addr & 0x000000FF); - cmd[4] = buf[buf_index++]; - do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0); - status = at91_dataflash26_waitready(); - addr++; - /* On write errors, the chip resets the write enable - flag. This also happens after the last byte of a - sector is successfully programmed. */ - if ( ( !(status & AT26_STATUS_WRITE_ENABLE)) - && ((addr % priv->page_size) != 0) ) { - DEBUG(MTD_DEBUG_LEVEL1, - "write error1: addr=0x%06x, " - "status=0x%02x\n", addr, status); - goto write_err; - } - (*retlen)++; - last_sector = sector; - } - - /* Write subsequent bytes in the same sector */ - cmd[0] = AT26_OP_SEQUENTIAL_WRITE; - cmd[1] = buf[buf_index++]; - do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0); - status = at91_dataflash26_waitready(); - addr++; - - if ( ( !(status & AT26_STATUS_WRITE_ENABLE)) - && ((addr % priv->page_size) != 0) ) { - DEBUG(MTD_DEBUG_LEVEL1, "write error2: addr=0x%06x, " - "status=0x%02x\n", addr, status); - goto write_err; - } - - (*retlen)++; - } - - ret = 0; - at91_dataflash26_write_enable(0); -write_err: - spi_release_bus(priv->spi); - return ret; -} - -/* - * Initialize and register DataFlash device with MTD subsystem. - */ -static int __init add_dataflash(int channel, char *name, int nr_pages, - int pagesize) -{ - struct mtd_info *device; - struct dataflash_local *priv; - - if (nr_devices >= DATAFLASH_MAX_DEVICES) { - printk(KERN_ERR "at91_dataflash26: Too many devices " - "detected\n"); - return 0; - } - - device = kzalloc(sizeof(struct mtd_info) + strlen(name) + 8, - GFP_KERNEL); - if (!device) - return -ENOMEM; - - device->name = (char *)&device[1]; - sprintf(device->name, "%s.spi%d", name, channel); - device->size = nr_pages * pagesize; - device->erasesize = pagesize; - device->owner = THIS_MODULE; - device->type = MTD_DATAFLASH; - device->flags = MTD_CAP_NORFLASH; - device->erase = at91_dataflash26_erase; - device->read = at91_dataflash26_read; - device->write = at91_dataflash26_write; - - priv = (struct dataflash_local *)kzalloc(sizeof(struct dataflash_local), - GFP_KERNEL); - if (!priv) { - kfree(device); - return -ENOMEM; - } - - priv->spi = channel; - priv->page_size = pagesize; - device->priv = priv; - - mtd_devices[nr_devices] = device; - nr_devices++; - printk(KERN_INFO "at91_dataflash26: %s detected [spi%i] (%i bytes)\n", - name, channel, device->size); - - return add_mtd_device(device); -} - -/* - * Detect and initialize DataFlash device connected to specified SPI channel. - * - */ - -struct dataflash26_types { - unsigned char id0; - unsigned char id1; - char *name; - int pagesize; - int nr_pages; -}; - -struct dataflash26_types df26_types[] = { - { - .id0 = 0x04, - .id1 = 0x00, - .name = "AT26F004", - .pagesize = 4096, - .nr_pages = 128, - }, - { - .id0 = 0x45, - .id1 = 0x01, - .name = "AT26DF081A", /* Not tested ! */ - .pagesize = 4096, - .nr_pages = 256, - }, -}; - -static int __init at91_dataflash26_detect(int channel) -{ - unsigned char status, cmd[5]; - int i; - - spi_access_bus(channel); - status = at91_dataflash26_status(); - - if (status == 0 || status == 0xff) { - printk(KERN_ERR "at91_dataflash26_detect: status error %d\n", - status); - spi_release_bus(channel); - return -ENODEV; - } - - cmd[0] = AT26_OP_READ_DEV_ID; - do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0); - spi_release_bus(channel); - - if (cmd[1] != MANUFACTURER_ID_ATMEL) - return -ENODEV; - - for (i = 0; i < ARRAY_SIZE(df26_types); i++) { - if ( cmd[2] == df26_types[i].id0 - && cmd[3] == df26_types[i].id1) - return add_dataflash(channel, - df26_types[i].name, - df26_types[i].nr_pages, - df26_types[i].pagesize); - } - - printk(KERN_ERR "at91_dataflash26_detect: Unsupported device " - "(0x%02x/0x%02x)\n", cmd[2], cmd[3]); - return -ENODEV; -} - -static int __init at91_dataflash26_init(void) -{ - spi_transfer_desc = kmalloc(sizeof(struct spi_transfer_list), - GFP_KERNEL); - if (!spi_transfer_desc) - return -ENOMEM; - - /* DataFlash (SPI chip select 0) */ - at91_dataflash26_detect(0); - -#ifdef CONFIG_MTD_AT91_DATAFLASH_CARD - /* DataFlash card (SPI chip select 3) */ - at91_dataflash26_detect(3); -#endif - return 0; -} - -static void __exit at91_dataflash26_exit(void) -{ - int i; - - for (i = 0; i < DATAFLASH_MAX_DEVICES; i++) { - if (mtd_devices[i]) { - del_mtd_device(mtd_devices[i]); - kfree(mtd_devices[i]->priv); - kfree(mtd_devices[i]); - } - } - nr_devices = 0; - kfree(spi_transfer_desc); -} - -module_init(at91_dataflash26_init); -module_exit(at91_dataflash26_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Hans J. Koch"); -MODULE_DESCRIPTION("DataFlash AT26xxx driver for Atmel AT91RM9200"); diff --git a/drivers/mtd/devices/docprobe.c b/drivers/mtd/devices/docprobe.c index 54aa7590764..d8cc94ec4e5 100644 --- a/drivers/mtd/devices/docprobe.c +++ b/drivers/mtd/devices/docprobe.c @@ -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 }; diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 78c2511ae9e..98df5bcc02f 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -1,5 +1,5 @@ /* - * MTD SPI driver for ST M25Pxx flash chips + * MTD SPI driver for ST M25Pxx (and similar) serial flash chips * * Author: Mike Lavender, mike@steroidmicros.com * @@ -19,33 +19,32 @@ #include <linux/module.h> #include <linux/device.h> #include <linux/interrupt.h> -#include <linux/interrupt.h> +#include <linux/mutex.h> + #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> + #include <linux/spi/spi.h> #include <linux/spi/flash.h> -#include <asm/semaphore.h> - - -/* NOTE: AT 25F and SST 25LF series are very similar, - * but commands for sector erase and chip id differ... - */ #define FLASH_PAGESIZE 256 /* Flash opcodes. */ -#define OPCODE_WREN 6 /* Write enable */ -#define OPCODE_RDSR 5 /* Read status register */ -#define OPCODE_READ 3 /* Read data bytes */ -#define OPCODE_PP 2 /* Page program */ -#define OPCODE_SE 0xd8 /* Sector erase */ -#define OPCODE_RES 0xab /* Read Electronic Signature */ +#define OPCODE_WREN 0x06 /* Write enable */ +#define OPCODE_RDSR 0x05 /* Read status register */ +#define OPCODE_READ 0x03 /* Read data bytes (low frequency) */ +#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */ +#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */ +#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */ +#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */ +#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */ #define OPCODE_RDID 0x9f /* Read JEDEC ID */ /* Status Register bits. */ #define SR_WIP 1 /* Write in progress */ #define SR_WEL 2 /* Write enable latch */ +/* meaning of other SR_* bits may differ between vendors */ #define SR_BP0 4 /* Block protect 0 */ #define SR_BP1 8 /* Block protect 1 */ #define SR_BP2 0x10 /* Block protect 2 */ @@ -65,9 +64,10 @@ struct m25p { struct spi_device *spi; - struct semaphore lock; + struct mutex lock; struct mtd_info mtd; - unsigned partitioned; + unsigned partitioned:1; + u8 erase_opcode; u8 command[4]; }; @@ -150,8 +150,9 @@ static int wait_till_ready(struct m25p *flash) */ static int erase_sector(struct m25p *flash, u32 offset) { - DEBUG(MTD_DEBUG_LEVEL3, "%s: %s at 0x%08x\n", flash->spi->dev.bus_id, - __FUNCTION__, offset); + DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n", + flash->spi->dev.bus_id, __FUNCTION__, + flash->mtd.erasesize / 1024, offset); /* Wait until finished previous write command. */ if (wait_till_ready(flash)) @@ -161,7 +162,7 @@ static int erase_sector(struct m25p *flash, u32 offset) write_enable(flash); /* Set up command buffer. */ - flash->command[0] = OPCODE_SE; + flash->command[0] = flash->erase_opcode; flash->command[1] = offset >> 16; flash->command[2] = offset >> 8; flash->command[3] = offset; @@ -201,13 +202,17 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) addr = instr->addr; len = instr->len; - down(&flash->lock); + mutex_lock(&flash->lock); + + /* REVISIT in some cases we could speed up erasing large regions + * by using OPCODE_SE instead of OPCODE_BE_4K + */ /* now erase those sectors */ while (len) { if (erase_sector(flash, addr)) { instr->state = MTD_ERASE_FAILED; - up(&flash->lock); + mutex_unlock(&flash->lock); return -EIO; } @@ -215,7 +220,7 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr) len -= mtd->erasesize; } - up(&flash->lock); + mutex_unlock(&flash->lock); instr->state = MTD_ERASE_DONE; mtd_erase_callback(instr); @@ -260,16 +265,19 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, if (retlen) *retlen = 0; - down(&flash->lock); + mutex_lock(&flash->lock); /* Wait till previous write/erase is done. */ if (wait_till_ready(flash)) { /* REVISIT status return?? */ - up(&flash->lock); + mutex_unlock(&flash->lock); return 1; } - /* NOTE: OPCODE_FAST_READ (if available) is faster... */ + /* FIXME switch to OPCODE_FAST_READ. It's required for higher + * clocks; and at this writing, every chip this driver handles + * supports that opcode. + */ /* Set up the write data buffer. */ flash->command[0] = OPCODE_READ; @@ -281,7 +289,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len, *retlen = m.actual_length - sizeof(flash->command); - up(&flash->lock); + mutex_unlock(&flash->lock); return 0; } @@ -323,7 +331,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, t[1].tx_buf = buf; spi_message_add_tail(&t[1], &m); - down(&flash->lock); + mutex_lock(&flash->lock); /* Wait until finished previous write command. */ if (wait_till_ready(flash)) @@ -381,10 +389,10 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, if (retlen) *retlen += m.actual_length - sizeof(flash->command); - } - } + } + } - up(&flash->lock); + mutex_unlock(&flash->lock); return 0; } @@ -398,24 +406,118 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len, struct flash_info { char *name; - u8 id; - u16 jedec_id; + + /* JEDEC id zero means "no ID" (most older chips); otherwise it has + * a high byte of zero plus three data bytes: the manufacturer id, + * then a two byte device id. + */ + u32 jedec_id; + + /* The size listed here is what works with OPCODE_SE, which isn't + * necessarily called a "sector" by the vendor. + */ unsigned sector_size; - unsigned n_sectors; + u16 n_sectors; + + u16 flags; +#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */ }; + +/* NOTE: double check command sets and memory organization when you add + * more flash chips. This current list focusses on newer chips, which + * have been converging on command sets which including JEDEC ID. + */ static struct flash_info __devinitdata m25p_data [] = { - /* REVISIT: fill in JEDEC ids, for parts that have them */ - { "m25p05", 0x05, 0x2010, 32 * 1024, 2 }, - { "m25p10", 0x10, 0x2011, 32 * 1024, 4 }, - { "m25p20", 0x11, 0x2012, 64 * 1024, 4 }, - { "m25p40", 0x12, 0x2013, 64 * 1024, 8 }, - { "m25p80", 0x13, 0x0000, 64 * 1024, 16 }, - { "m25p16", 0x14, 0x2015, 64 * 1024, 32 }, - { "m25p32", 0x15, 0x2016, 64 * 1024, 64 }, - { "m25p64", 0x16, 0x2017, 64 * 1024, 128 }, + + /* Atmel -- some are (confusingly) marketed as "DataFlash" */ + { "at25fs010", 0x1f6601, 32 * 1024, 4, SECT_4K, }, + { "at25fs040", 0x1f6604, 64 * 1024, 8, SECT_4K, }, + + { "at25df041a", 0x1f4401, 64 * 1024, 8, SECT_4K, }, + + { "at26f004", 0x1f0400, 64 * 1024, 8, SECT_4K, }, + { "at26df081a", 0x1f4501, 64 * 1024, 16, SECT_4K, }, + { "at26df161a", 0x1f4601, 64 * 1024, 32, SECT_4K, }, + { "at26df321", 0x1f4701, 64 * 1024, 64, SECT_4K, }, + + /* Spansion -- single (large) sector size only, at least + * for the chips listed here (without boot sectors). + */ + { "s25sl004a", 0x010212, 64 * 1024, 8, }, + { "s25sl008a", 0x010213, 64 * 1024, 16, }, + { "s25sl016a", 0x010214, 64 * 1024, 32, }, + { "s25sl032a", 0x010215, 64 * 1024, 64, }, + { "s25sl064a", 0x010216, 64 * 1024, 128, }, + + /* SST -- large erase sizes are "overlays", "sectors" are 4K */ + { "sst25vf040b", 0xbf258d, 64 * 1024, 8, SECT_4K, }, + { "sst25vf080b", 0xbf258e, 64 * 1024, 16, SECT_4K, }, + { "sst25vf016b", 0xbf2541, 64 * 1024, 32, SECT_4K, }, + { "sst25vf032b", 0xbf254a, 64 * 1024, 64, SECT_4K, }, + + /* ST Microelectronics -- newer production may have feature updates */ + { "m25p05", 0x202010, 32 * 1024, 2, }, + { "m25p10", 0x202011, 32 * 1024, 4, }, + { "m25p20", 0x202012, 64 * 1024, 4, }, + { "m25p40", 0x202013, 64 * 1024, 8, }, + { "m25p80", 0, 64 * 1024, 16, }, + { "m25p16", 0x202015, 64 * 1024, 32, }, + { "m25p32", 0x202016, 64 * 1024, 64, }, + { "m25p64", 0x202017, 64 * 1024, 128, }, + { "m25p128", 0x202018, 256 * 1024, 64, }, + + { "m45pe80", 0x204014, 64 * 1024, 16, }, + { "m45pe16", 0x204015, 64 * 1024, 32, }, + + { "m25pe80", 0x208014, 64 * 1024, 16, }, + { "m25pe16", 0x208015, 64 * 1024, 32, SECT_4K, }, + + /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ + { "w25x10", 0xef3011, 64 * 1024, 2, SECT_4K, }, + { "w25x20", 0xef3012, 64 * 1024, 4, SECT_4K, }, + { "w25x40", 0xef3013, 64 * 1024, 8, SECT_4K, }, + { "w25x80", 0xef3014, 64 * 1024, 16, SECT_4K, }, + { "w25x16", 0xef3015, 64 * 1024, 32, SECT_4K, }, + { "w25x32", 0xef3016, 64 * 1024, 64, SECT_4K, }, + { "w25x64", 0xef3017, 64 * 1024, 128, SECT_4K, }, }; +static struct flash_info *__devinit jedec_probe(struct spi_device *spi) +{ + int tmp; + u8 code = OPCODE_RDID; + u8 id[3]; + u32 jedec; + struct flash_info *info; + + /* JEDEC also defines an optional "extended device information" + * string for after vendor-specific data, after the three bytes + * we use here. Supporting some chips might require using it. + */ + tmp = spi_write_then_read(spi, &code, 1, id, 3); + if (tmp < 0) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n", + spi->dev.bus_id, tmp); + return NULL; + } + jedec = id[0]; + jedec = jedec << 8; + jedec |= id[1]; + jedec = jedec << 8; + jedec |= id[2]; + + for (tmp = 0, info = m25p_data; + tmp < ARRAY_SIZE(m25p_data); + tmp++, info++) { + if (info->jedec_id == jedec) + return info; + } + dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec); + return NULL; +} + + /* * board specific setup should have ensured the SPI clock used here * matches what the READ command supports, at least until this driver @@ -429,37 +531,51 @@ static int __devinit m25p_probe(struct spi_device *spi) unsigned i; /* Platform data helps sort out which chip type we have, as - * well as how this board partitions it. + * well as how this board partitions it. If we don't have + * a chip ID, try the JEDEC id commands; they'll work for most + * newer chips, even if we don't recognize the particular chip. */ data = spi->dev.platform_data; - if (!data || !data->type) { - /* FIXME some chips can identify themselves with RES - * or JEDEC get-id commands. Try them ... - */ - DEBUG(MTD_DEBUG_LEVEL1, "%s: no chip id\n", - spi->dev.bus_id); - return -ENODEV; - } + if (data && data->type) { + for (i = 0, info = m25p_data; + i < ARRAY_SIZE(m25p_data); + i++, info++) { + if (strcmp(data->type, info->name) == 0) + break; + } - for (i = 0, info = m25p_data; i < ARRAY_SIZE(m25p_data); i++, info++) { - if (strcmp(data->type, info->name) == 0) - break; - } - if (i == ARRAY_SIZE(m25p_data)) { - DEBUG(MTD_DEBUG_LEVEL1, "%s: unrecognized id %s\n", - spi->dev.bus_id, data->type); + /* unrecognized chip? */ + if (i == ARRAY_SIZE(m25p_data)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n", + spi->dev.bus_id, data->type); + info = NULL; + + /* recognized; is that chip really what's there? */ + } else if (info->jedec_id) { + struct flash_info *chip = jedec_probe(spi); + + if (!chip || chip != info) { + dev_warn(&spi->dev, "found %s, expected %s\n", + chip ? chip->name : "UNKNOWN", + info->name); + info = NULL; + } + } + } else + info = jedec_probe(spi); + + if (!info) return -ENODEV; - } flash = kzalloc(sizeof *flash, GFP_KERNEL); if (!flash) return -ENOMEM; flash->spi = spi; - init_MUTEX(&flash->lock); + mutex_init(&flash->lock); dev_set_drvdata(&spi->dev, flash); - if (data->name) + if (data && data->name) flash->mtd.name = data->name; else flash->mtd.name = spi->dev.bus_id; @@ -468,17 +584,25 @@ static int __devinit m25p_probe(struct spi_device *spi) flash->mtd.writesize = 1; flash->mtd.flags = MTD_CAP_NORFLASH; flash->mtd.size = info->sector_size * info->n_sectors; - flash->mtd.erasesize = info->sector_size; flash->mtd.erase = m25p80_erase; flash->mtd.read = m25p80_read; flash->mtd.write = m25p80_write; + /* prefer "small sector" erase if possible */ + if (info->flags & SECT_4K) { + flash->erase_opcode = OPCODE_BE_4K; + flash->mtd.erasesize = 4096; + } else { + flash->erase_opcode = OPCODE_SE; + flash->mtd.erasesize = info->sector_size; + } + dev_info(&spi->dev, "%s (%d Kbytes)\n", info->name, flash->mtd.size / 1024); DEBUG(MTD_DEBUG_LEVEL2, - "mtd .name = %s, .size = 0x%.8x (%uM) " - ".erasesize = 0x%.8x (%uK) .numeraseregions = %d\n", + "mtd .name = %s, .size = 0x%.8x (%uMiB) " + ".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n", flash->mtd.name, flash->mtd.size, flash->mtd.size / (1024*1024), flash->mtd.erasesize, flash->mtd.erasesize / 1024, @@ -488,7 +612,7 @@ static int __devinit m25p_probe(struct spi_device *spi) for (i = 0; i < flash->mtd.numeraseregions; i++) DEBUG(MTD_DEBUG_LEVEL2, "mtd.eraseregions[%d] = { .offset = 0x%.8x, " - ".erasesize = 0x%.8x (%uK), " + ".erasesize = 0x%.8x (%uKiB), " ".numblocks = %d }\n", i, flash->mtd.eraseregions[i].offset, flash->mtd.eraseregions[i].erasesize, @@ -516,14 +640,14 @@ static int __devinit m25p_probe(struct spi_device *spi) } if (nr_parts > 0) { - for (i = 0; i < data->nr_parts; i++) { + for (i = 0; i < nr_parts; i++) { DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = " "{.name = %s, .offset = 0x%.8x, " - ".size = 0x%.8x (%uK) }\n", - i, data->parts[i].name, - data->parts[i].offset, - data->parts[i].size, - data->parts[i].size / 1024); + ".size = 0x%.8x (%uKiB) }\n", + i, parts[i].name, + parts[i].offset, + parts[i].size, + parts[i].size / 1024); } flash->partitioned = 1; return add_mtd_partitions(&flash->mtd, parts, nr_parts); @@ -560,6 +684,11 @@ static struct spi_driver m25p80_driver = { }, .probe = m25p_probe, .remove = __devexit_p(m25p_remove), + + /* REVISIT: many of these chips have deep power-down modes, which + * should clearly be entered on suspend() to minimize power use. + * And also when they're otherwise idle... + */ }; diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c index a987e917f4e..a5ed6d232c3 100644 --- a/drivers/mtd/devices/mtd_dataflash.c +++ b/drivers/mtd/devices/mtd_dataflash.c @@ -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; diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index e8f686f7a35..7060a0895ce 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -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); } diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index ecac0e438f4..b8917beeb65 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -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 diff --git a/drivers/mtd/maps/alchemy-flash.c b/drivers/mtd/maps/alchemy-flash.c index 84fbe0e8c47..82811bcb043 100644 --- a/drivers/mtd/maps/alchemy-flash.c +++ b/drivers/mtd/maps/alchemy-flash.c @@ -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 diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c index 7b96cd02f82..0c9b305a72e 100644 --- a/drivers/mtd/maps/nettel.c +++ b/drivers/mtd/maps/nettel.c @@ -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 @@ out_unmap2: /****************************************************************************/ -void __exit nettel_cleanup(void) +static void __exit nettel_cleanup(void) { #ifdef CONFIG_MTD_CFI_INTELEXT unregister_reboot_notifier(&nettel_notifier_block); diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index bbb42c35b69..fbd61396871 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -141,7 +141,6 @@ static int __devinit of_physmap_probe(struct of_device *dev, const struct of_dev err = -ENOMEM; goto err_out; } - memset(info, 0, sizeof(*info)); dev_set_drvdata(&dev->dev, info); @@ -213,10 +212,6 @@ static int __devinit of_physmap_probe(struct of_device *dev, const struct of_dev err_out: of_physmap_remove(dev); return err; - - return 0; - - } static struct of_device_id of_physmap_match[] = { diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c index 7e0377ec1c4..02bde8c982e 100644 --- a/drivers/mtd/maps/pmcmsp-flash.c +++ b/drivers/mtd/maps/pmcmsp-flash.c @@ -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; diff --git a/drivers/mtd/maps/pmcmsp-ramroot.c b/drivers/mtd/maps/pmcmsp-ramroot.c index 18049bceba8..30de5c0c09a 100644 --- a/drivers/mtd/maps/pmcmsp-ramroot.c +++ b/drivers/mtd/maps/pmcmsp-ramroot.c @@ -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; } diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index ef89780eb9d..74d9d30edab 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -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, diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index d091b2430b4..22ed96c4b7b 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -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); diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 41844ea0246..96be7ef62f3 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -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; } diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index c153b64a830..6c2645e2837 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -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); diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h new file mode 100644 index 00000000000..a33251f4b87 --- /dev/null +++ b/drivers/mtd/mtdcore.h @@ -0,0 +1,11 @@ +/* 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]; diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c new file mode 100644 index 00000000000..f8af627f0b9 --- /dev/null +++ b/drivers/mtd/mtdoops.c @@ -0,0 +1,376 @@ +/* + * 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"); diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index f1d60b6f048..df25cabb048 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -134,10 +134,10 @@ config MTD_NAND_S3C2410_HWECC config MTD_NAND_NDFC tristate "NDFC NanD Flash Controller" - depends on 44x + depends on 4xx 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 +237,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 diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index e96259f22cc..ab9f5c5db38 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -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 diff --git a/drivers/mtd/nand/excite_nandflash.c b/drivers/mtd/nand/excite_nandflash.c index 7e9afc4c775..bed87290dec 100644 --- a/drivers/mtd/nand/excite_nandflash.c +++ b/drivers/mtd/nand/excite_nandflash.c @@ -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> diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 24ac6778b1a..d5691212058 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -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) diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 2fc674a190c..a3e3ab0185d 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -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"} }; diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c index fd7a8d5ba29..1c0e89f00e8 100644 --- a/drivers/mtd/nand/ndfc.c +++ b/drivers/mtd/nand/ndfc.c @@ -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; diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index c257d397d08..cb41cbca64f 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -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 diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile index 269cfe46734..4d2eacfd7e1 100644 --- a/drivers/mtd/onenand/Makefile +++ b/drivers/mtd/onenand/Makefile @@ -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 diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 0537fac8de7..7d194cfdb87 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -206,6 +206,15 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le default: block = (int) (addr >> this->erase_shift); page = (int) (addr >> this->page_shift); + + if (ONENAND_IS_2PLANE(this)) { + /* Make the even block number */ + block &= ~1; + /* Is it the odd plane? */ + if (addr & this->writesize) + block++; + page >>= 1; + } page &= this->page_mask; break; } @@ -216,8 +225,12 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le value = onenand_bufferram_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); - /* Switch to the next data buffer */ - ONENAND_SET_NEXT_BUFFERRAM(this); + if (ONENAND_IS_2PLANE(this)) + /* It is always BufferRAM0 */ + ONENAND_SET_BUFFERRAM0(this); + else + /* Switch to the next data buffer */ + ONENAND_SET_NEXT_BUFFERRAM(this); return 0; } @@ -247,6 +260,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le break; default: + if (ONENAND_IS_2PLANE(this) && cmd == ONENAND_CMD_PROG) + cmd = ONENAND_CMD_2X_PROG; dataram = ONENAND_CURRENT_BUFFERRAM(this); break; } @@ -445,8 +460,9 @@ static inline int onenand_bufferram_offset(struct mtd_info *mtd, int area) struct onenand_chip *this = mtd->priv; if (ONENAND_CURRENT_BUFFERRAM(this)) { + /* Note: the 'this->writesize' is a real page size */ if (area == ONENAND_DATARAM) - return mtd->writesize; + return this->writesize; if (area == ONENAND_SPARERAM) return mtd->oobsize; } @@ -572,6 +588,30 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area, } /** + * onenand_get_2x_blockpage - [GENERIC] Get blockpage at 2x program mode + * @param mtd MTD data structure + * @param addr address to check + * @return blockpage address + * + * Get blockpage address at 2x program mode + */ +static int onenand_get_2x_blockpage(struct mtd_info *mtd, loff_t addr) +{ + struct onenand_chip *this = mtd->priv; + int blockpage, block, page; + + /* Calculate the even block number */ + block = (int) (addr >> this->erase_shift) & ~1; + /* Is it the odd plane? */ + if (addr & this->writesize) + block++; + page = (int) (addr >> (this->page_shift + 1)) & this->page_mask; + blockpage = (block << 7) | page; + + return blockpage; +} + +/** * onenand_check_bufferram - [GENERIC] Check BufferRAM information * @param mtd MTD data structure * @param addr address to check @@ -585,7 +625,10 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) int blockpage, found = 0; unsigned int i; - blockpage = (int) (addr >> this->page_shift); + if (ONENAND_IS_2PLANE(this)) + blockpage = onenand_get_2x_blockpage(mtd, addr); + else + blockpage = (int) (addr >> this->page_shift); /* Is there valid data? */ i = ONENAND_CURRENT_BUFFERRAM(this); @@ -625,7 +668,10 @@ static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, int blockpage; unsigned int i; - blockpage = (int) (addr >> this->page_shift); + if (ONENAND_IS_2PLANE(this)) + blockpage = onenand_get_2x_blockpage(mtd, addr); + else + blockpage = (int) (addr >> this->page_shift); /* Invalidate another BufferRAM */ i = ONENAND_NEXT_BUFFERRAM(this); @@ -734,6 +780,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, int read = 0, column; int thislen; int ret = 0, boundary = 0; + int writesize = this->writesize; DEBUG(MTD_DEBUG_LEVEL3, "onenand_read: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); @@ -754,22 +801,22 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, /* Do first load to bufferRAM */ if (read < len) { if (!onenand_check_bufferram(mtd, from)) { - this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize); + this->command(mtd, ONENAND_CMD_READ, from, writesize); ret = this->wait(mtd, FL_READING); onenand_update_bufferram(mtd, from, !ret); } } - thislen = min_t(int, mtd->writesize, len - read); - column = from & (mtd->writesize - 1); - if (column + thislen > mtd->writesize) - thislen = mtd->writesize - column; + thislen = min_t(int, writesize, len - read); + column = from & (writesize - 1); + if (column + thislen > writesize) + thislen = writesize - column; while (!ret) { /* If there is more to load then start next load */ from += thislen; if (read + thislen < len) { - this->command(mtd, ONENAND_CMD_READ, from, mtd->writesize); + this->command(mtd, ONENAND_CMD_READ, from, writesize); /* * Chip boundary handling in DDP * Now we issued chip 1 read and pointed chip 1 @@ -794,7 +841,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); ONENAND_SET_NEXT_BUFFERRAM(this); buf += thislen; - thislen = min_t(int, mtd->writesize, len - read); + thislen = min_t(int, writesize, len - read); column = 0; cond_resched(); /* Now wait for load */ @@ -1079,7 +1126,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, /* Read more? */ if (read < len) { /* Update Page size */ - from += mtd->writesize; + from += this->writesize; column = 0; } } @@ -1135,12 +1182,12 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, int thislen, column; while (len != 0) { - thislen = min_t(int, mtd->writesize, len); - column = addr & (mtd->writesize - 1); - if (column + thislen > mtd->writesize) - thislen = mtd->writesize - column; + thislen = min_t(int, this->writesize, len); + column = addr & (this->writesize - 1); + if (column + thislen > this->writesize) + thislen = this->writesize - column; - this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); + this->command(mtd, ONENAND_CMD_READ, addr, this->writesize); onenand_update_bufferram(mtd, addr, 0); @@ -1236,6 +1283,10 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, /* In partial page write we don't update bufferram */ onenand_update_bufferram(mtd, to, !ret && !subpage); + if (ONENAND_IS_2PLANE(this)) { + ONENAND_SET_BUFFERRAM1(this); + onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage); + } if (ret) { printk(KERN_ERR "onenand_write: write filaed %d\n", ret); @@ -1384,6 +1435,10 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); onenand_update_bufferram(mtd, to, 0); + if (ONENAND_IS_2PLANE(this)) { + ONENAND_SET_BUFFERRAM1(this); + onenand_update_bufferram(mtd, to + this->writesize, 0); + } ret = this->wait(mtd, FL_WRITING); if (ret) { @@ -2107,6 +2162,7 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, * * Check and set OneNAND features * - lock scheme + * - two plane */ static void onenand_check_features(struct mtd_info *mtd) { @@ -2118,19 +2174,35 @@ static void onenand_check_features(struct mtd_info *mtd) process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT; /* Lock scheme */ - if (density >= ONENAND_DEVICE_DENSITY_1Gb) { + switch (density) { + case ONENAND_DEVICE_DENSITY_4Gb: + this->options |= ONENAND_HAS_2PLANE; + + case ONENAND_DEVICE_DENSITY_2Gb: + /* 2Gb DDP don't have 2 plane */ + if (!ONENAND_IS_DDP(this)) + this->options |= ONENAND_HAS_2PLANE; + this->options |= ONENAND_HAS_UNLOCK_ALL; + + case ONENAND_DEVICE_DENSITY_1Gb: /* A-Die has all block unlock */ - if (process) { - printk(KERN_DEBUG "Chip support all block unlock\n"); + if (process) this->options |= ONENAND_HAS_UNLOCK_ALL; - } - } else { - /* Some OneNAND has continues lock scheme */ - if (!process) { - printk(KERN_DEBUG "Lock scheme is Continues Lock\n"); + break; + + default: + /* Some OneNAND has continuous lock scheme */ + if (!process) this->options |= ONENAND_HAS_CONT_LOCK; - } + break; } + + if (this->options & ONENAND_HAS_CONT_LOCK) + printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); + if (this->options & ONENAND_HAS_UNLOCK_ALL) + printk(KERN_DEBUG "Chip support all block unlock\n"); + if (this->options & ONENAND_HAS_2PLANE) + printk(KERN_DEBUG "Chip has 2 plane\n"); } /** @@ -2257,6 +2329,8 @@ static int onenand_probe(struct mtd_info *mtd) this->erase_shift = ffs(mtd->erasesize) - 1; this->page_shift = ffs(mtd->writesize) - 1; this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; + /* It's real page size */ + this->writesize = mtd->writesize; /* REVIST: Multichip handling */ @@ -2265,6 +2339,17 @@ static int onenand_probe(struct mtd_info *mtd) /* Check OneNAND features */ onenand_check_features(mtd); + /* + * We emulate the 4KiB page and 256KiB erase block size + * But oobsize is still 64 bytes. + * It is only valid if you turn on 2X program support, + * Otherwise it will be ignored by compiler. + */ + if (ONENAND_IS_2PLANE(this)) { + mtd->writesize <<= 1; + mtd->erasesize <<= 1; + } + return 0; } diff --git a/drivers/mtd/onenand/onenand_sim.c b/drivers/mtd/onenand/onenand_sim.c new file mode 100644 index 00000000000..0d89ad5776f --- /dev/null +++ b/drivers/mtd/onenand/onenand_sim.c @@ -0,0 +1,495 @@ +/* + * 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"); diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index 006c03aacb5..823fba4e6d2 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -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); } diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index 94ee5493441..29c41eeb09f 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -1314,11 +1314,10 @@ static int paranoid_check_si(const struct ubi_device *ubi, * Make sure that all the physical eraseblocks are in one of the lists * or trees. */ - buf = kmalloc(ubi->peb_count, GFP_KERNEL); + buf = kzalloc(ubi->peb_count, GFP_KERNEL); if (!buf) return -ENOMEM; - memset(buf, 1, ubi->peb_count); for (pnum = 0; pnum < ubi->peb_count; pnum++) { err = ubi_io_is_bad(ubi, pnum); if (err < 0) { @@ -1326,28 +1325,28 @@ static int paranoid_check_si(const struct ubi_device *ubi, return err; } else if (err) - buf[pnum] = 0; + buf[pnum] = 1; } ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) - buf[seb->pnum] = 0; + buf[seb->pnum] = 1; list_for_each_entry(seb, &si->free, u.list) - buf[seb->pnum] = 0; + buf[seb->pnum] = 1; list_for_each_entry(seb, &si->corr, u.list) - buf[seb->pnum] = 0; + buf[seb->pnum] = 1; list_for_each_entry(seb, &si->erase, u.list) - buf[seb->pnum] = 0; + buf[seb->pnum] = 1; list_for_each_entry(seb, &si->alien, u.list) - buf[seb->pnum] = 0; + buf[seb->pnum] = 1; err = 0; for (pnum = 0; pnum < ubi->peb_count; pnum++) - if (buf[pnum]) { + if (!buf[pnum]) { ubi_err("PEB %d is not referred", pnum); err = 1; } |