Merge git://git.infradead.org/mtd-2.6

* git://git.infradead.org/mtd-2.6: (91 commits)
  [MTD] [NAND] Blackfin on-chip NAND Flash Controller driver
  [MTD] [NOR] fix ctrl-alt-del can't reboot for intel flash bug
  [MTD] [NAND] Fix compiler warning in Alauda driver
  [JFFS2] Remove stray debugging printk
  [JFFS2] Handle dirents on the flash with embedded zero bytes in names.
  [JFFS2] Check for creation of dirents with embedded zero bytes in name.
  [JFFS2] Don't count all 'very dirty' blocks except in debug mode
  [JFFS2] Check whether garbage-collection actually obsoleted its victim.
  [JFFS2] Relax threshold for triggering GC due to dirty blocks.
  [MTD] [OneNAND] Fix typo related with recent commit
  [JFFS2] Trigger garbage collection when very_dirty_list size becomes excessive
  [MTD] [NAND] Avoid deadlock in erase callback; release chip lock first.
  [MTD] [NAND] Resume method for CAFÉ NAND controller
  [MTD] [NAND] Fix PCI ident table for CAFÉ NAND controller.
  [MTD] [NAND] s3c2410: fix arch moves
  [MTD] [OneNAND] fix numerous races
  [MTD] map driver for NOR flash on the Intel Vermilion Range chipset
  [JFFS2] Fix unpoint length
  [MTD] fix CFI point method for discontiguous maps
  [MTD] MAPS: Merge Lubbock and Mainstone drivers into common PXA2xx driver
  ...
This commit is contained in:
Linus Torvalds 2007-10-13 10:12:15 -07:00
commit c8c55bcb43
91 changed files with 4745 additions and 2300 deletions

View File

@ -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){

View File

@ -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"

View File

@ -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

View File

@ -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);
}

View File

@ -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;

View File

@ -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,

View File

@ -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"

View File

@ -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

View File

@ -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");

View File

@ -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 };

View File

@ -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...
*/
};

View File

@ -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;

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,298 @@
/*
* drivers/mtd/maps/intel_vr_nor.c
*
* An MTD map driver for a NOR flash bank on the Expansion Bus of the Intel
* Vermilion Range chipset.
*
* The Vermilion Range Expansion Bus supports four chip selects, each of which
* has 64MiB of address space. The 2nd BAR of the Expansion Bus PCI Device
* is a 256MiB memory region containing the address spaces for all four of the
* chip selects, with start addresses hardcoded on 64MiB boundaries.
*
* This map driver only supports NOR flash on chip select 0. The buswidth
* (either 8 bits or 16 bits) is determined by reading the Expansion Bus Timing
* and Control Register for Chip Select 0 (EXP_TIMING_CS0). This driver does
* not modify the value in the EXP_TIMING_CS0 register except to enable writing
* and disable boot acceleration. The timing parameters in the register are
* assumed to have been properly initialized by the BIOS. The reset default
* timing parameters are maximally conservative (slow), so access to the flash
* will be slower than it should be if the BIOS has not initialized the timing
* parameters.
*
* Author: Andy Lowe <alowe@mvista.com>
*
* 2006 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/flashchip.h>
#define DRV_NAME "vr_nor"
struct vr_nor_mtd {
void __iomem *csr_base;
struct map_info map;
struct mtd_info *info;
int nr_parts;
struct pci_dev *dev;
};
/* Expansion Bus Configuration and Status Registers are in BAR 0 */
#define EXP_CSR_MBAR 0
/* Expansion Bus Memory Window is BAR 1 */
#define EXP_WIN_MBAR 1
/* Maximum address space for Chip Select 0 is 64MiB */
#define CS0_SIZE 0x04000000
/* Chip Select 0 is at offset 0 in the Memory Window */
#define CS0_START 0x0
/* Chip Select 0 Timing Register is at offset 0 in CSR */
#define EXP_TIMING_CS0 0x00
#define TIMING_CS_EN (1 << 31) /* Chip Select Enable */
#define TIMING_BOOT_ACCEL_DIS (1 << 8) /* Boot Acceleration Disable */
#define TIMING_WR_EN (1 << 1) /* Write Enable */
#define TIMING_BYTE_EN (1 << 0) /* 8-bit vs 16-bit bus */
#define TIMING_MASK 0x3FFF0000
static void __devexit vr_nor_destroy_partitions(struct vr_nor_mtd *p)
{
if (p->nr_parts > 0) {
#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
del_mtd_partitions(p->info);
#endif
} else
del_mtd_device(p->info);
}
static int __devinit vr_nor_init_partitions(struct vr_nor_mtd *p)
{
int err = 0;
#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
struct mtd_partition *parts;
static const char *part_probes[] = { "cmdlinepart", NULL };
#endif
/* register the flash bank */
#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
/* partition the flash bank */
p->nr_parts = parse_mtd_partitions(p->info, part_probes, &parts, 0);
if (p->nr_parts > 0)
err = add_mtd_partitions(p->info, parts, p->nr_parts);
#endif
if (p->nr_parts <= 0)
err = add_mtd_device(p->info);
return err;
}
static void __devexit vr_nor_destroy_mtd_setup(struct vr_nor_mtd *p)
{
map_destroy(p->info);
}
static int __devinit vr_nor_mtd_setup(struct vr_nor_mtd *p)
{
static const char *probe_types[] =
{ "cfi_probe", "jedec_probe", NULL };
const char **type;
for (type = probe_types; !p->info && *type; type++)
p->info = do_map_probe(*type, &p->map);
if (!p->info)
return -ENODEV;
p->info->owner = THIS_MODULE;
return 0;
}
static void __devexit vr_nor_destroy_maps(struct vr_nor_mtd *p)
{
unsigned int exp_timing_cs0;
/* write-protect the flash bank */
exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
exp_timing_cs0 &= ~TIMING_WR_EN;
writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
/* unmap the flash window */
iounmap(p->map.virt);
/* unmap the csr window */
iounmap(p->csr_base);
}
/*
* Initialize the map_info structure and map the flash.
* Returns 0 on success, nonzero otherwise.
*/
static int __devinit vr_nor_init_maps(struct vr_nor_mtd *p)
{
unsigned long csr_phys, csr_len;
unsigned long win_phys, win_len;
unsigned int exp_timing_cs0;
int err;
csr_phys = pci_resource_start(p->dev, EXP_CSR_MBAR);
csr_len = pci_resource_len(p->dev, EXP_CSR_MBAR);
win_phys = pci_resource_start(p->dev, EXP_WIN_MBAR);
win_len = pci_resource_len(p->dev, EXP_WIN_MBAR);
if (!csr_phys || !csr_len || !win_phys || !win_len)
return -ENODEV;
if (win_len < (CS0_START + CS0_SIZE))
return -ENXIO;
p->csr_base = ioremap_nocache(csr_phys, csr_len);
if (!p->csr_base)
return -ENOMEM;
exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
if (!(exp_timing_cs0 & TIMING_CS_EN)) {
dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 "
"is disabled.\n");
err = -ENODEV;
goto release;
}
if ((exp_timing_cs0 & TIMING_MASK) == TIMING_MASK) {
dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 "
"is configured for maximally slow access times.\n");
}
p->map.name = DRV_NAME;
p->map.bankwidth = (exp_timing_cs0 & TIMING_BYTE_EN) ? 1 : 2;
p->map.phys = win_phys + CS0_START;
p->map.size = CS0_SIZE;
p->map.virt = ioremap_nocache(p->map.phys, p->map.size);
if (!p->map.virt) {
err = -ENOMEM;
goto release;
}
simple_map_init(&p->map);
/* Enable writes to flash bank */
exp_timing_cs0 |= TIMING_BOOT_ACCEL_DIS | TIMING_WR_EN;
writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
return 0;
release:
iounmap(p->csr_base);
return err;
}
static struct pci_device_id vr_nor_pci_ids[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x500D)},
{0,}
};
static void __devexit vr_nor_pci_remove(struct pci_dev *dev)
{
struct vr_nor_mtd *p = pci_get_drvdata(dev);
pci_set_drvdata(dev, NULL);
vr_nor_destroy_partitions(p);
vr_nor_destroy_mtd_setup(p);
vr_nor_destroy_maps(p);
kfree(p);
pci_release_regions(dev);
pci_disable_device(dev);
}
static int __devinit
vr_nor_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct vr_nor_mtd *p = NULL;
unsigned int exp_timing_cs0;
int err;
err = pci_enable_device(dev);
if (err)
goto out;
err = pci_request_regions(dev, DRV_NAME);
if (err)
goto disable_dev;
p = kzalloc(sizeof(*p), GFP_KERNEL);
err = -ENOMEM;
if (!p)
goto release;
p->dev = dev;
err = vr_nor_init_maps(p);
if (err)
goto release;
err = vr_nor_mtd_setup(p);
if (err)
goto destroy_maps;
err = vr_nor_init_partitions(p);
if (err)
goto destroy_mtd_setup;
pci_set_drvdata(dev, p);
return 0;
destroy_mtd_setup:
map_destroy(p->info);
destroy_maps:
/* write-protect the flash bank */
exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
exp_timing_cs0 &= ~TIMING_WR_EN;
writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
/* unmap the flash window */
iounmap(p->map.virt);
/* unmap the csr window */
iounmap(p->csr_base);
release:
kfree(p);
pci_release_regions(dev);
disable_dev:
pci_disable_device(dev);
out:
return err;
}
static struct pci_driver vr_nor_pci_driver = {
.name = DRV_NAME,
.probe = vr_nor_pci_probe,
.remove = __devexit_p(vr_nor_pci_remove),
.id_table = vr_nor_pci_ids,
};
static int __init vr_nor_mtd_init(void)
{
return pci_register_driver(&vr_nor_pci_driver);
}
static void __exit vr_nor_mtd_exit(void)
{
pci_unregister_driver(&vr_nor_pci_driver);
}
module_init(vr_nor_mtd_init);
module_exit(vr_nor_mtd_exit);
MODULE_AUTHOR("Andy Lowe");
MODULE_DESCRIPTION("MTD map driver for NOR flash on Intel Vermilion Range");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, vr_nor_pci_ids);

View File

@ -1,168 +0,0 @@
/*
* $Id: lubbock-flash.c,v 1.21 2005/11/07 11:14:27 gleixner Exp $
*
* Map driver for the Lubbock developer platform.
*
* Author: Nicolas Pitre
* Copyright: (C) 2001 MontaVista Software Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/arch/pxa-regs.h>
#include <asm/arch/lubbock.h>
#include <asm/cacheflush.h>
#define ROM_ADDR 0x00000000
#define FLASH_ADDR 0x04000000
#define WINDOW_SIZE 64*1024*1024
static void lubbock_map_inval_cache(struct map_info *map, unsigned long from, ssize_t len)
{
flush_ioremap_region(map->phys, map->cached, from, len);
}
static struct map_info lubbock_maps[2] = { {
.size = WINDOW_SIZE,
.phys = 0x00000000,
.inval_cache = lubbock_map_inval_cache,
}, {
.size = WINDOW_SIZE,
.phys = 0x04000000,
.inval_cache = lubbock_map_inval_cache,
} };
static struct mtd_partition lubbock_partitions[] = {
{
.name = "Bootloader",
.size = 0x00040000,
.offset = 0,
.mask_flags = MTD_WRITEABLE /* force read-only */
},{
.name = "Kernel",
.size = 0x00100000,
.offset = 0x00040000,
},{
.name = "Filesystem",
.size = MTDPART_SIZ_FULL,
.offset = 0x00140000
}
};
static struct mtd_info *mymtds[2];
static struct mtd_partition *parsed_parts[2];
static int nr_parsed_parts[2];
static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
static int __init init_lubbock(void)
{
int flashboot = (LUB_CONF_SWITCHES & 1);
int ret = 0, i;
lubbock_maps[0].bankwidth = lubbock_maps[1].bankwidth =
(BOOT_DEF & 1) ? 2 : 4;
/* Compensate for the nROMBT switch which swaps the flash banks */
printk(KERN_NOTICE "Lubbock configured to boot from %s (bank %d)\n",
flashboot?"Flash":"ROM", flashboot);
lubbock_maps[flashboot^1].name = "Lubbock Application Flash";
lubbock_maps[flashboot].name = "Lubbock Boot ROM";
for (i = 0; i < 2; i++) {
lubbock_maps[i].virt = ioremap(lubbock_maps[i].phys, WINDOW_SIZE);
if (!lubbock_maps[i].virt) {
printk(KERN_WARNING "Failed to ioremap %s\n", lubbock_maps[i].name);
if (!ret)
ret = -ENOMEM;
continue;
}
lubbock_maps[i].cached = ioremap_cached(lubbock_maps[i].phys, WINDOW_SIZE);
if (!lubbock_maps[i].cached)
printk(KERN_WARNING "Failed to ioremap cached %s\n", lubbock_maps[i].name);
simple_map_init(&lubbock_maps[i]);
printk(KERN_NOTICE "Probing %s at physical address 0x%08lx (%d-bit bankwidth)\n",
lubbock_maps[i].name, lubbock_maps[i].phys,
lubbock_maps[i].bankwidth * 8);
mymtds[i] = do_map_probe("cfi_probe", &lubbock_maps[i]);
if (!mymtds[i]) {
iounmap((void *)lubbock_maps[i].virt);
if (lubbock_maps[i].cached)
iounmap(lubbock_maps[i].cached);
if (!ret)
ret = -EIO;
continue;
}
mymtds[i]->owner = THIS_MODULE;
ret = parse_mtd_partitions(mymtds[i], probes,
&parsed_parts[i], 0);
if (ret > 0)
nr_parsed_parts[i] = ret;
}
if (!mymtds[0] && !mymtds[1])
return ret;
for (i = 0; i < 2; i++) {
if (!mymtds[i]) {
printk(KERN_WARNING "%s is absent. Skipping\n", lubbock_maps[i].name);
} else if (nr_parsed_parts[i]) {
add_mtd_partitions(mymtds[i], parsed_parts[i], nr_parsed_parts[i]);
} else if (!i) {
printk("Using static partitions on %s\n", lubbock_maps[i].name);
add_mtd_partitions(mymtds[i], lubbock_partitions, ARRAY_SIZE(lubbock_partitions));
} else {
printk("Registering %s as whole device\n", lubbock_maps[i].name);
add_mtd_device(mymtds[i]);
}
}
return 0;
}
static void __exit cleanup_lubbock(void)
{
int i;
for (i = 0; i < 2; i++) {
if (!mymtds[i])
continue;
if (nr_parsed_parts[i] || !i)
del_mtd_partitions(mymtds[i]);
else
del_mtd_device(mymtds[i]);
map_destroy(mymtds[i]);
iounmap((void *)lubbock_maps[i].virt);
if (lubbock_maps[i].cached)
iounmap(lubbock_maps[i].cached);
kfree(parsed_parts[i]);
}
}
module_init(init_lubbock);
module_exit(cleanup_lubbock);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
MODULE_DESCRIPTION("MTD map driver for Intel Lubbock");

