Commit a8e98d6d authored by Linus Torvalds's avatar Linus Torvalds

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

* git://git.infradead.org/mtd-2.6: (120 commits)
  [MTD] Fix mtdoops.c compilation
  [MTD] [NOR] fix startup lock when using multiple nor flash chips
  [MTD] [DOC200x] eccbuf is statically defined and always evaluate to true
  [MTD] Fix maps/physmap.c compilation with CONFIG_PM
  [MTD] onenand: Add panic_write function to the onenand driver
  [MTD] mtdoops: Use the panic_write function when present
  [MTD] Add mtd panic_write function pointer
  [MTD] [NAND] Freescale enhanced Local Bus Controller FCM NAND support.
  [MTD] physmap.c: Add support for multiple resources
  [MTD] [NAND] Fix misparenthesization introduced by commit 78b65179...
  [MTD] [NAND] Fix Blackfin NFC ECC calculating bug with page size 512 bytes
  [MTD] [NAND] Remove wrong operation in PM function of the BF54x NFC driver
  [MTD] [NAND] Remove unused variable in plat_nand_remove
  [MTD] Unlocking all Intel flash that is locked on power up.
  [MTD] [NAND] at91_nand: Make mtdparts option can override board info
  [MTD] mtdoops: Various minor cleanups
  [MTD] mtdoops: Ensure sequential write to the buffer
  [MTD] mtdoops: Perform write operations in a workqueue
  [MTD] mtdoops: Add further error return code checking
  [MTD] [NOR] Test devtype, not definition in flash_probe(), drivers/mtd/devices/lart.c
  ...
