summaryrefslogtreecommitdiff
path: root/drivers/mtd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/Kconfig8
-rw-r--r--drivers/mtd/Makefile1
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0001.c25
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0002.c2
-rw-r--r--drivers/mtd/chips/jedec_probe.c11
-rw-r--r--drivers/mtd/cmdlinepart.c9
-rw-r--r--drivers/mtd/devices/lart.c2
-rw-r--r--drivers/mtd/maps/physmap_of.c89
-rw-r--r--drivers/mtd/mtdchar.c12
-rw-r--r--drivers/mtd/mtdcore.c2
-rw-r--r--drivers/mtd/mtdoops.c165
-rw-r--r--drivers/mtd/nand/at91_nand.c12
-rw-r--r--drivers/mtd/nand/bf5xx_nand.c19
-rw-r--r--drivers/mtd/nand/plat_nand.c2
-rw-r--r--drivers/mtd/nand/s3c2410.c26
-rw-r--r--drivers/mtd/ofpart.c74
-rw-r--r--drivers/mtd/onenand/onenand_base.c59
17 files changed, 309 insertions, 209 deletions
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 661eac09f5cb..e8503341e3b1 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -150,6 +150,14 @@ config MTD_AFS_PARTS
for your particular device. It won't happen automatically. The
'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"
config MTD_CHAR
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index 7f0b04b4caa7..538e33d11d46 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o
obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
+obj-$(CONFIG_MTD_OF_PARTS) += ofpart.o
# 'Users' - code which presents functionality to userspace.
obj-$(CONFIG_MTD_CHAR) += mtdchar.o
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c
index 350671ec5226..47794d23a42e 100644
--- a/drivers/mtd/chips/cfi_cmdset_0001.c
+++ b/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -269,10 +269,16 @@ static void fixup_use_write_buffers(struct mtd_info *mtd, void *param)
/*
* 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)
{
- printk(KERN_INFO "Using auto-unlock on power-up/resume\n" );
- mtd->flags |= MTD_STUPID_LOCK;
+ 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" );
+ mtd->flags |= MTD_POWERUP_LOCK;
+ }
}
static struct cfi_fixup cfi_fixup_table[] = {
@@ -288,7 +294,7 @@ static struct cfi_fixup cfi_fixup_table[] = {
#endif
{ CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, 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 }
};
@@ -1562,9 +1568,12 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
int ret, wbufsize, word_gap, words;
const struct kvec *vec;
unsigned long vec_seek;
+ unsigned long initial_adr;
+ int initial_len = len;
wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
adr += chip->start;
+ initial_adr = adr;
cmd_adr = adr & ~(wbufsize-1);
/* Let's determine this according to the interleave only once */
@@ -1577,7 +1586,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
return ret;
}
- XIP_INVAL_CACHED_RANGE(map, adr, len);
+ XIP_INVAL_CACHED_RANGE(map, initial_adr, initial_len);
ENABLE_VPP(map);
xip_disable(map, chip, cmd_adr);
@@ -1668,7 +1677,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
chip->state = FL_WRITING;
ret = INVAL_CACHE_AND_WAIT(map, chip, cmd_adr,
- adr, len,
+ initial_adr, initial_len,
chip->buffer_write_time);
if (ret) {
map_write(map, CMD(0x70), cmd_adr);
@@ -2349,7 +2358,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd)
struct flchip *chip;
int ret = 0;
- if ((mtd->flags & MTD_STUPID_LOCK)
+ if ((mtd->flags & MTD_POWERUP_LOCK)
&& extp && (extp->FeatureSupport & (1 << 5)))
cfi_intelext_save_locks(mtd);
@@ -2460,7 +2469,7 @@ static void cfi_intelext_resume(struct mtd_info *mtd)
spin_unlock(chip->mutex);
}
- if ((mtd->flags & MTD_STUPID_LOCK)
+ if ((mtd->flags & MTD_POWERUP_LOCK)
&& extp && (extp->FeatureSupport & (1 << 5)))
cfi_intelext_restore_locks(mtd);
}
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c
index 796bfeadea21..d072e87ce4e2 100644
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -217,7 +217,7 @@ static void fixup_use_atmel_lock(struct mtd_info *mtd, void *param)
{
mtd->lock = cfi_atmel_lock;
mtd->unlock = cfi_atmel_unlock;
- mtd->flags |= MTD_STUPID_LOCK;
+ mtd->flags |= MTD_POWERUP_LOCK;
}
static struct cfi_fixup cfi_fixup_table[] = {
diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c
index 640593845218..4be51a86a85c 100644
--- a/drivers/mtd/chips/jedec_probe.c
+++ b/drivers/mtd/chips/jedec_probe.c
@@ -1646,14 +1646,6 @@ static const struct amd_flash_info jedec_table[] = {
}
};
-
-static int cfi_jedec_setup(struct cfi_private *p_cfi, int index);
-
-static int jedec_probe_chip(struct map_info *map, uint32_t base,
- unsigned long *chip_map, struct cfi_private *cfi);
-
-static struct mtd_info *jedec_probe(struct map_info *map);
-
static inline u32 jedec_read_mfr(struct map_info *map, uint32_t base,
struct cfi_private *cfi)
{
@@ -1676,8 +1668,7 @@ static inline u32 jedec_read_id(struct map_info *map, uint32_t base,
return result.x[0] & mask;
}
-static inline void jedec_reset(u32 base, struct map_info *map,
- struct cfi_private *cfi)
+static void jedec_reset(u32 base, struct map_info *map, struct cfi_private *cfi)
{
/* Reset */
diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c
index 23fab14f1637..b44292abd9f7 100644
--- a/drivers/mtd/cmdlinepart.c
+++ b/drivers/mtd/cmdlinepart.c
@@ -9,7 +9,7 @@
*
* mtdparts=<mtddef>[;<mtddef]
* <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)
* <size> := standard linux memsize OR "-" to denote all remaining space
* <name> := '(' NAME ')'
@@ -143,6 +143,13 @@ static struct mtd_partition * newpart(char *s,
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 */
if (*s == ',')
{
diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c
index 4ea50a1dda85..99fd210feaec 100644
--- a/drivers/mtd/devices/lart.c
+++ b/drivers/mtd/devices/lart.c
@@ -323,7 +323,7 @@ static int flash_probe (void)
/* put the flash back into command mode */
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));
}
/*
diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c
index d4bcd3f8c57c..49acd4171893 100644
--- a/drivers/mtd/maps/physmap_of.c
+++ b/drivers/mtd/maps/physmap_of.c
@@ -80,65 +80,6 @@ static int parse_obsolete_partitions(struct of_device *dev,
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)
- return nr_parts;
-
- /* First count the subnodes */
- nr_parts = 0;
- for (pp = of_get_next_child(dp, NULL); pp;
- pp = of_get_next_child(dp, pp))
- 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 = of_get_next_child(dp, NULL), i = 0; pp;
- pp = of_get_next_child(dp, pp), i++) {
- const u32 *reg;
- int len;
-
- reg = of_get_property(pp, "reg", &len);
- if (!reg || (len != 2*sizeof(u32))) {
- of_node_put(pp);
- 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 */
#define OF_FLASH_PARTS(info) (0)
#define parse_partitions(info, dev) (0)
@@ -213,6 +154,10 @@ static struct mtd_info * __devinit obsolete_probe(struct of_device *dev,
static int __devinit of_flash_probe(struct of_device *dev,
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 resource res;
struct of_flash *info;
@@ -275,13 +220,33 @@ static int __devinit of_flash_probe(struct of_device *dev,
}
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)
- 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)
- add_mtd_partitions(info->mtd, OF_FLASH_PARTS(info), err);
+ add_mtd_partitions(info->mtd, info->parts, err);
else
+#endif
add_mtd_device(info->mtd);
return 0;
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index b42553cd9af5..5d3ac512ce16 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -27,12 +27,10 @@ static void mtd_notify_add(struct mtd_info* mtd)
if (!mtd)
return;
- class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
- NULL, "mtd%d", mtd->index);
+ device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2), "mtd%d", mtd->index);
- class_device_create(mtd_class, NULL,
- MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
- NULL, "mtd%dro", mtd->index);
+ device_create(mtd_class, NULL,
+ MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), "mtd%dro", mtd->index);
}
static void mtd_notify_remove(struct mtd_info* mtd)
@@ -40,8 +38,8 @@ static void mtd_notify_remove(struct mtd_info* mtd)
if (!mtd)
return;
- class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2));
- class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1));
+ device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2));
+ device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1));
}
static struct mtd_notifier notifier = {
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index 6c2645e28371..f7e7890e5bc6 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -61,7 +61,7 @@ int add_mtd_device(struct mtd_info *mtd)
/* Some chips always power up locked. Unlock them now */
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))
printk(KERN_WARNING
"%s: unlock failed, "
diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c
index 20eaf294f620..34681bc91105 100644
--- a/drivers/mtd/mtdoops.c
+++ b/drivers/mtd/mtdoops.c
@@ -28,19 +28,24 @@
#include <linux/workqueue.h>
#include <linux/sched.h>
#include <linux/wait.h>
+#include <linux/spinlock.h>
#include <linux/mtd/mtd.h>
#define OOPS_PAGE_SIZE 4096
-static struct mtdoops_context {
+struct mtdoops_context {
int mtd_index;
- struct work_struct work;
+ struct work_struct work_erase;
+ struct work_struct work_write;
struct mtd_info *mtd;
int oops_pages;
int nextpage;
int nextcount;
void *oops_buf;
+
+ /* writecount and disabling ready are spin lock protected */
+ spinlock_t writecount_lock;
int ready;
int writecount;
} oops_cxt;
@@ -62,10 +67,7 @@ static int mtdoops_erase_block(struct mtd_info *mtd, int offset)
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.len = mtd->erasesize;
erase.priv = (u_long)&wait_q;
set_current_state(TASK_INTERRUPTIBLE);
@@ -87,7 +89,7 @@ static int mtdoops_erase_block(struct mtd_info *mtd, int offset)
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;
size_t retlen;
@@ -103,25 +105,30 @@ static int mtdoops_inc_counter(struct mtdoops_context *cxt)
ret = mtd->read(mtd, cxt->nextpage * OOPS_PAGE_SIZE, 4,
&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)"
", err %d.\n", cxt->nextpage * OOPS_PAGE_SIZE,
retlen, ret);
- return 1;
+ schedule_work(&cxt->work_erase);
+ return;
}
/* See if we need to erase the next block */
- if (count != 0xffffffff)
- return 1;
+ if (count != 0xffffffff) {
+ schedule_work(&cxt->work_erase);
+ return;
+ }
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)
+/* 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;
int i = 0, j, ret, mod;
@@ -136,8 +143,14 @@ static void mtdoops_prepare(struct mtdoops_context *cxt)
cxt->nextpage = 0;
}
- while (mtd->block_isbad &&
- mtd->block_isbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE)) {
+ while (mtd->block_isbad) {
+ 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:
printk(KERN_WARNING "mtdoops: Bad block at %08x\n",
cxt->nextpage * OOPS_PAGE_SIZE);
@@ -154,34 +167,61 @@ badblock:
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;
+ if (ret >= 0) {
+ printk(KERN_DEBUG "mtdoops: Ready %d, %d \n", cxt->nextpage, cxt->nextcount);
+ cxt->ready = 1;
+ return;
}
- printk(KERN_DEBUG "mtdoops: Ready %d, %d \n", cxt->nextpage, cxt->nextcount);
-
- cxt->ready = 1;
+ if (mtd->block_markbad && (ret == -EIO)) {
+ ret = mtd->block_markbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE);
+ if (ret < 0) {
+ printk(KERN_ERR "mtdoops: block_markbad failed, aborting.\n");
+ return;
+ }
+ }
+ goto badblock;
}
-static void mtdoops_workfunc(struct work_struct *work)
+static void mtdoops_workfunc_write(struct work_struct *work)
{
struct mtdoops_context *cxt =
- container_of(work, struct mtdoops_context, work);
+ container_of(work, struct mtdoops_context, work_write);
+ struct mtd_info *mtd = cxt->mtd;
+ size_t retlen;
+ int ret;
- mtdoops_prepare(cxt);
-}
+ 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->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 int find_next_position(struct mtdoops_context *cxt)
+static void find_next_position(struct mtdoops_context *cxt)
{
struct mtd_info *mtd = cxt->mtd;
- int page, maxpos = 0;
+ int ret, 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);
+ 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)
continue;
if (maxcount == 0xffffffff) {
@@ -205,20 +245,19 @@ static int find_next_position(struct mtdoops_context *cxt)
cxt->ready = 1;
printk(KERN_DEBUG "mtdoops: Ready %d, %d (first init)\n",
cxt->nextpage, cxt->nextcount);
- return 0;
+ return;
}
cxt->nextpage = maxpos;
cxt->nextcount = maxcount;
- return mtdoops_inc_counter(cxt);
+ 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;
@@ -229,14 +268,18 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
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->oops_pages = mtd->size / OOPS_PAGE_SIZE;
- ret = find_next_position(cxt);
- if (ret == 1)
- mtdoops_prepare(cxt);
+ find_next_position(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)
@@ -254,31 +297,24 @@ static void mtdoops_console_sync(void)
{
struct mtdoops_context *cxt = &oops_cxt;
struct mtd_info *mtd = cxt->mtd;
- size_t retlen;
- int ret;
+ unsigned long flags;
- if (!cxt->ready || !mtd)
+ if (!cxt->ready || !mtd || cxt->writecount == 0)
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;
-
- 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;
+ spin_unlock_irqrestore(&cxt->writecount_lock, flags);
- 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);
+ schedule_work(&cxt->work_write);
}
static void
@@ -286,6 +322,7 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count)
{
struct mtdoops_context *cxt = co->data;
struct mtd_info *mtd = cxt->mtd;
+ unsigned long flags;
if (!oops_in_progress) {
mtdoops_console_sync();
@@ -295,6 +332,13 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count)
if (!cxt->ready || !mtd)
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) {
u32 *stamp = cxt->oops_buf;
*stamp = cxt->nextcount;
@@ -306,6 +350,11 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count)
memcpy(cxt->oops_buf + cxt->writecount, s, count);
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)
@@ -331,7 +380,6 @@ static struct console mtdoops_console = {
.write = mtdoops_console_write,
.setup = mtdoops_console_setup,
.unblank = mtdoops_console_sync,
- .flags = CON_PRINTBUFFER,
.index = -1,
.data = &oops_cxt,
};
@@ -344,11 +392,12 @@ static int __init mtdoops_console_init(void)
cxt->oops_buf = vmalloc(OOPS_PAGE_SIZE);
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;
}
- 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_mtd_user(&mtdoops_notifier);
diff --git a/drivers/mtd/nand/at91_nand.c b/drivers/mtd/nand/at91_nand.c
index b2a5672df6e0..c9fb2acf4056 100644
--- a/drivers/mtd/nand/at91_nand.c
+++ b/drivers/mtd/nand/at91_nand.c
@@ -156,14 +156,14 @@ static int __init at91_nand_probe(struct platform_device *pdev)
}
#ifdef CONFIG_MTD_PARTITIONS
- if (host->board->partition_info)
- partitions = host->board->partition_info(mtd->size, &num_partitions);
#ifdef CONFIG_MTD_CMDLINE_PARTS
- else {
- mtd->name = "at91_nand";
- num_partitions = parse_mtd_partitions(mtd, part_probes, &partitions, 0);
- }
+ mtd->name = "at91_nand";
+ num_partitions = parse_mtd_partitions(mtd, part_probes,
+ &partitions, 0);
#endif
+ if (num_partitions <= 0 && host->board->partition_info)
+ partitions = host->board->partition_info(mtd->size,
+ &num_partitions);
if ((!partitions) || (num_partitions == 0)) {
printk(KERN_ERR "at91_nand: No parititions defined, or unsupported device.\n");
diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c
index 542850cd4c37..7d6ac6a7d9a7 100644
--- a/drivers/mtd/nand/bf5xx_nand.c
+++ b/drivers/mtd/nand/bf5xx_nand.c
@@ -293,7 +293,6 @@ static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd,
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();
@@ -303,19 +302,24 @@ static int bf5xx_nand_calculate_ecc(struct mtd_info *mtd,
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 */
if (page_size == 512) {
ecc0 = bfin_read_NFC_ECC2();
ecc1 = bfin_read_NFC_ECC3();
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]);
}
- p = (u8 *)code;
- for (i = 0; i < bytes; i++)
- ecc_code[i] = p[i];
-
return 0;
}
@@ -760,9 +764,6 @@ 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;
}
diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c
index cd725fc5e813..f6d5c2adc4fd 100644
--- a/drivers/mtd/nand/plat_nand.c
+++ b/drivers/mtd/nand/plat_nand.c
@@ -110,7 +110,9 @@ out:
static int __devexit plat_nand_remove(struct platform_device *pdev)
{
struct plat_nand_data *data = platform_get_drvdata(pdev);
+#ifdef CONFIG_MTD_PARTITIONS
struct platform_nand_data *pdata = pdev->dev.platform_data;
+#endif
nand_release(&data->mtd);
#ifdef CONFIG_MTD_PARTITIONS
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 21a2cc8636df..d31cb7b3feeb 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -366,23 +366,21 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) {
/* calculate the bit position of the error */
- bit = (diff2 >> 2) & 1;
- bit |= (diff2 >> 3) & 2;
- bit |= (diff2 >> 4) & 4;
+ bit = ((diff2 >> 3) & 1) |
+ ((diff2 >> 4) & 2) |
+ ((diff2 >> 5) & 4);
/* calculate the byte position of the error */
- byte = (diff1 << 1) & 0x80;
- byte |= (diff1 << 2) & 0x40;
- byte |= (diff1 << 3) & 0x20;
- byte |= (diff1 << 4) & 0x10;
-
- byte |= (diff0 >> 3) & 0x08;
- byte |= (diff0 >> 2) & 0x04;
- byte |= (diff0 >> 1) & 0x02;
- byte |= (diff0 >> 0) & 0x01;
-
- byte |= (diff2 << 8) & 0x100;
+ byte = ((diff2 << 7) & 0x100) |
+ ((diff1 << 0) & 0x80) |
+ ((diff1 << 1) & 0x40) |
+ ((diff1 << 2) & 0x20) |
+ ((diff1 << 3) & 0x10) |
+ ((diff0 >> 4) & 0x08) |
+ ((diff0 >> 3) & 0x04) |
+ ((diff0 >> 2) & 0x02) |
+ ((diff0 >> 1) & 0x01);
dev_dbg(info->device, "correcting error bit %d, byte %d\n",
bit, byte);
diff --git a/drivers/mtd/ofpart.c b/drivers/mtd/ofpart.c
new file mode 100644
index 000000000000..f86e06934cd8
--- /dev/null
+++ b/drivers/mtd/ofpart.c
@@ -0,0 +1,74 @@
+/*
+ * Flash partitions described by the OF (or flattened) device tree
+ *
+ * Copyright (C) 2006 MontaVista Software Inc.
+ * Author: Vitaly Wool <vwool@ru.mvista.com>
+ *
+ * Revised to handle newer style flash binding by:
+ * Copyright (C) 2007 David Gibson, IBM Corporation.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+int __devinit of_mtd_parse_partitions(struct device *dev,
+ struct mtd_info *mtd,
+ struct device_node *node,
+ struct mtd_partition **pparts)
+{
+ const char *partname;
+ struct device_node *pp;
+ int nr_parts, i;
+
+ /* First count the subnodes */
+ pp = NULL;
+ nr_parts = 0;
+ while ((pp = of_get_next_child(node, pp)))
+ nr_parts++;
+
+ if (nr_parts == 0)
+ return 0;
+
+ *pparts = kzalloc(nr_parts * sizeof(**pparts), GFP_KERNEL);
+ if (!*pparts)
+ return -ENOMEM;
+
+ pp = NULL;
+ i = 0;
+ while ((pp = of_get_next_child(node, pp))) {
+ const u32 *reg;
+ int len;
+
+ reg = of_get_property(pp, "reg", &len);
+ if (!reg || (len != 2 * sizeof(u32))) {
+ of_node_put(pp);
+ dev_err(dev, "Invalid 'reg' on %s\n", node->full_name);
+ kfree(*pparts);
+ *pparts = NULL;
+ return -EINVAL;
+ }
+ (*pparts)[i].offset = reg[0];
+ (*pparts)[i].size = reg[1];
+
+ partname = of_get_property(pp, "label", &len);
+ if (!partname)
+ partname = of_get_property(pp, "name", &len);
+ (*pparts)[i].name = (char *)partname;
+
+ if (of_get_property(pp, "read-only", &len))
+ (*pparts)[i].mask_flags = MTD_WRITEABLE;
+
+ i++;
+ }
+
+ return nr_parts;
+}
+EXPORT_SYMBOL(of_mtd_parse_partitions);
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index ed9f9c061ac5..b281b116aaeb 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -170,6 +170,18 @@ static int onenand_buffer_address(int dataram1, int sectors, int count)
}
/**
+ * onenand_get_density - [DEFAULT] Get OneNAND density
+ * @param dev_id OneNAND device ID
+ *
+ * Get OneNAND density from device ID
+ */
+static inline int onenand_get_density(int dev_id)
+{
+ int density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+ return (density & ONENAND_DEVICE_DENSITY_MASK);
+}
+
+/**
* onenand_command - [DEFAULT] Send command to OneNAND device
* @param mtd MTD device structure
* @param cmd the command to be sent
@@ -182,8 +194,7 @@ static int onenand_buffer_address(int dataram1, int sectors, int count)
static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t len)
{
struct onenand_chip *this = mtd->priv;
- int value, readcmd = 0, block_cmd = 0;
- int block, page;
+ int value, block, page;
/* Address translation */
switch (cmd) {
@@ -198,7 +209,6 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
case ONENAND_CMD_ERASE:
case ONENAND_CMD_BUFFERRAM:
case ONENAND_CMD_OTP_ACCESS:
- block_cmd = 1;
block = (int) (addr >> this->erase_shift);
page = -1;
break;
@@ -240,11 +250,9 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
value = onenand_block_address(this, block);
this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
- if (block_cmd) {
- /* Select DataRAM for DDP */
- value = onenand_bufferram_address(this, block);
- this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
- }
+ /* Select DataRAM for DDP */
+ value = onenand_bufferram_address(this, block);
+ this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
}
if (page != -1) {
@@ -256,7 +264,6 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
case ONENAND_CMD_READ:
case ONENAND_CMD_READOOB:
dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
- readcmd = 1;
break;
default:
@@ -273,12 +280,6 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
/* Write 'BSA, BSC' of DataRAM */
value = onenand_buffer_address(dataram, sectors, count);
this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
-
- if (readcmd) {
- /* Select DataRAM for DDP */
- value = onenand_bufferram_address(this, block);
- this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
- }
}
/* Interrupt clear */
@@ -1118,12 +1119,10 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state)
interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
+ /* Initial bad block case: 0x2400 or 0x0400 */
if (ctrl & ONENAND_CTRL_ERROR) {
printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl);
- /* Initial bad block case */
- if (ctrl & ONENAND_CTRL_LOAD)
- return ONENAND_BBT_READ_ERROR;
- return ONENAND_BBT_READ_FATAL_ERROR;
+ return ONENAND_BBT_READ_ERROR;
}
if (interrupt & ONENAND_INT_READ) {
@@ -1218,7 +1217,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to)
{
struct onenand_chip *this = mtd->priv;
- char oobbuf[64];
+ u_char *oob_buf = this->oob_buf;
int status, i;
this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);
@@ -1227,9 +1226,9 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to
if (status)
return status;
- this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
+ this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
for (i = 0; i < mtd->oobsize; i++)
- if (buf[i] != 0xFF && buf[i] != oobbuf[i])
+ if (buf[i] != 0xFF && buf[i] != oob_buf[i])
return -EBADMSG;
return 0;
@@ -1431,7 +1430,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
}
/* Only check verify write turn on */
- ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen);
+ ret = onenand_verify(mtd, buf, to, thislen);
if (ret) {
printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
break;
@@ -1447,9 +1446,6 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
buf += thislen;
}
- /* Deselect and wake up anyone waiting on the device */
- onenand_release_device(mtd);
-
ops->retlen = written;
return ret;
@@ -2160,7 +2156,7 @@ static int onenand_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
*retlen = 0;
- density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+ density = onenand_get_density(this->device_id);
if (density < ONENAND_DEVICE_DENSITY_512Mb)
otp_pages = 20;
else
@@ -2311,7 +2307,8 @@ static int onenand_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
size_t len)
{
- unsigned char oob_buf[64];
+ struct onenand_chip *this = mtd->priv;
+ u_char *oob_buf = this->oob_buf;
size_t retlen;
int ret;
@@ -2351,7 +2348,7 @@ static void onenand_check_features(struct mtd_info *mtd)
unsigned int density, process;
/* Lock scheme depends on density and process */
- density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+ density = onenand_get_density(this->device_id);
process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
/* Lock scheme */
@@ -2400,7 +2397,7 @@ static void onenand_print_device_info(int device, int version)
vcc = device & ONENAND_DEVICE_VCC_MASK;
demuxed = device & ONENAND_DEVICE_IS_DEMUX;
ddp = device & ONENAND_DEVICE_IS_DDP;
- density = device >> ONENAND_DEVICE_DENSITY_SHIFT;
+ density = onenand_get_density(device);
printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n",
demuxed ? "" : "Muxed ",
ddp ? "(DDP)" : "",
@@ -2492,7 +2489,7 @@ static int onenand_probe(struct mtd_info *mtd)
this->device_id = dev_id;
this->version_id = ver_id;
- density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+ density = onenand_get_density(dev_id);
this->chipsize = (16 << density) << 20;
/* Set density mask. it is used for DDP */
if (ONENAND_IS_DDP(this))