View File

@ -1,180 +0,0 @@
/*
* $Id: $
*
* Map driver for the Mainstone developer platform.
*
* Author: Nicolas Pitre
* Copyright: (C) 2001 MontaVista Software Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/hardware.h>
#include <asm/arch/pxa-regs.h>
#include <asm/arch/mainstone.h>
#include <asm/cacheflush.h>
#define ROM_ADDR 0x00000000
#define FLASH_ADDR 0x04000000
#define WINDOW_SIZE 0x04000000
static void mainstone_map_inval_cache(struct map_info *map, unsigned long from,
ssize_t len)
{
flush_ioremap_region(map->phys, map->cached, from, len);
}
static struct map_info mainstone_maps[2] = { {
.size = WINDOW_SIZE,
.phys = PXA_CS0_PHYS,
.inval_cache = mainstone_map_inval_cache,
}, {
.size = WINDOW_SIZE,
.phys = PXA_CS1_PHYS,
.inval_cache = mainstone_map_inval_cache,
} };
static struct mtd_partition mainstone_partitions[] = {
{
.name = "Bootloader",
.size = 0x00040000,
.offset = 0,
.mask_flags = MTD_WRITEABLE /* force read-only */
},{
.name = "Kernel",
.size = 0x00400000,
.offset = 0x00040000,
},{
.name = "Filesystem",
.size = MTDPART_SIZ_FULL,
.offset = 0x00440000
}
};
static struct mtd_info *mymtds[2];
static struct mtd_partition *parsed_parts[2];
static int nr_parsed_parts[2];
static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
static int __init init_mainstone(void)
{
int SW7 = 0; /* FIXME: get from SCR (Mst doc section 3.2.1.1) */
int ret = 0, i;
mainstone_maps[0].bankwidth = (BOOT_DEF & 1) ? 2 : 4;
mainstone_maps[1].bankwidth = 4;
/* Compensate for SW7 which swaps the flash banks */
mainstone_maps[SW7].name = "processor flash";
mainstone_maps[SW7 ^ 1].name = "main board flash";
printk(KERN_NOTICE "Mainstone configured to boot from %s\n",
mainstone_maps[0].name);
for (i = 0; i < 2; i++) {
mainstone_maps[i].virt = ioremap(mainstone_maps[i].phys,
WINDOW_SIZE);
if (!mainstone_maps[i].virt) {
printk(KERN_WARNING "Failed to ioremap %s\n",
mainstone_maps[i].name);
if (!ret)
ret = -ENOMEM;
continue;
}
mainstone_maps[i].cached =
ioremap_cached(mainstone_maps[i].phys, WINDOW_SIZE);
if (!mainstone_maps[i].cached)
printk(KERN_WARNING "Failed to ioremap cached %s\n",
mainstone_maps[i].name);
simple_map_init(&mainstone_maps[i]);
printk(KERN_NOTICE
"Probing %s at physical address 0x%08lx"
" (%d-bit bankwidth)\n",
mainstone_maps[i].name, mainstone_maps[i].phys,
mainstone_maps[i].bankwidth * 8);
mymtds[i] = do_map_probe("cfi_probe", &mainstone_maps[i]);
if (!mymtds[i]) {
iounmap((void *)mainstone_maps[i].virt);
if (mainstone_maps[i].cached)
iounmap(mainstone_maps[i].cached);
if (!ret)
ret = -EIO;
continue;
}
mymtds[i]->owner = THIS_MODULE;
ret = parse_mtd_partitions(mymtds[i], probes,
&parsed_parts[i], 0);
if (ret > 0)
nr_parsed_parts[i] = ret;
}
if (!mymtds[0] && !mymtds[1])
return ret;
for (i = 0; i < 2; i++) {
if (!mymtds[i]) {
printk(KERN_WARNING "%s is absent. Skipping\n",
mainstone_maps[i].name);
} else if (nr_parsed_parts[i]) {
add_mtd_partitions(mymtds[i], parsed_parts[i],
nr_parsed_parts[i]);
} else if (!i) {
printk("Using static partitions on %s\n",
mainstone_maps[i].name);
add_mtd_partitions(mymtds[i], mainstone_partitions,
ARRAY_SIZE(mainstone_partitions));
} else {
printk("Registering %s as whole device\n",
mainstone_maps[i].name);
add_mtd_device(mymtds[i]);
}
}
return 0;
}
static void __exit cleanup_mainstone(void)
{
int i;
for (i = 0; i < 2; i++) {
if (!mymtds[i])
continue;
if (nr_parsed_parts[i] || !i)
del_mtd_partitions(mymtds[i]);
else
del_mtd_device(mymtds[i]);
map_destroy(mymtds[i]);
iounmap((void *)mainstone_maps[i].virt);
if (mainstone_maps[i].cached)
iounmap(mainstone_maps[i].cached);
kfree(parsed_parts[i]);
}
}
module_init(init_mainstone);
module_exit(cleanup_mainstone);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
MODULE_DESCRIPTION("MTD map driver for Intel Mainstone");

View File

@ -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);

View File