parents f0f1b336 f9f7dd22
...@@ -150,6 +150,14 @@ config MTD_AFS_PARTS ...@@ -150,6 +150,14 @@ config MTD_AFS_PARTS
for your particular device. It won't happen automatically. The for your particular device. It won't happen automatically. The
'armflash' map driver (CONFIG_MTD_ARMFLASH) does this, for example. 'armflash' map driver (CONFIG_MTD_ARMFLASH) does this, for example.
config MTD_OF_PARTS
tristate "Flash partition map based on OF description"
depends on PPC_OF && MTD_PARTITIONS
help
This provides a partition parsing function which derives
the partition map from the children of the flash node,
as described in Documentation/powerpc/booting-without-of.txt.
comment "User Modules And Translation Layers" comment "User Modules And Translation Layers"
config MTD_CHAR config MTD_CHAR
...@@ -286,6 +294,9 @@ config MTD_OOPS ...@@ -286,6 +294,9 @@ config MTD_OOPS
buffer in a flash partition where it can be read back at some buffer in a flash partition where it can be read back at some
later point. later point.
To use, add console=ttyMTDx to the kernel command line,
where x is the MTD device number to use.
source "drivers/mtd/chips/Kconfig" source "drivers/mtd/chips/Kconfig"
source "drivers/mtd/maps/Kconfig" source "drivers/mtd/maps/Kconfig"
......
...@@ -11,6 +11,7 @@ obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o ...@@ -11,6 +11,7 @@ obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o
obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
obj-$(CONFIG_MTD_AFS_PARTS) += afs.o obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o
# 'Users' - code which presents functionality to userspace. # 'Users' - code which presents functionality to userspace.
obj-$(CONFIG_MTD_CHAR) += mtdchar.o obj-$(CONFIG_MTD_CHAR) += mtdchar.o
......
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
#define I82802AC 0x00ac #define I82802AC 0x00ac
#define MANUFACTURER_ST 0x0020 #define MANUFACTURER_ST 0x0020
#define M50LPW080 0x002F #define M50LPW080 0x002F
#define AT49BV640D 0x02de
static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
...@@ -157,6 +158,47 @@ static void cfi_tell_features(struct cfi_pri_intelext *extp) ...@@ -157,6 +158,47 @@ static void cfi_tell_features(struct cfi_pri_intelext *extp)
} }
#endif #endif
/* Atmel chips don't use the same PRI format as Intel chips */
static void fixup_convert_atmel_pri(struct mtd_info *mtd, void *param)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
struct cfi_pri_intelext *extp = cfi->cmdset_priv;
struct cfi_pri_atmel atmel_pri;
uint32_t features = 0;
/* Reverse byteswapping */
extp->FeatureSupport = cpu_to_le32(extp->FeatureSupport);
extp->BlkStatusRegMask = cpu_to_le16(extp->BlkStatusRegMask);
extp->ProtRegAddr = cpu_to_le16(extp->ProtRegAddr);
memcpy(&atmel_pri, extp, sizeof(atmel_pri));
memset((char *)extp + 5, 0, sizeof(*extp) - 5);
printk(KERN_ERR "atmel Features: %02x\n", atmel_pri.Features);
if (atmel_pri.Features & 0x01) /* chip erase supported */
features |= (1<<0);
if (atmel_pri.Features & 0x02) /* erase suspend supported */
features |= (1<<1);
if (atmel_pri.Features & 0x04) /* program suspend supported */
features |= (1<<2);
if (atmel_pri.Features & 0x08) /* simultaneous operations supported */
features |= (1<<9);
if (atmel_pri.Features & 0x20) /* page mode read supported */
features |= (1<<7);
if (atmel_pri.Features & 0x40) /* queued erase supported */
features |= (1<<4);
if (atmel_pri.Features & 0x80) /* Protection bits supported */
features |= (1<<6);
extp->FeatureSupport = features;
/* burst write mode not supported */
cfi->cfiq->BufWriteTimeoutTyp = 0;
cfi->cfiq->BufWriteTimeoutMax = 0;
}
#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE #ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */ /* Some Intel Strata Flash prior to FPO revision C has bugs in this area */
static void fixup_intel_strataflash(struct mtd_info *mtd, void* param) static void fixup_intel_strataflash(struct mtd_info *mtd, void* param)
...@@ -227,13 +269,20 @@ static void fixup_use_write_buffers(struct mtd_info *mtd, void *param) ...@@ -227,13 +269,20 @@ static void fixup_use_write_buffers(struct mtd_info *mtd, void *param)
/* /*
* Some chips power-up with all sectors locked by default. * Some chips power-up with all sectors locked by default.
*/ */
static void fixup_use_powerup_lock(struct mtd_info *mtd, void *param) static void fixup_unlock_powerup_lock(struct mtd_info *mtd, void *param)
{ {
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
if (cfip->FeatureSupport&32) {
printk(KERN_INFO "Using auto-unlock on power-up/resume\n" ); printk(KERN_INFO "Using auto-unlock on power-up/resume\n" );
mtd->flags |= MTD_STUPID_LOCK; mtd->flags |= MTD_POWERUP_LOCK;
}
} }
static struct cfi_fixup cfi_fixup_table[] = { static struct cfi_fixup cfi_fixup_table[] = {
{ CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL },
#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE #ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL }, { CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL },
#endif #endif
...@@ -245,7 +294,7 @@ static struct cfi_fixup cfi_fixup_table[] = { ...@@ -245,7 +294,7 @@ static struct cfi_fixup cfi_fixup_table[] = {
#endif #endif
{ CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, NULL }, { CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, NULL },
{ CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb, NULL }, { CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb, NULL },
{ MANUFACTURER_INTEL, 0x891c, fixup_use_powerup_lock, NULL, }, { MANUFACTURER_INTEL, CFI_ID_ANY, fixup_unlock_powerup_lock, NULL, },
{ 0, 0, NULL, NULL } { 0, 0, NULL, NULL }
}; };
...@@ -277,7 +326,7 @@ read_pri_intelext(struct map_info *map, __u16 adr) ...@@ -277,7 +326,7 @@ read_pri_intelext(struct map_info *map, __u16 adr)
return NULL; return NULL;
if (extp->MajorVersion != '1' || if (extp->MajorVersion != '1' ||
(extp->MinorVersion < '0' || extp->MinorVersion > '4')) { (extp->MinorVersion < '0' || extp->MinorVersion > '5')) {
printk(KERN_ERR " Unknown Intel/Sharp Extended Query " printk(KERN_ERR " Unknown Intel/Sharp Extended Query "
"version %c.%c.\n", extp->MajorVersion, "version %c.%c.\n", extp->MajorVersion,
extp->MinorVersion); extp->MinorVersion);
...@@ -752,6 +801,7 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long ...@@ -752,6 +801,7 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long
static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode) static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
{ {
int ret; int ret;
DECLARE_WAITQUEUE(wait, current);
retry: retry:
if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING
...@@ -808,6 +858,20 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr ...@@ -808,6 +858,20 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
spin_unlock(contender->mutex); spin_unlock(contender->mutex);
} }
/* Check if we already have suspended erase
* on this chip. Sleep. */
if (mode == FL_ERASING && shared->erasing
&& shared->erasing->oldstate == FL_ERASING) {
spin_unlock(&shared->lock);
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
spin_unlock(chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
spin_lock(chip->mutex);
goto retry;
}
/* We now own it */ /* We now own it */
shared->writing = chip; shared->writing = chip;
if (mode == FL_ERASING) if (mode == FL_ERASING)
...@@ -2294,7 +2358,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) ...@@ -2294,7 +2358,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd)
struct flchip *chip; struct flchip *chip;
int ret = 0; int ret = 0;
if ((mtd->flags & MTD_STUPID_LOCK) if ((mtd->flags & MTD_POWERUP_LOCK)
&& extp && (extp->FeatureSupport & (1 << 5))) && extp && (extp->FeatureSupport & (1 << 5)))
cfi_intelext_save_locks(mtd); cfi_intelext_save_locks(mtd);
...@@ -2405,7 +2469,7 @@ static void cfi_intelext_resume(struct mtd_info *mtd) ...@@ -2405,7 +2469,7 @@ static void cfi_intelext_resume(struct mtd_info *mtd)
spin_unlock(chip->mutex); spin_unlock(chip->mutex);
} }
if ((mtd->flags & MTD_STUPID_LOCK) if ((mtd->flags & MTD_POWERUP_LOCK)
&& extp && (extp->FeatureSupport & (1 << 5))) && extp && (extp->FeatureSupport & (1 << 5)))
cfi_intelext_restore_locks(mtd); cfi_intelext_restore_locks(mtd);
} }
......
...@@ -185,6 +185,10 @@ static void fixup_convert_atmel_pri(struct mtd_info *mtd, void *param) ...@@ -185,6 +185,10 @@ static void fixup_convert_atmel_pri(struct mtd_info *mtd, void *param)
extp->TopBottom = 2; extp->TopBottom = 2;
else else
extp->TopBottom = 3; extp->TopBottom = 3;
/* burst write mode not supported */
cfi->cfiq->BufWriteTimeoutTyp = 0;
cfi->cfiq->BufWriteTimeoutMax = 0;
} }
static void fixup_use_secsi(struct mtd_info *mtd, void *param) static void fixup_use_secsi(struct mtd_info *mtd, void *param)
...@@ -213,10 +217,11 @@ static void fixup_use_atmel_lock(struct mtd_info *mtd, void *param) ...@@ -213,10 +217,11 @@ static void fixup_use_atmel_lock(struct mtd_info *mtd, void *param)
{ {
mtd->lock = cfi_atmel_lock; mtd->lock = cfi_atmel_lock;
mtd->unlock = cfi_atmel_unlock; mtd->unlock = cfi_atmel_unlock;
mtd->flags |= MTD_STUPID_LOCK; mtd->flags |= MTD_POWERUP_LOCK;
} }
static struct cfi_fixup cfi_fixup_table[] = { static struct cfi_fixup cfi_fixup_table[] = {
{ CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL },
#ifdef AMD_BOOTLOC_BUG #ifdef AMD_BOOTLOC_BUG
{ CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock, NULL }, { CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock, NULL },
#endif #endif
...@@ -229,7 +234,6 @@ static struct cfi_fixup cfi_fixup_table[] = { ...@@ -229,7 +234,6 @@ static struct cfi_fixup cfi_fixup_table[] = {
#if !FORCE_WORD_WRITE #if !FORCE_WORD_WRITE
{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, }, { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, },
#endif #endif
{ CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL },
{ 0, 0, NULL, NULL } { 0, 0, NULL, NULL }
}; };
static struct cfi_fixup jedec_fixup_table[] = { static struct cfi_fixup jedec_fixup_table[] = {
...@@ -338,10 +342,12 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) ...@@ -338,10 +342,12 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
/* Modify the unlock address if we are in compatibility mode */ /* Modify the unlock address if we are in compatibility mode */
if ( /* x16 in x8 mode */ if ( /* x16 in x8 mode */
((cfi->device_type == CFI_DEVICETYPE_X8) && ((cfi->device_type == CFI_DEVICETYPE_X8) &&
(cfi->cfiq->InterfaceDesc == 2)) || (cfi->cfiq->InterfaceDesc ==
CFI_INTERFACE_X8_BY_X16_ASYNC)) ||
/* x32 in x16 mode */ /* x32 in x16 mode */
((cfi->device_type == CFI_DEVICETYPE_X16) && ((cfi->device_type == CFI_DEVICETYPE_X16) &&
(cfi->cfiq->InterfaceDesc == 4))) (cfi->cfiq->InterfaceDesc ==
CFI_INTERFACE_X16_BY_X32_ASYNC)))
{ {
cfi->addr_unlock1 = 0xaaa; cfi->addr_unlock1 = 0xaaa;
cfi->addr_unlock2 = 0x555; cfi->addr_unlock2 = 0x555;
......
...@@ -370,27 +370,27 @@ static void print_cfi_ident(struct cfi_ident *cfip) ...@@ -370,27 +370,27 @@ static void print_cfi_ident(struct cfi_ident *cfip)
printk("Device size: 0x%X bytes (%d MiB)\n", 1 << cfip->DevSize, 1<< (cfip->DevSize - 20)); printk("Device size: 0x%X bytes (%d MiB)\n", 1 << cfip->DevSize, 1<< (cfip->DevSize - 20));
printk("Flash Device Interface description: 0x%4.4X\n", cfip->InterfaceDesc); printk("Flash Device Interface description: 0x%4.4X\n", cfip->InterfaceDesc);
switch(cfip->InterfaceDesc) { switch(cfip->InterfaceDesc) {
case 0: case CFI_INTERFACE_X8_ASYNC:
printk(" - x8-only asynchronous interface\n"); printk(" - x8-only asynchronous interface\n");
break; break;
case 1: case CFI_INTERFACE_X16_ASYNC:
printk(" - x16-only asynchronous interface\n"); printk(" - x16-only asynchronous interface\n");
break; break;
case 2: case CFI_INTERFACE_X8_BY_X16_ASYNC:
printk(" - supports x8 and x16 via BYTE# with asynchronous interface\n"); printk(" - supports x8 and x16 via BYTE# with asynchronous interface\n");
break; break;
case 3: case CFI_INTERFACE_X32_ASYNC:
printk(" - x32-only asynchronous interface\n"); printk(" - x32-only asynchronous interface\n");
break; break;
case 4: case CFI_INTERFACE_X16_BY_X32_ASYNC:
printk(" - supports x16 and x32 via Word# with asynchronous interface\n"); printk(" - supports x16 and x32 via Word# with asynchronous interface\n");
break; break;
case 65535: case CFI_INTERFACE_NOT_ALLOWED:
printk(" - Not Allowed / Reserved\n"); printk(" - Not Allowed / Reserved\n");
break; break;
......
...@@ -112,7 +112,7 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi ...@@ -112,7 +112,7 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi
max_chips = 1; max_chips = 1;
} }
mapsize = (max_chips + BITS_PER_LONG-1) / BITS_PER_LONG; mapsize = sizeof(long) * ( (max_chips + BITS_PER_LONG-1) / BITS_PER_LONG );
chip_map = kzalloc(mapsize, GFP_KERNEL); chip_map = kzalloc(mapsize, GFP_KERNEL);
if (!chip_map) { if (!chip_map) {
printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name); printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name);
......
This diff is collapsed.
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* mtdparts=<mtddef>[;<mtddef] * mtdparts=<mtddef>[;<mtddef]
* <mtddef> := <mtd-id>:<partdef>[,<partdef>] * <mtddef> := <mtd-id>:<partdef>[,<partdef>]
* <partdef> := <size>[@offset][<name>][ro] * <partdef> := <size>[@offset][<name>][ro][lk]
* <mtd-id> := unique name used in mapping driver/device (mtd->name) * <mtd-id> := unique name used in mapping driver/device (mtd->name)
* <size> := standard linux memsize OR "-" to denote all remaining space * <size> := standard linux memsize OR "-" to denote all remaining space
* <name> := '(' NAME ')' * <name> := '(' NAME ')'
...@@ -143,6 +143,13 @@ static struct mtd_partition * newpart(char *s, ...@@ -143,6 +143,13 @@ static struct mtd_partition * newpart(char *s,
s += 2; s += 2;
} }
/* if lk is found do NOT unlock the MTD partition*/
if (strncmp(s, "lk", 2) == 0)
{
mask_flags |= MTD_POWERUP_LOCK;
s += 2;
}
/* test if more partitions are following */ /* test if more partitions are following */
if (*s == ',') if (*s == ',')
{ {
......
...@@ -632,7 +632,7 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -632,7 +632,7 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
len = ((from | 0x1ff) + 1) - from; len = ((from | 0x1ff) + 1) - from;
/* The ECC will not be calculated correctly if less than 512 is read */ /* The ECC will not be calculated correctly if less than 512 is read */
if (len != 0x200 && eccbuf) if (len != 0x200)
printk(KERN_WARNING printk(KERN_WARNING
"ECC needs a full sector read (adr: %lx size %lx)\n", "ECC needs a full sector read (adr: %lx size %lx)\n",
(long) from, (long) len); (long) from, (long) len);
...@@ -896,7 +896,7 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -896,7 +896,7 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
/* Let the caller know we completed it */ /* Let the caller know we completed it */
*retlen += len; *retlen += len;
if (eccbuf) { {
unsigned char x[8]; unsigned char x[8];
size_t dummy; size_t dummy;
int ret; int ret;
......
...@@ -748,7 +748,7 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -748,7 +748,7 @@ static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
WriteDOC(DoC_GetDataOffset(mtd, &fto), docptr, Mplus_FlashCmd); WriteDOC(DoC_GetDataOffset(mtd, &fto), docptr, Mplus_FlashCmd);
/* On interleaved devices the flags for 2nd half 512 are before data */ /* On interleaved devices the flags for 2nd half 512 are before data */
if (eccbuf && before) if (before)
fto -= 2; fto -= 2;
/* issue the Serial Data In command to initial the Page Program process */ /* issue the Serial Data In command to initial the Page Program process */
......
...@@ -323,7 +323,7 @@ static int flash_probe (void) ...@@ -323,7 +323,7 @@ static int flash_probe (void)
/* put the flash back into command mode */ /* put the flash back into command mode */
write32 (DATA_TO_FLASH (READ_ARRAY),0x00000000); write32 (DATA_TO_FLASH (READ_ARRAY),0x00000000);
return (manufacturer == FLASH_MANUFACTURER && (devtype == FLASH_DEVICE_16mbit_TOP || FLASH_DEVICE_16mbit_BOTTOM)); return (manufacturer == FLASH_MANUFACTURER && (devtype == FLASH_DEVICE_16mbit_TOP || devtype == FLASH_DEVICE_16mbit_BOTTOM));
} }
/* /*
......
...@@ -420,7 +420,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -420,7 +420,7 @@ static int dataflash_write(struct mtd_info *mtd, loff_t to, size_t len,
status = dataflash_waitready(priv->spi); status = dataflash_waitready(priv->spi);
/* Check result of the compare operation */ /* Check result of the compare operation */
if ((status & (1 << 6)) == 1) { if (status & (1 << 6)) {
printk(KERN_ERR "%s: compare page %u, err %d\n", printk(KERN_ERR "%s: compare page %u, err %d\n",
spi->dev.bus_id, pageaddr, status); spi->dev.bus_id, pageaddr, status);
remaining = 0; remaining = 0;
......
...@@ -110,13 +110,6 @@ config MTD_SUN_UFLASH ...@@ -110,13 +110,6 @@ config MTD_SUN_UFLASH
Sun Microsystems boardsets. This driver will require CFI support Sun Microsystems boardsets. This driver will require CFI support
in the kernel, so if you did not enable CFI previously, do that now. in the kernel, so if you did not enable CFI previously, do that now.
config MTD_PNC2000
tristate "CFI Flash device mapped on Photron PNC-2000"
depends on X86 && MTD_CFI && MTD_PARTITIONS
help
PNC-2000 is the name of Network Camera product from PHOTRON
Ltd. in Japan. It uses CFI-compliant flash.
config MTD_SC520CDP config MTD_SC520CDP
tristate "CFI Flash device mapped on AMD SC520 CDP" tristate "CFI Flash device mapped on AMD SC520 CDP"
depends on X86 && MTD_CFI && MTD_CONCAT depends on X86 && MTD_CFI && MTD_CONCAT
...@@ -576,7 +569,7 @@ config MTD_BAST_MAXSIZE ...@@ -576,7 +569,7 @@ config MTD_BAST_MAXSIZE
default "4" default "4"
config MTD_SHARP_SL config MTD_SHARP_SL
bool "ROM mapped on Sharp SL Series" tristate "ROM mapped on Sharp SL Series"
depends on ARCH_PXA depends on ARCH_PXA
help help
This enables access to the flash chip on the Sharp SL Series of PDAs. This enables access to the flash chip on the Sharp SL Series of PDAs.
......
...@@ -28,7 +28,6 @@ obj-$(CONFIG_MTD_PHYSMAP) += physmap.o ...@@ -28,7 +28,6 @@ obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o
obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o
obj-$(CONFIG_MTD_PMC_MSP_RAMROOT)+= pmcmsp-ramroot.o obj-$(CONFIG_MTD_PMC_MSP_RAMROOT)+= pmcmsp-ramroot.o
obj-$(CONFIG_MTD_PNC2000) += pnc2000.o
obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o
obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o
obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o
......
...@@ -20,11 +20,15 @@ ...@@ -20,11 +20,15 @@
#include <linux/mtd/map.h> #include <linux/mtd/map.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <linux/mtd/physmap.h> #include <linux/mtd/physmap.h>
#include <linux/mtd/concat.h>
#include <asm/io.h> #include <asm/io.h>
#define MAX_RESOURCES 4
struct physmap_flash_info { struct physmap_flash_info {
struct mtd_info *mtd; struct mtd_info *mtd[MAX_RESOURCES];
struct map_info map; struct mtd_info *cmtd;
struct map_info map[MAX_RESOURCES];
struct resource *res; struct resource *res;
#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_PARTITIONS
int nr_parts; int nr_parts;
...@@ -32,11 +36,11 @@ struct physmap_flash_info { ...@@ -32,11 +36,11 @@ struct physmap_flash_info {
#endif #endif
}; };
static int physmap_flash_remove(struct platform_device *dev) static int physmap_flash_remove(struct platform_device *dev)
{ {
struct physmap_flash_info *info; struct physmap_flash_info *info;
struct physmap_flash_data *physmap_data; struct physmap_flash_data *physmap_data;
int i;
info = platform_get_drvdata(dev); info = platform_get_drvdata(dev);
if (info == NULL) if (info == NULL)
...@@ -45,24 +49,33 @@ static int physmap_flash_remove(struct platform_device *dev) ...@@ -45,24 +49,33 @@ static int physmap_flash_remove(struct platform_device *dev)
physmap_data = dev->dev.platform_data; physmap_data = dev->dev.platform_data;
if (info->mtd != NULL) { #ifdef CONFIG_MTD_CONCAT
if (info->cmtd != info->mtd[0]) {
del_mtd_device(info->cmtd);
mtd_concat_destroy(info->cmtd);
}
#endif
for (i = 0; i < MAX_RESOURCES; i++) {
if (info->mtd[i] != NULL) {
#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_PARTITIONS
if (info->nr_parts) { if (info->nr_parts) {
del_mtd_partitions(info->mtd); del_mtd_partitions(info->mtd[i]);
kfree(info->parts); kfree(info->parts);
} else if (physmap_data->nr_parts) { } else if (physmap_data->nr_parts) {
del_mtd_partitions(info->mtd); del_mtd_partitions(info->mtd[i]);
} else { } else {
del_mtd_device(info->mtd); del_mtd_device(info->mtd[i]);
} }
#else #else
del_mtd_device(info->mtd); del_mtd_device(info->mtd[i]);
#endif #endif
map_destroy(info->mtd); map_destroy(info->mtd[i]);
} }
if (info->map.virt != NULL) if (info->map[i].virt != NULL)
iounmap(info->map.virt); iounmap(info->map[i].virt);
}
if (info->res != NULL) { if (info->res != NULL) {
release_resource(info->res); release_resource(info->res);
...@@ -82,16 +95,14 @@ static int physmap_flash_probe(struct platform_device *dev) ...@@ -82,16 +95,14 @@ static int physmap_flash_probe(struct platform_device *dev)
struct physmap_flash_data *physmap_data; struct physmap_flash_data *physmap_data;
struct physmap_flash_info *info; struct physmap_flash_info *info;
const char **probe_type; const char **probe_type;
int err; int err = 0;
int i;
int devices_found = 0;
physmap_data = dev->dev.platform_data; physmap_data = dev->dev.platform_data;
if (physmap_data == NULL) if (physmap_data == NULL)
return -ENODEV; return -ENODEV;
printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n",
(unsigned long long)(dev->resource->end - dev->resource->start + 1),
(unsigned long long)dev->resource->start);
info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL); info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL);
if (info == NULL) { if (info == NULL) {
err = -ENOMEM; err = -ENOMEM;
...@@ -100,8 +111,13 @@ static int physmap_flash_probe(struct platform_device *dev) ...@@ -100,8 +111,13 @@ static int physmap_flash_probe(struct platform_device *dev)
platform_set_drvdata(dev, info); platform_set_drvdata(dev, info);
info->res = request_mem_region(dev->resource->start, for (i = 0; i < dev->num_resources; i++) {
dev->resource->end - dev->resource->start + 1, printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n",
(unsigned long long)(dev->resource[i].end - dev->resource[i].start + 1),
(unsigned long long)dev->resource[i].start);
info->res = request_mem_region(dev->resource[i].start,
dev->resource[i].end - dev->resource[i].start + 1,
dev->dev.bus_id); dev->dev.bus_id);
if (info->res == NULL) { if (info->res == NULL) {
dev_err(&dev->dev, "Could not reserve memory region\n"); dev_err(&dev->dev, "Could not reserve memory region\n");
...@@ -109,47 +125,69 @@ static int physmap_flash_probe(struct platform_device *dev) ...@@ -109,47 +125,69 @@ static int physmap_flash_probe(struct platform_device *dev)
goto err_out; goto err_out;
} }
info->map.name = dev->dev.bus_id; info->map[i].name = dev->dev.bus_id;
info->map.phys = dev->resource->start; info->map[i].phys = dev->resource[i].start;
info->map.size = dev->resource->end - dev->resource->start + 1; info->map[i].size = dev->resource[i].end - dev->resource[i].start + 1;
info->map.bankwidth = physmap_data->width; info->map[i].bankwidth = physmap_data->width;
info->map.set_vpp = physmap_data->set_vpp; info->map[i].set_vpp = physmap_data->set_vpp;
info->map.virt = ioremap(info->map.phys, info->map.size); info->map[i].virt = ioremap(info->map[i].phys, info->map[i].size);
if (info->map.virt == NULL) { if (info->map[i].virt == NULL) {
dev_err(&dev->dev, "Failed to ioremap flash region\n"); dev_err(&dev->dev, "Failed to ioremap flash region\n");
err = EIO; err = EIO;
goto err_out; goto err_out;
} }
simple_map_init(&info->map); simple_map_init(&info->map[i]);
probe_type = rom_probe_types; probe_type = rom_probe_types;
for (; info->mtd == NULL && *probe_type != NULL; probe_type++) for (; info->mtd[i] == NULL && *probe_type != NULL; probe_type++)
info->mtd = do_map_probe(*probe_type, &info->map); info->mtd[i] = do_map_probe(*probe_type, &info->map[i]);
if (info->mtd == NULL) { if (info->mtd[i] == NULL) {
dev_err(&dev->dev, "map_probe failed\n"); dev_err(&dev->dev, "map_probe failed\n");
err = -ENXIO; err = -ENXIO;
goto err_out; goto err_out;
} else {
devices_found++;
}
info->mtd[i]->owner = THIS_MODULE;
} }
info->mtd->owner = THIS_MODULE;
if (devices_found == 1) {
info->cmtd = info->mtd[0];
} else if (devices_found > 1) {
/*
* We detected multiple devices. Concatenate them together.
*/
#ifdef CONFIG_MTD_CONCAT
info->cmtd = mtd_concat_create(info->mtd, devices_found, dev->dev.bus_id);
if (info->cmtd == NULL)
err = -ENXIO;
#else
printk(KERN_ERR "physmap-flash: multiple devices "
"found but MTD concat support disabled.\n");
err = -ENXIO;
#endif
}
if (err)
goto err_out;
#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_PARTITIONS
err = parse_mtd_partitions(info->mtd, part_probe_types, &info->parts, 0); err = parse_mtd_partitions(info->cmtd, part_probe_types, &info->parts, 0);
if (err > 0) { if (err > 0) {
add_mtd_partitions(info->mtd, info->parts, err); add_mtd_partitions(info->cmtd, info->parts, err);
return 0; return 0;
} }
if (physmap_data->nr_parts) { if (physmap_data->nr_parts) {
printk(KERN_NOTICE "Using physmap partition information\n"); printk(KERN_NOTICE "Using physmap partition information\n");
add_mtd_partitions(info->mtd, physmap_data->parts, add_mtd_partitions(info->cmtd, physmap_data->parts,
physmap_data->nr_parts); physmap_data->nr_parts);
return 0; return 0;
} }
#endif #endif
add_mtd_device(info->mtd); add_mtd_device(info->cmtd);
return 0; return 0;
err_out: err_out:
...@@ -162,9 +200,11 @@ static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state ...@@ -162,9 +200,11 @@ static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state
{ {
struct physmap_flash_info *info = platform_get_drvdata(dev); struct physmap_flash_info *info = platform_get_drvdata(dev);
int ret = 0; int ret = 0;
int i;
if (info) if (info)
ret = info->mtd->suspend(info->mtd); for (i = 0; i < MAX_RESOURCES; i++)
ret |= info->mtd[i]->suspend(info->mtd[i]);
return ret; return ret;
} }
...@@ -172,27 +212,35 @@ static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state ...@@ -172,27 +212,35 @@ static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state
static int physmap_flash_resume(struct platform_device *dev) static int physmap_flash_resume(struct platform_device *dev)
{ {
struct physmap_flash_info *info = platform_get_drvdata(dev); struct physmap_flash_info *info = platform_get_drvdata(dev);
int i;
if (info) if (info)
info->mtd->resume(info->mtd); for (i = 0; i < MAX_RESOURCES; i++)
info->mtd[i]->resume(info->mtd[i]);
return 0; return 0;
} }
static void physmap_flash_shutdown(struct platform_device *dev) static void physmap_flash_shutdown(struct platform_device *dev)
{ {
struct physmap_flash_info *info = platform_get_drvdata(dev); struct physmap_flash_info *info = platform_get_drvdata(dev);
if (info && info->mtd->suspend(info->mtd) == 0) int i;
info->mtd->resume(info->mtd);
for (i = 0; i < MAX_RESOURCES; i++)
if (info && info->mtd[i]->suspend(info->mtd[i]) == 0)
info->mtd[i]->resume(info->mtd[i]);
} }
#else
#define physmap_flash_suspend NULL
#define physmap_flash_resume NULL
#define physmap_flash_shutdown NULL
#endif #endif
static struct platform_driver physmap_flash_driver = { static struct platform_driver physmap_flash_driver = {
.probe = physmap_flash_probe, .probe = physmap_flash_probe,
.remove = physmap_flash_remove, .remove = physmap_flash_remove,
#ifdef CONFIG_PM
.suspend = physmap_flash_suspend, .suspend = physmap_flash_suspend,
.resume = physmap_flash_resume, .resume = physmap_flash_resume,
.shutdown = physmap_flash_shutdown, .shutdown = physmap_flash_shutdown,
#endif
.driver = { .driver = {
.name = "physmap-flash", .name = "physmap-flash",
}, },
......
...@@ -80,64 +80,6 @@ static int parse_obsolete_partitions(struct of_device *dev, ...@@ -80,64 +80,6 @@ static int parse_obsolete_partitions(struct of_device *dev,
return nr_parts; return nr_parts;
} }
static int __devinit parse_partitions(struct of_flash *info,
struct of_device *dev)
{
const char *partname;
static const char *part_probe_types[]
= { "cmdlinepart", "RedBoot", NULL };
struct device_node *dp = dev->node, *pp;
int nr_parts, i;
/* First look for RedBoot table or partitions on the command
* line, these take precedence over device tree information */
nr_parts = parse_mtd_partitions(info->mtd, part_probe_types,
&info->parts, 0);
if (nr_parts > 0) {
add_mtd_partitions(info->mtd, info->parts, nr_parts);
return 0;
}
/* First count the subnodes */
nr_parts = 0;
for (pp = dp->child; pp; pp = pp->sibling)
nr_parts++;
if (nr_parts == 0)
return parse_obsolete_partitions(dev, info, dp);
info->parts = kzalloc(nr_parts * sizeof(*info->parts),
GFP_KERNEL);
if (!info->parts)
return -ENOMEM;
for (pp = dp->child, i = 0; pp; pp = pp->sibling, i++) {
const u32 *reg;
int len;
reg = of_get_property(pp, "reg", &len);
if (!reg || (len != 2*sizeof(u32))) {
dev_err(&dev->dev, "Invalid 'reg' on %s\n",
dp->full_name);
kfree(info->parts);
info->parts = NULL;
return -EINVAL;
}
info->parts[i].offset = reg[0];
info->parts[i].size = reg[1];
partname = of_get_property(pp, "label", &len);
if (!partname)
partname = of_get_property(pp, "name", &len);
info->parts[i].name = (char *)partname;
if (of_get_property(pp, "read-only", &len))
info->parts[i].mask_flags = MTD_WRITEABLE;
}
return nr_parts;
}
#else /* MTD_PARTITIONS */ #else /* MTD_PARTITIONS */
#define OF_FLASH_PARTS(info) (0) #define OF_FLASH_PARTS(info) (0)
#define parse_partitions(info, dev) (0) #define parse_partitions(info, dev) (0)
...@@ -212,6 +154,10 @@ static struct mtd_info * __devinit obsolete_probe(struct of_device *dev, ...@@ -212,6 +154,10 @@ static struct mtd_info * __devinit obsolete_probe(struct of_device *dev,
static int __devinit of_flash_probe(struct of_device *dev, static int __devinit of_flash_probe(struct of_device *dev,
const struct of_device_id *match) const struct of_device_id *match)
{ {
#ifdef CONFIG_MTD_PARTITIONS
static const char *part_probe_types[]
= { "cmdlinepart", "RedBoot", NULL };
#endif
struct device_node *dp = dev->node; struct device_node *dp = dev->node;
struct resource res; struct resource res;
struct of_flash *info; struct of_flash *info;
...@@ -274,13 +220,33 @@ static int __devinit of_flash_probe(struct of_device *dev, ...@@ -274,13 +220,33 @@ static int __devinit of_flash_probe(struct of_device *dev,
} }
info->mtd->owner = THIS_MODULE; info->mtd->owner = THIS_MODULE;
err = parse_partitions(info, dev); #ifdef CONFIG_MTD_PARTITIONS
/* First look for RedBoot table or partitions on the command
* line, these take precedence over device tree information */
err = parse_mtd_partitions(info->mtd, part_probe_types,
&info->parts, 0);
if (err < 0) if (err < 0)
goto err_out; return err;
#ifdef CONFIG_MTD_OF_PARTS
if (err == 0) {
err = of_mtd_parse_partitions(&dev->dev, info->mtd,
dp, &info->parts);
if (err < 0)
return err;
}
#endif
if (err == 0) {
err = parse_obsolete_partitions(dev, info, dp);
if (err < 0)
return err;
}
if (err > 0) if (err > 0)
add_mtd_partitions(info->mtd, OF_FLASH_PARTS(info), err); add_mtd_partitions(info->mtd, info->parts, err);
else else
#endif
add_mtd_device(info->mtd); add_mtd_device(info->mtd);
return 0; return 0;
......
/*
* pnc2000.c - mapper for Photron PNC-2000 board.
*
* Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp>
*
* This code is GPL
*
* $Id: pnc2000.c,v 1.18 2005/11/07 11:14:28 gleixner Exp $
*/
#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>
#define WINDOW_ADDR 0xbf000000
#define WINDOW_SIZE 0x00400000
/*
* MAP DRIVER STUFF
*/
static struct map_info pnc_map = {
.name = "PNC-2000",
.size = WINDOW_SIZE,
.bankwidth = 4,
.phys = 0xFFFFFFFF,
.virt = (void __iomem *)WINDOW_ADDR,
};
/*
* MTD 'PARTITIONING' STUFF
*/
static struct mtd_partition pnc_partitions[3] = {
{
.name = "PNC-2000 boot firmware",
.size = 0x20000,
.offset = 0
},
{
.name = "PNC-2000 kernel",
.size = 0x1a0000,
.offset = 0x20000
},
{
.name = "PNC-2000 filesystem",
.size = 0x240000,
.offset = 0x1c0000
}
};
/*
* This is the master MTD device for which all the others are just
* auto-relocating aliases.
*/
static struct mtd_info *mymtd;
static int __init init_pnc2000(void)
{
printk(KERN_NOTICE "Photron PNC-2000 flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
simple_map_init(&pnc_map);
mymtd = do_map_probe("cfi_probe", &pnc_map);
if (mymtd) {
mymtd->owner = THIS_MODULE;
return add_mtd_partitions(mymtd, pnc_partitions, 3);
}
return -ENXIO;
}
static void __exit cleanup_pnc2000(void)
{
if (mymtd) {
del_mtd_partitions(mymtd);
map_destroy(mymtd);
}
}
module_init(init_pnc2000);
module_exit(cleanup_pnc2000);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Crossnet Co. <info@crossnet.co.jp>");
MODULE_DESCRIPTION("MTD map driver for Photron PNC-2000 board");
...@@ -79,7 +79,7 @@ scb2_fixup_mtd(struct mtd_info *mtd) ...@@ -79,7 +79,7 @@ scb2_fixup_mtd(struct mtd_info *mtd)
struct cfi_private *cfi = map->fldrv_priv; struct cfi_private *cfi = map->fldrv_priv;
/* barf if this doesn't look right */ /* barf if this doesn't look right */
if (cfi->cfiq->InterfaceDesc != 1) { if (cfi->cfiq->InterfaceDesc != CFI_INTERFACE_X16_ASYNC) {
printk(KERN_ERR MODNAME ": unsupported InterfaceDesc: %#x\n", printk(KERN_ERR MODNAME ": unsupported InterfaceDesc: %#x\n",
cfi->cfiq->InterfaceDesc); cfi->cfiq->InterfaceDesc);
return -1; return -1;
......
...@@ -248,9 +248,9 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) ...@@ -248,9 +248,9 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
return -EBUSY; return -EBUSY;
} }
mutex_init(&new->lock);
list_add_tail(&new->list, &tr->devs); list_add_tail(&new->list, &tr->devs);
added: added:
mutex_init(&new->lock);
if (!tr->writesect) if (!tr->writesect)
new->readonly = 1; new->readonly = 1;
......
...@@ -481,6 +481,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, ...@@ -481,6 +481,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
{ {
struct mtd_oob_buf buf; struct mtd_oob_buf buf;
struct mtd_oob_ops ops; struct mtd_oob_ops ops;
uint32_t retlen;
if(!(file->f_mode & 2)) if(!(file->f_mode & 2))
return -EPERM; return -EPERM;
...@@ -520,8 +521,11 @@ static int mtd_ioctl(struct inode *inode, struct file *file, ...@@ -520,8 +521,11 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
buf.start &= ~(mtd->oobsize - 1); buf.start &= ~(mtd->oobsize - 1);
ret = mtd->write_oob(mtd, buf.start, &ops); ret = mtd->write_oob(mtd, buf.start, &ops);
if (copy_to_user(argp + sizeof(uint32_t), &ops.oobretlen, if (ops.oobretlen > 0xFFFFFFFFU)
sizeof(uint32_t))) ret = -EOVERFLOW;
retlen = ops.oobretlen;
if (copy_to_user(&((struct mtd_oob_buf *)argp)->length,
&retlen, sizeof(buf.length)))
ret = -EFAULT; ret = -EFAULT;
kfree(ops.oobbuf); kfree(ops.oobbuf);
......
...@@ -61,7 +61,7 @@ int add_mtd_device(struct mtd_info *mtd) ...@@ -61,7 +61,7 @@ int add_mtd_device(struct mtd_info *mtd)
/* Some chips always power up locked. Unlock them now */ /* Some chips always power up locked. Unlock them now */
if ((mtd->flags & MTD_WRITEABLE) if ((mtd->flags & MTD_WRITEABLE)
&& (mtd->flags & MTD_STUPID_LOCK) && mtd->unlock) { && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) {
if (mtd->unlock(mtd, 0, mtd->size)) if (mtd->unlock(mtd, 0, mtd->size))
printk(KERN_WARNING printk(KERN_WARNING
"%s: unlock failed, " "%s: unlock failed, "
......
...@@ -28,19 +28,26 @@ ...@@ -28,19 +28,26 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/delay.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#define OOPS_PAGE_SIZE 4096 #define OOPS_PAGE_SIZE 4096
static struct mtdoops_context { struct mtdoops_context {
int mtd_index; int mtd_index;
struct work_struct work; struct work_struct work_erase;
struct work_struct work_write;
struct mtd_info *mtd; struct mtd_info *mtd;
int oops_pages; int oops_pages;
int nextpage; int nextpage;
int nextcount; int nextcount;
void *oops_buf; void *oops_buf;
/* writecount and disabling ready are spin lock protected */
spinlock_t writecount_lock;
int ready; int ready;
int writecount; int writecount;
} oops_cxt; } oops_cxt;
...@@ -62,9 +69,6 @@ static int mtdoops_erase_block(struct mtd_info *mtd, int offset) ...@@ -62,9 +69,6 @@ static int mtdoops_erase_block(struct mtd_info *mtd, int offset)
erase.mtd = mtd; erase.mtd = mtd;
erase.callback = mtdoops_erase_callback; erase.callback = mtdoops_erase_callback;
erase.addr = offset; erase.addr = offset;
if (mtd->erasesize < OOPS_PAGE_SIZE)
erase.len = OOPS_PAGE_SIZE;
else
erase.len = mtd->erasesize; erase.len = mtd->erasesize;
erase.priv = (u_long)&wait_q; erase.priv = (u_long)&wait_q;
...@@ -87,7 +91,7 @@ static int mtdoops_erase_block(struct mtd_info *mtd, int offset) ...@@ -87,7 +91,7 @@ static int mtdoops_erase_block(struct mtd_info *mtd, int offset)
return 0; return 0;
} }
static int mtdoops_inc_counter(struct mtdoops_context *cxt) static void mtdoops_inc_counter(struct mtdoops_context *cxt)
{ {
struct mtd_info *mtd = cxt->mtd; struct mtd_info *mtd = cxt->mtd;
size_t retlen; size_t retlen;
...@@ -103,25 +107,30 @@ static int mtdoops_inc_counter(struct mtdoops_context *cxt) ...@@ -103,25 +107,30 @@ static int mtdoops_inc_counter(struct mtdoops_context *cxt)
ret = mtd->read(mtd, cxt->nextpage * OOPS_PAGE_SIZE, 4, ret = mtd->read(mtd, cxt->nextpage * OOPS_PAGE_SIZE, 4,
&retlen, (u_char *) &count); &retlen, (u_char *) &count);
if ((retlen != 4) || (ret < 0)) { if ((retlen != 4) || ((ret < 0) && (ret != -EUCLEAN))) {
printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)" printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)"
", err %d.\n", cxt->nextpage * OOPS_PAGE_SIZE, ", err %d.\n", cxt->nextpage * OOPS_PAGE_SIZE,
retlen, ret); retlen, ret);
return 1; schedule_work(&cxt->work_erase);
return;
} }
/* See if we need to erase the next block */ /* See if we need to erase the next block */
if (count != 0xffffffff) if (count != 0xffffffff) {
return 1; schedule_work(&cxt->work_erase);
return;
}
printk(KERN_DEBUG "mtdoops: Ready %d, %d (no erase)\n", printk(KERN_DEBUG "mtdoops: Ready %d, %d (no erase)\n",
cxt->nextpage, cxt->nextcount); cxt->nextpage, cxt->nextcount);
cxt->ready = 1; cxt->ready = 1;
return 0;
} }
static void mtdoops_prepare(struct mtdoops_context *cxt) /* Scheduled work - when we can't proceed without erasing a block */
static void mtdoops_workfunc_erase(struct work_struct *work)
{ {
struct mtdoops_context *cxt =
container_of(work, struct mtdoops_context, work_erase);
struct mtd_info *mtd = cxt->mtd; struct mtd_info *mtd = cxt->mtd;
int i = 0, j, ret, mod; int i = 0, j, ret, mod;
...@@ -136,8 +145,14 @@ static void mtdoops_prepare(struct mtdoops_context *cxt) ...@@ -136,8 +145,14 @@ static void mtdoops_prepare(struct mtdoops_context *cxt)
cxt->nextpage = 0; cxt->nextpage = 0;
} }
while (mtd->block_isbad && while (mtd->block_isbad) {
mtd->block_isbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE)) { ret = mtd->block_isbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE);
if (!ret)
break;
if (ret < 0) {
printk(KERN_ERR "mtdoops: block_isbad failed, aborting.\n");
return;
}
badblock: badblock:
printk(KERN_WARNING "mtdoops: Bad block at %08x\n", printk(KERN_WARNING "mtdoops: Bad block at %08x\n",
cxt->nextpage * OOPS_PAGE_SIZE); cxt->nextpage * OOPS_PAGE_SIZE);
...@@ -154,34 +169,72 @@ static void mtdoops_prepare(struct mtdoops_context *cxt) ...@@ -154,34 +169,72 @@ static void mtdoops_prepare(struct mtdoops_context *cxt)
for (j = 0, ret = -1; (j < 3) && (ret < 0); j++) for (j = 0, ret = -1; (j < 3) && (ret < 0); j++)
ret = mtdoops_erase_block(mtd, cxt->nextpage * OOPS_PAGE_SIZE); ret = mtdoops_erase_block(mtd, cxt->nextpage * OOPS_PAGE_SIZE);
if (ret >= 0) {
printk(KERN_DEBUG "mtdoops: Ready %d, %d \n", cxt->nextpage, cxt->nextcount);
cxt->ready = 1;
return;
}
if (mtd->block_markbad && (ret == -EIO)) {
ret = mtd->block_markbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE);
if (ret < 0) { if (ret < 0) {
if (mtd->block_markbad) printk(KERN_ERR "mtdoops: block_markbad failed, aborting.\n");
mtd->block_markbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE); return;
goto badblock;
} }
}
goto badblock;
}
printk(KERN_DEBUG "mtdoops: Ready %d, %d \n", cxt->nextpage, cxt->nextcount); static void mtdoops_write(struct mtdoops_context *cxt, int panic)
{
struct mtd_info *mtd = cxt->mtd;
size_t retlen;
int ret;
cxt->ready = 1; if (cxt->writecount < OOPS_PAGE_SIZE)
memset(cxt->oops_buf + cxt->writecount, 0xff,
OOPS_PAGE_SIZE - cxt->writecount);
if (panic)
ret = mtd->panic_write(mtd, cxt->nextpage * OOPS_PAGE_SIZE,
OOPS_PAGE_SIZE, &retlen, cxt->oops_buf);
else
ret = mtd->write(mtd, cxt->nextpage * OOPS_PAGE_SIZE,
OOPS_PAGE_SIZE, &retlen, cxt->oops_buf);
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);
mtdoops_inc_counter(cxt);
} }
static void mtdoops_workfunc(struct work_struct *work)
static void mtdoops_workfunc_write(struct work_struct *work)
{ {
struct mtdoops_context *cxt = struct mtdoops_context *cxt =
container_of(work, struct mtdoops_context, work); container_of(work, struct mtdoops_context, work_write);
mtdoops_prepare(cxt); mtdoops_write(cxt, 0);
} }
static int find_next_position(struct mtdoops_context *cxt) static void find_next_position(struct mtdoops_context *cxt)
{ {
struct mtd_info *mtd = cxt->mtd; struct mtd_info *mtd = cxt->mtd;
int page, maxpos = 0; int ret, page, maxpos = 0;
u32 count, maxcount = 0xffffffff; u32 count, maxcount = 0xffffffff;
size_t retlen; size_t retlen;
for (page = 0; page < cxt->oops_pages; page++) { for (page = 0; page < cxt->oops_pages; page++) {
mtd->read(mtd, page * OOPS_PAGE_SIZE, 4, &retlen, (u_char *) &count); ret = mtd->read(mtd, page * OOPS_PAGE_SIZE, 4, &retlen, (u_char *) &count);
if ((retlen != 4) || ((ret < 0) && (ret != -EUCLEAN))) {
printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)"
", err %d.\n", page * OOPS_PAGE_SIZE, retlen, ret);
continue;
}
if (count == 0xffffffff) if (count == 0xffffffff)
continue; continue;
if (maxcount == 0xffffffff) { if (maxcount == 0xffffffff) {
...@@ -205,20 +258,19 @@ static int find_next_position(struct mtdoops_context *cxt) ...@@ -205,20 +258,19 @@ static int find_next_position(struct mtdoops_context *cxt)
cxt->ready = 1; cxt->ready = 1;
printk(KERN_DEBUG "mtdoops: Ready %d, %d (first init)\n", printk(KERN_DEBUG "mtdoops: Ready %d, %d (first init)\n",
cxt->nextpage, cxt->nextcount); cxt->nextpage, cxt->nextcount);
return 0; return;
} }
cxt->nextpage = maxpos; cxt->nextpage = maxpos;
cxt->nextcount = maxcount; cxt->nextcount = maxcount;
return mtdoops_inc_counter(cxt); mtdoops_inc_counter(cxt);
} }
static void mtdoops_notify_add(struct mtd_info *mtd) static void mtdoops_notify_add(struct mtd_info *mtd)
{ {
struct mtdoops_context *cxt = &oops_cxt; struct mtdoops_context *cxt = &oops_cxt;
int ret;
if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0) if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0)
return; return;
...@@ -229,14 +281,18 @@ static void mtdoops_notify_add(struct mtd_info *mtd) ...@@ -229,14 +281,18 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
return; return;
} }
if (mtd->erasesize < OOPS_PAGE_SIZE) {
printk(KERN_ERR "Eraseblock size of MTD partition %d too small\n",
mtd->index);
return;
}
cxt->mtd = mtd; cxt->mtd = mtd;
cxt->oops_pages = mtd->size / OOPS_PAGE_SIZE; cxt->oops_pages = mtd->size / OOPS_PAGE_SIZE;
ret = find_next_position(cxt); find_next_position(cxt);
if (ret == 1)
mtdoops_prepare(cxt);
printk(KERN_DEBUG "mtdoops: Attached to MTD device %d\n", mtd->index); printk(KERN_INFO "mtdoops: Attached to MTD device %d\n", mtd->index);
} }
static void mtdoops_notify_remove(struct mtd_info *mtd) static void mtdoops_notify_remove(struct mtd_info *mtd)
...@@ -254,31 +310,28 @@ static void mtdoops_console_sync(void) ...@@ -254,31 +310,28 @@ static void mtdoops_console_sync(void)
{ {
struct mtdoops_context *cxt = &oops_cxt; struct mtdoops_context *cxt = &oops_cxt;
struct mtd_info *mtd = cxt->mtd; struct mtd_info *mtd = cxt->mtd;
size_t retlen; unsigned long flags;
int ret;
if (!cxt->ready || !mtd) if (!cxt->ready || !mtd || cxt->writecount == 0)
return; return;
if (cxt->writecount == 0) /*
* Once ready is 0 and we've held the lock no further writes to the
* buffer will happen
*/
spin_lock_irqsave(&cxt->writecount_lock, flags);
if (!cxt->ready) {
spin_unlock_irqrestore(&cxt->writecount_lock, flags);
return; 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->ready = 0;
cxt->writecount = 0; spin_unlock_irqrestore(&cxt->writecount_lock, flags);
if ((retlen != OOPS_PAGE_SIZE) || (ret < 0)) if (mtd->panic_write && in_interrupt())
printk(KERN_ERR "mtdoops: Write failure at %d (%td of %d written), err %d.\n", /* Interrupt context, we're going to panic so try and log */
cxt->nextpage * OOPS_PAGE_SIZE, retlen, OOPS_PAGE_SIZE, ret); mtdoops_write(cxt, 1);
else
ret = mtdoops_inc_counter(cxt); schedule_work(&cxt->work_write);
if (ret == 1)
schedule_work(&cxt->work);
} }
static void static void
...@@ -286,7 +339,7 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count) ...@@ -286,7 +339,7 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count)
{ {
struct mtdoops_context *cxt = co->data; struct mtdoops_context *cxt = co->data;
struct mtd_info *mtd = cxt->mtd; struct mtd_info *mtd = cxt->mtd;
int i; unsigned long flags;
if (!oops_in_progress) { if (!oops_in_progress) {
mtdoops_console_sync(); mtdoops_console_sync();
...@@ -296,6 +349,13 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count) ...@@ -296,6 +349,13 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count)
if (!cxt->ready || !mtd) if (!cxt->ready || !mtd)
return; return;
/* Locking on writecount ensures sequential writes to the buffer */
spin_lock_irqsave(&cxt->writecount_lock, flags);
/* Check ready status didn't change whilst waiting for the lock */
if (!cxt->ready)
return;
if (cxt->writecount == 0) { if (cxt->writecount == 0) {
u32 *stamp = cxt->oops_buf; u32 *stamp = cxt->oops_buf;
*stamp = cxt->nextcount; *stamp = cxt->nextcount;
...@@ -305,10 +365,13 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count) ...@@ -305,10 +365,13 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count)
if ((count + cxt->writecount) > OOPS_PAGE_SIZE) if ((count + cxt->writecount) > OOPS_PAGE_SIZE)
count = OOPS_PAGE_SIZE - cxt->writecount; count = OOPS_PAGE_SIZE - cxt->writecount;
for (i = 0; i < count; i++, s++) memcpy(cxt->oops_buf + cxt->writecount, s, count);
*((char *)(cxt->oops_buf) + cxt->writecount + i) = *s; cxt->writecount += count;
cxt->writecount = cxt->writecount + count; spin_unlock_irqrestore(&cxt->writecount_lock, flags);
if (cxt->writecount == OOPS_PAGE_SIZE)
mtdoops_console_sync();
} }
static int __init mtdoops_console_setup(struct console *co, char *options) static int __init mtdoops_console_setup(struct console *co, char *options)
...@@ -334,7 +397,6 @@ static struct console mtdoops_console = { ...@@ -334,7 +397,6 @@ static struct console mtdoops_console = {
.write = mtdoops_console_write, .write = mtdoops_console_write,
.setup = mtdoops_console_setup, .setup = mtdoops_console_setup,
.unblank = mtdoops_console_sync, .unblank = mtdoops_console_sync,
.flags = CON_PRINTBUFFER,
.index = -1, .index = -1,
.data = &oops_cxt, .data = &oops_cxt,
}; };
...@@ -347,11 +409,12 @@ static int __init mtdoops_console_init(void) ...@@ -347,11 +409,12 @@ static int __init mtdoops_console_init(void)
cxt->oops_buf = vmalloc(OOPS_PAGE_SIZE); cxt->oops_buf = vmalloc(OOPS_PAGE_SIZE);
if (!cxt->oops_buf) { if (!cxt->oops_buf) {
printk(KERN_ERR "Failed to allocate oops buffer workspace\n"); printk(KERN_ERR "Failed to allocate mtdoops buffer workspace\n");
return -ENOMEM; return -ENOMEM;
} }
INIT_WORK(&cxt->work, mtdoops_workfunc); INIT_WORK(&cxt->work_erase, mtdoops_workfunc_erase);
INIT_WORK(&cxt->work_write, mtdoops_workfunc_write);
register_console(&mtdoops_console); register_console(&mtdoops_console);
register_mtd_user(&mtdoops_notifier); register_mtd_user(&mtdoops_notifier);
......
...@@ -151,6 +151,20 @@ static int part_write (struct mtd_info *mtd, loff_t to, size_t len, ...@@ -151,6 +151,20 @@ static int part_write (struct mtd_info *mtd, loff_t to, size_t len,
len, retlen, buf); len, retlen, buf);
} }
static int part_panic_write (struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct mtd_part *part = PART(mtd);
if (!(mtd->flags & MTD_WRITEABLE))
return -EROFS;
if (to >= mtd->size)
len = 0;
else if (to + len > mtd->size)
len = mtd->size - to;
return part->master->panic_write (part->master, to + part->offset,
len, retlen, buf);
}
static int part_write_oob(struct mtd_info *mtd, loff_t to, static int part_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops) struct mtd_oob_ops *ops)
{ {
...@@ -352,6 +366,9 @@ int add_mtd_partitions(struct mtd_info *master, ...@@ -352,6 +366,9 @@ int add_mtd_partitions(struct mtd_info *master,
slave->mtd.read = part_read; slave->mtd.read = part_read;
slave->mtd.write = part_write; slave->mtd.write = part_write;
if (master->panic_write)
slave->mtd.panic_write = part_panic_write;
if(master->point && master->unpoint){ if(master->point && master->unpoint){
slave->mtd.point = part_point; slave->mtd.point = part_point;
slave->mtd.unpoint = part_unpoint; slave->mtd.unpoint = part_unpoint;
......
...@@ -93,7 +93,7 @@ config MTD_NAND_AU1550 ...@@ -93,7 +93,7 @@ config MTD_NAND_AU1550
config MTD_NAND_BF5XX config MTD_NAND_BF5XX
tristate "Blackfin on-chip NAND Flash Controller driver" tristate "Blackfin on-chip NAND Flash Controller driver"
depends on BF54x && MTD_NAND depends on (BF54x || BF52x) && MTD_NAND
help help
This enables the Blackfin on-chip NAND flash controller This enables the Blackfin on-chip NAND flash controller
...@@ -283,6 +283,12 @@ config MTD_NAND_CM_X270 ...@@ -283,6 +283,12 @@ config MTD_NAND_CM_X270
tristate "Support for NAND Flash on CM-X270 modules" tristate "Support for NAND Flash on CM-X270 modules"
depends on MTD_NAND && MACH_ARMCORE depends on MTD_NAND && MACH_ARMCORE
config MTD_NAND_PASEMI
tristate "NAND support for PA Semi PWRficient"
depends on MTD_NAND && PPC_PASEMI
help
Enables support for NAND Flash interface on PA Semi PWRficient
based boards
config MTD_NAND_NANDSIM config MTD_NAND_NANDSIM
tristate "Support for NAND Flash Simulator" tristate "Support for NAND Flash Simulator"
...@@ -306,4 +312,22 @@ config MTD_ALAUDA ...@@ -306,4 +312,22 @@ config MTD_ALAUDA
These two (and possibly other) Alauda-based cardreaders for These two (and possibly other) Alauda-based cardreaders for
SmartMedia and xD allow raw flash access. SmartMedia and xD allow raw flash access.
config MTD_NAND_ORION
tristate "NAND Flash support for Marvell Orion SoC"
depends on ARCH_ORION && MTD_NAND
help
This enables the NAND flash controller on Orion machines.
No board specific support is done by this driver, each board
must advertise a platform_device for the driver to attach.
config MTD_NAND_FSL_ELBC
tristate "NAND support for Freescale eLBC controllers"
depends on MTD_NAND && PPC_OF
help
Various Freescale chips, including the 8313, include a NAND Flash
Controller Module with built-in hardware ECC capabilities.
Enabling this option will enable you to use this to control
external NAND devices.
endif # MTD_NAND endif # MTD_NAND
...@@ -29,5 +29,8 @@ obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o ...@@ -29,5 +29,8 @@ obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o
obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o
obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o
obj-$(CONFIG_MTD_ALAUDA) += alauda.o obj-$(CONFIG_MTD_ALAUDA) += alauda.o
obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o
obj-$(CONFIG_MTD_NAND_ORION) += orion_nand.o
obj-$(CONFIG_MTD_NAND_FSL_ELBC) += fsl_elbc_nand.o
nand-objs := nand_base.o nand_bbt.o nand-objs := nand_base.o nand_bbt.o
...@@ -156,14 +156,14 @@ static int __init at91_nand_probe(struct platform_device *pdev) ...@@ -156,14 +156,14 @@ static int __init at91_nand_probe(struct platform_device *pdev)
} }
#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_PARTITIONS
if (host->board->partition_info)
partitions = host->board->partition_info(mtd->size, &num_partitions);
#ifdef CONFIG_MTD_CMDLINE_PARTS #ifdef CONFIG_MTD_CMDLINE_PARTS
else {
mtd->name = "at91_nand"; mtd->name = "at91_nand";
num_partitions = parse_mtd_partitions(mtd, part_probes, &partitions, 0); num_partitions = parse_mtd_partitions(mtd, part_probes,
} &partitions, 0);
#endif #endif
if (num_partitions <= 0 && host->board->partition_info)
partitions = host->board->partition_info(mtd->size,
&num_partitions);
if ((!partitions) || (num_partitions == 0)) { if ((!partitions) || (num_partitions == 0)) {
printk(KERN_ERR "at91_nand: No parititions defined, or unsupported device.\n"); printk(KERN_ERR "at91_nand: No parititions defined, or unsupported device.\n");
......
...@@ -74,7 +74,22 @@ static int hardware_ecc = 1; ...@@ -74,7 +74,22 @@ static int hardware_ecc = 1;
static int hardware_ecc; static int hardware_ecc;
#endif #endif
static unsigned short bfin_nfc_pin_req[] = {P_NAND_CE, P_NAND_RB, 0}; static unsigned short bfin_nfc_pin_req[] =
{P_NAND_CE,
P_NAND_RB,
P_NAND_D0,
P_NAND_D1,
P_NAND_D2,
P_NAND_D3,
P_NAND_D4,
P_NAND_D5,
P_NAND_D6,
P_NAND_D7,
P_NAND_WE,
P_NAND_RE,
P_NAND_CLE,
P_NAND_ALE,
0};
/* /*
* Data structures for bf5xx nand flash controller driver * Data structures for bf5xx nand flash controller driver
...@@ -278,7 +293,6 @@ static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd, ...@@ -278,7 +293,6 @@ static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd,
u16 ecc0, ecc1; u16 ecc0, ecc1;
u32 code[2]; u32 code[2];
u8 *p; u8 *p;
int bytes = 3, i;
/* first 4 bytes ECC code for 256 page size */ /* first 4 bytes ECC code for 256 page size */
ecc0 = bfin_read_NFC_ECC0(); ecc0 = bfin_read_NFC_ECC0();
...@@ -288,19 +302,24 @@ static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd, ...@@ -288,19 +302,24 @@ static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd,
dev_dbg(info->device, "returning ecc 0x%08x\n", code[0]); dev_dbg(info->device, "returning ecc 0x%08x\n", code[0]);
/* first 3 bytes in ecc_code for 256 page size */
p = (u8 *) code;
memcpy(ecc_code, p, 3);
/* second 4 bytes ECC code for 512 page size */ /* second 4 bytes ECC code for 512 page size */
if (page_size == 512) { if (page_size == 512) {
ecc0 = bfin_read_NFC_ECC2(); ecc0 = bfin_read_NFC_ECC2();
ecc1 = bfin_read_NFC_ECC3(); ecc1 = bfin_read_NFC_ECC3();
code[1] = (ecc0 & 0x3FF) | ((ecc1 & 0x3FF) << 11); code[1] = (ecc0 & 0x3FF) | ((ecc1 & 0x3FF) << 11);
bytes = 6;
/* second 3 bytes in ecc_code for second 256
* bytes of 512 page size
*/
p = (u8 *) (code + 1);
memcpy((ecc_code + 3), p, 3);
dev_dbg(info->device, "returning ecc 0x%08x\n", code[1]); 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; return 0;
} }
...@@ -507,12 +526,13 @@ static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info) ...@@ -507,12 +526,13 @@ static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info)
init_completion(&info->dma_completion); init_completion(&info->dma_completion);
#ifdef CONFIG_BF54x
/* Setup DMAC1 channel mux for NFC which shared with SDH */ /* Setup DMAC1 channel mux for NFC which shared with SDH */
val = bfin_read_DMAC1_PERIMUX(); val = bfin_read_DMAC1_PERIMUX();
val &= 0xFFFE; val &= 0xFFFE;
bfin_write_DMAC1_PERIMUX(val); bfin_write_DMAC1_PERIMUX(val);
SSYNC(); SSYNC();
#endif
/* Request NFC DMA channel */ /* Request NFC DMA channel */
ret = request_dma(CH_NFC, "BF5XX NFC driver"); ret = request_dma(CH_NFC, "BF5XX NFC driver");
if (ret < 0) { if (ret < 0) {
...@@ -744,9 +764,6 @@ static int bf5xx_nand_resume(struct platform_device *dev) ...@@ -744,9 +764,6 @@ static int bf5xx_nand_resume(struct platform_device *dev)
{ {
struct bf5xx_nand_info *info = platform_get_drvdata(dev); struct bf5xx_nand_info *info = platform_get_drvdata(dev);
if (info)
bf5xx_nand_hw_init(info);
return 0; return 0;
} }
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#undef DEBUG #undef DEBUG
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h> #include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/rslib.h> #include <linux/rslib.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/delay.h> #include <linux/delay.h>
...@@ -52,6 +53,7 @@ ...@@ -52,6 +53,7 @@
struct cafe_priv { struct cafe_priv {
struct nand_chip nand; struct nand_chip nand;
struct mtd_partition *parts;
struct pci_dev *pdev; struct pci_dev *pdev;
void __iomem *mmio; void __iomem *mmio;
struct rs_control *rs; struct rs_control *rs;
...@@ -84,6 +86,10 @@ static unsigned int numtimings; ...@@ -84,6 +86,10 @@ static unsigned int numtimings;
static int timing[3]; static int timing[3];
module_param_array(timing, int, &numtimings, 0644); module_param_array(timing, int, &numtimings, 0644);
#ifdef CONFIG_MTD_PARTITIONS
static const char *part_probes[] = { "RedBoot", NULL };
#endif
/* Hrm. Why isn't this already conditional on something in the struct device? */ /* Hrm. Why isn't this already conditional on something in the struct device? */
#define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) #define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0)
...@@ -620,7 +626,9 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, ...@@ -620,7 +626,9 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
{ {
struct mtd_info *mtd; struct mtd_info *mtd;
struct cafe_priv *cafe; struct cafe_priv *cafe;
struct mtd_partition *parts;
uint32_t ctrl; uint32_t ctrl;
int nr_parts;
int err = 0; int err = 0;
/* Very old versions shared the same PCI ident for all three /* Very old versions shared the same PCI ident for all three
...@@ -787,7 +795,18 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, ...@@ -787,7 +795,18 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
goto out_irq; goto out_irq;
pci_set_drvdata(pdev, mtd); pci_set_drvdata(pdev, mtd);
/* We register the whole device first, separate from the partitions */
add_mtd_device(mtd); add_mtd_device(mtd);
#ifdef CONFIG_MTD_PARTITIONS
nr_parts = parse_mtd_partitions(mtd, part_probes, &parts, 0);
if (nr_parts > 0) {
cafe->parts = parts;
dev_info(&cafe->pdev->dev, "%d RedBoot partitions found\n", nr_parts);
add_mtd_partitions(mtd, parts, nr_parts);
}
#endif
goto out; goto out;
out_irq: out_irq:
......
This diff is collapsed.
...@@ -2469,8 +2469,12 @@ int nand_scan_tail(struct mtd_info *mtd) ...@@ -2469,8 +2469,12 @@ int nand_scan_tail(struct mtd_info *mtd)
chip->ecc.write_oob = nand_write_oob_std; chip->ecc.write_oob = nand_write_oob_std;
case NAND_ECC_HW_SYNDROME: case NAND_ECC_HW_SYNDROME:
if (!chip->ecc.calculate || !chip->ecc.correct || if ((!chip->ecc.calculate || !chip->ecc.correct ||
!chip->ecc.hwctl) { !chip->ecc.hwctl) &&
(!chip->ecc.read_page ||
chip->ecc.read_page == nand_read_page_hwecc ||
!chip->ecc.write_page ||
chip->ecc.write_page == nand_write_page_hwecc)) {
printk(KERN_WARNING "No ECC functions supplied, " printk(KERN_WARNING "No ECC functions supplied, "
"Hardware ECC not possible\n"); "Hardware ECC not possible\n");
BUG(); BUG();
......
/*
* drivers/mtd/nand/orion_nand.c
*
* NAND support for Marvell Orion SoC platforms
*
* Tzachi Perelstein <tzachi@marvell.com>
*
* 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/slab.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/sizes.h>
#include <asm/arch/platform.h>
#include <asm/arch/hardware.h>
#ifdef CONFIG_MTD_CMDLINE_PARTS
static const char *part_probes[] = { "cmdlinepart", NULL };
#endif
static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *nc = mtd->priv;
struct orion_nand_data *board = nc->priv;
u32 offs;
if (cmd == NAND_CMD_NONE)
return;
if (ctrl & NAND_CLE)
offs = (1 << board->cle);
else if (ctrl & NAND_ALE)
offs = (1 << board->ale);
else
return;
if (nc->options & NAND_BUSWIDTH_16)
offs <<= 1;
writeb(cmd, nc->IO_ADDR_W + offs);
}
static int __init orion_nand_probe(struct platform_device *pdev)
{
struct mtd_info *mtd;
struct nand_chip *nc;
struct orion_nand_data *board;
void __iomem *io_base;
int ret = 0;
#ifdef CONFIG_MTD_PARTITIONS
struct mtd_partition *partitions = NULL;
int num_part = 0;
#endif
nc = kzalloc(sizeof(struct nand_chip) + sizeof(struct mtd_info), GFP_KERNEL);
if (!nc) {
printk(KERN_ERR "orion_nand: failed to allocate device structure.\n");
ret = -ENOMEM;
goto no_res;
}
mtd = (struct mtd_info *)(nc + 1);
io_base = ioremap(pdev->resource[0].start,
pdev->resource[0].end - pdev->resource[0].start + 1);
if (!io_base) {
printk(KERN_ERR "orion_nand: ioremap failed\n");
ret = -EIO;
goto no_res;
}
board = pdev->dev.platform_data;
mtd->priv = nc;
mtd->owner = THIS_MODULE;
nc->priv = board;
nc->IO_ADDR_R = nc->IO_ADDR_W = io_base;
nc->cmd_ctrl = orion_nand_cmd_ctrl;
nc->ecc.mode = NAND_ECC_SOFT;
if (board->width == 16)
nc->options |= NAND_BUSWIDTH_16;
platform_set_drvdata(pdev, mtd);
if (nand_scan(mtd, 1)) {
ret = -ENXIO;
goto no_dev;
}
#ifdef CONFIG_MTD_PARTITIONS
#ifdef CONFIG_MTD_CMDLINE_PARTS
mtd->name = "orion_nand";
num_part = parse_mtd_partitions(mtd, part_probes, &partitions, 0);
#endif
/* If cmdline partitions have been passed, let them be used */
if (num_part <= 0) {
num_part = board->nr_parts;
partitions = board->parts;
}
if (partitions && num_part > 0)
ret = add_mtd_partitions(mtd, partitions, num_part);
else
ret = add_mtd_device(mtd);
#else
ret = add_mtd_device(mtd);
#endif
if (ret) {
nand_release(mtd);
goto no_dev;
}
return 0;
no_dev:
platform_set_drvdata(pdev, NULL);
iounmap(io_base);
no_res:
kfree(nc);
return ret;
}
static int __devexit orion_nand_remove(struct platform_device *pdev)
{
struct mtd_info *mtd = platform_get_drvdata(pdev);
struct nand_chip *nc = mtd->priv;
nand_release(mtd);
iounmap(nc->IO_ADDR_W);
kfree(nc);
return 0;
}
static struct platform_driver orion_nand_driver = {
.probe = orion_nand_probe,
.remove = orion_nand_remove,
.driver = {
.name = "orion_nand",
.owner = THIS_MODULE,
},
};
static int __init orion_nand_init(void)
{
return platform_driver_register(&orion_nand_driver);
}
static void __exit orion_nand_exit(void)
{
platform_driver_unregister(&orion_nand_driver);
}
module_init(orion_nand_init);
module_exit(orion_nand_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tzachi Perelstein");
MODULE_DESCRIPTION("NAND glue for Orion platforms");
This diff is collapsed.
...@@ -110,7 +110,9 @@ static int __init plat_nand_probe(struct platform_device *pdev) ...@@ -110,7 +110,9 @@ static int __init plat_nand_probe(struct platform_device *pdev)
static int __devexit plat_nand_remove(struct platform_device *pdev) static int __devexit plat_nand_remove(struct platform_device *pdev)
{ {
struct plat_nand_data *data = platform_get_drvdata(pdev); struct plat_nand_data *data = platform_get_drvdata(pdev);
#ifdef CONFIG_MTD_PARTITIONS
struct platform_nand_data *pdata = pdev->dev.platform_data; struct platform_nand_data *pdata = pdev->dev.platform_data;
#endif
nand_release(&data->mtd); nand_release(&data->mtd);
#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_PARTITIONS
......
...@@ -120,6 +120,8 @@ struct s3c2410_nand_info { ...@@ -120,6 +120,8 @@ struct s3c2410_nand_info {
int sel_bit; int sel_bit;
int mtd_count; int mtd_count;
unsigned long save_nfconf;
enum s3c_cpu_type cpu_type; enum s3c_cpu_type cpu_type;
}; };
...@@ -364,23 +366,21 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, ...@@ -364,23 +366,21 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) { ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) {
/* calculate the bit position of the error */ /* calculate the bit position of the error */
bit = (diff2 >> 2) & 1; bit = ((diff2 >> 3) & 1) |
bit |= (diff2 >> 3) & 2; ((diff2 >> 4) & 2) |
bit |= (diff2 >> 4) & 4; ((diff2 >> 5) & 4);
/* calculate the byte position of the error */ /* calculate the byte position of the error */
byte = (diff1 << 1) & 0x80; byte = ((diff2 << 7) & 0x100) |
byte |= (diff1 << 2) & 0x40; ((diff1 << 0) & 0x80) |
byte |= (diff1 << 3) & 0x20; ((diff1 << 1) & 0x40) |
byte |= (diff1 << 4) & 0x10; ((diff1 << 2) & 0x20) |
((diff1 << 3) & 0x10) |
byte |= (diff0 >> 3) & 0x08; ((diff0 >> 4) & 0x08) |
byte |= (diff0 >> 2) & 0x04; ((diff0 >> 3) & 0x04) |
byte |= (diff0 >> 1) & 0x02; ((diff0 >> 2) & 0x02) |
byte |= (diff0 >> 0) & 0x01; ((diff0 >> 1) & 0x01);
byte |= (diff2 << 8) & 0x100;
dev_dbg(info->device, "correcting error bit %d, byte %d\n", dev_dbg(info->device, "correcting error bit %d, byte %d\n",
bit, byte); bit, byte);
...@@ -399,7 +399,7 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, ...@@ -399,7 +399,7 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
if ((diff0 & ~(1<<fls(diff0))) == 0) if ((diff0 & ~(1<<fls(diff0))) == 0)
return 1; return 1;
return 0; return -1;
} }
/* ECC functions /* ECC functions
...@@ -810,6 +810,16 @@ static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm) ...@@ -810,6 +810,16 @@ static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm)
struct s3c2410_nand_info *info = platform_get_drvdata(dev); struct s3c2410_nand_info *info = platform_get_drvdata(dev);
if (info) { if (info) {
info->save_nfconf = readl(info->regs + S3C2410_NFCONF);
/* For the moment, we must ensure nFCE is high during
* the time we are suspended. This really should be
* handled by suspending the MTDs we are using, but
* that is currently not the case. */
writel(info->save_nfconf | info->sel_bit,
info->regs + S3C2410_NFCONF);
if (!allow_clk_stop(info)) if (!allow_clk_stop(info))
clk_disable(info->clk); clk_disable(info->clk);
} }
...@@ -820,11 +830,19 @@ static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm) ...@@ -820,11 +830,19 @@ static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm)
static int s3c24xx_nand_resume(struct platform_device *dev) static int s3c24xx_nand_resume(struct platform_device *dev)
{ {
struct s3c2410_nand_info *info = platform_get_drvdata(dev); struct s3c2410_nand_info *info = platform_get_drvdata(dev);
unsigned long nfconf;
if (info) { if (info) {
clk_enable(info->clk); clk_enable(info->clk);
s3c2410_nand_inithw(info, dev); s3c2410_nand_inithw(info, dev);
/* Restore the state of the nFCE line. */
nfconf = readl(info->regs + S3C2410_NFCONF);
nfconf &= ~info->sel_bit;
nfconf |= info->save_nfconf & info->sel_bit;
writel(nfconf, info->regs + S3C2410_NFCONF);
if (allow_clk_stop(info)) if (allow_clk_stop(info))
clk_disable(info->clk); clk_disable(info->clk);
} }
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment