forked from luck/tmp_suning_uos_patched
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:
commit
c8c55bcb43
|
@ -275,16 +275,13 @@ int __init board_init (void)
|
|||
int err = 0;
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
board_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL);
|
||||
board_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
|
||||
if (!board_mtd) {
|
||||
printk ("Unable to allocate NAND MTD device structure.\n");
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Initialize structures */
|
||||
memset ((char *) board_mtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip));
|
||||
|
||||
/* map physical adress */
|
||||
baseaddr = (unsigned long)ioremap(CHIP_PHYSICAL_ADDRESS, 1024);
|
||||
if(!baseaddr){
|
||||
|
|
|
@ -278,6 +278,14 @@ config SSFDC
|
|||
This enables read only access to SmartMedia formatted NAND
|
||||
flash. You can mount it with FAT file system.
|
||||
|
||||
config MTD_OOPS
|
||||
tristate "Log panic/oops to an MTD buffer"
|
||||
depends on MTD
|
||||
help
|
||||
This enables panic and oops messages to be logged to a circular
|
||||
buffer in a flash partition where it can be read back at some
|
||||
later point.
|
||||
|
||||
source "drivers/mtd/chips/Kconfig"
|
||||
|
||||
source "drivers/mtd/maps/Kconfig"
|
||||
|
|
|
@ -22,6 +22,7 @@ obj-$(CONFIG_NFTL) += nftl.o
|
|||
obj-$(CONFIG_INFTL) += inftl.o
|
||||
obj-$(CONFIG_RFD_FTL) += rfd_ftl.o
|
||||
obj-$(CONFIG_SSFDC) += ssfdc.o
|
||||
obj-$(CONFIG_MTD_OOPS) += mtdoops.o
|
||||
|
||||
nftl-objs := nftlcore.o nftlmount.o
|
||||
inftl-objs := inftlcore.o inftlmount.o
|
||||
|
|
|
@ -526,7 +526,7 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd,
|
|||
struct cfi_pri_intelext *extp = cfi->cmdset_priv;
|
||||
|
||||
/*
|
||||
* Probing of multi-partition flash ships.
|
||||
* Probing of multi-partition flash chips.
|
||||
*
|
||||
* To support multiple partitions when available, we simply arrange
|
||||
* for each of them to have their own flchip structure even if they
|
||||
|
@ -653,7 +653,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
|
|||
resettime:
|
||||
timeo = jiffies + HZ;
|
||||
retry:
|
||||
if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE)) {
|
||||
if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE || mode == FL_SHUTDOWN)) {
|
||||
/*
|
||||
* OK. We have possibility for contension on the write/erase
|
||||
* operations which are global to the real chip and not per
|
||||
|
@ -798,6 +798,9 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
|
|||
if (mode == FL_READY && chip->oldstate == FL_READY)
|
||||
return 0;
|
||||
|
||||
case FL_SHUTDOWN:
|
||||
/* The machine is rebooting now,so no one can get chip anymore */
|
||||
return -EIO;
|
||||
default:
|
||||
sleep:
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
|
@ -1166,28 +1169,34 @@ static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, si
|
|||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
unsigned long ofs;
|
||||
unsigned long ofs, last_end = 0;
|
||||
int chipnum;
|
||||
int ret = 0;
|
||||
|
||||
if (!map->virt || (from + len > mtd->size))
|
||||
return -EINVAL;
|
||||
|
||||
*mtdbuf = (void *)map->virt + from;
|
||||
*retlen = 0;
|
||||
|
||||
/* Now lock the chip(s) to POINT state */
|
||||
|
||||
/* ofs: offset within the first chip that the first read should start */
|
||||
chipnum = (from >> cfi->chipshift);
|
||||
ofs = from - (chipnum << cfi->chipshift);
|
||||
|
||||
*mtdbuf = (void *)map->virt + cfi->chips[chipnum].start + ofs;
|
||||
*retlen = 0;
|
||||
|
||||
while (len) {
|
||||
unsigned long thislen;
|
||||
|
||||
if (chipnum >= cfi->numchips)
|
||||
break;
|
||||
|
||||
/* We cannot point across chips that are virtually disjoint */
|
||||
if (!last_end)
|
||||
last_end = cfi->chips[chipnum].start;
|
||||
else if (cfi->chips[chipnum].start != last_end)
|
||||
break;
|
||||
|
||||
if ((len + ofs -1) >> cfi->chipshift)
|
||||
thislen = (1<<cfi->chipshift) - ofs;
|
||||
else
|
||||
|
@ -1201,6 +1210,7 @@ static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, si
|
|||
len -= thislen;
|
||||
|
||||
ofs = 0;
|
||||
last_end += 1 << cfi->chipshift;
|
||||
chipnum++;
|
||||
}
|
||||
return 0;
|
||||
|
@ -1780,7 +1790,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
|
||||
static int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
|
||||
{
|
||||
unsigned long ofs, len;
|
||||
int ret;
|
||||
|
@ -1930,7 +1940,7 @@ static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
|
|||
printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
|
||||
__FUNCTION__, ofs, len);
|
||||
cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
|
||||
ofs, len, 0);
|
||||
ofs, len, NULL);
|
||||
#endif
|
||||
|
||||
ret = cfi_varsize_frob(mtd, do_xxlock_oneblock,
|
||||
|
@ -1940,7 +1950,7 @@ static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
|
|||
printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
|
||||
__FUNCTION__, ret);
|
||||
cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
|
||||
ofs, len, 0);
|
||||
ofs, len, NULL);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
|
@ -1954,7 +1964,7 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
|
|||
printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
|
||||
__FUNCTION__, ofs, len);
|
||||
cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
|
||||
ofs, len, 0);
|
||||
ofs, len, NULL);
|
||||
#endif
|
||||
|
||||
ret = cfi_varsize_frob(mtd, do_xxlock_oneblock,
|
||||
|
@ -1964,7 +1974,7 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
|
|||
printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
|
||||
__FUNCTION__, ret);
|
||||
cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
|
||||
ofs, len, 0);
|
||||
ofs, len, NULL);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
|
@ -2255,7 +2265,7 @@ static void cfi_intelext_save_locks(struct mtd_info *mtd)
|
|||
adr = region->offset + block * len;
|
||||
|
||||
status = cfi_varsize_frob(mtd,
|
||||
do_getlockstatus_oneblock, adr, len, 0);
|
||||
do_getlockstatus_oneblock, adr, len, NULL);
|
||||
if (status)
|
||||
set_bit(block, region->lockmap);
|
||||
else
|
||||
|
@ -2402,10 +2412,10 @@ static int cfi_intelext_reset(struct mtd_info *mtd)
|
|||
and switch to array mode so any bootloader in
|
||||
flash is accessible for soft reboot. */
|
||||
spin_lock(chip->mutex);
|
||||
ret = get_chip(map, chip, chip->start, FL_SYNCING);
|
||||
ret = get_chip(map, chip, chip->start, FL_SHUTDOWN);
|
||||
if (!ret) {
|
||||
map_write(map, CMD(0xff), chip->start);
|
||||
chip->state = FL_READY;
|
||||
chip->state = FL_SHUTDOWN;
|
||||
}
|
||||
spin_unlock(chip->mutex);
|
||||
}
|
||||
|
|
|
@ -1609,7 +1609,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
|
|||
}
|
||||
|
||||
|
||||
int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
|
||||
static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
|
||||
{
|
||||
unsigned long ofs, len;
|
||||
int ret;
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
|
@ -70,6 +69,7 @@
|
|||
|
||||
/* Fujitsu */
|
||||
#define MBM29F040C 0x00A4
|
||||
#define MBM29F800BA 0x2258
|
||||
#define MBM29LV650UE 0x22D7
|
||||
#define MBM29LV320TE 0x22F6
|
||||
#define MBM29LV320BE 0x22F9
|
||||
|
@ -129,6 +129,7 @@
|
|||
#define LH28F640BF 0x00b0
|
||||
|
||||
/* ST - www.st.com */
|
||||
#define M29F800AB 0x0058
|
||||
#define M29W800DT 0x00D7
|
||||
#define M29W800DB 0x005B
|
||||
#define M29W160DT 0x22C4
|
||||
|
@ -644,6 +645,23 @@ static const struct amd_flash_info jedec_table[] = {
|
|||
.regions = {
|
||||
ERASEINFO(0x10000,8)
|
||||
}
|
||||
}, {
|
||||
.mfr_id = MANUFACTURER_FUJITSU,
|
||||
.dev_id = MBM29F800BA,
|
||||
.name = "Fujitsu MBM29F800BA",
|
||||
.uaddr = {
|
||||
[0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
|
||||
[1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
|
||||
},
|
||||
.DevSize = SIZE_1MiB,
|
||||
.CmdSet = P_ID_AMD_STD,
|
||||
.NumEraseRegions= 4,
|
||||
.regions = {
|
||||
ERASEINFO(0x04000,1),
|
||||
ERASEINFO(0x02000,2),
|
||||
ERASEINFO(0x08000,1),
|
||||
ERASEINFO(0x10000,15),
|
||||
}
|
||||
}, {
|
||||
.mfr_id = MANUFACTURER_FUJITSU,
|
||||
.dev_id = MBM29LV650UE,
|
||||
|
@ -1510,6 +1528,23 @@ static const struct amd_flash_info jedec_table[] = {
|
|||
ERASEINFO(0x1000,256)
|
||||
}
|
||||
|
||||
}, {
|
||||
.mfr_id = MANUFACTURER_ST,
|
||||
.dev_id = M29F800AB,
|
||||
.name = "ST M29F800AB",
|
||||
.uaddr = {
|
||||
[0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
|
||||
[1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
|
||||
},
|
||||
.DevSize = SIZE_1MiB,
|
||||
.CmdSet = P_ID_AMD_STD,
|
||||
.NumEraseRegions= 4,
|
||||
.regions = {
|
||||
ERASEINFO(0x04000,1),
|
||||
ERASEINFO(0x02000,2),
|
||||
ERASEINFO(0x08000,1),
|
||||
ERASEINFO(0x10000,15),
|
||||
}
|
||||
}, {
|
||||
.mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */
|
||||
.dev_id = M29W800DT,
|
||||
|
|
|
@ -60,21 +60,22 @@ config MTD_DATAFLASH
|
|||
Sometimes DataFlash chips are packaged inside MMC-format
|
||||
cards; at this writing, the MMC stack won't handle those.
|
||||
|
||||
config MTD_DATAFLASH26
|
||||
tristate "AT91RM9200 DataFlash AT26xxx"
|
||||
depends on MTD && ARCH_AT91RM9200 && AT91_SPI
|
||||
help
|
||||
This enables access to the DataFlash chip (AT26xxx) on an
|
||||
AT91RM9200-based board.
|
||||
If you have such a board and such a DataFlash, say 'Y'.
|
||||
|
||||
config MTD_M25P80
|
||||
tristate "Support for M25 SPI Flash"
|
||||
tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
|
||||
depends on SPI_MASTER && EXPERIMENTAL
|
||||
help
|
||||
This enables access to ST M25P80 and similar SPI flash chips,
|
||||
used for program and data storage. Set up your spi devices
|
||||
with the right board-specific platform data.
|
||||
This enables access to most modern SPI flash chips, used for
|
||||
program and data storage. Series supported include Atmel AT26DF,
|
||||
Spansion S25SL, SST 25VF, ST M25P, and Winbond W25X. Other chips
|
||||
are supported as well. See the driver source for the current list,
|
||||
or to add other chips.
|
||||
|
||||
Note that the original DataFlash chips (AT45 series, not AT26DF),
|
||||
need an entirely different driver.
|
||||
|
||||
Set up your spi devices with the right board-specific platform data,
|
||||
if you want to specify device partitioning or to use a device which
|
||||
doesn't support the JEDEC ID instruction.
|
||||
|
||||
config MTD_SLRAM
|
||||
tristate "Uncached system RAM"
|
||||
|
|
|
@ -16,5 +16,4 @@ obj-$(CONFIG_MTD_MTDRAM) += mtdram.o
|
|||
obj-$(CONFIG_MTD_LART) += lart.o
|
||||
obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o
|
||||
obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o
|
||||
obj-$(CONFIG_MTD_DATAFLASH26) += at91_dataflash26.o
|
||||
obj-$(CONFIG_MTD_M25P80) += m25p80.o
|
||||
|
|
|
@ -1,485 +0,0 @@
|
|||
/*
|
||||
* Atmel DataFlash driver for Atmel AT91RM9200 (Thunder)
|
||||
* This is a largely modified version of at91_dataflash.c that
|
||||
* supports AT26xxx dataflash chips. The original driver supports
|
||||
* AT45xxx chips.
|
||||
*
|
||||
* Note: This driver was only tested with an AT26F004. It should be
|
||||
* easy to make it work with other AT26xxx dataflash devices, though.
|
||||
*
|
||||
* Copyright (C) 2007 Hans J. Koch <hjk@linutronix.de>
|
||||
* original Copyright (C) SAN People (Pty) Ltd
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
||||
#include <asm/arch/at91_spi.h>
|
||||
|
||||
#define DATAFLASH_MAX_DEVICES 4 /* max number of dataflash devices */
|
||||
|
||||
#define MANUFACTURER_ID_ATMEL 0x1F
|
||||
|
||||
/* command codes */
|
||||
|
||||
#define AT26_OP_READ_STATUS 0x05
|
||||
#define AT26_OP_READ_DEV_ID 0x9F
|
||||
#define AT26_OP_ERASE_PAGE_4K 0x20
|
||||
#define AT26_OP_READ_ARRAY_FAST 0x0B
|
||||
#define AT26_OP_SEQUENTIAL_WRITE 0xAF
|
||||
#define AT26_OP_WRITE_ENABLE 0x06
|
||||
#define AT26_OP_WRITE_DISABLE 0x04
|
||||
#define AT26_OP_SECTOR_PROTECT 0x36
|
||||
#define AT26_OP_SECTOR_UNPROTECT 0x39
|
||||
|
||||
/* status register bits */
|
||||
|
||||
#define AT26_STATUS_BUSY 0x01
|
||||
#define AT26_STATUS_WRITE_ENABLE 0x02
|
||||
|
||||
struct dataflash_local
|
||||
{
|
||||
int spi; /* SPI chip-select number */
|
||||
unsigned int page_size; /* number of bytes per page */
|
||||
};
|
||||
|
||||
|
||||
/* Detected DataFlash devices */
|
||||
static struct mtd_info* mtd_devices[DATAFLASH_MAX_DEVICES];
|
||||
static int nr_devices = 0;
|
||||
|
||||
/* Allocate a single SPI transfer descriptor. We're assuming that if multiple
|
||||
SPI transfers occur at the same time, spi_access_bus() will serialize them.
|
||||
If this is not valid, then either (i) each dataflash 'priv' structure
|
||||
needs it's own transfer descriptor, (ii) we lock this one, or (iii) use
|
||||
another mechanism. */
|
||||
static struct spi_transfer_list* spi_transfer_desc;
|
||||
|
||||
/*
|
||||
* Perform a SPI transfer to access the DataFlash device.
|
||||
*/
|
||||
static int do_spi_transfer(int nr, char* tx, int tx_len, char* rx, int rx_len,
|
||||
char* txnext, int txnext_len, char* rxnext, int rxnext_len)
|
||||
{
|
||||
struct spi_transfer_list* list = spi_transfer_desc;
|
||||
|
||||
list->tx[0] = tx; list->txlen[0] = tx_len;
|
||||
list->rx[0] = rx; list->rxlen[0] = rx_len;
|
||||
|
||||
list->tx[1] = txnext; list->txlen[1] = txnext_len;
|
||||
list->rx[1] = rxnext; list->rxlen[1] = rxnext_len;
|
||||
|
||||
list->nr_transfers = nr;
|
||||
/* Note: spi_transfer() always returns 0, there are no error checks */
|
||||
return spi_transfer(list);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the status of the DataFlash device.
|
||||
*/
|
||||
static unsigned char at91_dataflash26_status(void)
|
||||
{
|
||||
unsigned char command[2];
|
||||
|
||||
command[0] = AT26_OP_READ_STATUS;
|
||||
command[1] = 0;
|
||||
|
||||
do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0);
|
||||
|
||||
return command[1];
|
||||
}
|
||||
|
||||
/*
|
||||
* Poll the DataFlash device until it is READY.
|
||||
*/
|
||||
static unsigned char at91_dataflash26_waitready(void)
|
||||
{
|
||||
unsigned char status;
|
||||
|
||||
while (1) {
|
||||
status = at91_dataflash26_status();
|
||||
if (!(status & AT26_STATUS_BUSY))
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable/disable write access
|
||||
*/
|
||||
static void at91_dataflash26_write_enable(int enable)
|
||||
{
|
||||
unsigned char cmd[2];
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "write_enable: enable=%i\n", enable);
|
||||
|
||||
if (enable)
|
||||
cmd[0] = AT26_OP_WRITE_ENABLE;
|
||||
else
|
||||
cmd[0] = AT26_OP_WRITE_DISABLE;
|
||||
cmd[1] = 0;
|
||||
|
||||
do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Protect/unprotect sector
|
||||
*/
|
||||
static void at91_dataflash26_sector_protect(loff_t addr, int protect)
|
||||
{
|
||||
unsigned char cmd[4];
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "sector_protect: addr=0x%06x prot=%d\n",
|
||||
addr, protect);
|
||||
|
||||
if (protect)
|
||||
cmd[0] = AT26_OP_SECTOR_PROTECT;
|
||||
else
|
||||
cmd[0] = AT26_OP_SECTOR_UNPROTECT;
|
||||
cmd[1] = (addr & 0x00FF0000) >> 16;
|
||||
cmd[2] = (addr & 0x0000FF00) >> 8;
|
||||
cmd[3] = (addr & 0x000000FF);
|
||||
|
||||
do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Erase blocks of flash.
|
||||
*/
|
||||
static int at91_dataflash26_erase(struct mtd_info *mtd,
|
||||
struct erase_info *instr)
|
||||
{
|
||||
struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
|
||||
unsigned char cmd[4];
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL1, "dataflash_erase: addr=0x%06x len=%i\n",
|
||||
instr->addr, instr->len);
|
||||
|
||||
/* Sanity checks */
|
||||
if (priv->page_size != 4096)
|
||||
return -EINVAL; /* Can't handle other sizes at the moment */
|
||||
|
||||
if ( ((instr->len % mtd->erasesize) != 0)
|
||||
|| ((instr->len % priv->page_size) != 0)
|
||||
|| ((instr->addr % priv->page_size) != 0)
|
||||
|| ((instr->addr + instr->len) > mtd->size))
|
||||
return -EINVAL;
|
||||
|
||||
spi_access_bus(priv->spi);
|
||||
|
||||
while (instr->len > 0) {
|
||||
at91_dataflash26_write_enable(1);
|
||||
at91_dataflash26_sector_protect(instr->addr, 0);
|
||||
at91_dataflash26_write_enable(1);
|
||||
cmd[0] = AT26_OP_ERASE_PAGE_4K;
|
||||
cmd[1] = (instr->addr & 0x00FF0000) >> 16;
|
||||
cmd[2] = (instr->addr & 0x0000FF00) >> 8;
|
||||
cmd[3] = (instr->addr & 0x000000FF);
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "ERASE: (0x%02x) 0x%02x 0x%02x"
|
||||
"0x%02x\n",
|
||||
cmd[0], cmd[1], cmd[2], cmd[3]);
|
||||
|
||||
do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0);
|
||||
at91_dataflash26_waitready();
|
||||
|
||||
instr->addr += priv->page_size; /* next page */
|
||||
instr->len -= priv->page_size;
|
||||
}
|
||||
|
||||
at91_dataflash26_write_enable(0);
|
||||
spi_release_bus(priv->spi);
|
||||
|
||||
/* Inform MTD subsystem that erase is complete */
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
if (instr->callback)
|
||||
instr->callback(instr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read from the DataFlash device.
|
||||
* from : Start offset in flash device
|
||||
* len : Number of bytes to read
|
||||
* retlen : Number of bytes actually read
|
||||
* buf : Buffer that will receive data
|
||||
*/
|
||||
static int at91_dataflash26_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
|
||||
unsigned char cmd[5];
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL1, "dataflash_read: %lli .. %lli\n",
|
||||
from, from+len);
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
/* Sanity checks */
|
||||
if (!len)
|
||||
return 0;
|
||||
if (from + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
cmd[0] = AT26_OP_READ_ARRAY_FAST;
|
||||
cmd[1] = (from & 0x00FF0000) >> 16;
|
||||
cmd[2] = (from & 0x0000FF00) >> 8;
|
||||
cmd[3] = (from & 0x000000FF);
|
||||
/* cmd[4] is a "Don't care" byte */
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "READ: (0x%02x) 0x%02x 0x%02x 0x%02x\n",
|
||||
cmd[0], cmd[1], cmd[2], cmd[3]);
|
||||
|
||||
spi_access_bus(priv->spi);
|
||||
do_spi_transfer(2, cmd, 5, cmd, 5, buf, len, buf, len);
|
||||
spi_release_bus(priv->spi);
|
||||
|
||||
*retlen = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write to the DataFlash device.
|
||||
* to : Start offset in flash device
|
||||
* len : Number of bytes to write
|
||||
* retlen : Number of bytes actually written
|
||||
* buf : Buffer containing the data
|
||||
*/
|
||||
static int at91_dataflash26_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
struct dataflash_local *priv = (struct dataflash_local *) mtd->priv;
|
||||
unsigned int addr, buf_index = 0;
|
||||
int ret = -EIO, sector, last_sector;
|
||||
unsigned char status, cmd[5];
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL1, "dataflash_write: %lli .. %lli\n", to, to+len);
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
/* Sanity checks */
|
||||
if (!len)
|
||||
return 0;
|
||||
if (to + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
spi_access_bus(priv->spi);
|
||||
|
||||
addr = to;
|
||||
last_sector = -1;
|
||||
|
||||
while (buf_index < len) {
|
||||
sector = addr / priv->page_size;
|
||||
/* Write first byte if a new sector begins */
|
||||
if (sector != last_sector) {
|
||||
at91_dataflash26_write_enable(1);
|
||||
at91_dataflash26_sector_protect(addr, 0);
|
||||
at91_dataflash26_write_enable(1);
|
||||
|
||||
/* Program first byte of a new sector */
|
||||
cmd[0] = AT26_OP_SEQUENTIAL_WRITE;
|
||||
cmd[1] = (addr & 0x00FF0000) >> 16;
|
||||
cmd[2] = (addr & 0x0000FF00) >> 8;
|
||||
cmd[3] = (addr & 0x000000FF);
|
||||
cmd[4] = buf[buf_index++];
|
||||
do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0);
|
||||
status = at91_dataflash26_waitready();
|
||||
addr++;
|
||||
/* On write errors, the chip resets the write enable
|
||||
flag. This also happens after the last byte of a
|
||||
sector is successfully programmed. */
|
||||
if ( ( !(status & AT26_STATUS_WRITE_ENABLE))
|
||||
&& ((addr % priv->page_size) != 0) ) {
|
||||
DEBUG(MTD_DEBUG_LEVEL1,
|
||||
"write error1: addr=0x%06x, "
|
||||
"status=0x%02x\n", addr, status);
|
||||
goto write_err;
|
||||
}
|
||||
(*retlen)++;
|
||||
last_sector = sector;
|
||||
}
|
||||
|
||||
/* Write subsequent bytes in the same sector */
|
||||
cmd[0] = AT26_OP_SEQUENTIAL_WRITE;
|
||||
cmd[1] = buf[buf_index++];
|
||||
do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0);
|
||||
status = at91_dataflash26_waitready();
|
||||
addr++;
|
||||
|
||||
if ( ( !(status & AT26_STATUS_WRITE_ENABLE))
|
||||
&& ((addr % priv->page_size) != 0) ) {
|
||||
DEBUG(MTD_DEBUG_LEVEL1, "write error2: addr=0x%06x, "
|
||||
"status=0x%02x\n", addr, status);
|
||||
goto write_err;
|
||||
}
|
||||
|
||||
(*retlen)++;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
at91_dataflash26_write_enable(0);
|
||||
write_err:
|
||||
spi_release_bus(priv->spi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize and register DataFlash device with MTD subsystem.
|
||||
*/
|
||||
static int __init add_dataflash(int channel, char *name, int nr_pages,
|
||||
int pagesize)
|
||||
{
|
||||
struct mtd_info *device;
|
||||
struct dataflash_local *priv;
|
||||
|
||||
if (nr_devices >= DATAFLASH_MAX_DEVICES) {
|
||||
printk(KERN_ERR "at91_dataflash26: Too many devices "
|
||||
"detected\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
device = kzalloc(sizeof(struct mtd_info) + strlen(name) + 8,
|
||||
GFP_KERNEL);
|
||||
if (!device)
|
||||
return -ENOMEM;
|
||||
|
||||
device->name = (char *)&device[1];
|
||||
sprintf(device->name, "%s.spi%d", name, channel);
|
||||
device->size = nr_pages * pagesize;
|
||||
device->erasesize = pagesize;
|
||||
device->owner = THIS_MODULE;
|
||||
device->type = MTD_DATAFLASH;
|
||||
device->flags = MTD_CAP_NORFLASH;
|
||||
device->erase = at91_dataflash26_erase;
|
||||
device->read = at91_dataflash26_read;
|
||||
device->write = at91_dataflash26_write;
|
||||
|
||||
priv = (struct dataflash_local *)kzalloc(sizeof(struct dataflash_local),
|
||||
GFP_KERNEL);
|
||||
if (!priv) {
|
||||
kfree(device);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->spi = channel;
|
||||
priv->page_size = pagesize;
|
||||
device->priv = priv;
|
||||
|
||||
mtd_devices[nr_devices] = device;
|
||||
nr_devices++;
|
||||
printk(KERN_INFO "at91_dataflash26: %s detected [spi%i] (%i bytes)\n",
|
||||
name, channel, device->size);
|
||||
|
||||
return add_mtd_device(device);
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect and initialize DataFlash device connected to specified SPI channel.
|
||||
*
|
||||
*/
|
||||
|
||||
struct dataflash26_types {
|
||||
unsigned char id0;
|
||||
unsigned char id1;
|
||||
char *name;
|
||||
int pagesize;
|
||||
int nr_pages;
|
||||
};
|
||||
|
||||
struct dataflash26_types df26_types[] = {
|
||||
{
|
||||
.id0 = 0x04,
|
||||
.id1 = 0x00,
|
||||
.name = "AT26F004",
|
||||
.pagesize = 4096,
|
||||
.nr_pages = 128,
|
||||
},
|
||||
{
|
||||
.id0 = 0x45,
|
||||
.id1 = 0x01,
|
||||
.name = "AT26DF081A", /* Not tested ! */
|
||||
.pagesize = 4096,
|
||||
.nr_pages = 256,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init at91_dataflash26_detect(int channel)
|
||||
{
|
||||
unsigned char status, cmd[5];
|
||||
int i;
|
||||
|
||||
spi_access_bus(channel);
|
||||
status = at91_dataflash26_status();
|
||||
|
||||
if (status == 0 || status == 0xff) {
|
||||
printk(KERN_ERR "at91_dataflash26_detect: status error %d\n",
|
||||
status);
|
||||
spi_release_bus(channel);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
cmd[0] = AT26_OP_READ_DEV_ID;
|
||||
do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0);
|
||||
spi_release_bus(channel);
|
||||
|
||||
if (cmd[1] != MANUFACTURER_ID_ATMEL)
|
||||
return -ENODEV;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(df26_types); i++) {
|
||||
if ( cmd[2] == df26_types[i].id0
|
||||
&& cmd[3] == df26_types[i].id1)
|
||||
return add_dataflash(channel,
|
||||
df26_types[i].name,
|
||||
df26_types[i].nr_pages,
|
||||
df26_types[i].pagesize);
|
||||
}
|
||||
|
||||
printk(KERN_ERR "at91_dataflash26_detect: Unsupported device "
|
||||
"(0x%02x/0x%02x)\n", cmd[2], cmd[3]);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static int __init at91_dataflash26_init(void)
|
||||
{
|
||||
spi_transfer_desc = kmalloc(sizeof(struct spi_transfer_list),
|
||||
GFP_KERNEL);
|
||||
if (!spi_transfer_desc)
|
||||
return -ENOMEM;
|
||||
|
||||
/* DataFlash (SPI chip select 0) */
|
||||
at91_dataflash26_detect(0);
|
||||
|
||||
#ifdef CONFIG_MTD_AT91_DATAFLASH_CARD
|
||||
/* DataFlash card (SPI chip select 3) */
|
||||
at91_dataflash26_detect(3);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit at91_dataflash26_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DATAFLASH_MAX_DEVICES; i++) {
|
||||
if (mtd_devices[i]) {
|
||||
del_mtd_device(mtd_devices[i]);
|
||||
kfree(mtd_devices[i]->priv);
|
||||
kfree(mtd_devices[i]);
|
||||
}
|
||||
}
|
||||
nr_devices = 0;
|
||||
kfree(spi_transfer_desc);
|
||||
}
|
||||
|
||||
module_init(at91_dataflash26_init);
|
||||
module_exit(at91_dataflash26_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Hans J. Koch");
|
||||
MODULE_DESCRIPTION("DataFlash AT26xxx driver for Atmel AT91RM9200");
|
|
@ -81,9 +81,7 @@ static unsigned long __initdata doc_locations[] = {
|
|||
#endif /* CONFIG_MTD_DOCPROBE_HIGH */
|
||||
#elif defined(__PPC__)
|
||||
0xe4000000,
|
||||
#elif defined(CONFIG_MOMENCO_OCELOT_G)
|
||||
0xff000000,
|
||||
##else
|
||||
#else
|
||||
#warning Unknown architecture for DiskOnChip. No default probe locations defined
|
||||
#endif
|
||||
0xffffffff };
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* MTD SPI driver for ST M25Pxx flash chips
|
||||
* MTD SPI driver for ST M25Pxx (and similar) serial flash chips
|
||||
*
|
||||
* Author: Mike Lavender, mike@steroidmicros.com
|
||||
*
|
||||
|
@ -19,33 +19,32 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/flash.h>
|
||||
|
||||
#include <asm/semaphore.h>
|
||||
|
||||
|
||||
/* NOTE: AT 25F and SST 25LF series are very similar,
|
||||
* but commands for sector erase and chip id differ...
|
||||
*/
|
||||
|
||||
#define FLASH_PAGESIZE 256
|
||||
|
||||
/* Flash opcodes. */
|
||||
#define OPCODE_WREN 6 /* Write enable */
|
||||
#define OPCODE_RDSR 5 /* Read status register */
|
||||
#define OPCODE_READ 3 /* Read data bytes */
|
||||
#define OPCODE_PP 2 /* Page program */
|
||||
#define OPCODE_SE 0xd8 /* Sector erase */
|
||||
#define OPCODE_RES 0xab /* Read Electronic Signature */
|
||||
#define OPCODE_WREN 0x06 /* Write enable */
|
||||
#define OPCODE_RDSR 0x05 /* Read status register */
|
||||
#define OPCODE_READ 0x03 /* Read data bytes (low frequency) */
|
||||
#define OPCODE_FAST_READ 0x0b /* Read data bytes (high frequency) */
|
||||
#define OPCODE_PP 0x02 /* Page program (up to 256 bytes) */
|
||||
#define OPCODE_BE_4K 0x20 /* Erase 4KiB block */
|
||||
#define OPCODE_BE_32K 0x52 /* Erase 32KiB block */
|
||||
#define OPCODE_SE 0xd8 /* Sector erase (usually 64KiB) */
|
||||
#define OPCODE_RDID 0x9f /* Read JEDEC ID */
|
||||
|
||||
/* Status Register bits. */
|
||||
#define SR_WIP 1 /* Write in progress */
|
||||
#define SR_WEL 2 /* Write enable latch */
|
||||
/* meaning of other SR_* bits may differ between vendors */
|
||||
#define SR_BP0 4 /* Block protect 0 */
|
||||
#define SR_BP1 8 /* Block protect 1 */
|
||||
#define SR_BP2 0x10 /* Block protect 2 */
|
||||
|
@ -65,9 +64,10 @@
|
|||
|
||||
struct m25p {
|
||||
struct spi_device *spi;
|
||||
struct semaphore lock;
|
||||
struct mutex lock;
|
||||
struct mtd_info mtd;
|
||||
unsigned partitioned;
|
||||
unsigned partitioned:1;
|
||||
u8 erase_opcode;
|
||||
u8 command[4];
|
||||
};
|
||||
|
||||
|
@ -150,8 +150,9 @@ static int wait_till_ready(struct m25p *flash)
|
|||
*/
|
||||
static int erase_sector(struct m25p *flash, u32 offset)
|
||||
{
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "%s: %s at 0x%08x\n", flash->spi->dev.bus_id,
|
||||
__FUNCTION__, offset);
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "%s: %s %dKiB at 0x%08x\n",
|
||||
flash->spi->dev.bus_id, __FUNCTION__,
|
||||
flash->mtd.erasesize / 1024, offset);
|
||||
|
||||
/* Wait until finished previous write command. */
|
||||
if (wait_till_ready(flash))
|
||||
|
@ -161,7 +162,7 @@ static int erase_sector(struct m25p *flash, u32 offset)
|
|||
write_enable(flash);
|
||||
|
||||
/* Set up command buffer. */
|
||||
flash->command[0] = OPCODE_SE;
|
||||
flash->command[0] = flash->erase_opcode;
|
||||
flash->command[1] = offset >> 16;
|
||||
flash->command[2] = offset >> 8;
|
||||
flash->command[3] = offset;
|
||||
|
@ -201,13 +202,17 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
|
|||
addr = instr->addr;
|
||||
len = instr->len;
|
||||
|
||||
down(&flash->lock);
|
||||
mutex_lock(&flash->lock);
|
||||
|
||||
/* REVISIT in some cases we could speed up erasing large regions
|
||||
* by using OPCODE_SE instead of OPCODE_BE_4K
|
||||
*/
|
||||
|
||||
/* now erase those sectors */
|
||||
while (len) {
|
||||
if (erase_sector(flash, addr)) {
|
||||
instr->state = MTD_ERASE_FAILED;
|
||||
up(&flash->lock);
|
||||
mutex_unlock(&flash->lock);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
@ -215,7 +220,7 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
|
|||
len -= mtd->erasesize;
|
||||
}
|
||||
|
||||
up(&flash->lock);
|
||||
mutex_unlock(&flash->lock);
|
||||
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
mtd_erase_callback(instr);
|
||||
|
@ -260,16 +265,19 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
if (retlen)
|
||||
*retlen = 0;
|
||||
|
||||
down(&flash->lock);
|
||||
mutex_lock(&flash->lock);
|
||||
|
||||
/* Wait till previous write/erase is done. */
|
||||
if (wait_till_ready(flash)) {
|
||||
/* REVISIT status return?? */
|
||||
up(&flash->lock);
|
||||
mutex_unlock(&flash->lock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* NOTE: OPCODE_FAST_READ (if available) is faster... */
|
||||
/* FIXME switch to OPCODE_FAST_READ. It's required for higher
|
||||
* clocks; and at this writing, every chip this driver handles
|
||||
* supports that opcode.
|
||||
*/
|
||||
|
||||
/* Set up the write data buffer. */
|
||||
flash->command[0] = OPCODE_READ;
|
||||
|
@ -281,7 +289,7 @@ static int m25p80_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
|
||||
*retlen = m.actual_length - sizeof(flash->command);
|
||||
|
||||
up(&flash->lock);
|
||||
mutex_unlock(&flash->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -323,7 +331,7 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
t[1].tx_buf = buf;
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
down(&flash->lock);
|
||||
mutex_lock(&flash->lock);
|
||||
|
||||
/* Wait until finished previous write command. */
|
||||
if (wait_till_ready(flash))
|
||||
|
@ -381,10 +389,10 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
if (retlen)
|
||||
*retlen += m.actual_length
|
||||
- sizeof(flash->command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
up(&flash->lock);
|
||||
mutex_unlock(&flash->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -398,24 +406,118 @@ static int m25p80_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
|
||||
struct flash_info {
|
||||
char *name;
|
||||
u8 id;
|
||||
u16 jedec_id;
|
||||
|
||||
/* JEDEC id zero means "no ID" (most older chips); otherwise it has
|
||||
* a high byte of zero plus three data bytes: the manufacturer id,
|
||||
* then a two byte device id.
|
||||
*/
|
||||
u32 jedec_id;
|
||||
|
||||
/* The size listed here is what works with OPCODE_SE, which isn't
|
||||
* necessarily called a "sector" by the vendor.
|
||||
*/
|
||||
unsigned sector_size;
|
||||
unsigned n_sectors;
|
||||
u16 n_sectors;
|
||||
|
||||
u16 flags;
|
||||
#define SECT_4K 0x01 /* OPCODE_BE_4K works uniformly */
|
||||
};
|
||||
|
||||
|
||||
/* NOTE: double check command sets and memory organization when you add
|
||||
* more flash chips. This current list focusses on newer chips, which
|
||||
* have been converging on command sets which including JEDEC ID.
|
||||
*/
|
||||
static struct flash_info __devinitdata m25p_data [] = {
|
||||
/* REVISIT: fill in JEDEC ids, for parts that have them */
|
||||
{ "m25p05", 0x05, 0x2010, 32 * 1024, 2 },
|
||||
{ "m25p10", 0x10, 0x2011, 32 * 1024, 4 },
|
||||
{ "m25p20", 0x11, 0x2012, 64 * 1024, 4 },
|
||||
{ "m25p40", 0x12, 0x2013, 64 * 1024, 8 },
|
||||
{ "m25p80", 0x13, 0x0000, 64 * 1024, 16 },
|
||||
{ "m25p16", 0x14, 0x2015, 64 * 1024, 32 },
|
||||
{ "m25p32", 0x15, 0x2016, 64 * 1024, 64 },
|
||||
{ "m25p64", 0x16, 0x2017, 64 * 1024, 128 },
|
||||
|
||||
/* Atmel -- some are (confusingly) marketed as "DataFlash" */
|
||||
{ "at25fs010", 0x1f6601, 32 * 1024, 4, SECT_4K, },
|
||||
{ "at25fs040", 0x1f6604, 64 * 1024, 8, SECT_4K, },
|
||||
|
||||
{ "at25df041a", 0x1f4401, 64 * 1024, 8, SECT_4K, },
|
||||
|
||||
{ "at26f004", 0x1f0400, 64 * 1024, 8, SECT_4K, },
|
||||
{ "at26df081a", 0x1f4501, 64 * 1024, 16, SECT_4K, },
|
||||
{ "at26df161a", 0x1f4601, 64 * 1024, 32, SECT_4K, },
|
||||
{ "at26df321", 0x1f4701, 64 * 1024, 64, SECT_4K, },
|
||||
|
||||
/* Spansion -- single (large) sector size only, at least
|
||||
* for the chips listed here (without boot sectors).
|
||||
*/
|
||||
{ "s25sl004a", 0x010212, 64 * 1024, 8, },
|
||||
{ "s25sl008a", 0x010213, 64 * 1024, 16, },
|
||||
{ "s25sl016a", 0x010214, 64 * 1024, 32, },
|
||||
{ "s25sl032a", 0x010215, 64 * 1024, 64, },
|
||||
{ "s25sl064a", 0x010216, 64 * 1024, 128, },
|
||||
|
||||
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
|
||||
{ "sst25vf040b", 0xbf258d, 64 * 1024, 8, SECT_4K, },
|
||||
{ "sst25vf080b", 0xbf258e, 64 * 1024, 16, SECT_4K, },
|
||||
{ "sst25vf016b", 0xbf2541, 64 * 1024, 32, SECT_4K, },
|
||||
{ "sst25vf032b", 0xbf254a, 64 * 1024, 64, SECT_4K, },
|
||||
|
||||
/* ST Microelectronics -- newer production may have feature updates */
|
||||
{ "m25p05", 0x202010, 32 * 1024, 2, },
|
||||
{ "m25p10", 0x202011, 32 * 1024, 4, },
|
||||
{ "m25p20", 0x202012, 64 * 1024, 4, },
|
||||
{ "m25p40", 0x202013, 64 * 1024, 8, },
|
||||
{ "m25p80", 0, 64 * 1024, 16, },
|
||||
{ "m25p16", 0x202015, 64 * 1024, 32, },
|
||||
{ "m25p32", 0x202016, 64 * 1024, 64, },
|
||||
{ "m25p64", 0x202017, 64 * 1024, 128, },
|
||||
{ "m25p128", 0x202018, 256 * 1024, 64, },
|
||||
|
||||
{ "m45pe80", 0x204014, 64 * 1024, 16, },
|
||||
{ "m45pe16", 0x204015, 64 * 1024, 32, },
|
||||
|
||||
{ "m25pe80", 0x208014, 64 * 1024, 16, },
|
||||
{ "m25pe16", 0x208015, 64 * 1024, 32, SECT_4K, },
|
||||
|
||||
/* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
|
||||
{ "w25x10", 0xef3011, 64 * 1024, 2, SECT_4K, },
|
||||
{ "w25x20", 0xef3012, 64 * 1024, 4, SECT_4K, },
|
||||
{ "w25x40", 0xef3013, 64 * 1024, 8, SECT_4K, },
|
||||
{ "w25x80", 0xef3014, 64 * 1024, 16, SECT_4K, },
|
||||
{ "w25x16", 0xef3015, 64 * 1024, 32, SECT_4K, },
|
||||
{ "w25x32", 0xef3016, 64 * 1024, 64, SECT_4K, },
|
||||
{ "w25x64", 0xef3017, 64 * 1024, 128, SECT_4K, },
|
||||
};
|
||||
|
||||
static struct flash_info *__devinit jedec_probe(struct spi_device *spi)
|
||||
{
|
||||
int tmp;
|
||||
u8 code = OPCODE_RDID;
|
||||
u8 id[3];
|
||||
u32 jedec;
|
||||
struct flash_info *info;
|
||||
|
||||
/* JEDEC also defines an optional "extended device information"
|
||||
* string for after vendor-specific data, after the three bytes
|
||||
* we use here. Supporting some chips might require using it.
|
||||
*/
|
||||
tmp = spi_write_then_read(spi, &code, 1, id, 3);
|
||||
if (tmp < 0) {
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "%s: error %d reading JEDEC ID\n",
|
||||
spi->dev.bus_id, tmp);
|
||||
return NULL;
|
||||
}
|
||||
jedec = id[0];
|
||||
jedec = jedec << 8;
|
||||
jedec |= id[1];
|
||||
jedec = jedec << 8;
|
||||
jedec |= id[2];
|
||||
|
||||
for (tmp = 0, info = m25p_data;
|
||||
tmp < ARRAY_SIZE(m25p_data);
|
||||
tmp++, info++) {
|
||||
if (info->jedec_id == jedec)
|
||||
return info;
|
||||
}
|
||||
dev_err(&spi->dev, "unrecognized JEDEC id %06x\n", jedec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* board specific setup should have ensured the SPI clock used here
|
||||
* matches what the READ command supports, at least until this driver
|
||||
|
@ -429,37 +531,51 @@ static int __devinit m25p_probe(struct spi_device *spi)
|
|||
unsigned i;
|
||||
|
||||
/* Platform data helps sort out which chip type we have, as
|
||||
* well as how this board partitions it.
|
||||
* well as how this board partitions it. If we don't have
|
||||
* a chip ID, try the JEDEC id commands; they'll work for most
|
||||
* newer chips, even if we don't recognize the particular chip.
|
||||
*/
|
||||
data = spi->dev.platform_data;
|
||||
if (!data || !data->type) {
|
||||
/* FIXME some chips can identify themselves with RES
|
||||
* or JEDEC get-id commands. Try them ...
|
||||
*/
|
||||
DEBUG(MTD_DEBUG_LEVEL1, "%s: no chip id\n",
|
||||
spi->dev.bus_id);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (data && data->type) {
|
||||
for (i = 0, info = m25p_data;
|
||||
i < ARRAY_SIZE(m25p_data);
|
||||
i++, info++) {
|
||||
if (strcmp(data->type, info->name) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0, info = m25p_data; i < ARRAY_SIZE(m25p_data); i++, info++) {
|
||||
if (strcmp(data->type, info->name) == 0)
|
||||
break;
|
||||
}
|
||||
if (i == ARRAY_SIZE(m25p_data)) {
|
||||
DEBUG(MTD_DEBUG_LEVEL1, "%s: unrecognized id %s\n",
|
||||
spi->dev.bus_id, data->type);
|
||||
/* unrecognized chip? */
|
||||
if (i == ARRAY_SIZE(m25p_data)) {
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "%s: unrecognized id %s\n",
|
||||
spi->dev.bus_id, data->type);
|
||||
info = NULL;
|
||||
|
||||
/* recognized; is that chip really what's there? */
|
||||
} else if (info->jedec_id) {
|
||||
struct flash_info *chip = jedec_probe(spi);
|
||||
|
||||
if (!chip || chip != info) {
|
||||
dev_warn(&spi->dev, "found %s, expected %s\n",
|
||||
chip ? chip->name : "UNKNOWN",
|
||||
info->name);
|
||||
info = NULL;
|
||||
}
|
||||
}
|
||||
} else
|
||||
info = jedec_probe(spi);
|
||||
|
||||
if (!info)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
flash = kzalloc(sizeof *flash, GFP_KERNEL);
|
||||
if (!flash)
|
||||
return -ENOMEM;
|
||||
|
||||
flash->spi = spi;
|
||||
init_MUTEX(&flash->lock);
|
||||
mutex_init(&flash->lock);
|
||||
dev_set_drvdata(&spi->dev, flash);
|
||||
|
||||
if (data->name)
|
||||
if (data && data->name)
|
||||
flash->mtd.name = data->name;
|
||||
else
|
||||
flash->mtd.name = spi->dev.bus_id;
|
||||
|
@ -468,17 +584,25 @@ static int __devinit m25p_probe(struct spi_device *spi)
|
|||
flash->mtd.writesize = 1;
|
||||
flash->mtd.flags = MTD_CAP_NORFLASH;
|
||||
flash->mtd.size = info->sector_size * info->n_sectors;
|
||||
flash->mtd.erasesize = info->sector_size;
|
||||
flash->mtd.erase = m25p80_erase;
|
||||
flash->mtd.read = m25p80_read;
|
||||
flash->mtd.write = m25p80_write;
|
||||
|
||||
/* prefer "small sector" erase if possible */
|
||||
if (info->flags & SECT_4K) {
|
||||
flash->erase_opcode = OPCODE_BE_4K;
|
||||
flash->mtd.erasesize = 4096;
|
||||
} else {
|
||||
flash->erase_opcode = OPCODE_SE;
|
||||
flash->mtd.erasesize = info->sector_size;
|
||||
}
|
||||
|
||||
dev_info(&spi->dev, "%s (%d Kbytes)\n", info->name,
|
||||
flash->mtd.size / 1024);
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL2,
|
||||
"mtd .name = %s, .size = 0x%.8x (%uM) "
|
||||
".erasesize = 0x%.8x (%uK) .numeraseregions = %d\n",
|
||||
"mtd .name = %s, .size = 0x%.8x (%uMiB) "
|
||||
".erasesize = 0x%.8x (%uKiB) .numeraseregions = %d\n",
|
||||
flash->mtd.name,
|
||||
flash->mtd.size, flash->mtd.size / (1024*1024),
|
||||
flash->mtd.erasesize, flash->mtd.erasesize / 1024,
|
||||
|
@ -488,7 +612,7 @@ static int __devinit m25p_probe(struct spi_device *spi)
|
|||
for (i = 0; i < flash->mtd.numeraseregions; i++)
|
||||
DEBUG(MTD_DEBUG_LEVEL2,
|
||||
"mtd.eraseregions[%d] = { .offset = 0x%.8x, "
|
||||
".erasesize = 0x%.8x (%uK), "
|
||||
".erasesize = 0x%.8x (%uKiB), "
|
||||
".numblocks = %d }\n",
|
||||
i, flash->mtd.eraseregions[i].offset,
|
||||
flash->mtd.eraseregions[i].erasesize,
|
||||
|
@ -516,14 +640,14 @@ static int __devinit m25p_probe(struct spi_device *spi)
|
|||
}
|
||||
|
||||
if (nr_parts > 0) {
|
||||
for (i = 0; i < data->nr_parts; i++) {
|
||||
for (i = 0; i < nr_parts; i++) {
|
||||
DEBUG(MTD_DEBUG_LEVEL2, "partitions[%d] = "
|
||||
"{.name = %s, .offset = 0x%.8x, "
|
||||
".size = 0x%.8x (%uK) }\n",
|
||||
i, data->parts[i].name,
|
||||
data->parts[i].offset,
|
||||
data->parts[i].size,
|
||||
data->parts[i].size / 1024);
|
||||
".size = 0x%.8x (%uKiB) }\n",
|
||||
i, parts[i].name,
|
||||
parts[i].offset,
|
||||
parts[i].size,
|
||||
parts[i].size / 1024);
|
||||
}
|
||||
flash->partitioned = 1;
|
||||
return add_mtd_partitions(&flash->mtd, parts, nr_parts);
|
||||
|
@ -560,6 +684,11 @@ static struct spi_driver m25p80_driver = {
|
|||
},
|
||||
.probe = m25p_probe,
|
||||
.remove = __devexit_p(m25p_remove),
|
||||
|
||||
/* REVISIT: many of these chips have deep power-down modes, which
|
||||
* should clearly be entered on suspend() to minimize power use.
|
||||
* And also when they're otherwise idle...
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/flash.h>
|
||||
|
||||
|
@ -89,7 +90,7 @@ struct dataflash {
|
|||
unsigned short page_offset; /* offset in flash address */
|
||||
unsigned int page_size; /* of bytes per page */
|
||||
|
||||
struct semaphore lock;
|
||||
struct mutex lock;
|
||||
struct spi_device *spi;
|
||||
|
||||
struct mtd_info mtd;
|
||||
|
@ -167,7 +168,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
|
|||
x.len = 4;
|
||||
spi_message_add_tail(&x, &msg);
|
||||
|
||||
down(&priv->lock);
|
||||
mutex_lock(&priv->lock);
|
||||
while (instr->len > 0) {
|
||||
unsigned int pageaddr;
|
||||
int status;
|
||||
|
@ -210,7 +211,7 @@ static int dataflash_erase(struct mtd_info *mtd, struct erase_info *instr)
|
|||
instr->len -= priv->page_size;
|
||||
}
|
||||
}
|
||||
up(&priv->lock);
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
/* Inform MTD subsystem that erase is complete */
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
|
@ -266,7 +267,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
x[1].len = len;
|
||||
spi_message_add_tail(&x[1], &msg);
|
||||
|
||||
down(&priv->lock);
|
||||
mutex_lock(&priv->lock);
|
||||
|
||||
/* Continuous read, max clock = f(car) which may be less than
|
||||
* the peak rate available. Some chips support commands with
|
||||
|
@ -279,7 +280,7 @@ static int dataflash_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
/* plus 4 "don't care" bytes */
|
||||
|
||||
status = spi_sync(priv->spi, &msg);
|
||||
up(&priv->lock);
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
if (status >= 0) {
|
||||
*retlen = msg.actual_length - 8;
|
||||
|
@ -336,7 +337,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
else
|
||||
writelen = len;
|
||||
|
||||
down(&priv->lock);
|
||||
mutex_lock(&priv->lock);
|
||||
while (remaining > 0) {
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "write @ %i:%i len=%i\n",
|
||||
pageaddr, offset, writelen);
|
||||
|
@ -441,7 +442,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
else
|
||||
writelen = remaining;
|
||||
}
|
||||
up(&priv->lock);
|
||||
mutex_unlock(&priv->lock);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
@ -463,7 +464,7 @@ add_dataflash(struct spi_device *spi, char *name,
|
|||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
init_MUTEX(&priv->lock);
|
||||
mutex_init(&priv->lock);
|
||||
priv->spi = spi;
|
||||
priv->page_size = pagesize;
|
||||
priv->page_offset = pageoffset;
|
||||
|
|
|
@ -30,8 +30,8 @@
|
|||
*
|
||||
* Notes:
|
||||
* Due to what I assume is more buggy SROM, the 64M PMC551 I
|
||||
* have available claims that all 4 of it's DRAM banks have 64M
|
||||
* of ram configured (making a grand total of 256M onboard).
|
||||
* have available claims that all 4 of its DRAM banks have 64MiB
|
||||
* of ram configured (making a grand total of 256MiB onboard).
|
||||
* This is slightly annoying since the BAR0 size reflects the
|
||||
* aperture size, not the dram size, and the V370PDC supplies no
|
||||
* other method for memory size discovery. This problem is
|
||||
|
@ -70,7 +70,7 @@
|
|||
* made the memory unusable, added a fix to code to touch up
|
||||
* the DRAM some.
|
||||
*
|
||||
* Bugs/FIXME's:
|
||||
* Bugs/FIXMEs:
|
||||
* * MUST fix the init function to not spin on a register
|
||||
* waiting for it to set .. this does not safely handle busted
|
||||
* devices that never reset the register correctly which will
|
||||
|
@ -562,10 +562,10 @@ static u32 fixup_pmc551(struct pci_dev *dev)
|
|||
/*
|
||||
* Some screen fun
|
||||
*/
|
||||
printk(KERN_DEBUG "pmc551: %d%c (0x%x) of %sprefetchable memory at "
|
||||
printk(KERN_DEBUG "pmc551: %d%sB (0x%x) of %sprefetchable memory at "
|
||||
"0x%llx\n", (size < 1024) ? size : (size < 1048576) ?
|
||||
size >> 10 : size >> 20,
|
||||
(size < 1024) ? 'B' : (size < 1048576) ? 'K' : 'M', size,
|
||||
(size < 1024) ? "" : (size < 1048576) ? "Ki" : "Mi", size,
|
||||
((dcmd & (0x1 << 3)) == 0) ? "non-" : "",
|
||||
(unsigned long long)pci_resource_start(dev, 0));
|
||||
|
||||
|
@ -649,14 +649,10 @@ MODULE_DESCRIPTION(PMC551_VERSION);
|
|||
* Stuff these outside the ifdef so as to not bust compiled in driver support
|
||||
*/
|
||||
static int msize = 0;
|
||||
#if defined(CONFIG_MTD_PMC551_APERTURE_SIZE)
|
||||
static int asize = CONFIG_MTD_PMC551_APERTURE_SIZE;
|
||||
#else
|
||||
static int asize = 0;
|
||||
#endif
|
||||
|
||||
module_param(msize, int, 0);
|
||||
MODULE_PARM_DESC(msize, "memory size in Megabytes [1 - 1024]");
|
||||
MODULE_PARM_DESC(msize, "memory size in MiB [1 - 1024]");
|
||||
module_param(asize, int, 0);
|
||||
MODULE_PARM_DESC(asize, "aperture size, must be <= memsize [1-1024]");
|
||||
|
||||
|
@ -799,8 +795,7 @@ static int __init init_pmc551(void)
|
|||
mtd->owner = THIS_MODULE;
|
||||
|
||||
if (add_mtd_device(mtd)) {
|
||||
printk(KERN_NOTICE "pmc551: Failed to register new "
|
||||
"device\n");
|
||||
printk(KERN_NOTICE "pmc551: Failed to register new device\n");
|
||||
pci_iounmap(PCI_Device, priv->start);
|
||||
kfree(mtd->priv);
|
||||
kfree(mtd);
|
||||
|
@ -811,13 +806,13 @@ static int __init init_pmc551(void)
|
|||
pci_dev_get(PCI_Device);
|
||||
|
||||
printk(KERN_NOTICE "Registered pmc551 memory device.\n");
|
||||
printk(KERN_NOTICE "Mapped %dM of memory from 0x%p to 0x%p\n",
|
||||
printk(KERN_NOTICE "Mapped %dMiB of memory from 0x%p to 0x%p\n",
|
||||
priv->asize >> 20,
|
||||
priv->start, priv->start + priv->asize);
|
||||
printk(KERN_NOTICE "Total memory is %d%c\n",
|
||||
printk(KERN_NOTICE "Total memory is %d%sB\n",
|
||||
(length < 1024) ? length :
|
||||
(length < 1048576) ? length >> 10 : length >> 20,
|
||||
(length < 1024) ? 'B' : (length < 1048576) ? 'K' : 'M');
|
||||
(length < 1024) ? "" : (length < 1048576) ? "Ki" : "Mi");
|
||||
priv->nextpmc551 = pmc551list;
|
||||
pmc551list = mtd;
|
||||
found++;
|
||||
|
@ -850,7 +845,7 @@ static void __exit cleanup_pmc551(void)
|
|||
pmc551list = priv->nextpmc551;
|
||||
|
||||
if (priv->start) {
|
||||
printk(KERN_DEBUG "pmc551: unmapping %dM starting at "
|
||||
printk(KERN_DEBUG "pmc551: unmapping %dMiB starting at "
|
||||
"0x%p\n", priv->asize >> 20, priv->start);
|
||||
pci_iounmap(priv->dev, priv->start);
|
||||
}
|
||||
|
|
|
@ -580,14 +580,13 @@ int INFTL_mount(struct INFTLrecord *s)
|
|||
logical_block = block = BLOCK_NIL;
|
||||
|
||||
/* Temporary buffer to store ANAC numbers. */
|
||||
ANACtable = kmalloc(s->nb_blocks * sizeof(u8), GFP_KERNEL);
|
||||
ANACtable = kcalloc(s->nb_blocks, sizeof(u8), GFP_KERNEL);
|
||||
if (!ANACtable) {
|
||||
printk(KERN_WARNING "INFTL: allocation of ANACtable "
|
||||
"failed (%zd bytes)\n",
|
||||
s->nb_blocks * sizeof(u8));
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(ANACtable, 0, s->nb_blocks);
|
||||
|
||||
/*
|
||||
* First pass is to explore each physical unit, and construct the
|
||||
|
|
|
@ -163,20 +163,12 @@ config MTD_SBC_GXX
|
|||
More info at
|
||||
<http://www.arcomcontrols.com/products/icp/pc104/processors/SBC_GX1.htm>.
|
||||
|
||||
config MTD_LUBBOCK
|
||||
tristate "CFI Flash device mapped on Intel Lubbock XScale eval board"
|
||||
depends on ARCH_LUBBOCK && MTD_CFI_INTELEXT && MTD_PARTITIONS
|
||||
help
|
||||
This provides a driver for the on-board flash of the Intel
|
||||
'Lubbock' XScale evaluation board.
|
||||
|
||||
config MTD_MAINSTONE
|
||||
tristate "CFI Flash device mapped on Intel Mainstone XScale eval board"
|
||||
depends on MACH_MAINSTONE && MTD_CFI_INTELEXT
|
||||
config MTD_PXA2XX
|
||||
tristate "CFI Flash device mapped on Intel XScale PXA2xx based boards"
|
||||
depends on (PXA25x || PXA27x) && MTD_CFI_INTELEXT
|
||||
select MTD_PARTITIONS
|
||||
help
|
||||
This provides a driver for the on-board flash of the Intel
|
||||
'Mainstone PXA27x evaluation board.
|
||||
This provides a driver for the NOR flash attached to a PXA2xx chip.
|
||||
|
||||
config MTD_OCTAGON
|
||||
tristate "JEDEC Flash device mapped on Octagon 5066 SBC"
|
||||
|
@ -354,7 +346,7 @@ config MTD_CFI_FLAGADM
|
|||
|
||||
config MTD_WALNUT
|
||||
tristate "Flash device mapped on IBM 405GP Walnut"
|
||||
depends on MTD_JEDECPROBE && WALNUT
|
||||
depends on MTD_JEDECPROBE && WALNUT && !PPC_MERGE
|
||||
help
|
||||
This enables access routines for the flash chips on the IBM 405GP
|
||||
Walnut board. If you have one of these boards and would like to
|
||||
|
@ -370,7 +362,7 @@ config MTD_EBONY
|
|||
|
||||
config MTD_OCOTEA
|
||||
tristate "Flash devices mapped on IBM 440GX Ocotea"
|
||||
depends on MTD_CFI && OCOTEA
|
||||
depends on MTD_CFI && OCOTEA && !PPC_MERGE
|
||||
help
|
||||
This enables access routines for the flash chips on the IBM 440GX
|
||||
Ocotea board. If you have one of these boards and would like to
|
||||
|
@ -384,22 +376,6 @@ config MTD_REDWOOD
|
|||
Redwood board. If you have one of these boards and would like to
|
||||
use the flash chips on it, say 'Y'.
|
||||
|
||||
config MTD_TQM834x
|
||||
tristate "Flash device mapped on TQ Components TQM834x Boards"
|
||||
depends on MTD_CFI && TQM834x
|
||||
help
|
||||
This enables access routines for the flash chips on the
|
||||
TQ Components TQM834x boards. If you have one of these boards
|
||||
and would like to use the flash chips on it, say 'Y'.
|
||||
|
||||
config MTD_OCELOT
|
||||
tristate "Momenco Ocelot boot flash device"
|
||||
depends on MOMENCO_OCELOT
|
||||
help
|
||||
This enables access routines for the boot flash device and for the
|
||||
NVRAM on the Momenco Ocelot board. If you have one of these boards
|
||||
and would like access to either of these, say 'Y'.
|
||||
|
||||
config MTD_SOLUTIONENGINE
|
||||
tristate "CFI Flash device mapped on Hitachi SolutionEngine"
|
||||
depends on SUPERH && MTD_CFI && MTD_REDBOOT_PARTS
|
||||
|
@ -605,6 +581,13 @@ config MTD_SHARP_SL
|
|||
help
|
||||
This enables access to the flash chip on the Sharp SL Series of PDAs.
|
||||
|
||||
config MTD_INTEL_VR_NOR
|
||||
tristate "NOR flash on Intel Vermilion Range Expansion Bus CS0"
|
||||
depends on PCI
|
||||
help
|
||||
Map driver for a NOR flash bank located on the Expansion Bus of the
|
||||
Intel Vermilion Range chipset.
|
||||
|
||||
config MTD_PLATRAM
|
||||
tristate "Map driver for platform device RAM (mtd-ram)"
|
||||
select MTD_RAM
|
||||
|
|
|
@ -20,8 +20,7 @@ obj-$(CONFIG_MTD_ESB2ROM) += esb2rom.o
|
|||
obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o
|
||||
obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o
|
||||
obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o
|
||||
obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o
|
||||
obj-$(CONFIG_MTD_MAINSTONE) += mainstone-flash.o
|
||||
obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o
|
||||
obj-$(CONFIG_MTD_MBX860) += mbx860.o
|
||||
obj-$(CONFIG_MTD_CEIVA) += ceiva.o
|
||||
obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o
|
||||
|
@ -43,7 +42,6 @@ obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o
|
|||
obj-$(CONFIG_MTD_VMAX) += vmax301.o
|
||||
obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
|
||||
obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o
|
||||
obj-$(CONFIG_MTD_OCELOT) += ocelot.o
|
||||
obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
|
||||
obj-$(CONFIG_MTD_PCI) += pci.o
|
||||
obj-$(CONFIG_MTD_ALCHEMY) += alchemy-flash.o
|
||||
|
@ -70,4 +68,4 @@ obj-$(CONFIG_MTD_SHARP_SL) += sharpsl-flash.o
|
|||
obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
|
||||
obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o
|
||||
obj-$(CONFIG_MTD_MTX1) += mtx-1_flash.o
|
||||
obj-$(CONFIG_MTD_TQM834x) += tqm834x.o
|
||||
obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
|
||||
|
|
|
@ -75,13 +75,6 @@
|
|||
#define BOARD_FLASH_WIDTH 2 /* 16-bits */
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MIPS_HYDROGEN3
|
||||
#define BOARD_MAP_NAME "Hydrogen3 Flash"
|
||||
#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */
|
||||
#define BOARD_FLASH_WIDTH 4 /* 32-bits */
|
||||
#define USE_LOCAL_ACCESSORS /* why? */
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MIPS_BOSPORUS
|
||||
#define BOARD_MAP_NAME "Bosporus Flash"
|
||||
#define BOARD_FLASH_SIZE 0x01000000 /* 16MB */
|
||||
|
@ -130,13 +123,6 @@ int __init alchemy_mtd_init(void)
|
|||
|
||||
window_addr = 0x20000000 - BOARD_FLASH_SIZE;
|
||||
window_size = BOARD_FLASH_SIZE;
|
||||
#ifdef CONFIG_MIPS_MIRAGE_WHY
|
||||
/* Boot ROM flash bank only; no user bank */
|
||||
window_addr = 0x1C000000;
|
||||
window_size = 0x04000000;
|
||||
/* USERFS from 0x1C00 0000 to 0x1FC00000 */
|
||||
alchemy_partitions[0].size = 0x03C00000;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Static partition definition selection
|
||||
|
|
298
drivers/mtd/maps/intel_vr_nor.c
Normal file
298
drivers/mtd/maps/intel_vr_nor.c
Normal 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);
|
|
@ -1,168 +0,0 @@
|
|||
/*
|
||||
* $Id: lubbock-flash.c,v 1.21 2005/11/07 11:14:27 gleixner Exp $
|
||||
*
|
||||
* Map driver for the Lubbock developer platform.
|
||||
*
|
||||
* Author: Nicolas Pitre
|
||||
* Copyright: (C) 2001 MontaVista Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/arch/pxa-regs.h>
|
||||
#include <asm/arch/lubbock.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#define ROM_ADDR 0x00000000
|
||||
#define FLASH_ADDR 0x04000000
|
||||
|
||||
#define WINDOW_SIZE 64*1024*1024
|
||||
|
||||
static void lubbock_map_inval_cache(struct map_info *map, unsigned long from, ssize_t len)
|
||||
{
|
||||
flush_ioremap_region(map->phys, map->cached, from, len);
|
||||
}
|
||||
|
||||
static struct map_info lubbock_maps[2] = { {
|
||||
.size = WINDOW_SIZE,
|
||||
.phys = 0x00000000,
|
||||
.inval_cache = lubbock_map_inval_cache,
|
||||
}, {
|
||||
.size = WINDOW_SIZE,
|
||||
.phys = 0x04000000,
|
||||
.inval_cache = lubbock_map_inval_cache,
|
||||
} };
|
||||
|
||||
static struct mtd_partition lubbock_partitions[] = {
|
||||
{
|
||||
.name = "Bootloader",
|
||||
.size = 0x00040000,
|
||||
.offset = 0,
|
||||
.mask_flags = MTD_WRITEABLE /* force read-only */
|
||||
},{
|
||||
.name = "Kernel",
|
||||
.size = 0x00100000,
|
||||
.offset = 0x00040000,
|
||||
},{
|
||||
.name = "Filesystem",
|
||||
.size = MTDPART_SIZ_FULL,
|
||||
.offset = 0x00140000
|
||||
}
|
||||
};
|
||||
|
||||
static struct mtd_info *mymtds[2];
|
||||
static struct mtd_partition *parsed_parts[2];
|
||||
static int nr_parsed_parts[2];
|
||||
|
||||
static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||
|
||||
static int __init init_lubbock(void)
|
||||
{
|
||||
int flashboot = (LUB_CONF_SWITCHES & 1);
|
||||
int ret = 0, i;
|
||||
|
||||
lubbock_maps[0].bankwidth = lubbock_maps[1].bankwidth =
|
||||
(BOOT_DEF & 1) ? 2 : 4;
|
||||
|
||||
/* Compensate for the nROMBT switch which swaps the flash banks */
|
||||
printk(KERN_NOTICE "Lubbock configured to boot from %s (bank %d)\n",
|
||||
flashboot?"Flash":"ROM", flashboot);
|
||||
|
||||
lubbock_maps[flashboot^1].name = "Lubbock Application Flash";
|
||||
lubbock_maps[flashboot].name = "Lubbock Boot ROM";
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
lubbock_maps[i].virt = ioremap(lubbock_maps[i].phys, WINDOW_SIZE);
|
||||
if (!lubbock_maps[i].virt) {
|
||||
printk(KERN_WARNING "Failed to ioremap %s\n", lubbock_maps[i].name);
|
||||
if (!ret)
|
||||
ret = -ENOMEM;
|
||||
continue;
|
||||
}
|
||||
lubbock_maps[i].cached = ioremap_cached(lubbock_maps[i].phys, WINDOW_SIZE);
|
||||
if (!lubbock_maps[i].cached)
|
||||
printk(KERN_WARNING "Failed to ioremap cached %s\n", lubbock_maps[i].name);
|
||||
simple_map_init(&lubbock_maps[i]);
|
||||
|
||||
printk(KERN_NOTICE "Probing %s at physical address 0x%08lx (%d-bit bankwidth)\n",
|
||||
lubbock_maps[i].name, lubbock_maps[i].phys,
|
||||
lubbock_maps[i].bankwidth * 8);
|
||||
|
||||
mymtds[i] = do_map_probe("cfi_probe", &lubbock_maps[i]);
|
||||
|
||||
if (!mymtds[i]) {
|
||||
iounmap((void *)lubbock_maps[i].virt);
|
||||
if (lubbock_maps[i].cached)
|
||||
iounmap(lubbock_maps[i].cached);
|
||||
if (!ret)
|
||||
ret = -EIO;
|
||||
continue;
|
||||
}
|
||||
mymtds[i]->owner = THIS_MODULE;
|
||||
|
||||
ret = parse_mtd_partitions(mymtds[i], probes,
|
||||
&parsed_parts[i], 0);
|
||||
|
||||
if (ret > 0)
|
||||
nr_parsed_parts[i] = ret;
|
||||
}
|
||||
|
||||
if (!mymtds[0] && !mymtds[1])
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!mymtds[i]) {
|
||||
printk(KERN_WARNING "%s is absent. Skipping\n", lubbock_maps[i].name);
|
||||
} else if (nr_parsed_parts[i]) {
|
||||
add_mtd_partitions(mymtds[i], parsed_parts[i], nr_parsed_parts[i]);
|
||||
} else if (!i) {
|
||||
printk("Using static partitions on %s\n", lubbock_maps[i].name);
|
||||
add_mtd_partitions(mymtds[i], lubbock_partitions, ARRAY_SIZE(lubbock_partitions));
|
||||
} else {
|
||||
printk("Registering %s as whole device\n", lubbock_maps[i].name);
|
||||
add_mtd_device(mymtds[i]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cleanup_lubbock(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!mymtds[i])
|
||||
continue;
|
||||
|
||||
if (nr_parsed_parts[i] || !i)
|
||||
del_mtd_partitions(mymtds[i]);
|
||||
else
|
||||
del_mtd_device(mymtds[i]);
|
||||
|
||||
map_destroy(mymtds[i]);
|
||||
iounmap((void *)lubbock_maps[i].virt);
|
||||
if (lubbock_maps[i].cached)
|
||||
iounmap(lubbock_maps[i].cached);
|
||||
|
||||
kfree(parsed_parts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
module_init(init_lubbock);
|
||||
module_exit(cleanup_lubbock);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
|
||||
MODULE_DESCRIPTION("MTD map driver for Intel Lubbock");
|
|
@ -1,180 +0,0 @@
|
|||
/*
|
||||
* $Id: $
|
||||
*
|
||||
* Map driver for the Mainstone developer platform.
|
||||
*
|
||||
* Author: Nicolas Pitre
|
||||
* Copyright: (C) 2001 MontaVista Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/arch/pxa-regs.h>
|
||||
#include <asm/arch/mainstone.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
|
||||
#define ROM_ADDR 0x00000000
|
||||
#define FLASH_ADDR 0x04000000
|
||||
|
||||
#define WINDOW_SIZE 0x04000000
|
||||
|
||||
static void mainstone_map_inval_cache(struct map_info *map, unsigned long from,
|
||||
ssize_t len)
|
||||
{
|
||||
flush_ioremap_region(map->phys, map->cached, from, len);
|
||||
}
|
||||
|
||||
static struct map_info mainstone_maps[2] = { {
|
||||
.size = WINDOW_SIZE,
|
||||
.phys = PXA_CS0_PHYS,
|
||||
.inval_cache = mainstone_map_inval_cache,
|
||||
}, {
|
||||
.size = WINDOW_SIZE,
|
||||
.phys = PXA_CS1_PHYS,
|
||||
.inval_cache = mainstone_map_inval_cache,
|
||||
} };
|
||||
|
||||
static struct mtd_partition mainstone_partitions[] = {
|
||||
{
|
||||
.name = "Bootloader",
|
||||
.size = 0x00040000,
|
||||
.offset = 0,
|
||||
.mask_flags = MTD_WRITEABLE /* force read-only */
|
||||
},{
|
||||
.name = "Kernel",
|
||||
.size = 0x00400000,
|
||||
.offset = 0x00040000,
|
||||
},{
|
||||
.name = "Filesystem",
|
||||
.size = MTDPART_SIZ_FULL,
|
||||
.offset = 0x00440000
|
||||
}
|
||||
};
|
||||
|
||||
static struct mtd_info *mymtds[2];
|
||||
static struct mtd_partition *parsed_parts[2];
|
||||
static int nr_parsed_parts[2];
|
||||
|
||||
static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||
|
||||
static int __init init_mainstone(void)
|
||||
{
|
||||
int SW7 = 0; /* FIXME: get from SCR (Mst doc section 3.2.1.1) */
|
||||
int ret = 0, i;
|
||||
|
||||
mainstone_maps[0].bankwidth = (BOOT_DEF & 1) ? 2 : 4;
|
||||
mainstone_maps[1].bankwidth = 4;
|
||||
|
||||
/* Compensate for SW7 which swaps the flash banks */
|
||||
mainstone_maps[SW7].name = "processor flash";
|
||||
mainstone_maps[SW7 ^ 1].name = "main board flash";
|
||||
|
||||
printk(KERN_NOTICE "Mainstone configured to boot from %s\n",
|
||||
mainstone_maps[0].name);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
mainstone_maps[i].virt = ioremap(mainstone_maps[i].phys,
|
||||
WINDOW_SIZE);
|
||||
if (!mainstone_maps[i].virt) {
|
||||
printk(KERN_WARNING "Failed to ioremap %s\n",
|
||||
mainstone_maps[i].name);
|
||||
if (!ret)
|
||||
ret = -ENOMEM;
|
||||
continue;
|
||||
}
|
||||
mainstone_maps[i].cached =
|
||||
ioremap_cached(mainstone_maps[i].phys, WINDOW_SIZE);
|
||||
if (!mainstone_maps[i].cached)
|
||||
printk(KERN_WARNING "Failed to ioremap cached %s\n",
|
||||
mainstone_maps[i].name);
|
||||
simple_map_init(&mainstone_maps[i]);
|
||||
|
||||
printk(KERN_NOTICE
|
||||
"Probing %s at physical address 0x%08lx"
|
||||
" (%d-bit bankwidth)\n",
|
||||
mainstone_maps[i].name, mainstone_maps[i].phys,
|
||||
mainstone_maps[i].bankwidth * 8);
|
||||
|
||||
mymtds[i] = do_map_probe("cfi_probe", &mainstone_maps[i]);
|
||||
|
||||
if (!mymtds[i]) {
|
||||
iounmap((void *)mainstone_maps[i].virt);
|
||||
if (mainstone_maps[i].cached)
|
||||
iounmap(mainstone_maps[i].cached);
|
||||
if (!ret)
|
||||
ret = -EIO;
|
||||
continue;
|
||||
}
|
||||
mymtds[i]->owner = THIS_MODULE;
|
||||
|
||||
ret = parse_mtd_partitions(mymtds[i], probes,
|
||||
&parsed_parts[i], 0);
|
||||
|
||||
if (ret > 0)
|
||||
nr_parsed_parts[i] = ret;
|
||||
}
|
||||
|
||||
if (!mymtds[0] && !mymtds[1])
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!mymtds[i]) {
|
||||
printk(KERN_WARNING "%s is absent. Skipping\n",
|
||||
mainstone_maps[i].name);
|
||||
} else if (nr_parsed_parts[i]) {
|
||||
add_mtd_partitions(mymtds[i], parsed_parts[i],
|
||||
nr_parsed_parts[i]);
|
||||
} else if (!i) {
|
||||
printk("Using static partitions on %s\n",
|
||||
mainstone_maps[i].name);
|
||||
add_mtd_partitions(mymtds[i], mainstone_partitions,
|
||||
ARRAY_SIZE(mainstone_partitions));
|
||||
} else {
|
||||
printk("Registering %s as whole device\n",
|
||||
mainstone_maps[i].name);
|
||||
add_mtd_device(mymtds[i]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cleanup_mainstone(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!mymtds[i])
|
||||
continue;
|
||||
|
||||
if (nr_parsed_parts[i] || !i)
|
||||
del_mtd_partitions(mymtds[i]);
|
||||
else
|
||||
del_mtd_device(mymtds[i]);
|
||||
|
||||
map_destroy(mymtds[i]);
|
||||
iounmap((void *)mainstone_maps[i].virt);
|
||||
if (mainstone_maps[i].cached)
|
||||
iounmap(mainstone_maps[i].cached);
|
||||
kfree(parsed_parts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
module_init(init_mainstone);
|
||||
module_exit(cleanup_mainstone);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
|
||||
MODULE_DESCRIPTION("MTD map driver for Intel Mainstone");
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
|
@ -232,7 +232,6 @@ static int __devinit of_flash_probe(struct of_device *dev,
|
|||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
goto err_out;
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
dev_set_drvdata(&dev->dev, info);
|
||||
|
||||
|
|
|
@ -73,13 +73,16 @@ int __init init_msp_flash(void)
|
|||
return -ENXIO;
|
||||
|
||||
printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt);
|
||||
msp_flash = (struct mtd_info **)kmalloc(
|
||||
fcnt * sizeof(struct map_info *), GFP_KERNEL);
|
||||
msp_parts = (struct mtd_partition **)kmalloc(
|
||||
fcnt * sizeof(struct mtd_partition *), GFP_KERNEL);
|
||||
msp_maps = (struct map_info *)kmalloc(
|
||||
fcnt * sizeof(struct mtd_info), GFP_KERNEL);
|
||||
memset(msp_maps, 0, fcnt * sizeof(struct mtd_info));
|
||||
|
||||
msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL);
|
||||
msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL);
|
||||
msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL);
|
||||
if (!msp_flash || !msp_parts || !msp_maps) {
|
||||
kfree(msp_maps);
|
||||
kfree(msp_parts);
|
||||
kfree(msp_flash);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* loop over the flash devices, initializing each */
|
||||
for (i = 0; i < fcnt; i++) {
|
||||
|
@ -95,9 +98,8 @@ int __init init_msp_flash(void)
|
|||
continue;
|
||||
}
|
||||
|
||||
msp_parts[i] = (struct mtd_partition *)kmalloc(
|
||||
pcnt * sizeof(struct mtd_partition), GFP_KERNEL);
|
||||
memset(msp_parts[i], 0, pcnt * sizeof(struct mtd_partition));
|
||||
msp_parts[i] = kcalloc(pcnt, sizeof(struct mtd_partition),
|
||||
GFP_KERNEL);
|
||||
|
||||
/* now initialize the devices proper */
|
||||
flash_name[5] = '0' + i;
|
||||
|
|
|
@ -79,7 +79,6 @@ static int __init init_rrmap(void)
|
|||
rr_mtd->owner = THIS_MODULE;
|
||||
|
||||
add_mtd_device(rr_mtd);
|
||||
ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, rr_mtd->index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -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");
|
200
drivers/mtd/maps/pxa2xx-flash.c
Normal file
200
drivers/mtd/maps/pxa2xx-flash.c
Normal 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");
|
|
@ -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");
|
|
@ -24,10 +24,9 @@
|
|||
#include <linux/kthread.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
static LIST_HEAD(blktrans_majors);
|
||||
#include "mtdcore.h"
|
||||
|
||||
extern struct mutex mtd_table_mutex;
|
||||
extern struct mtd_info *mtd_table[];
|
||||
static LIST_HEAD(blktrans_majors);
|
||||
|
||||
struct mtd_blkcore_priv {
|
||||
struct task_struct *thread;
|
||||
|
@ -202,7 +201,7 @@ static int blktrans_ioctl(struct inode *inode, struct file *file,
|
|||
}
|
||||
}
|
||||
|
||||
struct block_device_operations mtd_blktrans_ops = {
|
||||
static struct block_device_operations mtd_blktrans_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = blktrans_open,
|
||||
.release = blktrans_release,
|
||||
|
|
|
@ -136,7 +136,8 @@ static int mtd_close(struct inode *inode, struct file *file)
|
|||
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
|
||||
|
||||
if (mtd->sync)
|
||||
/* Only sync if opened RW */
|
||||
if ((file->f_mode & 2) && mtd->sync)
|
||||
mtd->sync(mtd);
|
||||
|
||||
put_mtd_device(mtd);
|
||||
|
|
|
@ -178,7 +178,7 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs,
|
|||
|
||||
/* Check alignment */
|
||||
if (mtd->writesize > 1) {
|
||||
loff_t __to = to;
|
||||
uint64_t __to = to;
|
||||
if (do_div(__to, mtd->writesize) || (total_len % mtd->writesize))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
||||
#include "mtdcore.h"
|
||||
|
||||
/* These are exported solely for the purpose of mtd_blkdevs.c. You
|
||||
should not use them for _anything_ else */
|
||||
DEFINE_MUTEX(mtd_table_mutex);
|
||||
|
|
11
drivers/mtd/mtdcore.h
Normal file
11
drivers/mtd/mtdcore.h
Normal 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
376
drivers/mtd/mtdoops.c
Normal 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");
|
|
@ -91,6 +91,25 @@ config MTD_NAND_AU1550
|
|||
This enables the driver for the NAND flash controller on the
|
||||
AMD/Alchemy 1550 SOC.
|
||||
|
||||
config MTD_NAND_BF5XX
|
||||
tristate "Blackfin on-chip NAND Flash Controller driver"
|
||||
depends on BF54x && MTD_NAND
|
||||
help
|
||||
This enables the Blackfin on-chip NAND flash controller
|
||||
|
||||
No board specific support is done by this driver, each board
|
||||
must advertise a platform_device for the driver to attach.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called bf5xx-nand.
|
||||
|
||||
config MTD_NAND_BF5XX_HWECC
|
||||
bool "BF5XX NAND Hardware ECC"
|
||||
depends on MTD_NAND_BF5XX
|
||||
help
|
||||
Enable the use of the BF5XX's internal ECC generator when
|
||||
using NAND.
|
||||
|
||||
config MTD_NAND_RTC_FROM4
|
||||
tristate "Renesas Flash ROM 4-slot interface board (FROM_BOARD4)"
|
||||
depends on SH_SOLUTION_ENGINE
|
||||
|
@ -134,10 +153,10 @@ config MTD_NAND_S3C2410_HWECC
|
|||
|
||||
config MTD_NAND_NDFC
|
||||
tristate "NDFC NanD Flash Controller"
|
||||
depends on 44x
|
||||
depends on 4xx && !PPC_MERGE
|
||||
select MTD_NAND_ECC_SMC
|
||||
help
|
||||
NDFC Nand Flash Controllers are integrated in EP44x SoCs
|
||||
NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs
|
||||
|
||||
config MTD_NAND_S3C2410_CLKSTOP
|
||||
bool "S3C2410 NAND IDLE clock stop"
|
||||
|
@ -237,7 +256,7 @@ config MTD_NAND_CAFE
|
|||
select REED_SOLOMON
|
||||
select REED_SOLOMON_DEC16
|
||||
help
|
||||
Use NAND flash attached to the CAFÉ chip designed for the $100
|
||||
Use NAND flash attached to the CAFÉ chip designed for the OLPC
|
||||
laptop.
|
||||
|
||||
config MTD_NAND_CS553X
|
||||
|
@ -280,5 +299,11 @@ config MTD_NAND_PLATFORM
|
|||
devices. You will need to provide platform-specific functions
|
||||
via platform_data.
|
||||
|
||||
config MTD_ALAUDA
|
||||
tristate "MTD driver for Olympus MAUSB-10 and Fijufilm DPC-R1"
|
||||
depends on MTD_NAND && USB
|
||||
help
|
||||
These two (and possibly other) Alauda-based cardreaders for
|
||||
SmartMedia and xD allow raw flash access.
|
||||
|
||||
endif # MTD_NAND
|
||||
|
|
|
@ -13,6 +13,7 @@ obj-$(CONFIG_MTD_NAND_TOTO) += toto.o
|
|||
obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o
|
||||
obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o
|
||||
obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
|
||||
obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o
|
||||
obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
|
||||
obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
|
||||
|
@ -27,5 +28,6 @@ obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o
|
|||
obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
|
||||
obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
|
||||
obj-$(CONFIG_MTD_ALAUDA) += alauda.o
|
||||
|
||||
nand-objs := nand_base.o nand_bbt.o
|
||||
|
|
742
drivers/mtd/nand/alauda.c
Normal file
742
drivers/mtd/nand/alauda.c
Normal 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");
|
788
drivers/mtd/nand/bf5xx_nand.c
Normal file
788
drivers/mtd/nand/bf5xx_nand.c
Normal 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);
|
|
@ -623,6 +623,11 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
|
|||
uint32_t ctrl;
|
||||
int err = 0;
|
||||
|
||||
/* Very old versions shared the same PCI ident for all three
|
||||
functions on the chip. Verify the class too... */
|
||||
if ((pdev->class >> 8) != PCI_CLASS_MEMORY_FLASH)
|
||||
return -ENODEV;
|
||||
|
||||
err = pci_enable_device(pdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -816,21 +821,57 @@ static void __devexit cafe_nand_remove(struct pci_dev *pdev)
|
|||
}
|
||||
|
||||
static struct pci_device_id cafe_nand_tbl[] = {
|
||||
{ 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MEMORY_FLASH << 8, 0xFFFF0 },
|
||||
{ 0, }
|
||||
{ 0x11ab, 0x4100, PCI_ANY_ID, PCI_ANY_ID },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, cafe_nand_tbl);
|
||||
|
||||
static int cafe_nand_resume(struct pci_dev *pdev)
|
||||
{
|
||||
uint32_t ctrl;
|
||||
struct mtd_info *mtd = pci_get_drvdata(pdev);
|
||||
struct cafe_priv *cafe = mtd->priv;
|
||||
|
||||
/* Start off by resetting the NAND controller completely */
|
||||
cafe_writel(cafe, 1, NAND_RESET);
|
||||
cafe_writel(cafe, 0, NAND_RESET);
|
||||
cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK);
|
||||
|
||||
/* Restore timing configuration */
|
||||
cafe_writel(cafe, timing[0], NAND_TIMING1);
|
||||
cafe_writel(cafe, timing[1], NAND_TIMING2);
|
||||
cafe_writel(cafe, timing[2], NAND_TIMING3);
|
||||
|
||||
/* Disable master reset, enable NAND clock */
|
||||
ctrl = cafe_readl(cafe, GLOBAL_CTRL);
|
||||
ctrl &= 0xffffeff0;
|
||||
ctrl |= 0x00007000;
|
||||
cafe_writel(cafe, ctrl | 0x05, GLOBAL_CTRL);
|
||||
cafe_writel(cafe, ctrl | 0x0a, GLOBAL_CTRL);
|
||||
cafe_writel(cafe, 0, NAND_DMA_CTRL);
|
||||
cafe_writel(cafe, 0x7006, GLOBAL_CTRL);
|
||||
cafe_writel(cafe, 0x700a, GLOBAL_CTRL);
|
||||
|
||||
/* Set up DMA address */
|
||||
cafe_writel(cafe, cafe->dmaaddr & 0xffffffff, NAND_DMA_ADDR0);
|
||||
if (sizeof(cafe->dmaaddr) > 4)
|
||||
/* Shift in two parts to shut the compiler up */
|
||||
cafe_writel(cafe, (cafe->dmaaddr >> 16) >> 16, NAND_DMA_ADDR1);
|
||||
else
|
||||
cafe_writel(cafe, 0, NAND_DMA_ADDR1);
|
||||
|
||||
/* Enable NAND IRQ in global IRQ mask register */
|
||||
cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pci_driver cafe_nand_pci_driver = {
|
||||
.name = "CAFÉ NAND",
|
||||
.id_table = cafe_nand_tbl,
|
||||
.probe = cafe_nand_probe,
|
||||
.remove = __devexit_p(cafe_nand_remove),
|
||||
#ifdef CONFIG_PMx
|
||||
.suspend = cafe_nand_suspend,
|
||||
.resume = cafe_nand_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int cafe_nand_init(void)
|
||||
|
|
|
@ -56,8 +56,6 @@ static unsigned long __initdata doc_locations[] = {
|
|||
#endif /* CONFIG_MTD_DOCPROBE_HIGH */
|
||||
#elif defined(__PPC__)
|
||||
0xe4000000,
|
||||
#elif defined(CONFIG_MOMENCO_OCELOT_G)
|
||||
0xff000000,
|
||||
#else
|
||||
#warning Unknown architecture for DiskOnChip. No default probe locations defined
|
||||
#endif
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* Basic support for AG-AND chips is provided.
|
||||
*
|
||||
* Additional technical information is available on
|
||||
* http://www.linux-mtd.infradead.org/tech/nand.html
|
||||
* http://www.linux-mtd.infradead.org/doc/nand.html
|
||||
*
|
||||
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
|
||||
* 2002-2006 Thomas Gleixner (tglx@linutronix.de)
|
||||
|
@ -2069,13 +2069,14 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
|
|||
erase_exit:
|
||||
|
||||
ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
|
||||
/* Do call back function */
|
||||
if (!ret)
|
||||
mtd_erase_callback(instr);
|
||||
|
||||
/* Deselect and wake up anyone waiting on the device */
|
||||
nand_release_device(mtd);
|
||||
|
||||
/* Do call back function */
|
||||
if (!ret)
|
||||
mtd_erase_callback(instr);
|
||||
|
||||
/*
|
||||
* If BBT requires refresh and erase was successful, rewrite any
|
||||
* selected bad block tables
|
||||
|
|
|
@ -141,6 +141,7 @@ struct nand_manufacturers nand_manuf_ids[] = {
|
|||
{NAND_MFR_STMICRO, "ST Micro"},
|
||||
{NAND_MFR_HYNIX, "Hynix"},
|
||||
{NAND_MFR_MICRON, "Micron"},
|
||||
{NAND_MFR_AMD, "AMD"},
|
||||
{0x0, "Unknown"}
|
||||
};
|
||||
|
||||
|
|
|
@ -1272,7 +1272,13 @@ static int prog_page(struct nandsim *ns, int num)
|
|||
mypage = NS_GET_PAGE(ns);
|
||||
if (mypage->byte == NULL) {
|
||||
NS_DBG("prog_page: allocating page %d\n", ns->regs.row);
|
||||
mypage->byte = kmalloc(ns->geom.pgszoob, GFP_KERNEL);
|
||||
/*
|
||||
* We allocate memory with GFP_NOFS because a flash FS may
|
||||
* utilize this. If it is holding an FS lock, then gets here,
|
||||
* then kmalloc runs writeback which goes to the FS again
|
||||
* and deadlocks. This was seen in practice.
|
||||
*/
|
||||
mypage->byte = kmalloc(ns->geom.pgszoob, GFP_NOFS);
|
||||
if (mypage->byte == NULL) {
|
||||
NS_ERR("prog_page: error allocating memory for page %d\n", ns->regs.row);
|
||||
return -1;
|
||||
|
|
|
@ -24,7 +24,11 @@
|
|||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#ifdef CONFIG_40x
|
||||
#include <asm/ibm405.h>
|
||||
#else
|
||||
#include <asm/ibm44x.h>
|
||||
#endif
|
||||
|
||||
struct ndfc_nand_mtd {
|
||||
struct mtd_info mtd;
|
||||
|
@ -230,7 +234,11 @@ static int ndfc_nand_probe(struct platform_device *pdev)
|
|||
struct ndfc_controller *ndfc = &ndfc_ctrl;
|
||||
unsigned long long phys = settings->ndfc_erpn | res->start;
|
||||
|
||||
#ifndef CONFIG_PHYS_64BIT
|
||||
ndfc->ndfcbase = ioremap((phys_addr_t)phys, res->end - res->start + 1);
|
||||
#else
|
||||
ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1);
|
||||
#endif
|
||||
if (!ndfc->ndfcbase) {
|
||||
printk(KERN_ERR "NDFC: ioremap failed\n");
|
||||
return -EIO;
|
||||
|
|
|
@ -60,8 +60,8 @@
|
|||
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <asm/arch/regs-nand.h>
|
||||
#include <asm/arch/nand.h>
|
||||
#include <asm/plat-s3c/regs-nand.h>
|
||||
#include <asm/plat-s3c/nand.h>
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_S3C2410_HWECC
|
||||
static int hardware_ecc = 1;
|
||||
|
|
|
@ -40,4 +40,27 @@ config MTD_ONENAND_OTP
|
|||
|
||||
OTP block is fully-guaranteed to be a valid block.
|
||||
|
||||
config MTD_ONENAND_2X_PROGRAM
|
||||
bool "OneNAND 2X program support"
|
||||
help
|
||||
The 2X Program is an extension of Program Operation.
|
||||
Since the device is equipped with two DataRAMs, and two-plane NAND
|
||||
Flash memory array, these two component enables simultaneous program
|
||||
of 4KiB. Plane1 has only even blocks such as block0, block2, block4
|
||||
while Plane2 has only odd blocks such as block1, block3, block5.
|
||||
So MTD regards it as 4KiB page size and 256KiB block size
|
||||
|
||||
Now the following chips support it. (KFXXX16Q2M)
|
||||
Demux: KFG2G16Q2M, KFH4G16Q2M, KFW8G16Q2M,
|
||||
Mux: KFM2G16Q2M, KFN4G16Q2M,
|
||||
|
||||
And more recent chips
|
||||
|
||||
config MTD_ONENAND_SIM
|
||||
tristate "OneNAND simulator support"
|
||||
depends on MTD_PARTITIONS
|
||||
help
|
||||
The simulator may simulate various OneNAND flash chips for the
|
||||
OneNAND MTD layer.
|
||||
|
||||
endif # MTD_ONENAND
|
||||
|
|
|
@ -8,4 +8,7 @@ obj-$(CONFIG_MTD_ONENAND) += onenand.o
|
|||
# Board specific.
|
||||
obj-$(CONFIG_MTD_ONENAND_GENERIC) += generic.o
|
||||
|
||||
# Simulator
|
||||
obj-$(CONFIG_MTD_ONENAND_SIM) += onenand_sim.o
|
||||
|
||||
onenand-objs = onenand_base.o onenand_bbt.o
|
||||
|
|
File diff suppressed because it is too large
Load Diff
495
drivers/mtd/onenand/onenand_sim.c
Normal file
495
drivers/mtd/onenand/onenand_sim.c
Normal 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");
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
81
fs/Kconfig
81
fs/Kconfig
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
422
fs/jffs2/compr.c
422
fs/jffs2/compr.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
108
fs/jffs2/compr_lzo.c
Normal 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();
|
||||
}
|
|
@ -104,7 +104,7 @@ static int jffs2_rtime_decompress(unsigned char *data_in,
|
|||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct jffs2_compressor jffs2_rtime_comp = {
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.*" ----------------- */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
47
include/asm-blackfin/nand.h
Normal file
47
include/asm-blackfin/nand.h
Normal 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;
|
||||
};
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -40,6 +40,7 @@ typedef enum {
|
|||
FL_POINT,
|
||||
FL_XIP_WHILE_ERASING,
|
||||
FL_XIP_WHILE_WRITING,
|
||||
FL_SHUTDOWN,
|
||||
FL_UNKNOWN
|
||||
} flstate_t;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue
Block a user