@ -1,175 +0,0 @@
/*
* $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");

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -1,88 +0,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");

View File

@ -0,0 +1,200 @@
/*
* 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");

View File

@ -1,286 +0,0 @@
/*
* 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");

View File

@ -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,

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

11
drivers/mtd/mtdcore.h Normal file
View File

@ -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];

376
drivers/mtd/mtdoops.c Normal file
View File

@ -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");

View File

@ -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

View File

@ -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

742
drivers/mtd/nand/alauda.c Normal file
View File

@ -0,0 +1,742 @@
/*
* 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");

View File

@ -0,0 +1,788 @@
/* linux/drivers/mtd/nand/bf5xx_nand.c
*
* Copyright 2006-2007 Analog Devices Inc.
* http://blackfin.uclinux.org/
* Bryan Wu <bryan.wu@analog.com>
*
* Blackfin BF5xx on-chip NAND flash controler driver
*
* Derived from drivers/mtd/nand/s3c2410.c
* Copyright (c) 2007 Ben Dooks <ben@simtec.co.uk>
*
* Derived from drivers/mtd/nand/cafe.c
* Copyright © 2006 Red Hat, Inc.
* Copyright © 2006 David Woodhouse <dwmw2@infradead.org>
*
* Changelog:
* 12-Jun-2007 Bryan Wu: Initial version
* 18-Jul-2007 Bryan Wu:
* - ECC_HW and ECC_SW supported
* - DMA supported in ECC_HW
* - YAFFS tested as rootfs in both ECC_HW and ECC_SW
*
* TODO:
* Enable JFFS2 over NAND as rootfs
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/bitops.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <asm/blackfin.h>
#include <asm/dma.h>
#include <asm/cacheflush.h>
#include <asm/nand.h>
#include <asm/portmux.h>
#define DRV_NAME "bf5xx-nand"
#define DRV_VERSION "1.2"
#define DRV_AUTHOR "Bryan Wu <bryan.wu@analog.com>"
#define DRV_DESC "BF5xx on-chip NAND FLash Controller Driver"
#ifdef CONFIG_MTD_NAND_BF5XX_HWECC
static int hardware_ecc = 1;
#else
static int hardware_ecc;
#endif
static unsigned short bfin_nfc_pin_req[] = {P_NAND_CE, P_NAND_RB, 0};
/*
* Data structures for bf5xx nand flash controller driver
*/
/* bf5xx nand info */
struct bf5xx_nand_info {
/* mtd info */
struct nand_hw_control controller;
struct mtd_info mtd;
struct nand_chip chip;
/* platform info */
struct bf5xx_nand_platform *platform;
/* device info */
struct device *device;
/* DMA stuff */
struct completion dma_completion;
};
/*
* Conversion functions
*/
static struct bf5xx_nand_info *mtd_to_nand_info(struct mtd_info *mtd)
{
return container_of(mtd, struct bf5xx_nand_info, mtd);
}
static struct bf5xx_nand_info *to_nand_info(struct platform_device *pdev)
{
return platform_get_drvdata(pdev);
}
static struct bf5xx_nand_platform *to_nand_plat(struct platform_device *pdev)
{
return pdev->dev.platform_data;
}
/*
* struct nand_chip interface function pointers
*/
/*
* bf5xx_nand_hwcontrol
*
* Issue command and address cycles to the chip
*/
static void bf5xx_nand_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{
if (cmd == NAND_CMD_NONE)
return;
while (bfin_read_NFC_STAT() & WB_FULL)
cpu_relax();
if (ctrl & NAND_CLE)
bfin_write_NFC_CMD(cmd);
else
bfin_write_NFC_ADDR(cmd);
SSYNC();
}
/*
* bf5xx_nand_devready()
*
* returns 0 if the nand is busy, 1 if it is ready
*/
static int bf5xx_nand_devready(struct mtd_info *mtd)
{
unsigned short val = bfin_read_NFC_IRQSTAT();
if ((val & NBUSYIRQ) == NBUSYIRQ)
return 1;
else
return 0;
}
/*
* ECC functions
* These allow the bf5xx to use the controller's ECC
* generator block to ECC the data as it passes through
*/
/*
* ECC error correction function
*/
static int bf5xx_nand_correct_data_256(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
u32 syndrome[5];
u32 calced, stored;
int i;
unsigned short failing_bit, failing_byte;
u_char data;
calced = calc_ecc[0] | (calc_ecc[1] << 8) | (calc_ecc[2] << 16);
stored = read_ecc[0] | (read_ecc[1] << 8) | (read_ecc[2] << 16);
syndrome[0] = (calced ^ stored);
/*
* syndrome 0: all zero
* No error in data
* No action
*/
if (!syndrome[0] || !calced || !stored)
return 0;
/*
* sysdrome 0: only one bit is one
* ECC data was incorrect
* No action
*/
if (hweight32(syndrome[0]) == 1) {
dev_err(info->device, "ECC data was incorrect!\n");
return 1;
}
syndrome[1] = (calced & 0x7FF) ^ (stored & 0x7FF);
syndrome[2] = (calced & 0x7FF) ^ ((calced >> 11) & 0x7FF);
syndrome[3] = (stored & 0x7FF) ^ ((stored >> 11) & 0x7FF);
syndrome[4] = syndrome[2] ^ syndrome[3];
for (i = 0; i < 5; i++)
dev_info(info->device, "syndrome[%d] 0x%08x\n", i, syndrome[i]);
dev_info(info->device,
"calced[0x%08x], stored[0x%08x]\n",
calced, stored);
/*
* sysdrome 0: exactly 11 bits are one, each parity
* and parity' pair is 1 & 0 or 0 & 1.
* 1-bit correctable error
* Correct the error
*/
if (hweight32(syndrome[0]) == 11 && syndrome[4] == 0x7FF) {
dev_info(info->device,
"1-bit correctable error, correct it.\n");
dev_info(info->device,
"syndrome[1] 0x%08x\n", syndrome[1]);
failing_bit = syndrome[1] & 0x7;
failing_byte = syndrome[1] >> 0x3;
data = *(dat + failing_byte);
data = data ^ (0x1 << failing_bit);
*(dat + failing_byte) = data;
return 0;
}
/*
* sysdrome 0: random data
* More than 1-bit error, non-correctable error
* Discard data, mark bad block
*/
dev_err(info->device,
"More than 1-bit error, non-correctable error.\n");
dev_err(info->device,
"Please discard data, mark bad block\n");
return 1;
}
static int bf5xx_nand_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
struct bf5xx_nand_platform *plat = info->platform;
unsigned short page_size = (plat->page_size ? 512 : 256);
int ret;
ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc);
/* If page size is 512, correct second 256 bytes */
if (page_size == 512) {
dat += 256;
read_ecc += 8;
calc_ecc += 8;
ret = bf5xx_nand_correct_data_256(mtd, dat, read_ecc, calc_ecc);
}
return ret;
}
static void bf5xx_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
return;
}
static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd,
const u_char *dat, u_char *ecc_code)
{
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
struct bf5xx_nand_platform *plat = info->platform;
u16 page_size = (plat->page_size ? 512 : 256);
u16 ecc0, ecc1;
u32 code[2];
u8 *p;
int bytes = 3, i;
/* first 4 bytes ECC code for 256 page size */
ecc0 = bfin_read_NFC_ECC0();
ecc1 = bfin_read_NFC_ECC1();
code[0] = (ecc0 & 0x3FF) | ((ecc1 & 0x3FF) << 11);
dev_dbg(info->device, "returning ecc 0x%08x\n", code[0]);
/* second 4 bytes ECC code for 512 page size */
if (page_size == 512) {
ecc0 = bfin_read_NFC_ECC2();
ecc1 = bfin_read_NFC_ECC3();
code[1] = (ecc0 & 0x3FF) | ((ecc1 & 0x3FF) << 11);
bytes = 6;
dev_dbg(info->device, "returning ecc 0x%08x\n", code[1]);
}
p = (u8 *)code;
for (i = 0; i < bytes; i++)
ecc_code[i] = p[i];
return 0;
}
/*
* PIO mode for buffer writing and reading
*/
static void bf5xx_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
int i;
unsigned short val;
/*
* Data reads are requested by first writing to NFC_DATA_RD
* and then reading back from NFC_READ.
*/
for (i = 0; i < len; i++) {
while (bfin_read_NFC_STAT() & WB_FULL)
cpu_relax();
/* Contents do not matter */
bfin_write_NFC_DATA_RD(0x0000);
SSYNC();
while ((bfin_read_NFC_IRQSTAT() & RD_RDY) != RD_RDY)
cpu_relax();
buf[i] = bfin_read_NFC_READ();
val = bfin_read_NFC_IRQSTAT();
val |= RD_RDY;
bfin_write_NFC_IRQSTAT(val);
SSYNC();
}
}
static uint8_t bf5xx_nand_read_byte(struct mtd_info *mtd)
{
uint8_t val;
bf5xx_nand_read_buf(mtd, &val, 1);
return val;
}
static void bf5xx_nand_write_buf(struct mtd_info *mtd,
const uint8_t *buf, int len)
{
int i;
for (i = 0; i < len; i++) {
while (bfin_read_NFC_STAT() & WB_FULL)
cpu_relax();
bfin_write_NFC_DATA_WR(buf[i]);
SSYNC();
}
}
static void bf5xx_nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
{
int i;
u16 *p = (u16 *) buf;
len >>= 1;
/*
* Data reads are requested by first writing to NFC_DATA_RD
* and then reading back from NFC_READ.
*/
bfin_write_NFC_DATA_RD(0x5555);
SSYNC();
for (i = 0; i < len; i++)
p[i] = bfin_read_NFC_READ();
}
static void bf5xx_nand_write_buf16(struct mtd_info *mtd,
const uint8_t *buf, int len)
{
int i;
u16 *p = (u16 *) buf;
len >>= 1;
for (i = 0; i < len; i++)
bfin_write_NFC_DATA_WR(p[i]);
SSYNC();
}
/*
* DMA functions for buffer writing and reading
*/
static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id)
{
struct bf5xx_nand_info *info = dev_id;
clear_dma_irqstat(CH_NFC);
disable_dma(CH_NFC);
complete(&info->dma_completion);
return IRQ_HANDLED;
}
static int bf5xx_nand_dma_rw(struct mtd_info *mtd,
uint8_t *buf, int is_read)
{
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
struct bf5xx_nand_platform *plat = info->platform;
unsigned short page_size = (plat->page_size ? 512 : 256);
unsigned short val;
dev_dbg(info->device, " mtd->%p, buf->%p, is_read %d\n",
mtd, buf, is_read);
/*
* Before starting a dma transfer, be sure to invalidate/flush
* the cache over the address range of your DMA buffer to
* prevent cache coherency problems. Otherwise very subtle bugs
* can be introduced to your driver.
*/
if (is_read)
invalidate_dcache_range((unsigned int)buf,
(unsigned int)(buf + page_size));
else
flush_dcache_range((unsigned int)buf,
(unsigned int)(buf + page_size));
/*
* This register must be written before each page is
* transferred to generate the correct ECC register
* values.
*/
bfin_write_NFC_RST(0x1);
SSYNC();
disable_dma(CH_NFC);
clear_dma_irqstat(CH_NFC);
/* setup DMA register with Blackfin DMA API */
set_dma_config(CH_NFC, 0x0);
set_dma_start_addr(CH_NFC, (unsigned long) buf);
set_dma_x_count(CH_NFC, (page_size >> 2));
set_dma_x_modify(CH_NFC, 4);
/* setup write or read operation */
val = DI_EN | WDSIZE_32;
if (is_read)
val |= WNR;
set_dma_config(CH_NFC, val);
enable_dma(CH_NFC);
/* Start PAGE read/write operation */
if (is_read)
bfin_write_NFC_PGCTL(0x1);
else
bfin_write_NFC_PGCTL(0x2);
wait_for_completion(&info->dma_completion);
return 0;
}
static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd,
uint8_t *buf, int len)
{
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
struct bf5xx_nand_platform *plat = info->platform;
unsigned short page_size = (plat->page_size ? 512 : 256);
dev_dbg(info->device, "mtd->%p, buf->%p, int %d\n", mtd, buf, len);
if (len == page_size)
bf5xx_nand_dma_rw(mtd, buf, 1);
else
bf5xx_nand_read_buf(mtd, buf, len);
}
static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd,
const uint8_t *buf, int len)
{
struct bf5xx_nand_info *info = mtd_to_nand_info(mtd);
struct bf5xx_nand_platform *plat = info->platform;
unsigned short page_size = (plat->page_size ? 512 : 256);
dev_dbg(info->device, "mtd->%p, buf->%p, len %d\n", mtd, buf, len);
if (len == page_size)
bf5xx_nand_dma_rw(mtd, (uint8_t *)buf, 0);
else
bf5xx_nand_write_buf(mtd, buf, len);
}
/*
* System initialization functions
*/
static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info)
{
int ret;
unsigned short val;
/* Do not use dma */
if (!hardware_ecc)
return 0;
init_completion(&info->dma_completion);
/* Setup DMAC1 channel mux for NFC which shared with SDH */
val = bfin_read_DMAC1_PERIMUX();
val &= 0xFFFE;
bfin_write_DMAC1_PERIMUX(val);
SSYNC();
/* Request NFC DMA channel */
ret = request_dma(CH_NFC, "BF5XX NFC driver");
if (ret < 0) {
dev_err(info->device, " unable to get DMA channel\n");
return ret;
}
set_dma_callback(CH_NFC, (void *) bf5xx_nand_dma_irq, (void *) info);
/* Turn off the DMA channel first */
disable_dma(CH_NFC);
return 0;
}
/*
* BF5XX NFC hardware initialization
* - pin mux setup
* - clear interrupt status
*/
static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info)
{
int err = 0;
unsigned short val;
struct bf5xx_nand_platform *plat = info->platform;
/* setup NFC_CTL register */
dev_info(info->device,
"page_size=%d, data_width=%d, wr_dly=%d, rd_dly=%d\n",
(plat->page_size ? 512 : 256),
(plat->data_width ? 16 : 8),
plat->wr_dly, plat->rd_dly);
val = (plat->page_size << NFC_PG_SIZE_OFFSET) |
(plat->data_width << NFC_NWIDTH_OFFSET) |
(plat->rd_dly << NFC_RDDLY_OFFSET) |
(plat->rd_dly << NFC_WRDLY_OFFSET);
dev_dbg(info->device, "NFC_CTL is 0x%04x\n", val);
bfin_write_NFC_CTL(val);
SSYNC();
/* clear interrupt status */
bfin_write_NFC_IRQMASK(0x0);
SSYNC();
val = bfin_read_NFC_IRQSTAT();
bfin_write_NFC_IRQSTAT(val);
SSYNC();
if (peripheral_request_list(bfin_nfc_pin_req, DRV_NAME)) {
printk(KERN_ERR DRV_NAME
": Requesting Peripherals failed\n");
return -EFAULT;
}
/* DMA initialization */
if (bf5xx_nand_dma_init(info))
err = -ENXIO;
return err;
}
/*
* Device management interface
*/
static int bf5xx_nand_add_partition(struct bf5xx_nand_info *info)
{
struct mtd_info *mtd = &info->mtd;
#ifdef CONFIG_MTD_PARTITIONS
struct mtd_partition *parts = info->platform->partitions;
int nr = info->platform->nr_partitions;
return add_mtd_partitions(mtd, parts, nr);
#else
return add_mtd_device(mtd);
#endif
}
static int bf5xx_nand_remove(struct platform_device *pdev)
{
struct bf5xx_nand_info *info = to_nand_info(pdev);
struct mtd_info *mtd = NULL;
platform_set_drvdata(pdev, NULL);
/* first thing we need to do is release all our mtds
* and their partitions, then go through freeing the
* resources used
*/
mtd = &info->mtd;
if (mtd) {
nand_release(mtd);
kfree(mtd);
}
peripheral_free_list(bfin_nfc_pin_req);
/* free the common resources */
kfree(info);
return 0;
}
/*
* bf5xx_nand_probe
*
* called by device layer when it finds a device matching
* one our driver can handled. This code checks to see if
* it can allocate all necessary resources then calls the
* nand layer to look for devices
*/
static int bf5xx_nand_probe(struct platform_device *pdev)
{
struct bf5xx_nand_platform *plat = to_nand_plat(pdev);
struct bf5xx_nand_info *info = NULL;
struct nand_chip *chip = NULL;
struct mtd_info *mtd = NULL;
int err = 0;
dev_dbg(&pdev->dev, "(%p)\n", pdev);
if (!plat) {
dev_err(&pdev->dev, "no platform specific information\n");
goto exit_error;
}
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL) {
dev_err(&pdev->dev, "no memory for flash info\n");
err = -ENOMEM;
goto exit_error;
}
platform_set_drvdata(pdev, info);
spin_lock_init(&info->controller.lock);
init_waitqueue_head(&info->controller.wq);
info->device = &pdev->dev;
info->platform = plat;
/* initialise chip data struct */
chip = &info->chip;
if (plat->data_width)
chip->options |= NAND_BUSWIDTH_16;
chip->options |= NAND_CACHEPRG | NAND_SKIP_BBTSCAN;
chip->read_buf = (plat->data_width) ?
bf5xx_nand_read_buf16 : bf5xx_nand_read_buf;
chip->write_buf = (plat->data_width) ?
bf5xx_nand_write_buf16 : bf5xx_nand_write_buf;
chip->read_byte = bf5xx_nand_read_byte;
chip->cmd_ctrl = bf5xx_nand_hwcontrol;
chip->dev_ready = bf5xx_nand_devready;
chip->priv = &info->mtd;
chip->controller = &info->controller;
chip->IO_ADDR_R = (void __iomem *) NFC_READ;
chip->IO_ADDR_W = (void __iomem *) NFC_DATA_WR;
chip->chip_delay = 0;
/* initialise mtd info data struct */
mtd = &info->mtd;
mtd->priv = chip;
mtd->owner = THIS_MODULE;
/* initialise the hardware */
err = bf5xx_nand_hw_init(info);
if (err != 0)
goto exit_error;
/* setup hardware ECC data struct */
if (hardware_ecc) {
if (plat->page_size == NFC_PG_SIZE_256) {
chip->ecc.bytes = 3;
chip->ecc.size = 256;
} else if (plat->page_size == NFC_PG_SIZE_512) {
chip->ecc.bytes = 6;
chip->ecc.size = 512;
}
chip->read_buf = bf5xx_nand_dma_read_buf;
chip->write_buf = bf5xx_nand_dma_write_buf;
chip->ecc.calculate = bf5xx_nand_calculate_ecc;
chip->ecc.correct = bf5xx_nand_correct_data;
chip->ecc.mode = NAND_ECC_HW;
chip->ecc.hwctl = bf5xx_nand_enable_hwecc;
} else {
chip->ecc.mode = NAND_ECC_SOFT;
}
/* scan hardware nand chip and setup mtd info data struct */
if (nand_scan(mtd, 1)) {
err = -ENXIO;
goto exit_error;
}
/* add NAND partition */
bf5xx_nand_add_partition(info);
dev_dbg(&pdev->dev, "initialised ok\n");
return 0;
exit_error:
bf5xx_nand_remove(pdev);
if (err == 0)
err = -EINVAL;
return err;
}
/* PM Support */
#ifdef CONFIG_PM
static int bf5xx_nand_suspend(struct platform_device *dev, pm_message_t pm)
{
struct bf5xx_nand_info *info = platform_get_drvdata(dev);
return 0;
}
static int bf5xx_nand_resume(struct platform_device *dev)
{
struct bf5xx_nand_info *info = platform_get_drvdata(dev);
if (info)
bf5xx_nand_hw_init(info);
return 0;
}
#else
#define bf5xx_nand_suspend NULL
#define bf5xx_nand_resume NULL
#endif
/* driver device registration */
static struct platform_driver bf5xx_nand_driver = {
.probe = bf5xx_nand_probe,
.remove = bf5xx_nand_remove,
.suspend = bf5xx_nand_suspend,
.resume = bf5xx_nand_resume,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
};
static int __init bf5xx_nand_init(void)
{
printk(KERN_INFO "%s, Version %s (c) 2007 Analog Devices, Inc.\n",
DRV_DESC, DRV_VERSION);
return platform_driver_register(&bf5xx_nand_driver);
}
static void __exit bf5xx_nand_exit(void)
{
platform_driver_unregister(&bf5xx_nand_driver);
}
module_init(bf5xx_nand_init);
module_exit(bf5xx_nand_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION(DRV_DESC);

View File

@ -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)

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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"}
};

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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");

