summaryrefslogtreecommitdiffstats
path: root/drivers/mtd/chips
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/chips')
-rw-r--r--drivers/mtd/chips/Kconfig29
-rw-r--r--drivers/mtd/chips/amd_flash.c14
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0001.c580
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0002.c497
-rw-r--r--drivers/mtd/chips/fwh_lock.h6
-rw-r--r--drivers/mtd/chips/gen_probe.c4
-rw-r--r--drivers/mtd/chips/jedec_probe.c28
7 files changed, 837 insertions, 321 deletions
diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig
index d682dbc8157e..b5dc59389bb3 100644
--- a/drivers/mtd/chips/Kconfig
+++ b/drivers/mtd/chips/Kconfig
@@ -1,5 +1,5 @@
# drivers/mtd/chips/Kconfig
-# $Id: Kconfig,v 1.13 2004/12/01 15:49:10 nico Exp $
+# $Id: Kconfig,v 1.15 2005/06/06 23:04:35 tpoynor Exp $
menu "RAM/ROM/Flash chip drivers"
depends on MTD!=n
@@ -155,6 +155,31 @@ config MTD_CFI_I8
If your flash chips are interleaved in eights - i.e. you have eight
flash chips addressed by each bus cycle, then say 'Y'.
+config MTD_OTP
+ bool "Protection Registers aka one-time programmable (OTP) bits"
+ depends on MTD_CFI_ADV_OPTIONS
+ default n
+ help
+ This enables support for reading, writing and locking so called
+ "Protection Registers" present on some flash chips.
+ A subset of them are pre-programmed at the factory with a
+ unique set of values. The rest is user-programmable.
+
+ The user-programmable Protection Registers contain one-time
+ programmable (OTP) bits; when programmed, register bits cannot be
+ erased. Each Protection Register can be accessed multiple times to
+ program individual bits, as long as the register remains unlocked.
+
+ Each Protection Register has an associated Lock Register bit. When a
+ Lock Register bit is programmed, the associated Protection Register
+ can only be read; it can no longer be programmed. Additionally,
+ because the Lock Register bits themselves are OTP, when programmed,
+ Lock Register bits cannot be erased. Therefore, when a Protection
+ Register is locked, it cannot be unlocked.
+
+ This feature should therefore be used with extreme care. Any mistake
+ in the programming of OTP bits will waste them.
+
config MTD_CFI_INTELEXT
tristate "Support for Intel/Sharp flash chips"
depends on MTD_GEN_PROBE
@@ -275,7 +300,7 @@ config MTD_JEDEC
config MTD_XIP
bool "XIP aware MTD support"
- depends on !SMP && MTD_CFI_INTELEXT && EXPERIMENTAL
+ depends on !SMP && (MTD_CFI_INTELEXT || MTD_CFI_AMDSTD) && EXPERIMENTAL
default y if XIP_KERNEL
help
This allows MTD support to work with flash memory which is also
diff --git a/drivers/mtd/chips/amd_flash.c b/drivers/mtd/chips/amd_flash.c
index 41e2e3e31603..2dafeba3f3d5 100644
--- a/drivers/mtd/chips/amd_flash.c
+++ b/drivers/mtd/chips/amd_flash.c
@@ -3,7 +3,7 @@
*
* Author: Jonas Holmberg <jonas.holmberg@axis.com>
*
- * $Id: amd_flash.c,v 1.26 2004/11/20 12:49:04 dwmw2 Exp $
+ * $Id: amd_flash.c,v 1.27 2005/02/04 07:43:09 jonashg Exp $
*
* Copyright (c) 2001 Axis Communications AB
*
@@ -67,7 +67,6 @@
#define AM29LV160DT 0x22C4
#define AM29LV160DB 0x2249
#define AM29BDS323D 0x22D1
-#define AM29BDS643D 0x227E
/* Atmel */
#define AT49xV16x 0x00C0
@@ -618,17 +617,6 @@ static struct mtd_info *amd_flash_probe(struct map_info *map)
{ .offset = 0x3f0000, .erasesize = 0x02000, .numblocks = 8 },
}
}, {
- .mfr_id = MANUFACTURER_AMD,
- .dev_id = AM29BDS643D,
- .name = "AMD AM29BDS643D",
- .size = 0x00800000,
- .numeraseregions = 3,
- .regions = {
- { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 96 },
- { .offset = 0x600000, .erasesize = 0x10000, .numblocks = 31 },
- { .offset = 0x7f0000, .erasesize = 0x02000, .numblocks = 8 },
- }
- }, {
.mfr_id = MANUFACTURER_ATMEL,
.dev_id = AT49xV16x,
.name = "Atmel AT49xV16x",
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c
index c268bcd71720..0cfcd88468e0 100644
--- a/drivers/mtd/chips/cfi_cmdset_0001.c
+++ b/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -4,7 +4,7 @@
*
* (C) 2000 Red Hat. GPL'd
*
- * $Id: cfi_cmdset_0001.c,v 1.164 2004/11/16 18:29:00 dwmw2 Exp $
+ * $Id: cfi_cmdset_0001.c,v 1.178 2005/05/19 17:05:43 nico Exp $
*
*
* 10/10/2000 Nicolas Pitre <nico@cam.org>
@@ -29,6 +29,7 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/reboot.h>
#include <linux/mtd/xip.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
@@ -48,16 +49,25 @@
#define M50LPW080 0x002F
static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
-//static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
-//static int cfi_intelext_read_fact_prot_reg (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_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *);
static void cfi_intelext_sync (struct mtd_info *);
static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len);
static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len);
+#ifdef CONFIG_MTD_OTP
+static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+static int cfi_intelext_write_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+static int cfi_intelext_lock_user_prot_reg (struct mtd_info *, loff_t, size_t);
+static int cfi_intelext_get_fact_prot_info (struct mtd_info *,
+ struct otp_info *, size_t);
+static int cfi_intelext_get_user_prot_info (struct mtd_info *,
+ struct otp_info *, size_t);
+#endif
static int cfi_intelext_suspend (struct mtd_info *);
static void cfi_intelext_resume (struct mtd_info *);
+static int cfi_intelext_reboot (struct notifier_block *, unsigned long, void *);
static void cfi_intelext_destroy(struct mtd_info *);
@@ -252,7 +262,8 @@ read_pri_intelext(struct map_info *map, __u16 adr)
int nb_parts, i;
/* Protection Register info */
- extra_size += (extp->NumProtectionFields - 1) * (4 + 6);
+ extra_size += (extp->NumProtectionFields - 1) *
+ sizeof(struct cfi_intelext_otpinfo);
/* Burst Read info */
extra_size += 6;
@@ -324,7 +335,9 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
mtd->resume = cfi_intelext_resume;
mtd->flags = MTD_CAP_NORFLASH;
mtd->name = map->name;
-
+
+ mtd->reboot_notifier.notifier_call = cfi_intelext_reboot;
+
if (cfi->cfi_mode == CFI_MODE_CFI) {
/*
* It's a real CFI chip, not one for which the probe
@@ -422,9 +435,13 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
mtd->eraseregions[i].numblocks);
}
-#if 0
- mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg;
+#ifdef CONFIG_MTD_OTP
mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg;
+ mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg;
+ mtd->write_user_prot_reg = cfi_intelext_write_user_prot_reg;
+ mtd->lock_user_prot_reg = cfi_intelext_lock_user_prot_reg;
+ mtd->get_fact_prot_info = cfi_intelext_get_fact_prot_info;
+ mtd->get_user_prot_info = cfi_intelext_get_user_prot_info;
#endif
/* This function has the potential to distort the reality
@@ -433,6 +450,7 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
goto setup_err;
__module_get(THIS_MODULE);
+ register_reboot_notifier(&mtd->reboot_notifier);
return mtd;
setup_err:
@@ -471,7 +489,8 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd,
int offs, numregions, numparts, partshift, numvirtchips, i, j;
/* Protection Register info */
- offs = (extp->NumProtectionFields - 1) * (4 + 6);
+ offs = (extp->NumProtectionFields - 1) *
+ sizeof(struct cfi_intelext_otpinfo);
/* Burst Read info */
offs += 6;
@@ -563,7 +582,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
resettime:
timeo = jiffies + HZ;
retry:
- if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING)) {
+ if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE)) {
/*
* OK. We have possibility for contension on the write/erase
* operations which are global to the real chip and not per
@@ -807,10 +826,6 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
* assembly to make sure inline functions were actually inlined and that gcc
* didn't emit calls to its own support functions). Also configuring MTD CFI
* support to a single buswidth and a single interleave is also recommended.
- * Note that not only IRQs are disabled but the preemption count is also
- * increased to prevent other locking primitives (namely spin_unlock) from
- * decrementing the preempt count to zero and scheduling the CPU away while
- * not in array mode.
*/
static void xip_disable(struct map_info *map, struct flchip *chip,
@@ -818,7 +833,6 @@ static void xip_disable(struct map_info *map, struct flchip *chip,
{
/* TODO: chips with no XIP use should ignore and return */
(void) map_read(map, adr); /* ensure mmu mapping is up to date */
- preempt_disable();
local_irq_disable();
}
@@ -831,9 +845,8 @@ static void __xipram xip_enable(struct map_info *map, struct flchip *chip,
chip->state = FL_READY;
}
(void) map_read(map, adr);
- asm volatile (".rep 8; nop; .endr"); /* fill instruction prefetch */
+ xip_iprefetch();
local_irq_enable();
- preempt_enable();
}
/*
@@ -909,7 +922,7 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
(void) map_read(map, adr);
asm volatile (".rep 8; nop; .endr");
local_irq_enable();
- preempt_enable();
+ spin_unlock(chip->mutex);
asm volatile (".rep 8; nop; .endr");
cond_resched();
@@ -919,15 +932,15 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
* a suspended erase state. If so let's wait
* until it's done.
*/
- preempt_disable();
+ spin_lock(chip->mutex);
while (chip->state != newstate) {
DECLARE_WAITQUEUE(wait, current);
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
- preempt_enable();
+ spin_unlock(chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
- preempt_disable();
+ spin_lock(chip->mutex);
}
/* Disallow XIP again */
local_irq_disable();
@@ -956,12 +969,14 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
* The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while
* the flash is actively programming or erasing since we have to poll for
* the operation to complete anyway. We can't do that in a generic way with
- * a XIP setup so do it before the actual flash operation in this case.
+ * a XIP setup so do it before the actual flash operation in this case
+ * and stub it out from INVALIDATE_CACHE_UDELAY.
*/
-#undef INVALIDATE_CACHED_RANGE
-#define INVALIDATE_CACHED_RANGE(x...)
-#define XIP_INVAL_CACHED_RANGE(map, from, size) \
- do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0)
+#define XIP_INVAL_CACHED_RANGE(map, from, size) \
+ INVALIDATE_CACHED_RANGE(map, from, size)
+
+#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \
+ UDELAY(map, chip, adr, usec)
/*
* Extra notes:
@@ -984,11 +999,23 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
#define xip_disable(map, chip, adr)
#define xip_enable(map, chip, adr)
-
-#define UDELAY(map, chip, adr, usec) cfi_udelay(usec)
-
#define XIP_INVAL_CACHED_RANGE(x...)
+#define UDELAY(map, chip, adr, usec) \
+do { \
+ spin_unlock(chip->mutex); \
+ cfi_udelay(usec); \
+ spin_lock(chip->mutex); \
+} while (0)
+
+#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \
+do { \
+ spin_unlock(chip->mutex); \
+ INVALIDATE_CACHED_RANGE(map, adr, len); \
+ cfi_udelay(usec); \
+ spin_lock(chip->mutex); \
+} while (0)
+
#endif
static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len)
@@ -1176,111 +1203,11 @@ static int cfi_intelext_read (struct mtd_info *mtd, loff_t from, size_t len, siz
return ret;
}
-#if 0
-static int __xipram cfi_intelext_read_prot_reg (struct mtd_info *mtd,
- loff_t from, size_t len,
- size_t *retlen,
- u_char *buf,
- int base_offst, int reg_sz)
-{
- struct map_info *map = mtd->priv;
- struct cfi_private *cfi = map->fldrv_priv;
- struct cfi_pri_intelext *extp = cfi->cmdset_priv;
- struct flchip *chip;
- int ofs_factor = cfi->interleave * cfi->device_type;
- int count = len;
- int chip_num, offst;
- int ret;
-
- chip_num = ((unsigned int)from/reg_sz);
- offst = from - (reg_sz*chip_num)+base_offst;
-
- while (count) {
- /* Calculate which chip & protection register offset we need */
-
- if (chip_num >= cfi->numchips)
- goto out;
-
- chip = &cfi->chips[chip_num];
-
- spin_lock(chip->mutex);
- ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY);
- if (ret) {
- spin_unlock(chip->mutex);
- return (len-count)?:ret;
- }
-
- xip_disable(map, chip, chip->start);
-
- if (chip->state != FL_JEDEC_QUERY) {
- map_write(map, CMD(0x90), chip->start);
- chip->state = FL_JEDEC_QUERY;
- }
-
- while (count && ((offst-base_offst) < reg_sz)) {
- *buf = map_read8(map,(chip->start+((extp->ProtRegAddr+1)*ofs_factor)+offst));
- buf++;
- offst++;
- count--;
- }
-
- xip_enable(map, chip, chip->start);
- put_chip(map, chip, chip->start);
- spin_unlock(chip->mutex);
-
- /* Move on to the next chip */
- chip_num++;
- offst = base_offst;
- }
-
- out:
- return len-count;
-}
-
-static int cfi_intelext_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
-{
- struct map_info *map = mtd->priv;
- struct cfi_private *cfi = map->fldrv_priv;
- struct cfi_pri_intelext *extp=cfi->cmdset_priv;
- int base_offst,reg_sz;
-
- /* Check that we actually have some protection registers */
- if(!extp || !(extp->FeatureSupport&64)){
- printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name);
- return 0;
- }
-
- base_offst=(1<<extp->FactProtRegSize);
- reg_sz=(1<<extp->UserProtRegSize);
-
- return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz);
-}
-
-static int cfi_intelext_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
-{
- struct map_info *map = mtd->priv;
- struct cfi_private *cfi = map->fldrv_priv;
- struct cfi_pri_intelext *extp=cfi->cmdset_priv;
- int base_offst,reg_sz;
-
- /* Check that we actually have some protection registers */
- if(!extp || !(extp->FeatureSupport&64)){
- printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name);
- return 0;
- }
-
- base_offst=0;
- reg_sz=(1<<extp->FactProtRegSize);
-
- return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz);
-}
-#endif
-
static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
- unsigned long adr, map_word datum)
+ unsigned long adr, map_word datum, int mode)
{
struct cfi_private *cfi = map->fldrv_priv;
- map_word status, status_OK;
+ map_word status, status_OK, write_cmd;
unsigned long timeo;
int z, ret=0;
@@ -1288,9 +1215,14 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
/* Let's determine this according to the interleave only once */
status_OK = CMD(0x80);
+ switch (mode) {
+ case FL_WRITING: write_cmd = CMD(0x40); break;
+ case FL_OTP_WRITE: write_cmd = CMD(0xc0); break;
+ default: return -EINVAL;
+ }
spin_lock(chip->mutex);
- ret = get_chip(map, chip, adr, FL_WRITING);
+ ret = get_chip(map, chip, adr, mode);
if (ret) {
spin_unlock(chip->mutex);
return ret;
@@ -1299,19 +1231,18 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map));
ENABLE_VPP(map);
xip_disable(map, chip, adr);
- map_write(map, CMD(0x40), adr);
+ map_write(map, write_cmd, adr);
map_write(map, datum, adr);
- chip->state = FL_WRITING;
+ chip->state = mode;
- spin_unlock(chip->mutex);
- INVALIDATE_CACHED_RANGE(map, adr, map_bankwidth(map));
- UDELAY(map, chip, adr, chip->word_write_time);
- spin_lock(chip->mutex);
+ INVALIDATE_CACHE_UDELAY(map, chip,
+ adr, map_bankwidth(map),
+ chip->word_write_time);
timeo = jiffies + (HZ/2);
z = 0;
for (;;) {
- if (chip->state != FL_WRITING) {
+ if (chip->state != mode) {
/* Someone's suspended the write. Sleep */
DECLARE_WAITQUEUE(wait, current);
@@ -1339,10 +1270,8 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
}
/* Latency issues. Drop the lock, wait a while and retry */
- spin_unlock(chip->mutex);
z++;
UDELAY(map, chip, adr, 1);
- spin_lock(chip->mutex);
}
if (!z) {
chip->word_write_time--;
@@ -1399,7 +1328,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le
datum = map_word_load_partial(map, datum, buf, gap, n);
ret = do_write_oneword(map, &cfi->chips[chipnum],
- bus_ofs, datum);
+ bus_ofs, datum, FL_WRITING);
if (ret)
return ret;
@@ -1420,7 +1349,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le
map_word datum = map_word_load(map, buf);
ret = do_write_oneword(map, &cfi->chips[chipnum],
- ofs, datum);
+ ofs, datum, FL_WRITING);
if (ret)
return ret;
@@ -1444,7 +1373,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le
datum = map_word_load_partial(map, datum, buf, 0, len);
ret = do_write_oneword(map, &cfi->chips[chipnum],
- ofs, datum);
+ ofs, datum, FL_WRITING);
if (ret)
return ret;
@@ -1506,9 +1435,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
if (map_word_andequal(map, status, status_OK, status_OK))
break;
- spin_unlock(chip->mutex);
UDELAY(map, chip, cmd_adr, 1);
- spin_lock(chip->mutex);
if (++z > 20) {
/* Argh. Not ready for write to buffer */
@@ -1554,10 +1481,9 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
map_write(map, CMD(0xd0), cmd_adr);
chip->state = FL_WRITING;
- spin_unlock(chip->mutex);
- INVALIDATE_CACHED_RANGE(map, adr, len);
- UDELAY(map, chip, cmd_adr, chip->buffer_write_time);
- spin_lock(chip->mutex);
+ INVALIDATE_CACHE_UDELAY(map, chip,
+ cmd_adr, len,
+ chip->buffer_write_time);
timeo = jiffies + (HZ/2);
z = 0;
@@ -1589,10 +1515,8 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
}
/* Latency issues. Drop the lock, wait a while and retry */
- spin_unlock(chip->mutex);
- UDELAY(map, chip, cmd_adr, 1);
z++;
- spin_lock(chip->mutex);
+ UDELAY(map, chip, cmd_adr, 1);
}
if (!z) {
chip->buffer_write_time--;
@@ -1720,10 +1644,9 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
chip->state = FL_ERASING;
chip->erase_suspended = 0;
- spin_unlock(chip->mutex);
- INVALIDATE_CACHED_RANGE(map, adr, len);
- UDELAY(map, chip, adr, chip->erase_time*1000/2);
- spin_lock(chip->mutex);
+ INVALIDATE_CACHE_UDELAY(map, chip,
+ adr, len,
+ chip->erase_time*1000/2);
/* FIXME. Use a timer to check this, and return immediately. */
/* Once the state machine's known to be working I'll do that */
@@ -1768,9 +1691,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
}
/* Latency issues. Drop the lock, wait a while and retry */
- spin_unlock(chip->mutex);
UDELAY(map, chip, adr, 1000000/HZ);
- spin_lock(chip->mutex);
}
/* We've broken this before. It doesn't hurt to be safe */
@@ -1780,44 +1701,34 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
/* check for lock bit */
if (map_word_bitsset(map, status, CMD(0x3a))) {
- unsigned char chipstatus;
+ unsigned long chipstatus;
/* Reset the error bits */
map_write(map, CMD(0x50), adr);
map_write(map, CMD(0x70), adr);
xip_enable(map, chip, adr);
- chipstatus = status.x[0];
- if (!map_word_equal(map, status, CMD(chipstatus))) {
- int i, w;
- for (w=0; w<map_words(map); w++) {
- for (i = 0; i<cfi_interleave(cfi); i++) {
- chipstatus |= status.x[w] >> (cfi->device_type * 8);
- }
- }
- printk(KERN_WARNING "Status is not identical for all chips: 0x%lx. Merging to give 0x%02x\n",
- status.x[0], chipstatus);
- }
+ chipstatus = MERGESTATUS(status);
if ((chipstatus & 0x30) == 0x30) {
- printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", chipstatus);
+ printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%lx\n", chipstatus);
ret = -EIO;
} else if (chipstatus & 0x02) {
/* Protection bit set */
ret = -EROFS;
} else if (chipstatus & 0x8) {
/* Voltage */
- printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", chipstatus);
+ printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%lx\n", chipstatus);
ret = -EIO;
} else if (chipstatus & 0x20) {
if (retries--) {
- printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus);
+ printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%lx. Retrying...\n", adr, chipstatus);
timeo = jiffies + HZ;
put_chip(map, chip, adr);
spin_unlock(chip->mutex);
goto retry;
}
- printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus);
+ printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%lx\n", adr, chipstatus);
ret = -EIO;
}
} else {
@@ -1882,6 +1793,7 @@ static void cfi_intelext_sync (struct mtd_info *mtd)
if (chip->state == FL_SYNCING) {
chip->state = chip->oldstate;
+ chip->oldstate = FL_READY;
wake_up(&chip->wq);
}
spin_unlock(chip->mutex);
@@ -1897,8 +1809,9 @@ static int __xipram do_printlockstatus_oneblock(struct map_info *map,
struct cfi_private *cfi = map->fldrv_priv;
int status, ofs_factor = cfi->interleave * cfi->device_type;
+ adr += chip->start;
xip_disable(map, chip, adr+(2*ofs_factor));
- cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL);
+ map_write(map, CMD(0x90), adr+(2*ofs_factor));
chip->state = FL_JEDEC_QUERY;
status = cfi_read_query(map, adr+(2*ofs_factor));
xip_enable(map, chip, 0);
@@ -1915,6 +1828,7 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip
unsigned long adr, int len, void *thunk)
{
struct cfi_private *cfi = map->fldrv_priv;
+ struct cfi_pri_intelext *extp = cfi->cmdset_priv;
map_word status, status_OK;
unsigned long timeo = jiffies + HZ;
int ret;
@@ -1944,9 +1858,13 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip
} else
BUG();
- spin_unlock(chip->mutex);
- UDELAY(map, chip, adr, 1000000/HZ);
- spin_lock(chip->mutex);
+ /*
+ * If Instant Individual Block Locking supported then no need
+ * to delay.
+ */
+
+ if (!extp || !(extp->FeatureSupport & (1 << 5)))
+ UDELAY(map, chip, adr, 1000000/HZ);
/* FIXME. Use a timer to check this, and return immediately. */
/* Once the state machine's known to be working I'll do that */
@@ -1973,9 +1891,7 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip
}
/* Latency issues. Drop the lock, wait a while and retry */
- spin_unlock(chip->mutex);
UDELAY(map, chip, adr, 1);
- spin_lock(chip->mutex);
}
/* Done and happy. */
@@ -2034,6 +1950,274 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
return ret;
}
+#ifdef CONFIG_MTD_OTP
+
+typedef int (*otp_op_t)(struct map_info *map, struct flchip *chip,
+ u_long data_offset, u_char *buf, u_int size,
+ u_long prot_offset, u_int groupno, u_int groupsize);
+
+static int __xipram
+do_otp_read(struct map_info *map, struct flchip *chip, u_long offset,
+ u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz)
+{
+ struct cfi_private *cfi = map->fldrv_priv;
+ int ret;
+
+ spin_lock(chip->mutex);
+ ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY);
+ if (ret) {
+ spin_unlock(chip->mutex);
+ return ret;
+ }
+
+ /* let's ensure we're not reading back cached data from array mode */
+ INVALIDATE_CACHED_RANGE(map, chip->start + offset, size);
+
+ xip_disable(map, chip, chip->start);
+ if (chip->state != FL_JEDEC_QUERY) {
+ map_write(map, CMD(0x90), chip->start);
+ chip->state = FL_JEDEC_QUERY;
+ }
+ map_copy_from(map, buf, chip->start + offset, size);
+ xip_enable(map, chip, chip->start);
+
+ /* then ensure we don't keep OTP data in the cache */
+ INVALIDATE_CACHED_RANGE(map, chip->start + offset, size);
+
+ put_chip(map, chip, chip->start);
+ spin_unlock(chip->mutex);
+ return 0;
+}
+
+static int
+do_otp_write(struct map_info *map, struct flchip *chip, u_long offset,
+ u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz)
+{
+ int ret;
+
+ while (size) {
+ unsigned long bus_ofs = offset & ~(map_bankwidth(map)-1);
+ int gap = offset - bus_ofs;
+ int n = min_t(int, size, map_bankwidth(map)-gap);
+ map_word datum = map_word_ff(map);
+
+ datum = map_word_load_partial(map, datum, buf, gap, n);
+ ret = do_write_oneword(map, chip, bus_ofs, datum, FL_OTP_WRITE);
+ if (ret)
+ return ret;
+
+ offset += n;
+ buf += n;
+ size -= n;
+ }
+
+ return 0;
+}
+
+static int
+do_otp_lock(struct map_info *map, struct flchip *chip, u_long offset,
+ u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz)
+{
+ struct cfi_private *cfi = map->fldrv_priv;
+ map_word datum;
+
+ /* make sure area matches group boundaries */
+ if (size != grpsz)
+ return -EXDEV;
+
+ datum = map_word_ff(map);
+ datum = map_word_clr(map, datum, CMD(1 << grpno));
+ return do_write_oneword(map, chip, prot, datum, FL_OTP_WRITE);
+}
+
+static int cfi_intelext_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf,
+ otp_op_t action, int user_regs)
+{
+ struct map_info *map = mtd->priv;
+ struct cfi_private *cfi = map->fldrv_priv;
+ struct cfi_pri_intelext *extp = cfi->cmdset_priv;
+ struct flchip *chip;
+ struct cfi_intelext_otpinfo *otp;
+ u_long devsize, reg_prot_offset, data_offset;
+ u_int chip_num, chip_step, field, reg_fact_size, reg_user_size;
+ u_int groups, groupno, groupsize, reg_fact_groups, reg_user_groups;
+ int ret;
+
+ *retlen = 0;
+
+ /* Check that we actually have some OTP registers */
+ if (!extp || !(extp->FeatureSupport & 64) || !extp->NumProtectionFields)
+ return -ENODATA;
+
+ /* we need real chips here not virtual ones */
+ devsize = (1 << cfi->cfiq->DevSize) * cfi->interleave;
+ chip_step = devsize >> cfi->chipshift;
+ chip_num = 0;
+
+ /* Some chips have OTP located in the _top_ partition only.
+ For example: Intel 28F256L18T (T means top-parameter device) */
+ if (cfi->mfr == MANUFACTURER_INTEL) {
+ switch (cfi->id) {
+ case 0x880b:
+ case 0x880c:
+ case 0x880d:
+ chip_num = chip_step - 1;
+ }
+ }
+
+ for ( ; chip_num < cfi->numchips; chip_num += chip_step) {
+ chip = &cfi->chips[chip_num];
+ otp = (struct cfi_intelext_otpinfo *)&extp->extra[0];
+
+ /* first OTP region */
+ field = 0;
+ reg_prot_offset = extp->ProtRegAddr;
+ reg_fact_groups = 1;
+ reg_fact_size = 1 << extp->FactProtRegSize;
+ reg_user_groups = 1;
+ reg_user_size = 1 << extp->UserProtRegSize;
+
+ while (len > 0) {
+ /* flash geometry fixup */
+ data_offset = reg_prot_offset + 1;
+ data_offset *= cfi->interleave * cfi->device_type;
+ reg_prot_offset *= cfi->interleave * cfi->device_type;
+ reg_fact_size *= cfi->interleave;
+ reg_user_size *= cfi->interleave;
+
+ if (user_regs) {
+ groups = reg_user_groups;
+ groupsize = reg_user_size;
+ /* skip over factory reg area */
+ groupno = reg_fact_groups;
+ data_offset += reg_fact_groups * reg_fact_size;
+ } else {
+ groups = reg_fact_groups;
+ groupsize = reg_fact_size;
+ groupno = 0;
+ }
+
+ while (len > 0 && groups > 0) {
+ if (!action) {
+ /*
+ * Special case: if action is NULL
+ * we fill buf with otp_info records.
+ */
+ struct otp_info *otpinfo;
+ map_word lockword;
+ len -= sizeof(struct otp_info);
+ if (len <= 0)
+ return -ENOSPC;
+ ret = do_otp_read(map, chip,
+ reg_prot_offset,
+ (u_char *)&lockword,
+ map_bankwidth(map),
+ 0, 0, 0);
+ if (ret)
+ return ret;
+ otpinfo = (struct otp_info *)buf;
+ otpinfo->start = from;
+ otpinfo->length = groupsize;
+ otpinfo->locked =
+ !map_word_bitsset(map, lockword,
+ CMD(1 << groupno));
+ from += groupsize;
+ buf += sizeof(*otpinfo);
+ *retlen += sizeof(*otpinfo);
+ } else if (from >= groupsize) {
+ from -= groupsize;
+ data_offset += groupsize;
+ } else {
+ int size = groupsize;
+ data_offset += from;
+ size -= from;
+ from = 0;
+ if (size > len)
+ size = len;
+ ret = action(map, chip, data_offset,
+ buf, size, reg_prot_offset,
+ groupno, groupsize);
+ if (ret < 0)
+ return ret;
+ buf += size;
+ len -= size;
+ *retlen += size;
+ data_offset += size;
+ }
+ groupno++;
+ groups--;
+ }
+
+ /* next OTP region */
+ if (++field == extp->NumProtectionFields)
+ break;
+ reg_prot_offset = otp->ProtRegAddr;
+ reg_fact_groups = otp->FactGroups;
+ reg_fact_size = 1 << otp->FactProtRegSize;
+ reg_user_groups = otp->UserGroups;
+ reg_user_size = 1 << otp->UserProtRegSize;
+ otp++;
+ }
+ }
+
+ return 0;
+}
+
+static int cfi_intelext_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
+ size_t len, size_t *retlen,
+ u_char *buf)
+{
+ return cfi_intelext_otp_walk(mtd, from, len, retlen,
+ buf, do_otp_read, 0);
+}
+
+static int cfi_intelext_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
+ size_t len, size_t *retlen,
+ u_char *buf)
+{
+ return cfi_intelext_otp_walk(mtd, from, len, retlen,
+ buf, do_otp_read, 1);
+}
+
+static int cfi_intelext_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
+ size_t len, size_t *retlen,
+ u_char *buf)
+{
+ return cfi_intelext_otp_walk(mtd, from, len, retlen,
+ buf, do_otp_write, 1);
+}
+
+static int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd,
+ loff_t from, size_t len)
+{
+ size_t retlen;
+ return cfi_intelext_otp_walk(mtd, from, len, &retlen,
+ NULL, do_otp_lock, 1);
+}
+
+static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd,
+ struct otp_info *buf, size_t len)
+{
+ size_t retlen;
+ int ret;
+
+ ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 0);
+ return ret ? : retlen;
+}
+
+static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd,
+ struct otp_info *buf, size_t len)
+{
+ size_t retlen;
+ int ret;
+
+ ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 1);
+ return ret ? : retlen;
+}
+
+#endif
+
static int cfi_intelext_suspend(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
@@ -2125,10 +2309,46 @@ static void cfi_intelext_resume(struct mtd_info *mtd)
}
}
+static int cfi_intelext_reset(struct mtd_info *mtd)
+{
+ struct map_info *map = mtd->priv;
+ struct cfi_private *cfi = map->fldrv_priv;
+ int i, ret;
+
+ for (i=0; i < cfi->numchips; i++) {
+ struct flchip *chip = &cfi->chips[i];
+
+ /* force the completion of any ongoing operation
+ and switch to array mode so any bootloader in
+ flash is accessible for soft reboot. */
+ spin_lock(chip->mutex);
+ ret = get_chip(map, chip, chip->start, FL_SYNCING);
+ if (!ret) {
+ map_write(map, CMD(0xff), chip->start);
+ chip->state = FL_READY;
+ }
+ spin_unlock(chip->mutex);
+ }
+
+ return 0;
+}
+
+static int cfi_intelext_reboot(struct notifier_block *nb, unsigned long val,
+ void *v)
+{
+ struct mtd_info *mtd;
+
+ mtd = container_of(nb, struct mtd_info, reboot_notifier);
+ cfi_intelext_reset(mtd);
+ return NOTIFY_DONE;
+}
+
static void cfi_intelext_destroy(struct mtd_info *mtd)
{
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
+ cfi_intelext_reset(mtd);
+ unregister_reboot_notifier(&mtd->reboot_notifier);
kfree(cfi->cmdset_priv);
kfree(cfi->cfiq);
kfree(cfi->chips[0].priv);
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c
index fca8ff6f7e14..8505f118f2db 100644
--- a/drivers/mtd/chips/cfi_cmdset_0002.c
+++ b/drivers/mtd/chips/cfi_cmdset_0002.c
@@ -4,16 +4,20 @@
*
* Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp>
* Copyright (C) 2004 Arcom Control Systems Ltd <linux@arcom.com>
+ * Copyright (C) 2005 MontaVista Software Inc. <source@mvista.com>
*
* 2_by_8 routines added by Simon Munton
*
* 4_by_16 work by Carolyn J. Smith
*
+ * XIP support hooks by Vitaly Wool (based on code for Intel flash
+ * by Nicolas Pitre)
+ *
* Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com
*
* This code is GPL
*
- * $Id: cfi_cmdset_0002.c,v 1.114 2004/12/11 15:43:53 dedekind Exp $
+ * $Id: cfi_cmdset_0002.c,v 1.118 2005/07/04 22:34:29 gleixner Exp $
*
*/
@@ -34,6 +38,7 @@
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/cfi.h>
+#include <linux/mtd/xip.h>
#define AMD_BOOTLOC_BUG
#define FORCE_WORD_WRITE 0
@@ -43,6 +48,7 @@
#define MANUFACTURER_AMD 0x0001
#define MANUFACTURER_SST 0x00BF
#define SST49LF004B 0x0060
+#define SST49LF008A 0x005a
static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
@@ -191,6 +197,7 @@ static struct cfi_fixup cfi_fixup_table[] = {
};
static struct cfi_fixup jedec_fixup_table[] = {
{ MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, },
+ { MANUFACTURER_SST, SST49LF008A, fixup_use_fwh_lock, NULL, },
{ 0, 0, NULL, NULL }
};
@@ -391,7 +398,7 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
* correctly and is therefore not done (particulary with interleaved chips
* as each chip must be checked independantly of the others).
*/
-static int chip_ready(struct map_info *map, unsigned long addr)
+static int __xipram chip_ready(struct map_info *map, unsigned long addr)
{
map_word d, t;
@@ -401,6 +408,32 @@ static int chip_ready(struct map_info *map, unsigned long addr)
return map_word_equal(map, d, t);
}
+/*
+ * Return true if the chip is ready and has the correct value.
+ *
+ * Ready is one of: read mode, query mode, erase-suspend-read mode (in any
+ * non-suspended sector) and it is indicated by no bits toggling.
+ *
+ * Error are indicated by toggling bits or bits held with the wrong value,
+ * or with bits toggling.
+ *
+ * Note that anything more complicated than checking if no bits are toggling
+ * (including checking DQ5 for an error status) is tricky to get working
+ * correctly and is therefore not done (particulary with interleaved chips
+ * as each chip must be checked independantly of the others).
+ *
+ */
+static int __xipram chip_good(struct map_info *map, unsigned long addr, map_word expected)
+{
+ map_word oldd, curd;
+
+ oldd = map_read(map, addr);
+ curd = map_read(map, addr);
+
+ return map_word_equal(map, oldd, curd) &&
+ map_word_equal(map, curd, expected);
+}
+
static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
{
DECLARE_WAITQUEUE(wait, current);
@@ -420,12 +453,12 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
if (time_after(jiffies, timeo)) {
printk(KERN_ERR "Waiting for chip to be ready timed out.\n");
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
return -EIO;
}
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
cfi_udelay(1);
- cfi_spin_lock(chip->mutex);
+ spin_lock(chip->mutex);
/* Someone else might have been playing with it. */
goto retry;
}
@@ -473,15 +506,23 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
return -EIO;
}
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
cfi_udelay(1);
- cfi_spin_lock(chip->mutex);
+ spin_lock(chip->mutex);
/* Nobody will touch it while it's in state FL_ERASE_SUSPENDING.
So we can just loop here. */
}
chip->state = FL_READY;
return 0;
+ case FL_XIP_WHILE_ERASING:
+ if (mode != FL_READY && mode != FL_POINT &&
+ (!cfip || !(cfip->EraseSuspend&2)))
+ goto sleep;
+ chip->oldstate = chip->state;
+ chip->state = FL_READY;
+ return 0;
+
case FL_POINT:
/* Only if there's no operation suspended... */
if (mode == FL_READY && chip->oldstate == FL_READY)
@@ -491,10 +532,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
sleep:
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
- cfi_spin_lock(chip->mutex);
+ spin_lock(chip->mutex);
goto resettime;
}
}
@@ -512,6 +553,11 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
chip->state = FL_ERASING;
break;
+ case FL_XIP_WHILE_ERASING:
+ chip->state = chip->oldstate;
+ chip->oldstate = FL_READY;
+ break;
+
case FL_READY:
case FL_STATUS:
/* We should really make set_vpp() count, rather than doing this */
@@ -523,6 +569,198 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
wake_up(&chip->wq);
}
+#ifdef CONFIG_MTD_XIP
+
+/*
+ * No interrupt what so ever can be serviced while the flash isn't in array
+ * mode. This is ensured by the xip_disable() and xip_enable() functions
+ * enclosing any code path where the flash is known not to be in array mode.
+ * And within a XIP disabled code path, only functions marked with __xipram
+ * may be called and nothing else (it's a good thing to inspect generated
+ * assembly to make sure inline functions were actually inlined and that gcc
+ * didn't emit calls to its own support functions). Also configuring MTD CFI
+ * support to a single buswidth and a single interleave is also recommended.
+ */
+
+static void xip_disable(struct map_info *map, struct flchip *chip,
+ unsigned long adr)
+{
+ /* TODO: chips with no XIP use should ignore and return */
+ (void) map_read(map, adr); /* ensure mmu mapping is up to date */
+ local_irq_disable();
+}
+
+static void __xipram xip_enable(struct map_info *map, struct flchip *chip,
+ unsigned long adr)
+{
+ struct cfi_private *cfi = map->fldrv_priv;
+
+ if (chip->state != FL_POINT && chip->state != FL_READY) {
+ map_write(map, CMD(0xf0), adr);
+ chip->state = FL_READY;
+ }
+ (void) map_read(map, adr);
+ xip_iprefetch();
+ local_irq_enable();
+}
+
+/*
+ * When a delay is required for the flash operation to complete, the
+ * xip_udelay() function is polling for both the given timeout and pending
+ * (but still masked) hardware interrupts. Whenever there is an interrupt
+ * pending then the flash erase operation is suspended, array mode restored
+ * and interrupts unmasked. Task scheduling might also happen at that
+ * point. The CPU eventually returns from the interrupt or the call to
+ * schedule() and the suspended flash operation is resumed for the remaining
+ * of the delay period.
+ *
+ * Warning: this function _will_ fool interrupt latency tracing tools.
+ */
+
+static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
+ unsigned long adr, int usec)
+{
+ struct cfi_private *cfi = map->fldrv_priv;
+ struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
+ map_word status, OK = CMD(0x80);
+ unsigned long suspended, start = xip_currtime();
+ flstate_t oldstate;
+
+ do {
+ cpu_relax();
+ if (xip_irqpending() && extp &&
+ ((chip->state == FL_ERASING && (extp->EraseSuspend & 2))) &&
+ (cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) {
+ /*
+ * Let's suspend the erase operation when supported.
+ * Note that we currently don't try to suspend
+ * interleaved chips if there is already another
+ * operation suspended (imagine what happens
+ * when one chip was already done with the current
+ * operation while another chip suspended it, then
+ * we resume the whole thing at once). Yes, it
+ * can happen!
+ */
+ map_write(map, CMD(0xb0), adr);
+ usec -= xip_elapsed_since(start);
+ suspended = xip_currtime();
+ do {
+ if (xip_elapsed_since(suspended) > 100000) {
+ /*
+ * The chip doesn't want to suspend
+ * after waiting for 100 msecs.
+ * This is a critical error but there
+ * is not much we can do here.
+ */
+ return;
+ }
+ status = map_read(map, adr);
+ } while (!map_word_andequal(map, status, OK, OK));
+
+ /* Suspend succeeded */
+ oldstate = chip->state;
+ if (!map_word_bitsset(map, status, CMD(0x40)))
+ break;
+ chip->state = FL_XIP_WHILE_ERASING;
+ chip->erase_suspended = 1;
+ map_write(map, CMD(0xf0), adr);
+ (void) map_read(map, adr);
+ asm volatile (".rep 8; nop; .endr");
+ local_irq_enable();
+ spin_unlock(chip->mutex);
+ asm volatile (".rep 8; nop; .endr");
+ cond_resched();
+
+ /*
+ * We're back. However someone else might have
+ * decided to go write to the chip if we are in
+ * a suspended erase state. If so let's wait
+ * until it's done.
+ */
+ spin_lock(chip->mutex);
+ while (chip->state != FL_XIP_WHILE_ERASING) {
+ DECLARE_WAITQUEUE(wait, current);
+ 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);
+ }
+ /* Disallow XIP again */
+ local_irq_disable();
+
+ /* Resume the write or erase operation */
+ map_write(map, CMD(0x30), adr);
+ chip->state = oldstate;
+ start = xip_currtime();
+ } else if (usec >= 1000000/HZ) {
+ /*
+ * Try to save on CPU power when waiting delay
+ * is at least a system timer tick period.
+ * No need to be extremely accurate here.
+ */
+ xip_cpu_idle();
+ }
+ status = map_read(map, adr);
+ } while (!map_word_andequal(map, status, OK, OK)
+ && xip_elapsed_since(start) < usec);
+}
+
+#define UDELAY(map, chip, adr, usec) xip_udelay(map, chip, adr, usec)
+
+/*
+ * The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while
+ * the flash is actively programming or erasing since we have to poll for
+ * the operation to complete anyway. We can't do that in a generic way with
+ * a XIP setup so do it before the actual flash operation in this case
+ * and stub it out from INVALIDATE_CACHE_UDELAY.
+ */
+#define XIP_INVAL_CACHED_RANGE(map, from, size) \
+ INVALIDATE_CACHED_RANGE(map, from, size)
+
+#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \
+ UDELAY(map, chip, adr, usec)
+
+/*
+ * Extra notes:
+ *
+ * Activating this XIP support changes the way the code works a bit. For
+ * example the code to suspend the current process when concurrent access
+ * happens is never executed because xip_udelay() will always return with the
+ * same chip state as it was entered with. This is why there is no care for
+ * the presence of add_wait_queue() or schedule() calls from within a couple
+ * xip_disable()'d areas of code, like in do_erase_oneblock for example.
+ * The queueing and scheduling are always happening within xip_udelay().
+ *
+ * Similarly, get_chip() and put_chip() just happen to always be executed
+ * with chip->state set to FL_READY (or FL_XIP_WHILE_*) where flash state
+ * is in array mode, therefore never executing many cases therein and not
+ * causing any problem with XIP.
+ */
+
+#else
+
+#define xip_disable(map, chip, adr)
+#define xip_enable(map, chip, adr)
+#define XIP_INVAL_CACHED_RANGE(x...)
+
+#define UDELAY(map, chip, adr, usec) \
+do { \
+ spin_unlock(chip->mutex); \
+ cfi_udelay(usec); \
+ spin_lock(chip->mutex); \
+} while (0)
+
+#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \
+do { \
+ spin_unlock(chip->mutex); \
+ INVALIDATE_CACHED_RANGE(map, adr, len); \
+ cfi_udelay(usec); \
+ spin_lock(chip->mutex); \
+} while (0)
+
+#endif
static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
{
@@ -535,10 +773,10 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof
/* Ensure cmd read/writes are aligned. */
cmd_addr = adr & ~(map_bankwidth(map)-1);
- cfi_spin_lock(chip->mutex);
+ spin_lock(chip->mutex);
ret = get_chip(map, chip, cmd_addr, FL_READY);
if (ret) {
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
return ret;
}
@@ -551,7 +789,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof
put_chip(map, chip, cmd_addr);
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
return 0;
}
@@ -605,7 +843,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
struct cfi_private *cfi = map->fldrv_priv;
retry:
- cfi_spin_lock(chip->mutex);
+ spin_lock(chip->mutex);
if (chip->state != FL_READY){
#if 0
@@ -614,7 +852,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
@@ -643,7 +881,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
cfi_send_gen_cmd(0x00, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
wake_up(&chip->wq);
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
return 0;
}
@@ -692,7 +930,7 @@ static int cfi_amdstd_secsi_read (struct mtd_info *mtd, loff_t from, size_t len,
}
-static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum)
+static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum)
{
struct cfi_private *cfi = map->fldrv_priv;
unsigned long timeo = jiffies + HZ;
@@ -712,10 +950,10 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
adr += chip->start;
- cfi_spin_lock(chip->mutex);
+ spin_lock(chip->mutex);
ret = get_chip(map, chip, adr, FL_WRITING);
if (ret) {
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
return ret;
}
@@ -735,7 +973,9 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
goto op_done;
}
+ XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map));
ENABLE_VPP(map);
+ xip_disable(map, chip, adr);
retry:
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
@@ -743,9 +983,9 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
map_write(map, datum, adr);
chip->state = FL_WRITING;
- cfi_spin_unlock(chip->mutex);
- cfi_udelay(chip->word_write_time);
- cfi_spin_lock(chip->mutex);
+ INVALIDATE_CACHE_UDELAY(map, chip,
+ adr, map_bankwidth(map),
+ chip->word_write_time);
/* See comment above for timeout value. */
timeo = jiffies + uWriteTimeout;
@@ -756,39 +996,43 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
timeo = jiffies + (HZ / 2); /* FIXME */
- cfi_spin_lock(chip->mutex);
+ spin_lock(chip->mutex);
continue;
}
if (chip_ready(map, adr))
- goto op_done;
+ break;
- if (time_after(jiffies, timeo))
+ if (time_after(jiffies, timeo)) {
+ xip_enable(map, chip, adr);
+ printk(KERN_WARNING "MTD %s(): software timeout\n", __func__);
+ xip_disable(map, chip, adr);
break;
+ }
/* Latency issues. Drop the lock, wait a while and retry */
- cfi_spin_unlock(chip->mutex);
- cfi_udelay(1);
- cfi_spin_lock(chip->mutex);
+ UDELAY(map, chip, adr, 1);
}
+ /* Did we succeed? */
+ if (!chip_good(map, adr, datum)) {
+ /* reset on all failures. */
+ map_write( map, CMD(0xF0), chip->start );
+ /* FIXME - should have reset delay before continuing */
- printk(KERN_WARNING "MTD %s(): software timeout\n", __func__);
-
- /* reset on all failures. */
- map_write( map, CMD(0xF0), chip->start );
- /* FIXME - should have reset delay before continuing */
- if (++retry_cnt <= MAX_WORD_RETRIES)
- goto retry;
+ if (++retry_cnt <= MAX_WORD_RETRIES)
+ goto retry;
- ret = -EIO;
+ ret = -EIO;
+ }
+ xip_enable(map, chip, adr);
op_done:
chip->state = FL_READY;
put_chip(map, chip, adr);
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
return ret;
}
@@ -820,7 +1064,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
map_word tmp_buf;
retry:
- cfi_spin_lock(cfi->chips[chipnum].mutex);
+ spin_lock(cfi->chips[chipnum].mutex);
if (cfi->chips[chipnum].state != FL_READY) {
#if 0
@@ -829,7 +1073,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&cfi->chips[chipnum].wq, &wait);
- cfi_spin_unlock(cfi->chips[chipnum].mutex);
+ spin_unlock(cfi->chips[chipnum].mutex);
schedule();
remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
@@ -843,7 +1087,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
/* Load 'tmp_buf' with old contents of flash */
tmp_buf = map_read(map, bus_ofs+chipstart);
- cfi_spin_unlock(cfi->chips[chipnum].mutex);
+ spin_unlock(cfi->chips[chipnum].mutex);
/* Number of bytes to copy from buffer */
n = min_t(int, len, map_bankwidth(map)-i);
@@ -898,7 +1142,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
map_word tmp_buf;
retry1:
- cfi_spin_lock(cfi->chips[chipnum].mutex);
+ spin_lock(cfi->chips[chipnum].mutex);
if (cfi->chips[chipnum].state != FL_READY) {
#if 0
@@ -907,7 +1151,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&cfi->chips[chipnum].wq, &wait);
- cfi_spin_unlock(cfi->chips[chipnum].mutex);
+ spin_unlock(cfi->chips[chipnum].mutex);
schedule();
remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
@@ -920,7 +1164,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
tmp_buf = map_read(map, ofs + chipstart);
- cfi_spin_unlock(cfi->chips[chipnum].mutex);
+ spin_unlock(cfi->chips[chipnum].mutex);
tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len);
@@ -939,8 +1183,9 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
/*
* FIXME: interleaved mode not tested, and probably not supported!
*/
-static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
- unsigned long adr, const u_char *buf, int len)
+static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
+ unsigned long adr, const u_char *buf,
+ int len)
{
struct cfi_private *cfi = map->fldrv_priv;
unsigned long timeo = jiffies + HZ;
@@ -954,10 +1199,10 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
adr += chip->start;
cmd_adr = adr;
- cfi_spin_lock(chip->mutex);
+ spin_lock(chip->mutex);
ret = get_chip(map, chip, adr, FL_WRITING);
if (ret) {
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
return ret;
}
@@ -966,7 +1211,10 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
__func__, adr, datum.x[0] );
+ XIP_INVAL_CACHED_RANGE(map, adr, len);
ENABLE_VPP(map);
+ xip_disable(map, chip, cmd_adr);
+
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
//cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
@@ -996,9 +1244,9 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
map_write(map, CMD(0x29), cmd_adr);
chip->state = FL_WRITING;
- cfi_spin_unlock(chip->mutex);
- cfi_udelay(chip->buffer_write_time);
- cfi_spin_lock(chip->mutex);
+ INVALIDATE_CACHE_UDELAY(map, chip,
+ adr, map_bankwidth(map),
+ chip->word_write_time);
timeo = jiffies + uWriteTimeout;
@@ -1009,38 +1257,39 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
timeo = jiffies + (HZ / 2); /* FIXME */
- cfi_spin_lock(chip->mutex);
+ spin_lock(chip->mutex);
continue;
}
- if (chip_ready(map, adr))
+ if (chip_ready(map, adr)) {
+ xip_enable(map, chip, adr);
goto op_done;
+ }
if( time_after(jiffies, timeo))
break;
/* Latency issues. Drop the lock, wait a while and retry */
- cfi_spin_unlock(chip->mutex);
- cfi_udelay(1);
- cfi_spin_lock(chip->mutex);
+ UDELAY(map, chip, adr, 1);
}
- printk(KERN_WARNING "MTD %s(): software timeout\n",
- __func__ );
-
/* reset on all failures. */
map_write( map, CMD(0xF0), chip->start );
+ xip_enable(map, chip, adr);
/* FIXME - should have reset delay before continuing */
+ printk(KERN_WARNING "MTD %s(): software timeout\n",
+ __func__ );
+
ret = -EIO;
op_done:
chip->state = FL_READY;
put_chip(map, chip, adr);
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
return ret;
}
@@ -1130,7 +1379,7 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
* Handle devices with one erase region, that only implement
* the chip erase command.
*/
-static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
+static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
{
struct cfi_private *cfi = map->fldrv_priv;
unsigned long timeo = jiffies + HZ;
@@ -1140,17 +1389,20 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
adr = cfi->addr_unlock1;
- cfi_spin_lock(chip->mutex);
+ spin_lock(chip->mutex);
ret = get_chip(map, chip, adr, FL_WRITING);
if (ret) {
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
return ret;
}
DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
__func__, chip->start );
+ XIP_INVAL_CACHED_RANGE(map, adr, map->size);
ENABLE_VPP(map);
+ xip_disable(map, chip, adr);
+
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
@@ -1162,9 +1414,9 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
chip->erase_suspended = 0;
chip->in_progress_block_addr = adr;
- cfi_spin_unlock(chip->mutex);
- msleep(chip->erase_time/2);
- cfi_spin_lock(chip->mutex);
+ INVALIDATE_CACHE_UDELAY(map, chip,
+ adr, map->size,
+ chip->erase_time*500);
timeo = jiffies + (HZ*20);
@@ -1173,10 +1425,10 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
/* Someone's suspended the erase. Sleep */
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
- cfi_spin_lock(chip->mutex);
+ spin_lock(chip->mutex);
continue;
}
if (chip->erase_suspended) {
@@ -1187,36 +1439,36 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
}
if (chip_ready(map, adr))
- goto op_done;
+ break;
- if (time_after(jiffies, timeo))
+ if (time_after(jiffies, timeo)) {
+ printk(KERN_WARNING "MTD %s(): software timeout\n",
+ __func__ );
break;
+ }
/* Latency issues. Drop the lock, wait a while and retry */
- cfi_spin_unlock(chip->mutex);
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(1);
- cfi_spin_lock(chip->mutex);
+ UDELAY(map, chip, adr, 1000000/HZ);
}
+ /* Did we succeed? */
+ if (!chip_good(map, adr, map_word_ff(map))) {
+ /* reset on all failures. */
+ map_write( map, CMD(0xF0), chip->start );
+ /* FIXME - should have reset delay before continuing */
- printk(KERN_WARNING "MTD %s(): software timeout\n",
- __func__ );
-
- /* reset on all failures. */
- map_write( map, CMD(0xF0), chip->start );
- /* FIXME - should have reset delay before continuing */
+ ret = -EIO;
+ }
- ret = -EIO;
- op_done:
chip->state = FL_READY;
+ xip_enable(map, chip, adr);
put_chip(map, chip, adr);
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
return ret;
}
-static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk)
+static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk)
{
struct cfi_private *cfi = map->fldrv_priv;
unsigned long timeo = jiffies + HZ;
@@ -1225,17 +1477,20 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u
adr += chip->start;
- cfi_spin_lock(chip->mutex);
+ spin_lock(chip->mutex);
ret = get_chip(map, chip, adr, FL_ERASING);
if (ret) {
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
return ret;
}
DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
__func__, adr );
+ XIP_INVAL_CACHED_RANGE(map, adr, len);
ENABLE_VPP(map);
+ xip_disable(map, chip, adr);
+
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
@@ -1246,10 +1501,10 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u
chip->state = FL_ERASING;
chip->erase_suspended = 0;
chip->in_progress_block_addr = adr;
-
- cfi_spin_unlock(chip->mutex);
- msleep(chip->erase_time/2);
- cfi_spin_lock(chip->mutex);
+
+ INVALIDATE_CACHE_UDELAY(map, chip,
+ adr, len,
+ chip->erase_time*500);
timeo = jiffies + (HZ*20);
@@ -1258,10 +1513,10 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u
/* Someone's suspended the erase. Sleep */
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
schedule();
remove_wait_queue(&chip->wq, &wait);
- cfi_spin_lock(chip->mutex);
+ spin_lock(chip->mutex);
continue;
}
if (chip->erase_suspended) {
@@ -1271,31 +1526,33 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u
chip->erase_suspended = 0;
}
- if (chip_ready(map, adr))
- goto op_done;
+ if (chip_ready(map, adr)) {
+ xip_enable(map, chip, adr);
+ break;
+ }
- if (time_after(jiffies, timeo))
+ if (time_after(jiffies, timeo)) {
+ xip_enable(map, chip, adr);
+ printk(KERN_WARNING "MTD %s(): software timeout\n",
+ __func__ );
break;
+ }
/* Latency issues. Drop the lock, wait a while and retry */
- cfi_spin_unlock(chip->mutex);
- set_current_state(TASK_UNINTERRUPTIBLE);
- schedule_timeout(1);
- cfi_spin_lock(chip->mutex);
+ UDELAY(map, chip, adr, 1000000/HZ);
+ }
+ /* Did we succeed? */
+ if (!chip_good(map, adr, map_word_ff(map))) {
+ /* reset on all failures. */
+ map_write( map, CMD(0xF0), chip->start );
+ /* FIXME - should have reset delay before continuing */
+
+ ret = -EIO;
}
-
- printk(KERN_WARNING "MTD %s(): software timeout\n",
- __func__ );
-
- /* reset on all failures. */
- map_write( map, CMD(0xF0), chip->start );
- /* FIXME - should have reset delay before continuing */
- ret = -EIO;
- op_done:
chip->state = FL_READY;
put_chip(map, chip, adr);
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
return ret;
}
@@ -1355,7 +1612,7 @@ static void cfi_amdstd_sync (struct mtd_info *mtd)
chip = &cfi->chips[i];
retry:
- cfi_spin_lock(chip->mutex);
+ spin_lock(chip->mutex);
switch(chip->state) {
case FL_READY:
@@ -1369,14 +1626,14 @@ static void cfi_amdstd_sync (struct mtd_info *mtd)
* with the chip now anyway.
*/
case FL_SYNCING:
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
break;
default:
/* Not an idle state */
add_wait_queue(&chip->wq, &wait);
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
schedule();
@@ -1391,13 +1648,13 @@ static void cfi_amdstd_sync (struct mtd_info *mtd)
for (i--; i >=0; i--) {
chip = &cfi->chips[i];
- cfi_spin_lock(chip->mutex);
+ spin_lock(chip->mutex);
if (chip->state == FL_SYNCING) {
chip->state = chip->oldstate;
wake_up(&chip->wq);
}
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
}
}
@@ -1413,7 +1670,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd)
for (i=0; !ret && i<cfi->numchips; i++) {
chip = &cfi->chips[i];
- cfi_spin_lock(chip->mutex);
+ spin_lock(chip->mutex);
switch(chip->state) {
case FL_READY:
@@ -1433,7 +1690,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd)
ret = -EAGAIN;
break;
}
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
}
/* Unlock the chips again */
@@ -1442,13 +1699,13 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd)
for (i--; i >=0; i--) {
chip = &cfi->chips[i];
- cfi_spin_lock(chip->mutex);
+ spin_lock(chip->mutex);
if (chip->state == FL_PM_SUSPENDED) {
chip->state = chip->oldstate;
wake_up(&chip->wq);
}
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
}
}
@@ -1467,7 +1724,7 @@ static void cfi_amdstd_resume(struct mtd_info *mtd)
chip = &cfi->chips[i];
- cfi_spin_lock(chip->mutex);
+ spin_lock(chip->mutex);
if (chip->state == FL_PM_SUSPENDED) {
chip->state = FL_READY;
@@ -1477,7 +1734,7 @@ static void cfi_amdstd_resume(struct mtd_info *mtd)
else
printk(KERN_ERR "Argh. Chip not in PM_SUSPENDED state upon resume()\n");
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
}
}
diff --git a/drivers/mtd/chips/fwh_lock.h b/drivers/mtd/chips/fwh_lock.h
index fbf44708a861..e1a5b76596c5 100644
--- a/drivers/mtd/chips/fwh_lock.h
+++ b/drivers/mtd/chips/fwh_lock.h
@@ -58,10 +58,10 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip,
* to flash memory - that means that we don't have to check status
* and timeout.
*/
- cfi_spin_lock(chip->mutex);
+ spin_lock(chip->mutex);
ret = get_chip(map, chip, adr, FL_LOCKING);
if (ret) {
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
return ret;
}
@@ -71,7 +71,7 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip,
/* Done and happy. */
chip->state = FL_READY;
put_chip(map, chip, adr);
- cfi_spin_unlock(chip->mutex);
+ spin_unlock(chip->mutex);
return 0;
}
diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c
index fc982c4671f0..dc065b22f79e 100644
--- a/drivers/mtd/chips/gen_probe.c
+++ b/drivers/mtd/chips/gen_probe.c
@@ -2,7 +2,7 @@
* Routines common to all CFI-type probes.
* (C) 2001-2003 Red Hat, Inc.
* GPL'd
- * $Id: gen_probe.c,v 1.21 2004/08/14 15:14:05 dwmw2 Exp $
+ * $Id: gen_probe.c,v 1.22 2005/01/24 23:49:50 rmk Exp $
*/
#include <linux/kernel.h>
@@ -162,7 +162,7 @@ static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp,
int max_chips = map_bankwidth(map); /* And minimum 1 */
int nr_chips, type;
- for (nr_chips = min_chips; nr_chips <= max_chips; nr_chips <<= 1) {
+ for (nr_chips = max_chips; nr_chips >= min_chips; nr_chips >>= 1) {
if (!cfi_interleave_supported(nr_chips))
continue;
diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c
index 30325a25ab95..30da428eb7b9 100644
--- a/drivers/mtd/chips/jedec_probe.c
+++ b/drivers/mtd/chips/jedec_probe.c
@@ -1,7 +1,7 @@
/*
Common Flash Interface probe code.
(C) 2000 Red Hat. GPL'd.
- $Id: jedec_probe.c,v 1.61 2004/11/19 20:52:16 thayne Exp $
+ $Id: jedec_probe.c,v 1.63 2005/02/14 16:30:32 bjd Exp $
See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5)
for the standard this probe goes back to.
@@ -142,6 +142,7 @@
#define SST29LE512 0x003d
#define SST39LF800 0x2781
#define SST39LF160 0x2782
+#define SST39VF1601 0x234b
#define SST39LF512 0x00D4
#define SST39LF010 0x00D5
#define SST39LF020 0x00D6
@@ -1448,6 +1449,21 @@ static const struct amd_flash_info jedec_table[] = {
ERASEINFO(0x1000,256),
ERASEINFO(0x1000,256)
}
+ }, {
+ .mfr_id = MANUFACTURER_SST, /* should be CFI */
+ .dev_id = SST39VF1601,
+ .name = "SST 39VF1601",
+ .uaddr = {
+ [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */
+ [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */
+ },
+ .DevSize = SIZE_2MiB,
+ .CmdSet = P_ID_AMD_STD,
+ .NumEraseRegions= 2,
+ .regions = {
+ ERASEINFO(0x1000,256),
+ ERASEINFO(0x1000,256)
+ }
}, {
.mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */
@@ -1856,6 +1872,16 @@ static inline int jedec_match( __u32 base,
case CFI_DEVICETYPE_X8:
mfr = (__u8)finfo->mfr_id;
id = (__u8)finfo->dev_id;
+
+ /* bjd: it seems that if we do this, we can end up
+ * detecting 16bit flashes as an 8bit device, even though
+ * there aren't.
+ */
+ if (finfo->dev_id > 0xff) {
+ DEBUG( MTD_DEBUG_LEVEL3, "%s(): ID is not 8bit\n",
+ __func__);
+ goto match_done;
+ }
break;
case CFI_DEVICETYPE_X16:
mfr = (__u16)finfo->mfr_id;
OpenPOWER on IntegriCloud