View File

@ -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);
}

View File

@ -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;
}

View File

@ -1225,6 +1225,14 @@ config JFFS2_FS_WRITEBUFFER
- NOR flash with transparent ECC
- DataFlash
config JFFS2_FS_WBUF_VERIFY
bool "Verify JFFS2 write-buffer reads"
depends on JFFS2_FS_WRITEBUFFER
default n
help
This causes JFFS2 to read back every page written through the
write-buffer, and check for errors.
config JFFS2_SUMMARY
bool "JFFS2 summary support (EXPERIMENTAL)"
depends on JFFS2_FS && EXPERIMENTAL
@ -1295,52 +1303,71 @@ config JFFS2_ZLIB
select ZLIB_DEFLATE
depends on JFFS2_FS
default y
help
Zlib is designed to be a free, general-purpose, legally unencumbered,
lossless data-compression library for use on virtually any computer
hardware and operating system. See <http://www.gzip.org/zlib/> for
further information.
help
Zlib is designed to be a free, general-purpose, legally unencumbered,
lossless data-compression library for use on virtually any computer
hardware and operating system. See <http://www.gzip.org/zlib/> for
further information.
Say 'Y' if unsure.
Say 'Y' if unsure.
config JFFS2_LZO
bool "JFFS2 LZO compression support" if JFFS2_COMPRESSION_OPTIONS
select LZO_COMPRESS
select LZO_DECOMPRESS
depends on JFFS2_FS
default n
help
minilzo-based compression. Generally works better than Zlib.
This feature was added in July, 2007. Say 'N' if you need
compatibility with older bootloaders or kernels.
config JFFS2_RTIME
bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS
depends on JFFS2_FS
default y
help
Rtime does manage to recompress already-compressed data. Say 'Y' if unsure.
help
Rtime does manage to recompress already-compressed data. Say 'Y' if unsure.
config JFFS2_RUBIN
bool "JFFS2 RUBIN compression support" if JFFS2_COMPRESSION_OPTIONS
depends on JFFS2_FS
default n
help
RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure.
help
RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure.
choice
prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS
default JFFS2_CMODE_PRIORITY
depends on JFFS2_FS
help
You can set here the default compression mode of JFFS2 from
the available compression modes. Don't touch if unsure.
prompt "JFFS2 default compression mode" if JFFS2_COMPRESSION_OPTIONS
default JFFS2_CMODE_PRIORITY
depends on JFFS2_FS
help
You can set here the default compression mode of JFFS2 from
the available compression modes. Don't touch if unsure.
config JFFS2_CMODE_NONE
bool "no compression"
help
Uses no compression.
bool "no compression"
help
Uses no compression.
config JFFS2_CMODE_PRIORITY
bool "priority"
help
Tries the compressors in a predefined order and chooses the first
successful one.
bool "priority"
help
Tries the compressors in a predefined order and chooses the first
successful one.
config JFFS2_CMODE_SIZE
bool "size (EXPERIMENTAL)"
help
Tries all compressors and chooses the one which has the smallest
result.
bool "size (EXPERIMENTAL)"
help
Tries all compressors and chooses the one which has the smallest
result.
config JFFS2_CMODE_FAVOURLZO
bool "Favour LZO"
help
Tries all compressors and chooses the one which has the smallest
result but gives some preference to LZO (which has faster
decompression) at the expense of size.
endchoice

View File

@ -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

View File

@ -176,7 +176,7 @@ static void jffs2_iset_acl(struct inode *inode, struct posix_acl **i_acl, struct
spin_unlock(&inode->i_lock);
}
static struct posix_acl *jffs2_get_acl(struct inode *inode, int type)
struct posix_acl *jffs2_get_acl(struct inode *inode, int type)
{
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct posix_acl *acl;
@ -247,8 +247,13 @@ static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
if (rc < 0)
return rc;
if (inode->i_mode != mode) {
inode->i_mode = mode;
jffs2_dirty_inode(inode);
struct iattr attr;
attr.ia_valid = ATTR_MODE;
attr.ia_mode = mode;
rc = jffs2_do_setattr(inode, &attr);
if (rc < 0)
return rc;
}
if (rc == 0)
acl = NULL;
@ -307,22 +312,16 @@ int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd)
return generic_permission(inode, mask, jffs2_check_acl);
}
int jffs2_init_acl(struct inode *inode, struct inode *dir)
int jffs2_init_acl(struct inode *inode, struct posix_acl *acl)
{
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct posix_acl *acl = NULL, *clone;
struct posix_acl *clone;
mode_t mode;
int rc = 0;
f->i_acl_access = JFFS2_ACL_NOT_CACHED;
f->i_acl_default = JFFS2_ACL_NOT_CACHED;
if (!S_ISLNK(inode->i_mode)) {
acl = jffs2_get_acl(dir, ACL_TYPE_DEFAULT);
if (IS_ERR(acl))
return PTR_ERR(acl);
if (!acl)
inode->i_mode &= ~current->fs->umask;
}
if (acl) {
if (S_ISDIR(inode->i_mode)) {
rc = jffs2_set_acl(inode, ACL_TYPE_DEFAULT, acl);

View File

@ -28,9 +28,10 @@ struct jffs2_acl_header {
#define JFFS2_ACL_NOT_CACHED ((void *)-1)
extern struct posix_acl *jffs2_get_acl(struct inode *inode, int type);
extern int jffs2_permission(struct inode *, int, struct nameidata *);
extern int jffs2_acl_chmod(struct inode *);
extern int jffs2_init_acl(struct inode *, struct inode *);
extern int jffs2_init_acl(struct inode *, struct posix_acl *);
extern void jffs2_clear_acl(struct jffs2_inode_info *);
extern struct xattr_handler jffs2_acl_access_xattr_handler;
@ -38,6 +39,7 @@ extern struct xattr_handler jffs2_acl_default_xattr_handler;
#else
#define jffs2_get_acl(inode, type) (NULL)
#define jffs2_permission NULL
#define jffs2_acl_chmod(inode) (0)
#define jffs2_init_acl(inode,dir) (0)

View File

@ -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);
}

View File

@ -285,6 +285,14 @@ static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c)
than actually making progress? */
c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2;
/* What number of 'very dirty' eraseblocks do we allow before we
trigger the GC thread even if we don't _need_ the space. When we
can't mark nodes obsolete on the medium, the old dirty nodes cause
performance problems because we have to inspect and discard them. */
c->vdirty_blocks_gctrigger = c->resv_blocks_gctrigger;
if (jffs2_can_mark_obsolete(c))
c->vdirty_blocks_gctrigger *= 10;
/* If there's less than this amount of dirty space, don't bother
trying to GC to make more space. It'll be a fruitless task */
c->nospc_dirty_size = c->sector_size + (c->flash_size / 100);
@ -303,6 +311,8 @@ static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c)
c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024);
dbg_fsbuild("Amount of dirty space required to GC: %d bytes\n",
c->nospc_dirty_size);
dbg_fsbuild("Very dirty blocks before GC triggered: %d\n",
c->vdirty_blocks_gctrigger);
}
int jffs2_do_mount_fs(struct jffs2_sb_info *c)

View File

@ -5,7 +5,7 @@
* Created by Arjan van de Ven <arjanv@redhat.com>
*
* Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
* University of Szeged, Hungary
* University of Szeged, Hungary
*
* For licensing information, see the file 'LICENCE' in this directory.
*
@ -24,6 +24,34 @@ static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
/* Statistics for blocks stored without compression */
static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0;
/*
* Return 1 to use this compression
*/
static int jffs2_is_best_compression(struct jffs2_compressor *this,
struct jffs2_compressor *best, uint32_t size, uint32_t bestsize)
{
switch (jffs2_compression_mode) {
case JFFS2_COMPR_MODE_SIZE:
if (bestsize > size)
return 1;
return 0;
case JFFS2_COMPR_MODE_FAVOURLZO:
if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > size))
return 1;
if ((best->compr != JFFS2_COMPR_LZO) && (bestsize > size))
return 1;
if ((this->compr == JFFS2_COMPR_LZO) && (bestsize > (size * FAVOUR_LZO_PERCENT / 100)))
return 1;
if ((bestsize * FAVOUR_LZO_PERCENT / 100) > size)
return 1;
return 0;
}
/* Shouldn't happen */
return 0;
}
/* jffs2_compress:
* @data: Pointer to uncompressed data
* @cdata: Pointer to returned pointer to buffer for compressed data
@ -43,121 +71,124 @@ static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_co
* *datalen accordingly to show the amount of data which were compressed.
*/
uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
unsigned char *data_in, unsigned char **cpage_out,
uint32_t *datalen, uint32_t *cdatalen)
unsigned char *data_in, unsigned char **cpage_out,
uint32_t *datalen, uint32_t *cdatalen)
{
int ret = JFFS2_COMPR_NONE;
int compr_ret;
struct jffs2_compressor *this, *best=NULL;
unsigned char *output_buf = NULL, *tmp_buf;
uint32_t orig_slen, orig_dlen;
uint32_t best_slen=0, best_dlen=0;
int compr_ret;
struct jffs2_compressor *this, *best=NULL;
unsigned char *output_buf = NULL, *tmp_buf;
uint32_t orig_slen, orig_dlen;
uint32_t best_slen=0, best_dlen=0;
switch (jffs2_compression_mode) {
case JFFS2_COMPR_MODE_NONE:
break;
case JFFS2_COMPR_MODE_PRIORITY:
output_buf = kmalloc(*cdatalen,GFP_KERNEL);
if (!output_buf) {
printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n");
goto out;
}
orig_slen = *datalen;
orig_dlen = *cdatalen;
spin_lock(&jffs2_compressor_list_lock);
list_for_each_entry(this, &jffs2_compressor_list, list) {
/* Skip decompress-only backwards-compatibility and disabled modules */
if ((!this->compress)||(this->disabled))
continue;
switch (jffs2_compression_mode) {
case JFFS2_COMPR_MODE_NONE:
break;
case JFFS2_COMPR_MODE_PRIORITY:
output_buf = kmalloc(*cdatalen,GFP_KERNEL);
if (!output_buf) {
printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n");
goto out;
}
orig_slen = *datalen;
orig_dlen = *cdatalen;
spin_lock(&jffs2_compressor_list_lock);
list_for_each_entry(this, &jffs2_compressor_list, list) {
/* Skip decompress-only backwards-compatibility and disabled modules */
if ((!this->compress)||(this->disabled))
continue;
this->usecount++;
spin_unlock(&jffs2_compressor_list_lock);
*datalen = orig_slen;
*cdatalen = orig_dlen;
compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL);
spin_lock(&jffs2_compressor_list_lock);
this->usecount--;
if (!compr_ret) {
ret = this->compr;
this->stat_compr_blocks++;
this->stat_compr_orig_size += *datalen;
this->stat_compr_new_size += *cdatalen;
break;
}
}
spin_unlock(&jffs2_compressor_list_lock);
if (ret == JFFS2_COMPR_NONE) kfree(output_buf);
break;
case JFFS2_COMPR_MODE_SIZE:
orig_slen = *datalen;
orig_dlen = *cdatalen;
spin_lock(&jffs2_compressor_list_lock);
list_for_each_entry(this, &jffs2_compressor_list, list) {
/* Skip decompress-only backwards-compatibility and disabled modules */
if ((!this->compress)||(this->disabled))
continue;
/* Allocating memory for output buffer if necessary */
if ((this->compr_buf_size<orig_dlen)&&(this->compr_buf)) {
spin_unlock(&jffs2_compressor_list_lock);
kfree(this->compr_buf);
spin_lock(&jffs2_compressor_list_lock);
this->compr_buf_size=0;
this->compr_buf=NULL;
}
if (!this->compr_buf) {
spin_unlock(&jffs2_compressor_list_lock);
tmp_buf = kmalloc(orig_dlen,GFP_KERNEL);
spin_lock(&jffs2_compressor_list_lock);
if (!tmp_buf) {
printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n",orig_dlen);
continue;
}
else {
this->compr_buf = tmp_buf;
this->compr_buf_size = orig_dlen;
}
}
this->usecount++;
spin_unlock(&jffs2_compressor_list_lock);
*datalen = orig_slen;
*cdatalen = orig_dlen;
compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL);
spin_lock(&jffs2_compressor_list_lock);
this->usecount--;
if (!compr_ret) {
if ((!best_dlen)||(best_dlen>*cdatalen)) {
best_dlen = *cdatalen;
best_slen = *datalen;
best = this;
}
}
}
if (best_dlen) {
*cdatalen = best_dlen;
*datalen = best_slen;
output_buf = best->compr_buf;
best->compr_buf = NULL;
best->compr_buf_size = 0;
best->stat_compr_blocks++;
best->stat_compr_orig_size += best_slen;
best->stat_compr_new_size += best_dlen;
ret = best->compr;
}
spin_unlock(&jffs2_compressor_list_lock);
break;
default:
printk(KERN_ERR "JFFS2: unknow compression mode.\n");
}
this->usecount++;
spin_unlock(&jffs2_compressor_list_lock);
*datalen = orig_slen;
*cdatalen = orig_dlen;
compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL);
spin_lock(&jffs2_compressor_list_lock);
this->usecount--;
if (!compr_ret) {
ret = this->compr;
this->stat_compr_blocks++;
this->stat_compr_orig_size += *datalen;
this->stat_compr_new_size += *cdatalen;
break;
}
}
spin_unlock(&jffs2_compressor_list_lock);
if (ret == JFFS2_COMPR_NONE)
kfree(output_buf);
break;
case JFFS2_COMPR_MODE_SIZE:
case JFFS2_COMPR_MODE_FAVOURLZO:
orig_slen = *datalen;
orig_dlen = *cdatalen;
spin_lock(&jffs2_compressor_list_lock);
list_for_each_entry(this, &jffs2_compressor_list, list) {
/* Skip decompress-only backwards-compatibility and disabled modules */
if ((!this->compress)||(this->disabled))
continue;
/* Allocating memory for output buffer if necessary */
if ((this->compr_buf_size < orig_slen) && (this->compr_buf)) {
spin_unlock(&jffs2_compressor_list_lock);
kfree(this->compr_buf);
spin_lock(&jffs2_compressor_list_lock);
this->compr_buf_size=0;
this->compr_buf=NULL;
}
if (!this->compr_buf) {
spin_unlock(&jffs2_compressor_list_lock);
tmp_buf = kmalloc(orig_slen, GFP_KERNEL);
spin_lock(&jffs2_compressor_list_lock);
if (!tmp_buf) {
printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n", orig_slen);
continue;
}
else {
this->compr_buf = tmp_buf;
this->compr_buf_size = orig_slen;
}
}
this->usecount++;
spin_unlock(&jffs2_compressor_list_lock);
*datalen = orig_slen;
*cdatalen = orig_dlen;
compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL);
spin_lock(&jffs2_compressor_list_lock);
this->usecount--;
if (!compr_ret) {
if (((!best_dlen) || jffs2_is_best_compression(this, best, *cdatalen, best_dlen))
&& (*cdatalen < *datalen)) {
best_dlen = *cdatalen;
best_slen = *datalen;
best = this;
}
}
}
if (best_dlen) {
*cdatalen = best_dlen;
*datalen = best_slen;
output_buf = best->compr_buf;
best->compr_buf = NULL;
best->compr_buf_size = 0;
best->stat_compr_blocks++;
best->stat_compr_orig_size += best_slen;
best->stat_compr_new_size += best_dlen;
ret = best->compr;
}
spin_unlock(&jffs2_compressor_list_lock);
break;
default:
printk(KERN_ERR "JFFS2: unknow compression mode.\n");
}
out:
if (ret == JFFS2_COMPR_NONE) {
*cpage_out = data_in;
*datalen = *cdatalen;
none_stat_compr_blocks++;
none_stat_compr_size += *datalen;
}
else {
*cpage_out = output_buf;
}
if (ret == JFFS2_COMPR_NONE) {
*cpage_out = data_in;
*datalen = *cdatalen;
none_stat_compr_blocks++;
none_stat_compr_size += *datalen;
}
else {
*cpage_out = output_buf;
}
return ret;
}
@ -165,8 +196,8 @@ int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
uint16_t comprtype, unsigned char *cdata_in,
unsigned char *data_out, uint32_t cdatalen, uint32_t datalen)
{
struct jffs2_compressor *this;
int ret;
struct jffs2_compressor *this;
int ret;
/* Older code had a bug where it would write non-zero 'usercompr'
fields. Deal with it. */
@ -177,32 +208,32 @@ int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
case JFFS2_COMPR_NONE:
/* This should be special-cased elsewhere, but we might as well deal with it */
memcpy(data_out, cdata_in, datalen);
none_stat_decompr_blocks++;
none_stat_decompr_blocks++;
break;
case JFFS2_COMPR_ZERO:
memset(data_out, 0, datalen);
break;
default:
spin_lock(&jffs2_compressor_list_lock);
list_for_each_entry(this, &jffs2_compressor_list, list) {
if (comprtype == this->compr) {
this->usecount++;
spin_unlock(&jffs2_compressor_list_lock);
ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL);
spin_lock(&jffs2_compressor_list_lock);
if (ret) {
printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret);
}
else {
this->stat_decompr_blocks++;
}
this->usecount--;
spin_unlock(&jffs2_compressor_list_lock);
return ret;
}
}
spin_lock(&jffs2_compressor_list_lock);
list_for_each_entry(this, &jffs2_compressor_list, list) {
if (comprtype == this->compr) {
this->usecount++;
spin_unlock(&jffs2_compressor_list_lock);
ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL);
spin_lock(&jffs2_compressor_list_lock);
if (ret) {
printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret);
}
else {
this->stat_decompr_blocks++;
}
this->usecount--;
spin_unlock(&jffs2_compressor_list_lock);
return ret;
}
}
printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype);
spin_unlock(&jffs2_compressor_list_lock);
spin_unlock(&jffs2_compressor_list_lock);
return -EIO;
}
return 0;
@ -210,108 +241,119 @@ int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
int jffs2_register_compressor(struct jffs2_compressor *comp)
{
struct jffs2_compressor *this;
struct jffs2_compressor *this;
if (!comp->name) {
printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n");
return -1;
}
comp->compr_buf_size=0;
comp->compr_buf=NULL;
comp->usecount=0;
comp->stat_compr_orig_size=0;
comp->stat_compr_new_size=0;
comp->stat_compr_blocks=0;
comp->stat_decompr_blocks=0;
D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name));
if (!comp->name) {
printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n");
return -1;
}
comp->compr_buf_size=0;
comp->compr_buf=NULL;
comp->usecount=0;
comp->stat_compr_orig_size=0;
comp->stat_compr_new_size=0;
comp->stat_compr_blocks=0;
comp->stat_decompr_blocks=0;
D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name));
spin_lock(&jffs2_compressor_list_lock);
spin_lock(&jffs2_compressor_list_lock);
list_for_each_entry(this, &jffs2_compressor_list, list) {
if (this->priority < comp->priority) {
list_add(&comp->list, this->list.prev);
goto out;
}
}
list_add_tail(&comp->list, &jffs2_compressor_list);
list_for_each_entry(this, &jffs2_compressor_list, list) {
if (this->priority < comp->priority) {
list_add(&comp->list, this->list.prev);
goto out;
}
}
list_add_tail(&comp->list, &jffs2_compressor_list);
out:
D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
})
D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
})
spin_unlock(&jffs2_compressor_list_lock);
spin_unlock(&jffs2_compressor_list_lock);
return 0;
return 0;
}
int jffs2_unregister_compressor(struct jffs2_compressor *comp)
{
D2(struct jffs2_compressor *this;)
D2(struct jffs2_compressor *this;)
D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name));
D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name));
spin_lock(&jffs2_compressor_list_lock);
spin_lock(&jffs2_compressor_list_lock);
if (comp->usecount) {
spin_unlock(&jffs2_compressor_list_lock);
printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n");
return -1;
}
list_del(&comp->list);
if (comp->usecount) {
spin_unlock(&jffs2_compressor_list_lock);
printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n");
return -1;
}
list_del(&comp->list);
D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
})
spin_unlock(&jffs2_compressor_list_lock);
return 0;
D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
})
spin_unlock(&jffs2_compressor_list_lock);
return 0;
}
void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
{
if (orig != comprbuf)
kfree(comprbuf);
if (orig != comprbuf)
kfree(comprbuf);
}
int __init jffs2_compressors_init(void)
{
/* Registering compressors */
#ifdef CONFIG_JFFS2_ZLIB
jffs2_zlib_init();
jffs2_zlib_init();
#endif
#ifdef CONFIG_JFFS2_RTIME
jffs2_rtime_init();
jffs2_rtime_init();
#endif
#ifdef CONFIG_JFFS2_RUBIN
jffs2_rubinmips_init();
jffs2_dynrubin_init();
jffs2_rubinmips_init();
jffs2_dynrubin_init();
#endif
#ifdef CONFIG_JFFS2_LZO
jffs2_lzo_init();
#endif
/* Setting default compression mode */
#ifdef CONFIG_JFFS2_CMODE_NONE
jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");)
jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");)
#else
#ifdef CONFIG_JFFS2_CMODE_SIZE
jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");)
jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");)
#else
D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");)
#ifdef CONFIG_JFFS2_CMODE_FAVOURLZO
jffs2_compression_mode = JFFS2_COMPR_MODE_FAVOURLZO;
D1(printk(KERN_INFO "JFFS2: default compression mode: favourlzo\n");)
#else
D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");)
#endif
#endif
return 0;
#endif
return 0;
}
int jffs2_compressors_exit(void)
{
/* Unregistering compressors */
#ifdef CONFIG_JFFS2_LZO
jffs2_lzo_exit();
#endif
#ifdef CONFIG_JFFS2_RUBIN
jffs2_dynrubin_exit();
jffs2_rubinmips_exit();
jffs2_dynrubin_exit();
jffs2_rubinmips_exit();
#endif
#ifdef CONFIG_JFFS2_RTIME
jffs2_rtime_exit();
jffs2_rtime_exit();
#endif
#ifdef CONFIG_JFFS2_ZLIB
jffs2_zlib_exit();
jffs2_zlib_exit();
#endif
return 0;
return 0;
}

View File

@ -2,7 +2,7 @@
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
* University of Szeged, Hungary
* University of Szeged, Hungary
*
* For licensing information, see the file 'LICENCE' in this directory.
*
@ -27,34 +27,38 @@
#define JFFS2_RUBINMIPS_PRIORITY 10
#define JFFS2_DYNRUBIN_PRIORITY 20
#define JFFS2_LZARI_PRIORITY 30
#define JFFS2_LZO_PRIORITY 40
#define JFFS2_RTIME_PRIORITY 50
#define JFFS2_ZLIB_PRIORITY 60
#define JFFS2_LZO_PRIORITY 80
#define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */
#define JFFS2_DYNRUBIN_DISABLED /* for decompression */
#define JFFS2_DYNRUBIN_DISABLED /* for decompression */
#define JFFS2_COMPR_MODE_NONE 0
#define JFFS2_COMPR_MODE_PRIORITY 1
#define JFFS2_COMPR_MODE_SIZE 2
#define JFFS2_COMPR_MODE_FAVOURLZO 3
#define FAVOUR_LZO_PERCENT 80
struct jffs2_compressor {
struct list_head list;
int priority; /* used by prirority comr. mode */
char *name;
char compr; /* JFFS2_COMPR_XXX */
int (*compress)(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *srclen, uint32_t *destlen, void *model);
int (*decompress)(unsigned char *cdata_in, unsigned char *data_out,
uint32_t cdatalen, uint32_t datalen, void *model);
int usecount;
int disabled; /* if seted the compressor won't compress */
unsigned char *compr_buf; /* used by size compr. mode */
uint32_t compr_buf_size; /* used by size compr. mode */
uint32_t stat_compr_orig_size;
uint32_t stat_compr_new_size;
uint32_t stat_compr_blocks;
uint32_t stat_decompr_blocks;
struct list_head list;
int priority; /* used by prirority comr. mode */
char *name;
char compr; /* JFFS2_COMPR_XXX */
int (*compress)(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *srclen, uint32_t *destlen, void *model);
int (*decompress)(unsigned char *cdata_in, unsigned char *data_out,
uint32_t cdatalen, uint32_t datalen, void *model);
int usecount;
int disabled; /* if set the compressor won't compress */
unsigned char *compr_buf; /* used by size compr. mode */
uint32_t compr_buf_size; /* used by size compr. mode */
uint32_t stat_compr_orig_size;
uint32_t stat_compr_new_size;
uint32_t stat_compr_blocks;
uint32_t stat_decompr_blocks;
};
int jffs2_register_compressor(struct jffs2_compressor *comp);
@ -64,12 +68,12 @@ int jffs2_compressors_init(void);
int jffs2_compressors_exit(void);
uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
unsigned char *data_in, unsigned char **cpage_out,
uint32_t *datalen, uint32_t *cdatalen);
unsigned char *data_in, unsigned char **cpage_out,
uint32_t *datalen, uint32_t *cdatalen);
int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
uint16_t comprtype, unsigned char *cdata_in,
unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
uint16_t comprtype, unsigned char *cdata_in,
unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig);
@ -90,5 +94,9 @@ void jffs2_rtime_exit(void);
int jffs2_zlib_init(void);
void jffs2_zlib_exit(void);
#endif
#ifdef CONFIG_JFFS2_LZO
int jffs2_lzo_init(void);
void jffs2_lzo_exit(void);
#endif
#endif /* __JFFS2_COMPR_H__ */

108
fs/jffs2/compr_lzo.c Normal file
View File

@ -0,0 +1,108 @@
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright © 2007 Nokia Corporation. All rights reserved.
*
* Created by Richard Purdie <rpurdie@openedhand.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/lzo.h>
#include "compr.h"
static void *lzo_mem;
static void *lzo_compress_buf;
static DEFINE_MUTEX(deflate_mutex);
static void free_workspace(void)
{
vfree(lzo_mem);
vfree(lzo_compress_buf);
}
static int __init alloc_workspace(void)
{
lzo_mem = vmalloc(LZO1X_MEM_COMPRESS);
lzo_compress_buf = vmalloc(lzo1x_worst_compress(PAGE_SIZE));
if (!lzo_mem || !lzo_compress_buf) {
printk(KERN_WARNING "Failed to allocate lzo deflate workspace\n");
free_workspace();
return -ENOMEM;
}
return 0;
}
static int jffs2_lzo_compress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t *sourcelen, uint32_t *dstlen, void *model)
{
size_t compress_size;
int ret;
mutex_lock(&deflate_mutex);
ret = lzo1x_1_compress(data_in, *sourcelen, lzo_compress_buf, &compress_size, lzo_mem);
mutex_unlock(&deflate_mutex);
if (ret != LZO_E_OK)
return -1;
if (compress_size > *dstlen)
return -1;
memcpy(cpage_out, lzo_compress_buf, compress_size);
*dstlen = compress_size;
return 0;
}
static int jffs2_lzo_decompress(unsigned char *data_in, unsigned char *cpage_out,
uint32_t srclen, uint32_t destlen, void *model)
{
size_t dl = destlen;
int ret;
ret = lzo1x_decompress_safe(data_in, srclen, cpage_out, &dl);
if (ret != LZO_E_OK || dl != destlen)
return -1;
return 0;
}
static struct jffs2_compressor jffs2_lzo_comp = {
.priority = JFFS2_LZO_PRIORITY,
.name = "lzo",
.compr = JFFS2_COMPR_LZO,
.compress = &jffs2_lzo_compress,
.decompress = &jffs2_lzo_decompress,
.disabled = 0,
};
int __init jffs2_lzo_init(void)
{
int ret;
ret = alloc_workspace();
if (ret < 0)
return ret;
ret = jffs2_register_compressor(&jffs2_lzo_comp);
if (ret)
free_workspace();
return ret;
}
void jffs2_lzo_exit(void)
{
jffs2_unregister_compressor(&jffs2_lzo_comp);
free_workspace();
}

View File

@ -104,7 +104,7 @@ static int jffs2_rtime_decompress(unsigned char *data_in,
}
}
}
return 0;
return 0;
}
static struct jffs2_compressor jffs2_rtime_comp = {

View File

@ -384,7 +384,7 @@ static int jffs2_rubinmips_decompress(unsigned char *data_in,
void *model)
{
rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
return 0;
return 0;
}
static int jffs2_dynrubin_decompress(unsigned char *data_in,
@ -399,7 +399,7 @@ static int jffs2_dynrubin_decompress(unsigned char *data_in,
bits[c] = data_in[c];
rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen);
return 0;
return 0;
}
static struct jffs2_compressor jffs2_rubinmips_comp = {

View File

@ -181,7 +181,7 @@ static int jffs2_zlib_decompress(unsigned char *data_in,
}
zlib_inflateEnd(&inf_strm);
mutex_unlock(&inflate_mutex);
return 0;
return 0;
}
static struct jffs2_compressor jffs2_zlib_comp = {
@ -203,11 +203,11 @@ int __init jffs2_zlib_init(void)
ret = alloc_workspaces();
if (ret)
return ret;
return ret;
ret = jffs2_register_compressor(&jffs2_zlib_comp);
if (ret)
free_workspaces();
free_workspaces();
return ret;
}

View File

@ -32,7 +32,7 @@ static int jffs2_mkdir (struct inode *,struct dentry *,int);
static int jffs2_rmdir (struct inode *,struct dentry *);
static int jffs2_mknod (struct inode *,struct dentry *,int,dev_t);
static int jffs2_rename (struct inode *, struct dentry *,
struct inode *, struct dentry *);
struct inode *, struct dentry *);
const struct file_operations jffs2_dir_operations =
{
@ -182,6 +182,7 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
struct jffs2_inode_info *f, *dir_f;
struct jffs2_sb_info *c;
struct inode *inode;
struct posix_acl *acl;
int ret;
ri = jffs2_alloc_raw_inode();
@ -192,7 +193,7 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
D1(printk(KERN_DEBUG "jffs2_create()\n"));
inode = jffs2_new_inode(dir_i, mode, ri);
inode = jffs2_new_inode(dir_i, mode, ri, &acl);
if (IS_ERR(inode)) {
D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n"));
@ -212,12 +213,12 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
dentry->d_name.name, dentry->d_name.len);
if (ret)
goto fail;
goto fail_acl;
ret = jffs2_init_security(inode, dir_i);
if (ret)
goto fail;
ret = jffs2_init_acl(inode, dir_i);
goto fail_acl;
ret = jffs2_init_acl(inode, acl);
if (ret)
goto fail;
@ -230,6 +231,8 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages));
return 0;
fail_acl:
posix_acl_release(acl);
fail:
make_bad_inode(inode);
iput(inode);
@ -306,6 +309,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
struct jffs2_full_dirent *fd;
int namelen;
uint32_t alloclen;
struct posix_acl *acl;
int ret, targetlen = strlen(target);
/* FIXME: If you care. We'd need to use frags for the target
@ -332,7 +336,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
return ret;
}
inode = jffs2_new_inode(dir_i, S_IFLNK | S_IRWXUGO, ri);
inode = jffs2_new_inode(dir_i, S_IFLNK | S_IRWXUGO, ri, &acl);
if (IS_ERR(inode)) {
jffs2_free_raw_inode(ri);
@ -362,6 +366,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
up(&f->sem);
jffs2_complete_reservation(c);
jffs2_clear_inode(inode);
posix_acl_release(acl);
return PTR_ERR(fn);
}
@ -372,6 +377,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
up(&f->sem);
jffs2_complete_reservation(c);
jffs2_clear_inode(inode);
posix_acl_release(acl);
return -ENOMEM;
}
@ -389,9 +395,10 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
ret = jffs2_init_security(inode, dir_i);
if (ret) {
jffs2_clear_inode(inode);
posix_acl_release(acl);
return ret;
}
ret = jffs2_init_acl(inode, dir_i);
ret = jffs2_init_acl(inode, acl);
if (ret) {
jffs2_clear_inode(inode);
return ret;
@ -469,6 +476,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
struct jffs2_full_dirent *fd;
int namelen;
uint32_t alloclen;
struct posix_acl *acl;
int ret;
mode |= S_IFDIR;
@ -491,7 +499,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
return ret;
}
inode = jffs2_new_inode(dir_i, mode, ri);
inode = jffs2_new_inode(dir_i, mode, ri, &acl);
if (IS_ERR(inode)) {
jffs2_free_raw_inode(ri);
@ -518,6 +526,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
up(&f->sem);
jffs2_complete_reservation(c);
jffs2_clear_inode(inode);
posix_acl_release(acl);
return PTR_ERR(fn);
}
/* No data here. Only a metadata node, which will be
@ -531,9 +540,10 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
ret = jffs2_init_security(inode, dir_i);
if (ret) {
jffs2_clear_inode(inode);
posix_acl_release(acl);
return ret;
}
ret = jffs2_init_acl(inode, dir_i);
ret = jffs2_init_acl(inode, acl);
if (ret) {
jffs2_clear_inode(inode);
return ret;
@ -629,6 +639,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
union jffs2_device_node dev;
int devlen = 0;
uint32_t alloclen;
struct posix_acl *acl;
int ret;
if (!new_valid_dev(rdev))
@ -655,7 +666,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
return ret;
}
inode = jffs2_new_inode(dir_i, mode, ri);
inode = jffs2_new_inode(dir_i, mode, ri, &acl);
if (IS_ERR(inode)) {
jffs2_free_raw_inode(ri);
@ -684,6 +695,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
up(&f->sem);
jffs2_complete_reservation(c);
jffs2_clear_inode(inode);
posix_acl_release(acl);
return PTR_ERR(fn);
}
/* No data here. Only a metadata node, which will be
@ -697,9 +709,10 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
ret = jffs2_init_security(inode, dir_i);
if (ret) {
jffs2_clear_inode(inode);
posix_acl_release(acl);
return ret;
}
ret = jffs2_init_acl(inode, dir_i);
ret = jffs2_init_acl(inode, acl);
if (ret) {
jffs2_clear_inode(inode);
return ret;
@ -770,7 +783,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
}
static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry,
struct inode *new_dir_i, struct dentry *new_dentry)
struct inode *new_dir_i, struct dentry *new_dentry)
{
int ret;
struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb);

View File

@ -38,8 +38,8 @@ static void jffs2_erase_block(struct jffs2_sb_info *c,
#ifdef __ECOS
ret = jffs2_flash_erase(c, jeb);
if (!ret) {
jffs2_erase_succeeded(c, jeb);
return;
jffs2_erase_succeeded(c, jeb);
return;
}
bad_offset = jeb->offset;
#else /* Linux */
@ -50,12 +50,14 @@ static void jffs2_erase_block(struct jffs2_sb_info *c,
instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
if (!instr) {
printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
list_move(&jeb->list, &c->erase_pending_list);
c->erasing_size -= c->sector_size;
c->dirty_size += c->sector_size;
jeb->dirty_size = c->sector_size;
spin_unlock(&c->erase_completion_lock);
up(&c->erase_free_sem);
return;
}
@ -82,12 +84,14 @@ static void jffs2_erase_block(struct jffs2_sb_info *c,
if (ret == -ENOMEM || ret == -EAGAIN) {
/* Erase failed immediately. Refile it on the list */
D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret));
down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
list_move(&jeb->list, &c->erase_pending_list);
c->erasing_size -= c->sector_size;
c->dirty_size += c->sector_size;
jeb->dirty_size = c->sector_size;
spin_unlock(&c->erase_completion_lock);
up(&c->erase_free_sem);
return;
}
@ -114,6 +118,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
list_del(&jeb->list);
spin_unlock(&c->erase_completion_lock);
up(&c->erase_free_sem);
jffs2_mark_erased_block(c, jeb);
if (!--count) {
@ -134,6 +139,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
jffs2_free_jeb_node_refs(c, jeb);
list_add(&jeb->list, &c->erasing_list);
spin_unlock(&c->erase_completion_lock);
up(&c->erase_free_sem);
jffs2_erase_block(c, jeb);
@ -142,23 +148,25 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
}
/* Be nice */
cond_resched();
yield();
down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
}
spin_unlock(&c->erase_completion_lock);
up(&c->erase_free_sem);
done:
D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
up(&c->erase_free_sem);
}
static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
{
D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset));
down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
list_move_tail(&jeb->list, &c->erase_complete_list);
spin_unlock(&c->erase_completion_lock);
up(&c->erase_free_sem);
/* Ensure that kupdated calls us again to mark them clean */
jffs2_erase_pending_trigger(c);
}
@ -172,22 +180,26 @@ static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock
failed too many times. */
if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) {
/* We'd like to give this block another try. */
down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
list_move(&jeb->list, &c->erase_pending_list);
c->erasing_size -= c->sector_size;
c->dirty_size += c->sector_size;
jeb->dirty_size = c->sector_size;
spin_unlock(&c->erase_completion_lock);
up(&c->erase_free_sem);
return;
}
}
down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
c->erasing_size -= c->sector_size;
c->bad_size += c->sector_size;
list_move(&jeb->list, &c->bad_list);
c->nr_erasing_blocks--;
spin_unlock(&c->erase_completion_lock);
up(&c->erase_free_sem);
wake_up(&c->erase_wait);
}
@ -317,6 +329,33 @@ static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_erasebl
size_t retlen;
int ret = -EIO;
if (c->mtd->point) {
unsigned long *wordebuf;
ret = c->mtd->point(c->mtd, jeb->offset, c->sector_size, &retlen, (unsigned char **)&ebuf);
if (ret) {
D1(printk(KERN_DEBUG "MTD point failed %d\n", ret));
goto do_flash_read;
}
if (retlen < c->sector_size) {
/* Don't muck about if it won't let us point to the whole erase sector */
D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", retlen));
c->mtd->unpoint(c->mtd, ebuf, jeb->offset, retlen);
goto do_flash_read;
}
wordebuf = ebuf-sizeof(*wordebuf);
retlen /= sizeof(*wordebuf);
do {
if (*++wordebuf != ~0)
break;
} while(--retlen);
c->mtd->unpoint(c->mtd, ebuf, jeb->offset, c->sector_size);
if (retlen)
printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08tx\n",
*wordebuf, jeb->offset + c->sector_size-retlen*sizeof(*wordebuf));
return 0;
}
do_flash_read:
ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!ebuf) {
printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Refiling\n", jeb->offset);
@ -362,7 +401,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
{
size_t retlen;
int ret;
uint32_t bad_offset;
uint32_t uninitialized_var(bad_offset);
switch (jffs2_block_check_erase(c, jeb, &bad_offset)) {
case -EAGAIN: goto refile;
@ -417,6 +456,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL);
}
down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
c->erasing_size -= c->sector_size;
c->free_size += jeb->free_size;
@ -429,23 +469,28 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
c->nr_erasing_blocks--;
c->nr_free_blocks++;
spin_unlock(&c->erase_completion_lock);
up(&c->erase_free_sem);
wake_up(&c->erase_wait);
return;
filebad:
down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
/* Stick it on a list (any list) so erase_failed can take it
right off again. Silly, but shouldn't happen often. */
list_add(&jeb->list, &c->erasing_list);
spin_unlock(&c->erase_completion_lock);
up(&c->erase_free_sem);
jffs2_erase_failed(c, jeb, bad_offset);
return;
refile:
/* Stick it back on the list from whence it came and come back later */
jffs2_erase_pending_trigger(c);
down(&c->erase_free_sem);
spin_lock(&c->erase_completion_lock);
list_add(&jeb->list, &c->erase_complete_list);
spin_unlock(&c->erase_completion_lock);
up(&c->erase_free_sem);
return;
}

View File

@ -24,7 +24,7 @@
static int jffs2_flash_setup(struct jffs2_sb_info *c);
static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
{
struct jffs2_full_dnode *old_metadata, *new_metadata;
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
@ -36,10 +36,8 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
unsigned int ivalid;
uint32_t alloclen;
int ret;
D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
ret = inode_change_ok(inode, iattr);
if (ret)
return ret;
/* Special cases - we don't want more than one data node
for these types on the medium at any time. So setattr
@ -183,9 +181,14 @@ int jffs2_setattr(struct dentry *dentry, struct iattr *iattr)
{
int rc;
rc = inode_change_ok(dentry->d_inode, iattr);
if (rc)
return rc;
rc = jffs2_do_setattr(dentry->d_inode, iattr);
if (!rc && (iattr->ia_valid & ATTR_MODE))
rc = jffs2_acl_chmod(dentry->d_inode);
return rc;
}
@ -399,7 +402,8 @@ void jffs2_write_super (struct super_block *sb)
/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash,
fill in the raw_inode while you're at it. */
struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri)
struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri,
struct posix_acl **acl)
{
struct inode *inode;
struct super_block *sb = dir_i->i_sb;
@ -431,7 +435,23 @@ struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_i
} else {
ri->gid = cpu_to_je16(current->fsgid);
}
ri->mode = cpu_to_jemode(mode);
/* POSIX ACLs have to be processed now, at least partly.
The umask is only applied if there's no default ACL */
if (!S_ISLNK(mode)) {
*acl = jffs2_get_acl(dir_i, ACL_TYPE_DEFAULT);
if (IS_ERR(*acl)) {
make_bad_inode(inode);
iput(inode);
inode = (void *)*acl;
*acl = NULL;
return inode;
}
if (!(*acl))
mode &= ~current->fs->umask;
} else {
*acl = NULL;
}
ret = jffs2_do_new_inode (c, f, mode, ri);
if (ret) {
make_bad_inode(inode);

View File

@ -122,6 +122,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
struct jffs2_inode_cache *ic;
struct jffs2_eraseblock *jeb;
struct jffs2_raw_node_ref *raw;
uint32_t gcblock_dirty;
int ret = 0, inum, nlink;
int xattr = 0;
@ -236,6 +237,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
}
raw = jeb->gc_node;
gcblock_dirty = jeb->dirty_size;
while(ref_obsolete(raw)) {
D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw)));
@ -282,7 +284,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
} else {
ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic, raw);
}
goto release_sem;
goto test_gcnode;
}
#endif
@ -376,7 +378,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
if (ret != -EBADFD) {
spin_unlock(&c->inocache_lock);
goto release_sem;
goto test_gcnode;
}
/* Fall through if it wanted us to, with inocache_lock held */
@ -407,6 +409,12 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
jffs2_gc_release_inode(c, f);
test_gcnode:
if (jeb->dirty_size == gcblock_dirty && !ref_obsolete(jeb->gc_node)) {
/* Eep. This really should never happen. GC is broken */
printk(KERN_ERR "Error garbage collecting node at %08x!\n", ref_offset(jeb->gc_node));
ret = -ENOSPC;
}
release_sem:
up(&c->alloc_sem);
@ -556,7 +564,7 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
node = kmalloc(rawlen, GFP_KERNEL);
if (!node)
return -ENOMEM;
return -ENOMEM;
ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node);
if (!ret && retlen != rawlen)
@ -598,10 +606,15 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
goto bail;
}
if (strnlen(node->d.name, node->d.nsize) != node->d.nsize) {
printk(KERN_WARNING "Name in dirent node at 0x%08x contains zeroes\n", ref_offset(raw));
goto bail;
}
if (node->d.nsize) {
crc = crc32(0, node->d.name, node->d.nsize);
if (je32_to_cpu(node->d.name_crc) != crc) {
printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent ode at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
ref_offset(raw), je32_to_cpu(node->d.name_crc), crc);
goto bail;
}
@ -624,7 +637,7 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
if (ret || (retlen != rawlen)) {
printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
rawlen, phys_ofs, ret, retlen);
rawlen, phys_ofs, ret, retlen);
if (retlen) {
jffs2_add_physical_node_ref(c, phys_ofs | REF_OBSOLETE, rawlen, NULL);
} else {

View File

@ -69,6 +69,8 @@ struct jffs2_sb_info {
uint8_t resv_blocks_gctrigger; /* ... wake up the GC thread */
uint8_t resv_blocks_gcbad; /* ... pick a block from the bad_list to GC */
uint8_t resv_blocks_gcmerge; /* ... merge pages when garbage collecting */
/* Number of 'very dirty' blocks before we trigger immediate GC */
uint8_t vdirty_blocks_gctrigger;
uint32_t nospc_dirty_size;
@ -106,6 +108,9 @@ struct jffs2_sb_info {
uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */
#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
unsigned char *wbuf_verify; /* read-back buffer for verification */
#endif
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
unsigned char *wbuf; /* Write-behind buffer for NAND flash */
uint32_t wbuf_ofs;

View File

@ -127,7 +127,7 @@ static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_nod
return ((struct jffs2_inode_cache *)raw);
}
/* flash_offset & 3 always has to be zero, because nodes are
/* flash_offset & 3 always has to be zero, because nodes are
always aligned at 4 bytes. So we have a couple of extra bits
to play with, which indicate the node's status; see below: */
#define REF_UNCHECKED 0 /* We haven't yet checked the CRC or built its inode */

View File

@ -154,7 +154,7 @@ int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize,
while(ret == -EAGAIN) {
ret = jffs2_do_reserve_space(c, minsize, len, sumsize);
if (ret) {
D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
}
}
spin_unlock(&c->erase_completion_lock);
@ -423,7 +423,12 @@ struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info *c,
even after refiling c->nextblock */
if ((c->nextblock || ((ofs & 3) != REF_OBSOLETE))
&& (jeb != c->nextblock || (ofs & ~3) != jeb->offset + (c->sector_size - jeb->free_size))) {
printk(KERN_WARNING "argh. node added in wrong place\n");
printk(KERN_WARNING "argh. node added in wrong place at 0x%08x(%d)\n", ofs & ~3, ofs & 3);
if (c->nextblock)
printk(KERN_WARNING "nextblock 0x%08x", c->nextblock->offset);
else
printk(KERN_WARNING "No nextblock");
printk(", expected at %08x\n", jeb->offset + (c->sector_size - jeb->free_size));
return ERR_PTR(-EINVAL);
}
#endif
@ -717,6 +722,8 @@ int jffs2_thread_should_wake(struct jffs2_sb_info *c)
{
int ret = 0;
uint32_t dirty;
int nr_very_dirty = 0;
struct jffs2_eraseblock *jeb;
if (c->unchecked_size) {
D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
@ -738,8 +745,18 @@ int jffs2_thread_should_wake(struct jffs2_sb_info *c)
(dirty > c->nospc_dirty_size))
ret = 1;
D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n",
c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no"));
list_for_each_entry(jeb, &c->very_dirty_list, list) {
nr_very_dirty++;
if (nr_very_dirty == c->vdirty_blocks_gctrigger) {
ret = 1;
/* In debug mode, actually go through and count them all */
D1(continue);
break;
}
}
D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x, vdirty_blocks %d: %s\n",
c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, nr_very_dirty, ret?"yes":"no"));
return ret;
}

View File

@ -173,12 +173,15 @@ int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
extern const struct inode_operations jffs2_symlink_inode_operations;
/* fs.c */
struct posix_acl;
int jffs2_setattr (struct dentry *, struct iattr *);
int jffs2_do_setattr (struct inode *, struct iattr *);
void jffs2_read_inode (struct inode *);
void jffs2_clear_inode (struct inode *);
void jffs2_dirty_inode(struct inode *inode);
struct inode *jffs2_new_inode (struct inode *dir_i, int mode,
struct jffs2_raw_inode *ri);
struct jffs2_raw_inode *ri, struct posix_acl **acl);
int jffs2_statfs (struct dentry *, struct kstatfs *);
void jffs2_write_super (struct super_block *);
int jffs2_remount_fs (struct super_block *, int *, char *);

View File

@ -65,7 +65,7 @@ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info
err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer);
if (!err && retlen < tn->csize) {
JFFS2_WARNING("MTD point returned len too short: %zu instead of %u.\n", retlen, tn->csize);
c->mtd->unpoint(c->mtd, buffer, ofs, len);
c->mtd->unpoint(c->mtd, buffer, ofs, retlen);
} else if (err)
JFFS2_WARNING("MTD point failed: error code %d.\n", err);
else
@ -211,7 +211,7 @@ static void jffs2_kill_tn(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info *
* ordering.
*
* Returns 0 if the node was handled (including marking it obsolete)
* < 0 an if error occurred
* < 0 an if error occurred
*/
static int jffs2_add_tn_to_tree(struct jffs2_sb_info *c,
struct jffs2_readinode_info *rii,
@ -862,8 +862,8 @@ static inline int read_unknown(struct jffs2_sb_info *c, struct jffs2_raw_node_re
JFFS2_ERROR("REF_UNCHECKED but unknown node at %#08x\n",
ref_offset(ref));
JFFS2_ERROR("Node is {%04x,%04x,%08x,%08x}. Please report this error.\n",
je16_to_cpu(un->magic), je16_to_cpu(un->nodetype),
je32_to_cpu(un->totlen), je32_to_cpu(un->hdr_crc));
je16_to_cpu(un->magic), je16_to_cpu(un->nodetype),
je32_to_cpu(un->totlen), je32_to_cpu(un->hdr_crc));
jffs2_mark_node_obsolete(c, ref);
return 0;
}

View File

@ -101,7 +101,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
if (!ret && pointlen < c->mtd->size) {
/* Don't muck about if it won't let us point to the whole flash */
D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen));
c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
c->mtd->unpoint(c->mtd, flashbuf, 0, pointlen);
flashbuf = NULL;
}
if (ret)
@ -863,7 +863,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) {
case JFFS2_FEATURE_ROCOMPAT:
printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs);
c->flags |= JFFS2_SB_FLAG_RO;
c->flags |= JFFS2_SB_FLAG_RO;
if (!(jffs2_is_readonly(c)))
return -EROFS;
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
@ -1004,6 +1004,7 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
{
struct jffs2_full_dirent *fd;
struct jffs2_inode_cache *ic;
uint32_t checkedlen;
uint32_t crc;
int err;
@ -1024,12 +1025,18 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
pseudo_random += je32_to_cpu(rd->version);
fd = jffs2_alloc_full_dirent(rd->nsize+1);
/* Should never happen. Did. (OLPC trac #4184)*/
checkedlen = strnlen(rd->name, rd->nsize);
if (checkedlen < rd->nsize) {
printk(KERN_ERR "Dirent at %08x has zeroes in name. Truncating to %d chars\n",
ofs, checkedlen);
}
fd = jffs2_alloc_full_dirent(checkedlen+1);
if (!fd) {
return -ENOMEM;
}
memcpy(&fd->name, rd->name, rd->nsize);
fd->name[rd->nsize] = 0;
memcpy(&fd->name, rd->name, checkedlen);
fd->name[checkedlen] = 0;
crc = crc32(0, fd->name, rd->nsize);
if (crc != je32_to_cpu(rd->name_crc)) {
@ -1055,7 +1062,7 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
fd->next = NULL;
fd->version = je32_to_cpu(rd->version);
fd->ino = je32_to_cpu(rd->ino);
fd->nhash = full_name_hash(fd->name, rd->nsize);
fd->nhash = full_name_hash(fd->name, checkedlen);
fd->type = rd->type;
jffs2_add_fd_to_list(c, fd, &ic->scan_dents);

View File

@ -38,9 +38,9 @@ int jffs2_init_security(struct inode *inode, struct inode *dir)
}
rc = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, value, len, 0);
kfree(name);
kfree(value);
return rc;
kfree(name);
kfree(value);
return rc;
}
/* ---- XATTR Handler for "security.*" ----------------- */

View File

@ -2,10 +2,10 @@
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
* Zoltan Sogor <weth@inf.u-szeged.hu>,
* Patrik Kluba <pajko@halom.u-szeged.hu>,
* University of Szeged, Hungary
* 2006 KaiGai Kohei <kaigai@ak.jp.nec.com>
* Zoltan Sogor <weth@inf.u-szeged.hu>,
* Patrik Kluba <pajko@halom.u-szeged.hu>,
* University of Szeged, Hungary
* 2006 KaiGai Kohei <kaigai@ak.jp.nec.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
@ -429,6 +429,7 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras
case JFFS2_NODETYPE_DIRENT: {
struct jffs2_sum_dirent_flash *spd;
int checkedlen;
spd = sp;
dbg_summary("Dirent at 0x%08x-0x%08x\n",
@ -436,12 +437,25 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras
jeb->offset + je32_to_cpu(spd->offset) + je32_to_cpu(spd->totlen));
fd = jffs2_alloc_full_dirent(spd->nsize+1);
/* This should never happen, but https://dev.laptop.org/ticket/4184 */
checkedlen = strnlen(spd->name, spd->nsize);
if (!checkedlen) {
printk(KERN_ERR "Dirent at %08x has zero at start of name. Aborting mount.\n",
jeb->offset + je32_to_cpu(spd->offset));
return -EIO;
}
if (checkedlen < spd->nsize) {
printk(KERN_ERR "Dirent at %08x has zeroes in name. Truncating to %d chars\n",
jeb->offset + je32_to_cpu(spd->offset), checkedlen);
}
fd = jffs2_alloc_full_dirent(checkedlen+1);
if (!fd)
return -ENOMEM;
memcpy(&fd->name, spd->name, spd->nsize);
fd->name[spd->nsize] = 0;
memcpy(&fd->name, spd->name, checkedlen);
fd->name[checkedlen] = 0;
ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(spd->pino));
if (!ic) {
@ -455,7 +469,7 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras
fd->next = NULL;
fd->version = je32_to_cpu(spd->version);
fd->ino = je32_to_cpu(spd->ino);
fd->nhash = full_name_hash(fd->name, spd->nsize);
fd->nhash = full_name_hash(fd->name, checkedlen);
fd->type = spd->type;
jffs2_add_fd_to_list(c, fd, &ic->scan_dents);

View File

@ -2,9 +2,9 @@
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright © 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
* Zoltan Sogor <weth@inf.u-szeged.hu>,
* Patrik Kluba <pajko@halom.u-szeged.hu>,
* University of Szeged, Hungary
* Zoltan Sogor <weth@inf.u-szeged.hu>,
* Patrik Kluba <pajko@halom.u-szeged.hu>,
* University of Szeged, Hungary
*
* For licensing information, see the file 'LICENCE' in this directory.
*

View File

@ -220,6 +220,47 @@ static struct jffs2_raw_node_ref **jffs2_incore_replace_raw(struct jffs2_sb_info
return NULL;
}
#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
static int jffs2_verify_write(struct jffs2_sb_info *c, unsigned char *buf,
uint32_t ofs)
{
int ret;
size_t retlen;
char *eccstr;
ret = c->mtd->read(c->mtd, ofs, c->wbuf_pagesize, &retlen, c->wbuf_verify);
if (ret && ret != -EUCLEAN && ret != -EBADMSG) {
printk(KERN_WARNING "jffs2_verify_write(): Read back of page at %08x failed: %d\n", c->wbuf_ofs, ret);
return ret;
} else if (retlen != c->wbuf_pagesize) {
printk(KERN_WARNING "jffs2_verify_write(): Read back of page at %08x gave short read: %zd not %d.\n", ofs, retlen, c->wbuf_pagesize);
return -EIO;
}
if (!memcmp(buf, c->wbuf_verify, c->wbuf_pagesize))
return 0;
if (ret == -EUCLEAN)
eccstr = "corrected";
else if (ret == -EBADMSG)
eccstr = "correction failed";
else
eccstr = "OK or unused";
printk(KERN_WARNING "Write verify error (ECC %s) at %08x. Wrote:\n",
eccstr, c->wbuf_ofs);
print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
c->wbuf, c->wbuf_pagesize, 0);
printk(KERN_WARNING "Read back:\n");
print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 16, 1,
c->wbuf_verify, c->wbuf_pagesize, 0);
return -EIO;
}
#else
#define jffs2_verify_write(c,b,o) (0)
#endif
/* Recover from failure to write wbuf. Recover the nodes up to the
* wbuf, not the one which we were starting to try to write. */
@ -380,7 +421,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
ret = c->mtd->write(c->mtd, ofs, towrite, &retlen,
rewrite_buf);
if (ret || retlen != towrite) {
if (ret || retlen != towrite || jffs2_verify_write(c, rewrite_buf, ofs)) {
/* Argh. We tried. Really we did. */
printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n");
kfree(buf);
@ -587,15 +628,16 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf);
if (ret || retlen != c->wbuf_pagesize) {
if (ret)
printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n",ret);
else {
printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n",
retlen, c->wbuf_pagesize);
ret = -EIO;
}
if (ret) {
printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n", ret);
goto wfail;
} else if (retlen != c->wbuf_pagesize) {
printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n",
retlen, c->wbuf_pagesize);
ret = -EIO;
goto wfail;
} else if ((ret = jffs2_verify_write(c, c->wbuf, c->wbuf_ofs))) {
wfail:
jffs2_wbuf_recover(c);
return ret;
@ -966,8 +1008,8 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re
#define NR_OOB_SCAN_PAGES 4
/* For historical reasons we use only 12 bytes for OOB clean marker */
#define OOB_CM_SIZE 12
/* For historical reasons we use only 8 bytes for OOB clean marker */
#define OOB_CM_SIZE 8
static const struct jffs2_unknown_node oob_cleanmarker =
{
@ -1021,8 +1063,8 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c,
/*
* Check for a valid cleanmarker.
* Returns: 0 if a valid cleanmarker was found
* 1 if no cleanmarker was found
* negative error code if an error occurred
* 1 if no cleanmarker was found
* negative error code if an error occurred
*/
int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c,
struct jffs2_eraseblock *jeb)
@ -1138,11 +1180,22 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
return -ENOMEM;
}
#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
c->wbuf_verify = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
if (!c->wbuf_verify) {
kfree(c->oobbuf);
kfree(c->wbuf);
return -ENOMEM;
}
#endif
return 0;
}
void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
{
#ifdef CONFIG_JFFS2_FS_WBUF_VERIFY
kfree(c->wbuf_verify);
#endif
kfree(c->wbuf);
kfree(c->oobbuf);
}

View File

@ -215,6 +215,17 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
BUG();
});
if (strnlen(name, namelen) != namelen) {
/* This should never happen, but seems to have done on at least one
occasion: https://dev.laptop.org/ticket/4184 */
printk(KERN_CRIT "Error in jffs2_write_dirent() -- name contains zero bytes!\n");
printk(KERN_CRIT "Directory inode #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x\n",
je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino),
je32_to_cpu(rd->name_crc));
WARN_ON(1);
return ERR_PTR(-EIO);
}
vecs[0].iov_base = rd;
vecs[0].iov_len = sizeof(*rd);
vecs[1].iov_base = (unsigned char *)name;
@ -226,7 +237,7 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
fd->version = je32_to_cpu(rd->version);
fd->ino = je32_to_cpu(rd->ino);
fd->nhash = full_name_hash(name, strlen(name));
fd->nhash = full_name_hash(name, namelen);
fd->type = rd->type;
memcpy(fd->name, name, namelen);
fd->name[namelen]=0;

View File

@ -75,7 +75,7 @@ extern void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c);
extern void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c);
extern struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c,
uint32_t xid, uint32_t version);
uint32_t xid, uint32_t version);
extern void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
extern void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);

View File

@ -17,7 +17,7 @@
#include "nodelist.h"
static int jffs2_user_getxattr(struct inode *inode, const char *name,
void *buffer, size_t size)
void *buffer, size_t size)
{
if (!strcmp(name, ""))
return -EINVAL;
@ -25,7 +25,7 @@ static int jffs2_user_getxattr(struct inode *inode, const char *name,
}
static int jffs2_user_setxattr(struct inode *inode, const char *name, const void *buffer,
size_t size, int flags)
size_t size, int flags)
{
if (!strcmp(name, ""))
return -EINVAL;

View File

@ -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

View File

@ -0,0 +1,47 @@
/* linux/include/asm-blackfin/nand.h
*
* Copyright (c) 2007 Analog Devices, Inc.
* Bryan Wu <bryan.wu@analog.com>
*
* BF5XX - NAND flash controller platfrom_device info
*
* 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.
*/
/* struct bf5xx_nand_platform
*
* define a interface between platfrom board specific code and
* bf54x NFC driver.
*
* nr_partitions = number of partitions pointed to be partitoons (or zero)
* partitions = mtd partition list
*/
#define NFC_PG_SIZE_256 0
#define NFC_PG_SIZE_512 1
#define NFC_PG_SIZE_OFFSET 9
#define NFC_NWIDTH_8 0
#define NFC_NWIDTH_16 1
#define NFC_NWIDTH_OFFSET 8
#define NFC_RDDLY_OFFSET 4
#define NFC_WRDLY_OFFSET 0
#define NFC_STAT_NBUSY 1
struct bf5xx_nand_platform {
/* NAND chip information */
unsigned short page_size;
unsigned short data_width;
/* RD/WR strobe delay timing information, all times in SCLK cycles */
unsigned short rd_dly;
unsigned short wr_dly;
/* NAND MTD partition information */
int nr_partitions;
struct mtd_partition *partitions;
};

View File

@ -46,6 +46,7 @@
#define JFFS2_COMPR_COPY 0x04
#define JFFS2_COMPR_DYNRUBIN 0x05
#define JFFS2_COMPR_ZLIB 0x06
#define JFFS2_COMPR_LZO 0x07
/* Compatibility flags. */
#define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */
#define JFFS2_NODE_ACCURATE 0x2000

View File

@ -57,6 +57,15 @@
#define cfi_interleave_is_8(cfi) (0)
#endif
#ifndef cfi_interleave
#warning No CONFIG_MTD_CFI_Ix selected. No NOR chip support can work.
static inline int cfi_interleave(void *cfi)
{
BUG();
return 0;
}
#endif
static inline int cfi_interleave_supported(int i)
{
switch (i) {

View File

@ -40,6 +40,7 @@ typedef enum {
FL_POINT,
FL_XIP_WHILE_ERASING,
FL_XIP_WHILE_WRITING,
FL_SHUTDOWN,
FL_UNKNOWN
} flstate_t;

View File

@ -125,7 +125,15 @@
#endif
#ifndef map_bankwidth
#error "No bus width supported. What's the point?"
#warning "No CONFIG_MTD_MAP_BANK_WIDTH_xx selected. No NOR chip support can work"
static inline int map_bankwidth(void *map)
{
BUG();
return 0;
}
#define map_bankwidth_is_large(map) (0)
#define map_words(map) (0)
#define MAX_MAP_BANKWIDTH 1
#endif
static inline int map_bankwidth_supported(int w)

View File

@ -133,6 +133,13 @@ struct mtd_info {
int numeraseregions;
struct mtd_erase_region_info *eraseregions;
/*
* Erase is an asynchronous operation. Device drivers are supposed
* to call instr->callback() whenever the operation completes, even
* if it completes with a failure.
* Callers are supposed to pass a callback function and wait for it
* to be called before writing to the block.
*/
int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
/* This stuff for eXecute-In-Place */

View File

@ -432,6 +432,7 @@ struct nand_chip {
#define NAND_MFR_STMICRO 0x20
#define NAND_MFR_HYNIX 0xad
#define NAND_MFR_MICRON 0x2c
#define NAND_MFR_AMD 0x01
/**
* struct nand_flash_dev - NAND Flash Device ID Structure

View File

@ -60,6 +60,7 @@ struct onenand_bufferram {
* @erase_shift: [INTERN] number of address bits in a block
* @page_shift: [INTERN] number of address bits in a page
* @page_mask: [INTERN] a page per block mask
* @writesize: [INTERN] a real page size
* @bufferram_index: [INTERN] BufferRAM index
* @bufferram: [INTERN] BufferRAM info
* @readw: [REPLACEABLE] hardware specific function for read short
@ -100,6 +101,7 @@ struct onenand_chip {
unsigned int erase_shift;
unsigned int page_shift;
unsigned int page_mask;
unsigned int writesize;
unsigned int bufferram_index;
struct onenand_bufferram bufferram[MAX_BUFFERRAM];
@ -140,6 +142,8 @@ struct onenand_chip {
#define ONENAND_NEXT_BUFFERRAM(this) (this->bufferram_index ^ 1)
#define ONENAND_SET_NEXT_BUFFERRAM(this) (this->bufferram_index ^= 1)
#define ONENAND_SET_PREV_BUFFERRAM(this) (this->bufferram_index ^= 1)
#define ONENAND_SET_BUFFERRAM0(this) (this->bufferram_index = 0)
#define ONENAND_SET_BUFFERRAM1(this) (this->bufferram_index = 1)
#define ONENAND_GET_SYS_CFG1(this) \
(this->read_word(this->base + ONENAND_REG_SYS_CFG1))
@ -149,6 +153,13 @@ struct onenand_chip {
#define ONENAND_IS_DDP(this) \
(this->device_id & ONENAND_DEVICE_IS_DDP)
#ifdef CONFIG_MTD_ONENAND_2X_PROGRAM
#define ONENAND_IS_2PLANE(this) \
(this->options & ONENAND_HAS_2PLANE)
#else
#define ONENAND_IS_2PLANE(this) (0)
#endif
/* Check byte access in OneNAND */
#define ONENAND_CHECK_BYTE_ACCESS(addr) (addr & 0x1)
@ -157,6 +168,7 @@ struct onenand_chip {
*/
#define ONENAND_HAS_CONT_LOCK (0x0001)
#define ONENAND_HAS_UNLOCK_ALL (0x0002)
#define ONENAND_HAS_2PLANE (0x0004)
#define ONENAND_PAGEBUF_ALLOC (0x1000)
#define ONENAND_OOBBUF_ALLOC (0x2000)

View File

@ -74,6 +74,8 @@
#define ONENAND_DEVICE_DENSITY_512Mb (0x002)
#define ONENAND_DEVICE_DENSITY_1Gb (0x003)
#define ONENAND_DEVICE_DENSITY_2Gb (0x004)
#define ONENAND_DEVICE_DENSITY_4Gb (0x005)
/*
* Version ID Register F002h (R)
@ -111,6 +113,8 @@
#define ONENAND_CMD_READOOB (0x13)
#define ONENAND_CMD_PROG (0x80)
#define ONENAND_CMD_PROGOOB (0x1A)
#define ONENAND_CMD_2X_PROG (0x7D)
#define ONENAND_CMD_2X_CACHE_PROG (0x7F)
#define ONENAND_CMD_UNLOCK (0x23)
#define ONENAND_CMD_LOCK (0x2A)
#define ONENAND_CMD_LOCK_TIGHT (0x2C)