Skip to content
Snippets Groups Projects
Commit 200d481f authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge master.kernel.org:/pub/scm/linux/kernel/git/tglx/mtd-2.6

parents f43a64c5 97f927a4
No related merge requests found
Showing
with 1240 additions and 805 deletions
# drivers/mtd/chips/Kconfig # 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" menu "RAM/ROM/Flash chip drivers"
depends on MTD!=n depends on MTD!=n
...@@ -155,6 +155,31 @@ config MTD_CFI_I8 ...@@ -155,6 +155,31 @@ config MTD_CFI_I8
If your flash chips are interleaved in eights - i.e. you have eight If your flash chips are interleaved in eights - i.e. you have eight
flash chips addressed by each bus cycle, then say 'Y'. 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 config MTD_CFI_INTELEXT
tristate "Support for Intel/Sharp flash chips" tristate "Support for Intel/Sharp flash chips"
depends on MTD_GEN_PROBE depends on MTD_GEN_PROBE
...@@ -275,7 +300,7 @@ config MTD_JEDEC ...@@ -275,7 +300,7 @@ config MTD_JEDEC
config MTD_XIP config MTD_XIP
bool "XIP aware MTD support" 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 default y if XIP_KERNEL
help help
This allows MTD support to work with flash memory which is also This allows MTD support to work with flash memory which is also
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Author: Jonas Holmberg <jonas.holmberg@axis.com> * 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 * Copyright (c) 2001 Axis Communications AB
* *
...@@ -67,7 +67,6 @@ ...@@ -67,7 +67,6 @@
#define AM29LV160DT 0x22C4 #define AM29LV160DT 0x22C4
#define AM29LV160DB 0x2249 #define AM29LV160DB 0x2249
#define AM29BDS323D 0x22D1 #define AM29BDS323D 0x22D1
#define AM29BDS643D 0x227E
/* Atmel */ /* Atmel */
#define AT49xV16x 0x00C0 #define AT49xV16x 0x00C0
...@@ -617,17 +616,6 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) ...@@ -617,17 +616,6 @@ static struct mtd_info *amd_flash_probe(struct map_info *map)
{ .offset = 0x300000, .erasesize = 0x10000, .numblocks = 15 }, { .offset = 0x300000, .erasesize = 0x10000, .numblocks = 15 },
{ .offset = 0x3f0000, .erasesize = 0x02000, .numblocks = 8 }, { .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, .mfr_id = MANUFACTURER_ATMEL,
.dev_id = AT49xV16x, .dev_id = AT49xV16x,
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* *
* (C) 2000 Red Hat. GPL'd * (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> * 10/10/2000 Nicolas Pitre <nico@cam.org>
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/reboot.h>
#include <linux/mtd/xip.h> #include <linux/mtd/xip.h>
#include <linux/mtd/map.h> #include <linux/mtd/map.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
...@@ -48,16 +49,25 @@ ...@@ -48,16 +49,25 @@
#define M50LPW080 0x002F #define M50LPW080 0x002F
static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
//static int cfi_intelext_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_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_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 int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *);
static void cfi_intelext_sync (struct mtd_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_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); 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 int cfi_intelext_suspend (struct mtd_info *);
static void cfi_intelext_resume (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 *); static void cfi_intelext_destroy(struct mtd_info *);
...@@ -252,7 +262,8 @@ read_pri_intelext(struct map_info *map, __u16 adr) ...@@ -252,7 +262,8 @@ read_pri_intelext(struct map_info *map, __u16 adr)
int nb_parts, i; int nb_parts, i;
/* Protection Register info */ /* Protection Register info */
extra_size += (extp->NumProtectionFields - 1) * (4 + 6); extra_size += (extp->NumProtectionFields - 1) *
sizeof(struct cfi_intelext_otpinfo);
/* Burst Read info */ /* Burst Read info */
extra_size += 6; extra_size += 6;
...@@ -324,7 +335,9 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) ...@@ -324,7 +335,9 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
mtd->resume = cfi_intelext_resume; mtd->resume = cfi_intelext_resume;
mtd->flags = MTD_CAP_NORFLASH; mtd->flags = MTD_CAP_NORFLASH;
mtd->name = map->name; mtd->name = map->name;
mtd->reboot_notifier.notifier_call = cfi_intelext_reboot;
if (cfi->cfi_mode == CFI_MODE_CFI) { if (cfi->cfi_mode == CFI_MODE_CFI) {
/* /*
* It's a real CFI chip, not one for which the probe * 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) ...@@ -422,9 +435,13 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
mtd->eraseregions[i].numblocks); mtd->eraseregions[i].numblocks);
} }
#if 0 #ifdef CONFIG_MTD_OTP
mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg;
mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg; 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 #endif
/* This function has the potential to distort the reality /* This function has the potential to distort the reality
...@@ -433,6 +450,7 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) ...@@ -433,6 +450,7 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
goto setup_err; goto setup_err;
__module_get(THIS_MODULE); __module_get(THIS_MODULE);
register_reboot_notifier(&mtd->reboot_notifier);
return mtd; return mtd;
setup_err: setup_err:
...@@ -471,7 +489,8 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, ...@@ -471,7 +489,8 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd,
int offs, numregions, numparts, partshift, numvirtchips, i, j; int offs, numregions, numparts, partshift, numvirtchips, i, j;
/* Protection Register info */ /* Protection Register info */
offs = (extp->NumProtectionFields - 1) * (4 + 6); offs = (extp->NumProtectionFields - 1) *
sizeof(struct cfi_intelext_otpinfo);
/* Burst Read info */ /* Burst Read info */
offs += 6; offs += 6;
...@@ -563,7 +582,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr ...@@ -563,7 +582,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
resettime: resettime:
timeo = jiffies + HZ; timeo = jiffies + HZ;
retry: 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 * OK. We have possibility for contension on the write/erase
* operations which are global to the real chip and not per * 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 ...@@ -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 * 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 * 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. * 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, 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, ...@@ -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 */ /* TODO: chips with no XIP use should ignore and return */
(void) map_read(map, adr); /* ensure mmu mapping is up to date */ (void) map_read(map, adr); /* ensure mmu mapping is up to date */
preempt_disable();
local_irq_disable(); local_irq_disable();
} }
...@@ -831,9 +845,8 @@ static void __xipram xip_enable(struct map_info *map, struct flchip *chip, ...@@ -831,9 +845,8 @@ static void __xipram xip_enable(struct map_info *map, struct flchip *chip,
chip->state = FL_READY; chip->state = FL_READY;
} }
(void) map_read(map, adr); (void) map_read(map, adr);
asm volatile (".rep 8; nop; .endr"); /* fill instruction prefetch */ xip_iprefetch();
local_irq_enable(); local_irq_enable();
preempt_enable();
} }
/* /*
...@@ -909,7 +922,7 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, ...@@ -909,7 +922,7 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
(void) map_read(map, adr); (void) map_read(map, adr);
asm volatile (".rep 8; nop; .endr"); asm volatile (".rep 8; nop; .endr");
local_irq_enable(); local_irq_enable();
preempt_enable(); spin_unlock(chip->mutex);
asm volatile (".rep 8; nop; .endr"); asm volatile (".rep 8; nop; .endr");
cond_resched(); cond_resched();
...@@ -919,15 +932,15 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, ...@@ -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 * a suspended erase state. If so let's wait
* until it's done. * until it's done.
*/ */
preempt_disable(); spin_lock(chip->mutex);
while (chip->state != newstate) { while (chip->state != newstate) {
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait); add_wait_queue(&chip->wq, &wait);
preempt_enable(); spin_unlock(chip->mutex);
schedule(); schedule();
remove_wait_queue(&chip->wq, &wait); remove_wait_queue(&chip->wq, &wait);
preempt_disable(); spin_lock(chip->mutex);
} }
/* Disallow XIP again */ /* Disallow XIP again */
local_irq_disable(); local_irq_disable();
...@@ -956,12 +969,14 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, ...@@ -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 INVALIDATE_CACHED_RANGE() macro is normally used in parallel while
* the flash is actively programming or erasing since we have to poll for * 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 * 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 XIP_INVAL_CACHED_RANGE(map, from, size) \
#define INVALIDATE_CACHED_RANGE(x...) INVALIDATE_CACHED_RANGE(map, from, size)
#define XIP_INVAL_CACHED_RANGE(map, from, size) \
do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0) #define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \
UDELAY(map, chip, adr, usec)
/* /*
* Extra notes: * Extra notes:
...@@ -984,11 +999,23 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, ...@@ -984,11 +999,23 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
#define xip_disable(map, chip, adr) #define xip_disable(map, chip, adr)
#define xip_enable(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 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 #endif
static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len) 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 ...@@ -1176,111 +1203,11 @@ static int cfi_intelext_read (struct mtd_info *mtd, loff_t from, size_t len, siz
return ret; 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, 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; struct cfi_private *cfi = map->fldrv_priv;
map_word status, status_OK; map_word status, status_OK, write_cmd;
unsigned long timeo; unsigned long timeo;
int z, ret=0; int z, ret=0;
...@@ -1288,9 +1215,14 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, ...@@ -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 */ /* Let's determine this according to the interleave only once */
status_OK = CMD(0x80); 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); spin_lock(chip->mutex);
ret = get_chip(map, chip, adr, FL_WRITING); ret = get_chip(map, chip, adr, mode);
if (ret) { if (ret) {
spin_unlock(chip->mutex); spin_unlock(chip->mutex);
return ret; return ret;
...@@ -1299,19 +1231,18 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, ...@@ -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)); XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map));
ENABLE_VPP(map); ENABLE_VPP(map);
xip_disable(map, chip, adr); xip_disable(map, chip, adr);
map_write(map, CMD(0x40), adr); map_write(map, write_cmd, adr);
map_write(map, datum, adr); map_write(map, datum, adr);
chip->state = FL_WRITING; chip->state = mode;
spin_unlock(chip->mutex); INVALIDATE_CACHE_UDELAY(map, chip,
INVALIDATE_CACHED_RANGE(map, adr, map_bankwidth(map)); adr, map_bankwidth(map),
UDELAY(map, chip, adr, chip->word_write_time); chip->word_write_time);
spin_lock(chip->mutex);
timeo = jiffies + (HZ/2); timeo = jiffies + (HZ/2);
z = 0; z = 0;
for (;;) { for (;;) {
if (chip->state != FL_WRITING) { if (chip->state != mode) {
/* Someone's suspended the write. Sleep */ /* Someone's suspended the write. Sleep */
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
...@@ -1339,10 +1270,8 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, ...@@ -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 */ /* Latency issues. Drop the lock, wait a while and retry */
spin_unlock(chip->mutex);
z++; z++;
UDELAY(map, chip, adr, 1); UDELAY(map, chip, adr, 1);
spin_lock(chip->mutex);
} }
if (!z) { if (!z) {
chip->word_write_time--; chip->word_write_time--;
...@@ -1399,7 +1328,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le ...@@ -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); datum = map_word_load_partial(map, datum, buf, gap, n);
ret = do_write_oneword(map, &cfi->chips[chipnum], ret = do_write_oneword(map, &cfi->chips[chipnum],
bus_ofs, datum); bus_ofs, datum, FL_WRITING);
if (ret) if (ret)
return ret; return ret;
...@@ -1420,7 +1349,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le ...@@ -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); map_word datum = map_word_load(map, buf);
ret = do_write_oneword(map, &cfi->chips[chipnum], ret = do_write_oneword(map, &cfi->chips[chipnum],
ofs, datum); ofs, datum, FL_WRITING);
if (ret) if (ret)
return ret; return ret;
...@@ -1444,7 +1373,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le ...@@ -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); datum = map_word_load_partial(map, datum, buf, 0, len);
ret = do_write_oneword(map, &cfi->chips[chipnum], ret = do_write_oneword(map, &cfi->chips[chipnum],
ofs, datum); ofs, datum, FL_WRITING);
if (ret) if (ret)
return ret; return ret;
...@@ -1506,9 +1435,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, ...@@ -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)) if (map_word_andequal(map, status, status_OK, status_OK))
break; break;
spin_unlock(chip->mutex);
UDELAY(map, chip, cmd_adr, 1); UDELAY(map, chip, cmd_adr, 1);
spin_lock(chip->mutex);
if (++z > 20) { if (++z > 20) {
/* Argh. Not ready for write to buffer */ /* Argh. Not ready for write to buffer */
...@@ -1554,10 +1481,9 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, ...@@ -1554,10 +1481,9 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
map_write(map, CMD(0xd0), cmd_adr); map_write(map, CMD(0xd0), cmd_adr);
chip->state = FL_WRITING; chip->state = FL_WRITING;
spin_unlock(chip->mutex); INVALIDATE_CACHE_UDELAY(map, chip,
INVALIDATE_CACHED_RANGE(map, adr, len); cmd_adr, len,
UDELAY(map, chip, cmd_adr, chip->buffer_write_time); chip->buffer_write_time);
spin_lock(chip->mutex);
timeo = jiffies + (HZ/2); timeo = jiffies + (HZ/2);
z = 0; z = 0;
...@@ -1589,10 +1515,8 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, ...@@ -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 */ /* Latency issues. Drop the lock, wait a while and retry */
spin_unlock(chip->mutex);
UDELAY(map, chip, cmd_adr, 1);
z++; z++;
spin_lock(chip->mutex); UDELAY(map, chip, cmd_adr, 1);
} }
if (!z) { if (!z) {
chip->buffer_write_time--; chip->buffer_write_time--;
...@@ -1720,10 +1644,9 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, ...@@ -1720,10 +1644,9 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
chip->state = FL_ERASING; chip->state = FL_ERASING;
chip->erase_suspended = 0; chip->erase_suspended = 0;
spin_unlock(chip->mutex); INVALIDATE_CACHE_UDELAY(map, chip,
INVALIDATE_CACHED_RANGE(map, adr, len); adr, len,
UDELAY(map, chip, adr, chip->erase_time*1000/2); chip->erase_time*1000/2);
spin_lock(chip->mutex);
/* FIXME. Use a timer to check this, and return immediately. */ /* FIXME. Use a timer to check this, and return immediately. */
/* Once the state machine's known to be working I'll do that */ /* 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, ...@@ -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 */ /* Latency issues. Drop the lock, wait a while and retry */
spin_unlock(chip->mutex);
UDELAY(map, chip, adr, 1000000/HZ); UDELAY(map, chip, adr, 1000000/HZ);
spin_lock(chip->mutex);
} }
/* We've broken this before. It doesn't hurt to be safe */ /* 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, ...@@ -1780,44 +1701,34 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
/* check for lock bit */ /* check for lock bit */
if (map_word_bitsset(map, status, CMD(0x3a))) { if (map_word_bitsset(map, status, CMD(0x3a))) {
unsigned char chipstatus; unsigned long chipstatus;
/* Reset the error bits */ /* Reset the error bits */
map_write(map, CMD(0x50), adr); map_write(map, CMD(0x50), adr);
map_write(map, CMD(0x70), adr); map_write(map, CMD(0x70), adr);
xip_enable(map, chip, adr); xip_enable(map, chip, adr);
chipstatus = status.x[0]; chipstatus = MERGESTATUS(status);
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);
}
if ((chipstatus & 0x30) == 0x30) { 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; ret = -EIO;
} else if (chipstatus & 0x02) { } else if (chipstatus & 0x02) {
/* Protection bit set */ /* Protection bit set */
ret = -EROFS; ret = -EROFS;
} else if (chipstatus & 0x8) { } else if (chipstatus & 0x8) {
/* Voltage */ /* 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; ret = -EIO;
} else if (chipstatus & 0x20) { } else if (chipstatus & 0x20) {
if (retries--) { 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; timeo = jiffies + HZ;
put_chip(map, chip, adr); put_chip(map, chip, adr);
spin_unlock(chip->mutex); spin_unlock(chip->mutex);
goto retry; 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; ret = -EIO;
} }
} else { } else {
...@@ -1882,6 +1793,7 @@ static void cfi_intelext_sync (struct mtd_info *mtd) ...@@ -1882,6 +1793,7 @@ static void cfi_intelext_sync (struct mtd_info *mtd)
if (chip->state == FL_SYNCING) { if (chip->state == FL_SYNCING) {
chip->state = chip->oldstate; chip->state = chip->oldstate;
chip->oldstate = FL_READY;
wake_up(&chip->wq); wake_up(&chip->wq);
} }
spin_unlock(chip->mutex); spin_unlock(chip->mutex);
...@@ -1897,8 +1809,9 @@ static int __xipram do_printlockstatus_oneblock(struct map_info *map, ...@@ -1897,8 +1809,9 @@ static int __xipram do_printlockstatus_oneblock(struct map_info *map,
struct cfi_private *cfi = map->fldrv_priv; struct cfi_private *cfi = map->fldrv_priv;
int status, ofs_factor = cfi->interleave * cfi->device_type; int status, ofs_factor = cfi->interleave * cfi->device_type;
adr += chip->start;
xip_disable(map, chip, adr+(2*ofs_factor)); 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; chip->state = FL_JEDEC_QUERY;
status = cfi_read_query(map, adr+(2*ofs_factor)); status = cfi_read_query(map, adr+(2*ofs_factor));
xip_enable(map, chip, 0); xip_enable(map, chip, 0);
...@@ -1915,6 +1828,7 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip ...@@ -1915,6 +1828,7 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip
unsigned long adr, int len, void *thunk) unsigned long adr, int len, void *thunk)
{ {
struct cfi_private *cfi = map->fldrv_priv; struct cfi_private *cfi = map->fldrv_priv;
struct cfi_pri_intelext *extp = cfi->cmdset_priv;
map_word status, status_OK; map_word status, status_OK;
unsigned long timeo = jiffies + HZ; unsigned long timeo = jiffies + HZ;
int ret; int ret;
...@@ -1944,9 +1858,13 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip ...@@ -1944,9 +1858,13 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip
} else } else
BUG(); BUG();
spin_unlock(chip->mutex); /*
UDELAY(map, chip, adr, 1000000/HZ); * If Instant Individual Block Locking supported then no need
spin_lock(chip->mutex); * to delay.
*/
if (!extp || !(extp->FeatureSupport & (1 << 5)))
UDELAY(map, chip, adr, 1000000/HZ);
/* FIXME. Use a timer to check this, and return immediately. */ /* FIXME. Use a timer to check this, and return immediately. */
/* Once the state machine's known to be working I'll do that */ /* 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 ...@@ -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 */ /* Latency issues. Drop the lock, wait a while and retry */
spin_unlock(chip->mutex);
UDELAY(map, chip, adr, 1); UDELAY(map, chip, adr, 1);
spin_lock(chip->mutex);
} }
/* Done and happy. */ /* Done and happy. */
...@@ -2034,6 +1950,274 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) ...@@ -2034,6 +1950,274 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
return ret; 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) static int cfi_intelext_suspend(struct mtd_info *mtd)
{ {
struct map_info *map = mtd->priv; struct map_info *map = mtd->priv;
...@@ -2125,10 +2309,46 @@ static void cfi_intelext_resume(struct mtd_info *mtd) ...@@ -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) static void cfi_intelext_destroy(struct mtd_info *mtd)
{ {
struct map_info *map = mtd->priv; struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv; struct cfi_private *cfi = map->fldrv_priv;
cfi_intelext_reset(mtd);
unregister_reboot_notifier(&mtd->reboot_notifier);
kfree(cfi->cmdset_priv); kfree(cfi->cmdset_priv);
kfree(cfi->cfiq); kfree(cfi->cfiq);
kfree(cfi->chips[0].priv); kfree(cfi->chips[0].priv);
......
...@@ -4,16 +4,20 @@ ...@@ -4,16 +4,20 @@
* *
* Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp> * Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp>
* Copyright (C) 2004 Arcom Control Systems Ltd <linux@arcom.com> * 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 * 2_by_8 routines added by Simon Munton
* *
* 4_by_16 work by Carolyn J. Smith * 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 * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com
* *
* This code is GPL * 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 @@ ...@@ -34,6 +38,7 @@
#include <linux/mtd/map.h> #include <linux/mtd/map.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/cfi.h> #include <linux/mtd/cfi.h>
#include <linux/mtd/xip.h>
#define AMD_BOOTLOC_BUG #define AMD_BOOTLOC_BUG
#define FORCE_WORD_WRITE 0 #define FORCE_WORD_WRITE 0
...@@ -43,6 +48,7 @@ ...@@ -43,6 +48,7 @@
#define MANUFACTURER_AMD 0x0001 #define MANUFACTURER_AMD 0x0001
#define MANUFACTURER_SST 0x00BF #define MANUFACTURER_SST 0x00BF
#define SST49LF004B 0x0060 #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_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 *); 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[] = { ...@@ -191,6 +197,7 @@ static struct cfi_fixup cfi_fixup_table[] = {
}; };
static struct cfi_fixup jedec_fixup_table[] = { static struct cfi_fixup jedec_fixup_table[] = {
{ MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, }, { MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, },
{ MANUFACTURER_SST, SST49LF008A, fixup_use_fwh_lock, NULL, },
{ 0, 0, NULL, NULL } { 0, 0, NULL, NULL }
}; };
...@@ -391,7 +398,7 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) ...@@ -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 * correctly and is therefore not done (particulary with interleaved chips
* as each chip must be checked independantly of the others). * 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; map_word d, t;
...@@ -401,6 +408,32 @@ static int chip_ready(struct map_info *map, unsigned long addr) ...@@ -401,6 +408,32 @@ static int chip_ready(struct map_info *map, unsigned long addr)
return map_word_equal(map, d, t); 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) static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
{ {
DECLARE_WAITQUEUE(wait, current); DECLARE_WAITQUEUE(wait, current);
...@@ -420,12 +453,12 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr ...@@ -420,12 +453,12 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
if (time_after(jiffies, timeo)) { if (time_after(jiffies, timeo)) {
printk(KERN_ERR "Waiting for chip to be ready timed out.\n"); printk(KERN_ERR "Waiting for chip to be ready timed out.\n");
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
return -EIO; return -EIO;
} }
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
cfi_udelay(1); cfi_udelay(1);
cfi_spin_lock(chip->mutex); spin_lock(chip->mutex);
/* Someone else might have been playing with it. */ /* Someone else might have been playing with it. */
goto retry; goto retry;
} }
...@@ -473,15 +506,23 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr ...@@ -473,15 +506,23 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
return -EIO; return -EIO;
} }
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
cfi_udelay(1); cfi_udelay(1);
cfi_spin_lock(chip->mutex); spin_lock(chip->mutex);
/* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING.
So we can just loop here. */ So we can just loop here. */
} }
chip->state = FL_READY; chip->state = FL_READY;
return 0; 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: case FL_POINT:
/* Only if there's no operation suspended... */ /* Only if there's no operation suspended... */
if (mode == FL_READY && chip->oldstate == FL_READY) 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 ...@@ -491,10 +532,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
sleep: sleep:
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait); add_wait_queue(&chip->wq, &wait);
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
schedule(); schedule();
remove_wait_queue(&chip->wq, &wait); remove_wait_queue(&chip->wq, &wait);
cfi_spin_lock(chip->mutex); spin_lock(chip->mutex);
goto resettime; goto resettime;
} }
} }
...@@ -512,6 +553,11 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad ...@@ -512,6 +553,11 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
chip->state = FL_ERASING; chip->state = FL_ERASING;
break; break;
case FL_XIP_WHILE_ERASING:
chip->state = chip->oldstate;
chip->oldstate = FL_READY;
break;
case FL_READY: case FL_READY:
case FL_STATUS: case FL_STATUS:
/* We should really make set_vpp() count, rather than doing this */ /* 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 ...@@ -523,6 +569,198 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
wake_up(&chip->wq); 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) 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 ...@@ -535,10 +773,10 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof
/* Ensure cmd read/writes are aligned. */ /* Ensure cmd read/writes are aligned. */
cmd_addr = adr & ~(map_bankwidth(map)-1); 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); ret = get_chip(map, chip, cmd_addr, FL_READY);
if (ret) { if (ret) {
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
return ret; return ret;
} }
...@@ -551,7 +789,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof ...@@ -551,7 +789,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof
put_chip(map, chip, cmd_addr); put_chip(map, chip, cmd_addr);
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
return 0; return 0;
} }
...@@ -605,7 +843,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi ...@@ -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; struct cfi_private *cfi = map->fldrv_priv;
retry: retry:
cfi_spin_lock(chip->mutex); spin_lock(chip->mutex);
if (chip->state != FL_READY){ if (chip->state != FL_READY){
#if 0 #if 0
...@@ -614,7 +852,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi ...@@ -614,7 +852,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait); add_wait_queue(&chip->wq, &wait);
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
schedule(); schedule();
remove_wait_queue(&chip->wq, &wait); remove_wait_queue(&chip->wq, &wait);
...@@ -643,7 +881,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi ...@@ -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); cfi_send_gen_cmd(0x00, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
wake_up(&chip->wq); wake_up(&chip->wq);
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
return 0; return 0;
} }
...@@ -692,7 +930,7 @@ static int cfi_amdstd_secsi_read (struct mtd_info *mtd, loff_t from, size_t len, ...@@ -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; struct cfi_private *cfi = map->fldrv_priv;
unsigned long timeo = jiffies + HZ; unsigned long timeo = jiffies + HZ;
...@@ -712,10 +950,10 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned ...@@ -712,10 +950,10 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
adr += chip->start; adr += chip->start;
cfi_spin_lock(chip->mutex); spin_lock(chip->mutex);
ret = get_chip(map, chip, adr, FL_WRITING); ret = get_chip(map, chip, adr, FL_WRITING);
if (ret) { if (ret) {
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
return ret; return ret;
} }
...@@ -735,7 +973,9 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned ...@@ -735,7 +973,9 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
goto op_done; goto op_done;
} }
XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map));
ENABLE_VPP(map); ENABLE_VPP(map);
xip_disable(map, chip, adr);
retry: retry:
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); 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(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 ...@@ -743,9 +983,9 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
map_write(map, datum, adr); map_write(map, datum, adr);
chip->state = FL_WRITING; chip->state = FL_WRITING;
cfi_spin_unlock(chip->mutex); INVALIDATE_CACHE_UDELAY(map, chip,
cfi_udelay(chip->word_write_time); adr, map_bankwidth(map),
cfi_spin_lock(chip->mutex); chip->word_write_time);
/* See comment above for timeout value. */ /* See comment above for timeout value. */
timeo = jiffies + uWriteTimeout; timeo = jiffies + uWriteTimeout;
...@@ -756,39 +996,43 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned ...@@ -756,39 +996,43 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait); add_wait_queue(&chip->wq, &wait);
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
schedule(); schedule();
remove_wait_queue(&chip->wq, &wait); remove_wait_queue(&chip->wq, &wait);
timeo = jiffies + (HZ / 2); /* FIXME */ timeo = jiffies + (HZ / 2); /* FIXME */
cfi_spin_lock(chip->mutex); spin_lock(chip->mutex);
continue; continue;
} }
if (chip_ready(map, adr)) 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; break;
}
/* Latency issues. Drop the lock, wait a while and retry */ /* Latency issues. Drop the lock, wait a while and retry */
cfi_spin_unlock(chip->mutex); UDELAY(map, chip, adr, 1);
cfi_udelay(1);
cfi_spin_lock(chip->mutex);
} }
/* 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__); if (++retry_cnt <= MAX_WORD_RETRIES)
goto retry;
/* 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;
ret = -EIO; ret = -EIO;
}
xip_enable(map, chip, adr);
op_done: op_done:
chip->state = FL_READY; chip->state = FL_READY;
put_chip(map, chip, adr); put_chip(map, chip, adr);
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
return ret; return ret;
} }
...@@ -820,7 +1064,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -820,7 +1064,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
map_word tmp_buf; map_word tmp_buf;
retry: retry:
cfi_spin_lock(cfi->chips[chipnum].mutex); spin_lock(cfi->chips[chipnum].mutex);
if (cfi->chips[chipnum].state != FL_READY) { if (cfi->chips[chipnum].state != FL_READY) {
#if 0 #if 0
...@@ -829,7 +1073,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -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); set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&cfi->chips[chipnum].wq, &wait); add_wait_queue(&cfi->chips[chipnum].wq, &wait);
cfi_spin_unlock(cfi->chips[chipnum].mutex); spin_unlock(cfi->chips[chipnum].mutex);
schedule(); schedule();
remove_wait_queue(&cfi->chips[chipnum].wq, &wait); 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, ...@@ -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 */ /* Load 'tmp_buf' with old contents of flash */
tmp_buf = map_read(map, bus_ofs+chipstart); 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 */ /* Number of bytes to copy from buffer */
n = min_t(int, len, map_bankwidth(map)-i); 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, ...@@ -898,7 +1142,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
map_word tmp_buf; map_word tmp_buf;
retry1: retry1:
cfi_spin_lock(cfi->chips[chipnum].mutex); spin_lock(cfi->chips[chipnum].mutex);
if (cfi->chips[chipnum].state != FL_READY) { if (cfi->chips[chipnum].state != FL_READY) {
#if 0 #if 0
...@@ -907,7 +1151,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -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); set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&cfi->chips[chipnum].wq, &wait); add_wait_queue(&cfi->chips[chipnum].wq, &wait);
cfi_spin_unlock(cfi->chips[chipnum].mutex); spin_unlock(cfi->chips[chipnum].mutex);
schedule(); schedule();
remove_wait_queue(&cfi->chips[chipnum].wq, &wait); 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, ...@@ -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); 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); 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, ...@@ -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! * FIXME: interleaved mode not tested, and probably not supported!
*/ */
static inline int do_write_buffer(struct map_info *map, struct flchip *chip, static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
unsigned long adr, const u_char *buf, int len) unsigned long adr, const u_char *buf,
int len)
{ {
struct cfi_private *cfi = map->fldrv_priv; struct cfi_private *cfi = map->fldrv_priv;
unsigned long timeo = jiffies + HZ; unsigned long timeo = jiffies + HZ;
...@@ -954,10 +1199,10 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, ...@@ -954,10 +1199,10 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
adr += chip->start; adr += chip->start;
cmd_adr = adr; cmd_adr = adr;
cfi_spin_lock(chip->mutex); spin_lock(chip->mutex);
ret = get_chip(map, chip, adr, FL_WRITING); ret = get_chip(map, chip, adr, FL_WRITING);
if (ret) { if (ret) {
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
return ret; return ret;
} }
...@@ -966,7 +1211,10 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, ...@@ -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", DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
__func__, adr, datum.x[0] ); __func__, adr, datum.x[0] );
XIP_INVAL_CACHED_RANGE(map, adr, len);
ENABLE_VPP(map); 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(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(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); //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, ...@@ -996,9 +1244,9 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
map_write(map, CMD(0x29), cmd_adr); map_write(map, CMD(0x29), cmd_adr);
chip->state = FL_WRITING; chip->state = FL_WRITING;
cfi_spin_unlock(chip->mutex); INVALIDATE_CACHE_UDELAY(map, chip,
cfi_udelay(chip->buffer_write_time); adr, map_bankwidth(map),
cfi_spin_lock(chip->mutex); chip->word_write_time);
timeo = jiffies + uWriteTimeout; timeo = jiffies + uWriteTimeout;
...@@ -1009,38 +1257,39 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, ...@@ -1009,38 +1257,39 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait); add_wait_queue(&chip->wq, &wait);
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
schedule(); schedule();
remove_wait_queue(&chip->wq, &wait); remove_wait_queue(&chip->wq, &wait);
timeo = jiffies + (HZ / 2); /* FIXME */ timeo = jiffies + (HZ / 2); /* FIXME */
cfi_spin_lock(chip->mutex); spin_lock(chip->mutex);
continue; continue;
} }
if (chip_ready(map, adr)) if (chip_ready(map, adr)) {
xip_enable(map, chip, adr);
goto op_done; goto op_done;
}
if( time_after(jiffies, timeo)) if( time_after(jiffies, timeo))
break; break;
/* Latency issues. Drop the lock, wait a while and retry */ /* Latency issues. Drop the lock, wait a while and retry */
cfi_spin_unlock(chip->mutex); UDELAY(map, chip, adr, 1);
cfi_udelay(1);
cfi_spin_lock(chip->mutex);
} }
printk(KERN_WARNING "MTD %s(): software timeout\n",
__func__ );
/* reset on all failures. */ /* reset on all failures. */
map_write( map, CMD(0xF0), chip->start ); map_write( map, CMD(0xF0), chip->start );
xip_enable(map, chip, adr);
/* FIXME - should have reset delay before continuing */ /* FIXME - should have reset delay before continuing */
printk(KERN_WARNING "MTD %s(): software timeout\n",
__func__ );
ret = -EIO; ret = -EIO;
op_done: op_done:
chip->state = FL_READY; chip->state = FL_READY;
put_chip(map, chip, adr); put_chip(map, chip, adr);
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
return ret; return ret;
} }
...@@ -1130,7 +1379,7 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -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 * Handle devices with one erase region, that only implement
* the chip erase command. * 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; struct cfi_private *cfi = map->fldrv_priv;
unsigned long timeo = jiffies + HZ; unsigned long timeo = jiffies + HZ;
...@@ -1140,17 +1389,20 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip) ...@@ -1140,17 +1389,20 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
adr = cfi->addr_unlock1; adr = cfi->addr_unlock1;
cfi_spin_lock(chip->mutex); spin_lock(chip->mutex);
ret = get_chip(map, chip, adr, FL_WRITING); ret = get_chip(map, chip, adr, FL_WRITING);
if (ret) { if (ret) {
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
return ret; return ret;
} }
DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n", DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
__func__, chip->start ); __func__, chip->start );
XIP_INVAL_CACHED_RANGE(map, adr, map->size);
ENABLE_VPP(map); 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(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(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); 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) ...@@ -1162,9 +1414,9 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
chip->erase_suspended = 0; chip->erase_suspended = 0;
chip->in_progress_block_addr = adr; chip->in_progress_block_addr = adr;
cfi_spin_unlock(chip->mutex); INVALIDATE_CACHE_UDELAY(map, chip,
msleep(chip->erase_time/2); adr, map->size,
cfi_spin_lock(chip->mutex); chip->erase_time*500);
timeo = jiffies + (HZ*20); timeo = jiffies + (HZ*20);
...@@ -1173,10 +1425,10 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip) ...@@ -1173,10 +1425,10 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
/* Someone's suspended the erase. Sleep */ /* Someone's suspended the erase. Sleep */
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait); add_wait_queue(&chip->wq, &wait);
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
schedule(); schedule();
remove_wait_queue(&chip->wq, &wait); remove_wait_queue(&chip->wq, &wait);
cfi_spin_lock(chip->mutex); spin_lock(chip->mutex);
continue; continue;
} }
if (chip->erase_suspended) { if (chip->erase_suspended) {
...@@ -1187,36 +1439,36 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip) ...@@ -1187,36 +1439,36 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
} }
if (chip_ready(map, adr)) 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; break;
}
/* Latency issues. Drop the lock, wait a while and retry */ /* Latency issues. Drop the lock, wait a while and retry */
cfi_spin_unlock(chip->mutex); UDELAY(map, chip, adr, 1000000/HZ);
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(1);
cfi_spin_lock(chip->mutex);
} }
/* 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", ret = -EIO;
__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; chip->state = FL_READY;
xip_enable(map, chip, adr);
put_chip(map, chip, adr); put_chip(map, chip, adr);
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
return ret; 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; struct cfi_private *cfi = map->fldrv_priv;
unsigned long timeo = jiffies + HZ; unsigned long timeo = jiffies + HZ;
...@@ -1225,17 +1477,20 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u ...@@ -1225,17 +1477,20 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u
adr += chip->start; adr += chip->start;
cfi_spin_lock(chip->mutex); spin_lock(chip->mutex);
ret = get_chip(map, chip, adr, FL_ERASING); ret = get_chip(map, chip, adr, FL_ERASING);
if (ret) { if (ret) {
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
return ret; return ret;
} }
DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n", DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
__func__, adr ); __func__, adr );
XIP_INVAL_CACHED_RANGE(map, adr, len);
ENABLE_VPP(map); 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(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(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); 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 ...@@ -1246,10 +1501,10 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u
chip->state = FL_ERASING; chip->state = FL_ERASING;
chip->erase_suspended = 0; chip->erase_suspended = 0;
chip->in_progress_block_addr = adr; chip->in_progress_block_addr = adr;
cfi_spin_unlock(chip->mutex); INVALIDATE_CACHE_UDELAY(map, chip,
msleep(chip->erase_time/2); adr, len,
cfi_spin_lock(chip->mutex); chip->erase_time*500);
timeo = jiffies + (HZ*20); timeo = jiffies + (HZ*20);
...@@ -1258,10 +1513,10 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u ...@@ -1258,10 +1513,10 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u
/* Someone's suspended the erase. Sleep */ /* Someone's suspended the erase. Sleep */
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait); add_wait_queue(&chip->wq, &wait);
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
schedule(); schedule();
remove_wait_queue(&chip->wq, &wait); remove_wait_queue(&chip->wq, &wait);
cfi_spin_lock(chip->mutex); spin_lock(chip->mutex);
continue; continue;
} }
if (chip->erase_suspended) { if (chip->erase_suspended) {
...@@ -1271,31 +1526,33 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u ...@@ -1271,31 +1526,33 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u
chip->erase_suspended = 0; chip->erase_suspended = 0;
} }
if (chip_ready(map, adr)) if (chip_ready(map, adr)) {
goto op_done; 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; break;
}
/* Latency issues. Drop the lock, wait a while and retry */ /* Latency issues. Drop the lock, wait a while and retry */
cfi_spin_unlock(chip->mutex); UDELAY(map, chip, adr, 1000000/HZ);
set_current_state(TASK_UNINTERRUPTIBLE); }
schedule_timeout(1); /* Did we succeed? */
cfi_spin_lock(chip->mutex); 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; chip->state = FL_READY;
put_chip(map, chip, adr); put_chip(map, chip, adr);
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
return ret; return ret;
} }
...@@ -1355,7 +1612,7 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) ...@@ -1355,7 +1612,7 @@ static void cfi_amdstd_sync (struct mtd_info *mtd)
chip = &cfi->chips[i]; chip = &cfi->chips[i];
retry: retry:
cfi_spin_lock(chip->mutex); spin_lock(chip->mutex);
switch(chip->state) { switch(chip->state) {
case FL_READY: case FL_READY:
...@@ -1369,14 +1626,14 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) ...@@ -1369,14 +1626,14 @@ static void cfi_amdstd_sync (struct mtd_info *mtd)
* with the chip now anyway. * with the chip now anyway.
*/ */
case FL_SYNCING: case FL_SYNCING:
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
break; break;
default: default:
/* Not an idle state */ /* Not an idle state */
add_wait_queue(&chip->wq, &wait); add_wait_queue(&chip->wq, &wait);
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
schedule(); schedule();
...@@ -1391,13 +1648,13 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) ...@@ -1391,13 +1648,13 @@ static void cfi_amdstd_sync (struct mtd_info *mtd)
for (i--; i >=0; i--) { for (i--; i >=0; i--) {
chip = &cfi->chips[i]; chip = &cfi->chips[i];
cfi_spin_lock(chip->mutex); spin_lock(chip->mutex);
if (chip->state == FL_SYNCING) { if (chip->state == FL_SYNCING) {
chip->state = chip->oldstate; chip->state = chip->oldstate;
wake_up(&chip->wq); 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) ...@@ -1413,7 +1670,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd)
for (i=0; !ret && i<cfi->numchips; i++) { for (i=0; !ret && i<cfi->numchips; i++) {
chip = &cfi->chips[i]; chip = &cfi->chips[i];
cfi_spin_lock(chip->mutex); spin_lock(chip->mutex);
switch(chip->state) { switch(chip->state) {
case FL_READY: case FL_READY:
...@@ -1433,7 +1690,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd) ...@@ -1433,7 +1690,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd)
ret = -EAGAIN; ret = -EAGAIN;
break; break;
} }
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
} }
/* Unlock the chips again */ /* Unlock the chips again */
...@@ -1442,13 +1699,13 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd) ...@@ -1442,13 +1699,13 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd)
for (i--; i >=0; i--) { for (i--; i >=0; i--) {
chip = &cfi->chips[i]; chip = &cfi->chips[i];
cfi_spin_lock(chip->mutex); spin_lock(chip->mutex);
if (chip->state == FL_PM_SUSPENDED) { if (chip->state == FL_PM_SUSPENDED) {
chip->state = chip->oldstate; chip->state = chip->oldstate;
wake_up(&chip->wq); 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) ...@@ -1467,7 +1724,7 @@ static void cfi_amdstd_resume(struct mtd_info *mtd)
chip = &cfi->chips[i]; chip = &cfi->chips[i];
cfi_spin_lock(chip->mutex); spin_lock(chip->mutex);
if (chip->state == FL_PM_SUSPENDED) { if (chip->state == FL_PM_SUSPENDED) {
chip->state = FL_READY; chip->state = FL_READY;
...@@ -1477,7 +1734,7 @@ static void cfi_amdstd_resume(struct mtd_info *mtd) ...@@ -1477,7 +1734,7 @@ static void cfi_amdstd_resume(struct mtd_info *mtd)
else else
printk(KERN_ERR "Argh. Chip not in PM_SUSPENDED state upon resume()\n"); printk(KERN_ERR "Argh. Chip not in PM_SUSPENDED state upon resume()\n");
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
} }
} }
......
...@@ -58,10 +58,10 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, ...@@ -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 * to flash memory - that means that we don't have to check status
* and timeout. * and timeout.
*/ */
cfi_spin_lock(chip->mutex); spin_lock(chip->mutex);
ret = get_chip(map, chip, adr, FL_LOCKING); ret = get_chip(map, chip, adr, FL_LOCKING);
if (ret) { if (ret) {
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
return ret; return ret;
} }
...@@ -71,7 +71,7 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, ...@@ -71,7 +71,7 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip,
/* Done and happy. */ /* Done and happy. */
chip->state = FL_READY; chip->state = FL_READY;
put_chip(map, chip, adr); put_chip(map, chip, adr);
cfi_spin_unlock(chip->mutex); spin_unlock(chip->mutex);
return 0; return 0;
} }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Routines common to all CFI-type probes. * Routines common to all CFI-type probes.
* (C) 2001-2003 Red Hat, Inc. * (C) 2001-2003 Red Hat, Inc.
* GPL'd * 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> #include <linux/kernel.h>
...@@ -162,7 +162,7 @@ static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp, ...@@ -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 max_chips = map_bankwidth(map); /* And minimum 1 */
int nr_chips, type; 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)) if (!cfi_interleave_supported(nr_chips))
continue; continue;
......
/* /*
Common Flash Interface probe code. Common Flash Interface probe code.
(C) 2000 Red Hat. GPL'd. (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) See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5)
for the standard this probe goes back to. for the standard this probe goes back to.
...@@ -142,6 +142,7 @@ ...@@ -142,6 +142,7 @@
#define SST29LE512 0x003d #define SST29LE512 0x003d
#define SST39LF800 0x2781 #define SST39LF800 0x2781
#define SST39LF160 0x2782 #define SST39LF160 0x2782
#define SST39VF1601 0x234b
#define SST39LF512 0x00D4 #define SST39LF512 0x00D4
#define SST39LF010 0x00D5 #define SST39LF010 0x00D5
#define SST39LF020 0x00D6 #define SST39LF020 0x00D6
...@@ -1448,6 +1449,21 @@ static const struct amd_flash_info jedec_table[] = { ...@@ -1448,6 +1449,21 @@ static const struct amd_flash_info jedec_table[] = {
ERASEINFO(0x1000,256), ERASEINFO(0x1000,256),
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? */ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */
...@@ -1856,6 +1872,16 @@ static inline int jedec_match( __u32 base, ...@@ -1856,6 +1872,16 @@ static inline int jedec_match( __u32 base,
case CFI_DEVICETYPE_X8: case CFI_DEVICETYPE_X8:
mfr = (__u8)finfo->mfr_id; mfr = (__u8)finfo->mfr_id;
id = (__u8)finfo->dev_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; break;
case CFI_DEVICETYPE_X16: case CFI_DEVICETYPE_X16:
mfr = (__u16)finfo->mfr_id; mfr = (__u16)finfo->mfr_id;
......
/* /*
* $Id: cmdlinepart.c,v 1.17 2004/11/26 11:18:47 lavinen Exp $ * $Id: cmdlinepart.c,v 1.18 2005/06/07 15:04:26 joern Exp $
* *
* Read flash partition table from command line * Read flash partition table from command line
* *
...@@ -239,7 +239,8 @@ static int mtdpart_setup_real(char *s) ...@@ -239,7 +239,8 @@ static int mtdpart_setup_real(char *s)
&num_parts, /* out: number of parts */ &num_parts, /* out: number of parts */
0, /* first partition */ 0, /* first partition */
(unsigned char**)&this_mtd, /* out: extra mem */ (unsigned char**)&this_mtd, /* out: extra mem */
mtd_id_len + 1 + sizeof(*this_mtd)); mtd_id_len + 1 + sizeof(*this_mtd) +
sizeof(void*)-1 /*alignment*/);
if(!parts) if(!parts)
{ {
/* /*
...@@ -252,6 +253,9 @@ static int mtdpart_setup_real(char *s) ...@@ -252,6 +253,9 @@ static int mtdpart_setup_real(char *s)
return 0; return 0;
} }
/* align this_mtd */
this_mtd = (struct cmdline_mtd_partition *)
ALIGN((unsigned long)this_mtd, sizeof(void*));
/* enter results */ /* enter results */
this_mtd->parts = parts; this_mtd->parts = parts;
this_mtd->num_parts = num_parts; this_mtd->num_parts = num_parts;
......
/* /*
* $Id: block2mtd.c,v 1.23 2005/01/05 17:05:46 dwmw2 Exp $ * $Id: block2mtd.c,v 1.28 2005/03/19 22:40:44 gleixner Exp $
* *
* block2mtd.c - create an mtd from a block device * block2mtd.c - create an mtd from a block device
* *
* Copyright (C) 2001,2002 Simon Evans <spse@secret.org.uk> * Copyright (C) 2001,2002 Simon Evans <spse@secret.org.uk>
* Copyright (C) 2004 Gareth Bult <Gareth@Encryptec.net>
* Copyright (C) 2004,2005 Jrn Engel <joern@wh.fh-wedel.de> * Copyright (C) 2004,2005 Jrn Engel <joern@wh.fh-wedel.de>
* *
* Licence: GPL * Licence: GPL
...@@ -20,7 +19,7 @@ ...@@ -20,7 +19,7 @@
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#define VERSION "$Revision: 1.23 $" #define VERSION "$Revision: 1.28 $"
#define ERROR(fmt, args...) printk(KERN_ERR "block2mtd: " fmt "\n" , ## args) #define ERROR(fmt, args...) printk(KERN_ERR "block2mtd: " fmt "\n" , ## args)
...@@ -89,7 +88,6 @@ void cache_readahead(struct address_space *mapping, int index) ...@@ -89,7 +88,6 @@ void cache_readahead(struct address_space *mapping, int index)
static struct page* page_readahead(struct address_space *mapping, int index) static struct page* page_readahead(struct address_space *mapping, int index)
{ {
filler_t *filler = (filler_t*)mapping->a_ops->readpage; filler_t *filler = (filler_t*)mapping->a_ops->readpage;
//do_page_cache_readahead(mapping, index, XXX, 64);
cache_readahead(mapping, index); cache_readahead(mapping, index);
return read_cache_page(mapping, index, filler, NULL); return read_cache_page(mapping, index, filler, NULL);
} }
...@@ -157,7 +155,7 @@ static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -157,7 +155,7 @@ static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len,
struct block2mtd_dev *dev = mtd->priv; struct block2mtd_dev *dev = mtd->priv;
struct page *page; struct page *page;
int index = from >> PAGE_SHIFT; int index = from >> PAGE_SHIFT;
int offset = from & (PAGE_SHIFT-1); int offset = from & (PAGE_SIZE-1);
int cpylen; int cpylen;
if (from > mtd->size) if (from > mtd->size)
...@@ -370,16 +368,16 @@ static int ustrtoul(const char *cp, char **endp, unsigned int base) ...@@ -370,16 +368,16 @@ static int ustrtoul(const char *cp, char **endp, unsigned int base)
} }
static int parse_num32(u32 *num32, const char *token) static int parse_num(size_t *num, const char *token)
{ {
char *endp; char *endp;
unsigned long n; size_t n;
n = ustrtoul(token, &endp, 0); n = (size_t) ustrtoul(token, &endp, 0);
if (*endp) if (*endp)
return -EINVAL; return -EINVAL;
*num32 = n; *num = n;
return 0; return 0;
} }
...@@ -422,7 +420,7 @@ static int block2mtd_setup(const char *val, struct kernel_param *kp) ...@@ -422,7 +420,7 @@ static int block2mtd_setup(const char *val, struct kernel_param *kp)
char buf[80+12], *str=buf; /* 80 for device, 12 for erase size */ char buf[80+12], *str=buf; /* 80 for device, 12 for erase size */
char *token[2]; char *token[2];
char *name; char *name;
u32 erase_size = PAGE_SIZE; size_t erase_size = PAGE_SIZE;
int i, ret; int i, ret;
if (strnlen(val, sizeof(buf)) >= sizeof(buf)) if (strnlen(val, sizeof(buf)) >= sizeof(buf))
...@@ -449,7 +447,7 @@ static int block2mtd_setup(const char *val, struct kernel_param *kp) ...@@ -449,7 +447,7 @@ static int block2mtd_setup(const char *val, struct kernel_param *kp)
return 0; return 0;
if (token[1]) { if (token[1]) {
ret = parse_num32(&erase_size, token[1]); ret = parse_num(&erase_size, token[1]);
if (ret) if (ret)
parse_err("illegal erase size"); parse_err("illegal erase size");
} }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* as published by the Free Software Foundation; either version * as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version. * 2 of the License, or (at your option) any later version.
* *
* $Id: ms02-nv.c,v 1.8 2005/01/05 18:05:12 dwmw2 Exp $ * $Id: ms02-nv.c,v 1.10 2005/06/20 12:24:41 macro Exp $
*/ */
#include <linux/init.h> #include <linux/init.h>
...@@ -99,8 +99,8 @@ static inline uint ms02nv_probe_one(ulong addr) ...@@ -99,8 +99,8 @@ static inline uint ms02nv_probe_one(ulong addr)
* The firmware writes MS02NV_ID at MS02NV_MAGIC and also * The firmware writes MS02NV_ID at MS02NV_MAGIC and also
* a diagnostic status at MS02NV_DIAG. * a diagnostic status at MS02NV_DIAG.
*/ */
ms02nv_diagp = (ms02nv_uint *)(KSEG1ADDR(addr + MS02NV_DIAG)); ms02nv_diagp = (ms02nv_uint *)(CKSEG1ADDR(addr + MS02NV_DIAG));
ms02nv_magicp = (ms02nv_uint *)(KSEG1ADDR(addr + MS02NV_MAGIC)); ms02nv_magicp = (ms02nv_uint *)(CKSEG1ADDR(addr + MS02NV_MAGIC));
err = get_dbe(ms02nv_magic, ms02nv_magicp); err = get_dbe(ms02nv_magic, ms02nv_magicp);
if (err) if (err)
return 0; return 0;
...@@ -233,7 +233,7 @@ static int __init ms02nv_init_one(ulong addr) ...@@ -233,7 +233,7 @@ static int __init ms02nv_init_one(ulong addr)
goto err_out_csr_res; goto err_out_csr_res;
} }
printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %uMiB.\n", printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %zuMiB.\n",
mtd->index, ms02nv_name, addr, size >> 20); mtd->index, ms02nv_name, addr, size >> 20);
mp->next = root_ms02nv_mtd; mp->next = root_ms02nv_mtd;
......
/* /*
* mtdram - a test mtd device * mtdram - a test mtd device
* $Id: mtdram.c,v 1.35 2005/01/05 18:05:12 dwmw2 Exp $ * $Id: mtdram.c,v 1.37 2005/04/21 03:42:11 joern Exp $
* Author: Alexander Larsson <alex@cendio.se> * Author: Alexander Larsson <alex@cendio.se>
* *
* Copyright (c) 1999 Alexander Larsson <alex@cendio.se> * Copyright (c) 1999 Alexander Larsson <alex@cendio.se>
* Copyright (c) 2005 Joern Engel <joern@wh.fh-wedel.de>
* *
* This code is GPL * This code is GPL
* *
...@@ -18,213 +19,140 @@ ...@@ -18,213 +19,140 @@
#include <linux/mtd/compatmac.h> #include <linux/mtd/compatmac.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#ifndef CONFIG_MTDRAM_ABS_POS
#define CONFIG_MTDRAM_ABS_POS 0
#endif
#if CONFIG_MTDRAM_ABS_POS > 0
#include <asm/io.h>
#endif
#ifdef MODULE
static unsigned long total_size = CONFIG_MTDRAM_TOTAL_SIZE; static unsigned long total_size = CONFIG_MTDRAM_TOTAL_SIZE;
static unsigned long erase_size = CONFIG_MTDRAM_ERASE_SIZE; static unsigned long erase_size = CONFIG_MTDRAM_ERASE_SIZE;
module_param(total_size,ulong,0);
MODULE_PARM_DESC(total_size, "Total device size in KiB");
module_param(erase_size,ulong,0);
MODULE_PARM_DESC(erase_size, "Device erase block size in KiB");
#define MTDRAM_TOTAL_SIZE (total_size * 1024) #define MTDRAM_TOTAL_SIZE (total_size * 1024)
#define MTDRAM_ERASE_SIZE (erase_size * 1024) #define MTDRAM_ERASE_SIZE (erase_size * 1024)
#else
#define MTDRAM_TOTAL_SIZE (CONFIG_MTDRAM_TOTAL_SIZE * 1024)
#define MTDRAM_ERASE_SIZE (CONFIG_MTDRAM_ERASE_SIZE * 1024)
#endif
#ifdef MODULE
module_param(total_size, ulong, 0);
MODULE_PARM_DESC(total_size, "Total device size in KiB");
module_param(erase_size, ulong, 0);
MODULE_PARM_DESC(erase_size, "Device erase block size in KiB");
#endif
// We could store these in the mtd structure, but we only support 1 device.. // We could store these in the mtd structure, but we only support 1 device..
static struct mtd_info *mtd_info; static struct mtd_info *mtd_info;
static int ram_erase(struct mtd_info *mtd, struct erase_info *instr)
static int
ram_erase(struct mtd_info *mtd, struct erase_info *instr)
{ {
DEBUG(MTD_DEBUG_LEVEL2, "ram_erase(pos:%ld, len:%ld)\n", (long)instr->addr, (long)instr->len); if (instr->addr + instr->len > mtd->size)
if (instr->addr + instr->len > mtd->size) { return -EINVAL;
DEBUG(MTD_DEBUG_LEVEL1, "ram_erase() out of bounds (%ld > %ld)\n", (long)(instr->addr + instr->len), (long)mtd->size);
return -EINVAL; memset((char *)mtd->priv + instr->addr, 0xff, instr->len);
}
instr->state = MTD_ERASE_DONE;
memset((char *)mtd->priv + instr->addr, 0xff, instr->len); mtd_erase_callback(instr);
instr->state = MTD_ERASE_DONE; return 0;
mtd_erase_callback(instr);
return 0;
} }
static int ram_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) static int ram_point(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char **mtdbuf)
{ {
if (from + len > mtd->size) if (from + len > mtd->size)
return -EINVAL; return -EINVAL;
*mtdbuf = mtd->priv + from; *mtdbuf = mtd->priv + from;
*retlen = len; *retlen = len;
return 0; return 0;
} }
static void ram_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, static void ram_unpoint(struct mtd_info *mtd, u_char * addr, loff_t from,
size_t len) size_t len)
{ {
DEBUG(MTD_DEBUG_LEVEL2, "ram_unpoint\n");
} }
static int ram_read(struct mtd_info *mtd, loff_t from, size_t len, static int ram_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf) size_t *retlen, u_char *buf)
{ {
DEBUG(MTD_DEBUG_LEVEL2, "ram_read(pos:%ld, len:%ld)\n", (long)from, (long)len); if (from + len > mtd->size)
if (from + len > mtd->size) { return -EINVAL;
DEBUG(MTD_DEBUG_LEVEL1, "ram_read() out of bounds (%ld > %ld)\n", (long)(from + len), (long)mtd->size);
return -EINVAL;
}
memcpy(buf, mtd->priv + from, len); memcpy(buf, mtd->priv + from, len);
*retlen=len; *retlen = len;
return 0; return 0;
} }
static int ram_write(struct mtd_info *mtd, loff_t to, size_t len, static int ram_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf) size_t *retlen, const u_char *buf)
{ {
DEBUG(MTD_DEBUG_LEVEL2, "ram_write(pos:%ld, len:%ld)\n", (long)to, (long)len); if (to + len > mtd->size)
if (to + len > mtd->size) { return -EINVAL;
DEBUG(MTD_DEBUG_LEVEL1, "ram_write() out of bounds (%ld > %ld)\n", (long)(to + len), (long)mtd->size);
return -EINVAL;
}
memcpy ((char *)mtd->priv + to, buf, len); memcpy((char *)mtd->priv + to, buf, len);
*retlen=len; *retlen = len;
return 0; return 0;
} }
static void __exit cleanup_mtdram(void) static void __exit cleanup_mtdram(void)
{ {
if (mtd_info) { if (mtd_info) {
del_mtd_device(mtd_info); del_mtd_device(mtd_info);
#if CONFIG_MTDRAM_TOTAL_SIZE > 0 if (mtd_info->priv)
if (mtd_info->priv) vfree(mtd_info->priv);
#if CONFIG_MTDRAM_ABS_POS > 0 kfree(mtd_info);
iounmap(mtd_info->priv); }
#else
vfree(mtd_info->priv);
#endif
#endif
kfree(mtd_info);
}
}
int mtdram_init_device(struct mtd_info *mtd, void *mapped_address,
unsigned long size, char *name)
{
memset(mtd, 0, sizeof(*mtd));
/* Setup the MTD structure */
mtd->name = name;
mtd->type = MTD_RAM;
mtd->flags = MTD_CAP_RAM;
mtd->size = size;
mtd->erasesize = MTDRAM_ERASE_SIZE;
mtd->priv = mapped_address;
mtd->owner = THIS_MODULE;
mtd->erase = ram_erase;
mtd->point = ram_point;
mtd->unpoint = ram_unpoint;
mtd->read = ram_read;
mtd->write = ram_write;
if (add_mtd_device(mtd)) {
return -EIO;
}
return 0;
}
#if CONFIG_MTDRAM_TOTAL_SIZE > 0
#if CONFIG_MTDRAM_ABS_POS > 0
static int __init init_mtdram(void)
{
void *addr;
int err;
/* Allocate some memory */
mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
if (!mtd_info)
return -ENOMEM;
addr = ioremap(CONFIG_MTDRAM_ABS_POS, MTDRAM_TOTAL_SIZE);
if (!addr) {
DEBUG(MTD_DEBUG_LEVEL1,
"Failed to ioremap) memory region of size %ld at ABS_POS:%ld\n",
(long)MTDRAM_TOTAL_SIZE, (long)CONFIG_MTDRAM_ABS_POS);
kfree(mtd_info);
mtd_info = NULL;
return -ENOMEM;
}
err = mtdram_init_device(mtd_info, addr,
MTDRAM_TOTAL_SIZE, "mtdram test device");
if (err)
{
iounmap(addr);
kfree(mtd_info);
mtd_info = NULL;
return err;
}
memset(mtd_info->priv, 0xff, MTDRAM_TOTAL_SIZE);
return err;
} }
#else /* CONFIG_MTDRAM_ABS_POS > 0 */ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address,
unsigned long size, char *name)
static int __init init_mtdram(void)
{ {
void *addr; memset(mtd, 0, sizeof(*mtd));
int err;
/* Allocate some memory */ /* Setup the MTD structure */
mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); mtd->name = name;
if (!mtd_info) mtd->type = MTD_RAM;
return -ENOMEM; mtd->flags = MTD_CAP_RAM;
mtd->size = size;
addr = vmalloc(MTDRAM_TOTAL_SIZE); mtd->erasesize = MTDRAM_ERASE_SIZE;
if (!addr) { mtd->priv = mapped_address;
DEBUG(MTD_DEBUG_LEVEL1,
"Failed to vmalloc memory region of size %ld\n", mtd->owner = THIS_MODULE;
(long)MTDRAM_TOTAL_SIZE); mtd->erase = ram_erase;
kfree(mtd_info); mtd->point = ram_point;
mtd_info = NULL; mtd->unpoint = ram_unpoint;
return -ENOMEM; mtd->read = ram_read;
} mtd->write = ram_write;
err = mtdram_init_device(mtd_info, addr,
MTDRAM_TOTAL_SIZE, "mtdram test device"); if (add_mtd_device(mtd)) {
if (err) return -EIO;
{ }
vfree(addr);
kfree(mtd_info); return 0;
mtd_info = NULL;
return err;
}
memset(mtd_info->priv, 0xff, MTDRAM_TOTAL_SIZE);
return err;
} }
#endif /* !(CONFIG_MTDRAM_ABS_POS > 0) */
#else /* CONFIG_MTDRAM_TOTAL_SIZE > 0 */
static int __init init_mtdram(void) static int __init init_mtdram(void)
{ {
return 0; void *addr;
int err;
if (!total_size)
return -EINVAL;
/* Allocate some memory */
mtd_info = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
if (!mtd_info)
return -ENOMEM;
addr = vmalloc(MTDRAM_TOTAL_SIZE);
if (!addr) {
kfree(mtd_info);
mtd_info = NULL;
return -ENOMEM;
}
err = mtdram_init_device(mtd_info, addr, MTDRAM_TOTAL_SIZE, "mtdram test device");
if (err) {
vfree(addr);
kfree(mtd_info);
mtd_info = NULL;
return err;
}
memset(mtd_info->priv, 0xff, MTDRAM_TOTAL_SIZE);
return err;
} }
#endif /* !(CONFIG_MTDRAM_TOTAL_SIZE > 0) */
module_init(init_mtdram); module_init(init_mtdram);
module_exit(cleanup_mtdram); module_exit(cleanup_mtdram);
...@@ -232,4 +160,3 @@ module_exit(cleanup_mtdram); ...@@ -232,4 +160,3 @@ module_exit(cleanup_mtdram);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alexander Larsson <alexl@redhat.com>"); MODULE_AUTHOR("Alexander Larsson <alexl@redhat.com>");
MODULE_DESCRIPTION("Simulated MTD driver for testing"); MODULE_DESCRIPTION("Simulated MTD driver for testing");
/** /**
* $Id: phram.c,v 1.11 2005/01/05 18:05:13 dwmw2 Exp $ * $Id: phram.c,v 1.14 2005/03/07 21:43:38 joern Exp $
* *
* Copyright (c) ???? Jochen Schuble <psionic@psionic.de> * Copyright (c) ???? Jochen Schuble <psionic@psionic.de>
* Copyright (c) 2003-2004 Jrn Engel <joern@wh.fh-wedel.de> * Copyright (c) 2003-2004 Jrn Engel <joern@wh.fh-wedel.de>
...@@ -15,9 +15,7 @@ ...@@ -15,9 +15,7 @@
* *
* Example: * Example:
* phram=swap,64Mi,128Mi phram=test,900Mi,1Mi * phram=swap,64Mi,128Mi phram=test,900Mi,1Mi
*
*/ */
#include <asm/io.h> #include <asm/io.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -36,7 +34,6 @@ struct phram_mtd_list { ...@@ -36,7 +34,6 @@ struct phram_mtd_list {
static LIST_HEAD(phram_list); static LIST_HEAD(phram_list);
static int phram_erase(struct mtd_info *mtd, struct erase_info *instr) static int phram_erase(struct mtd_info *mtd, struct erase_info *instr)
{ {
u_char *start = mtd->priv; u_char *start = mtd->priv;
...@@ -71,7 +68,8 @@ static int phram_point(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -71,7 +68,8 @@ static int phram_point(struct mtd_info *mtd, loff_t from, size_t len,
return 0; return 0;
} }
static void phram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) static void phram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from,
size_t len)
{ {
} }
...@@ -80,8 +78,11 @@ static int phram_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -80,8 +78,11 @@ static int phram_read(struct mtd_info *mtd, loff_t from, size_t len,
{ {
u_char *start = mtd->priv; u_char *start = mtd->priv;
if (from + len > mtd->size) if (from >= mtd->size)
return -EINVAL; return -EINVAL;
if (len > mtd->size - from)
len = mtd->size - from;
memcpy(buf, start + from, len); memcpy(buf, start + from, len);
...@@ -94,8 +95,11 @@ static int phram_write(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -94,8 +95,11 @@ static int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
{ {
u_char *start = mtd->priv; u_char *start = mtd->priv;
if (to + len > mtd->size) if (to >= mtd->size)
return -EINVAL; return -EINVAL;
if (len > mtd->size - to)
len = mtd->size - to;
memcpy(start + to, buf, len); memcpy(start + to, buf, len);
...@@ -107,9 +111,9 @@ static int phram_write(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -107,9 +111,9 @@ static int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
static void unregister_devices(void) static void unregister_devices(void)
{ {
struct phram_mtd_list *this; struct phram_mtd_list *this, *safe;
list_for_each_entry(this, &phram_list, list) { list_for_each_entry_safe(this, safe, &phram_list, list) {
del_mtd_device(&this->mtd); del_mtd_device(&this->mtd);
iounmap(this->mtd.priv); iounmap(this->mtd.priv);
kfree(this); kfree(this);
...@@ -145,7 +149,7 @@ static int register_device(char *name, unsigned long start, unsigned long len) ...@@ -145,7 +149,7 @@ static int register_device(char *name, unsigned long start, unsigned long len)
new->mtd.write = phram_write; new->mtd.write = phram_write;
new->mtd.owner = THIS_MODULE; new->mtd.owner = THIS_MODULE;
new->mtd.type = MTD_RAM; new->mtd.type = MTD_RAM;
new->mtd.erasesize = 0; new->mtd.erasesize = PAGE_SIZE;
ret = -EAGAIN; ret = -EAGAIN;
if (add_mtd_device(&new->mtd)) { if (add_mtd_device(&new->mtd)) {
...@@ -214,6 +218,15 @@ static int parse_name(char **pname, const char *token) ...@@ -214,6 +218,15 @@ static int parse_name(char **pname, const char *token)
return 0; return 0;
} }
static inline void kill_final_newline(char *str)
{
char *newline = strrchr(str, '\n');
if (newline && !newline[1])
*newline = 0;
}
#define parse_err(fmt, args...) do { \ #define parse_err(fmt, args...) do { \
ERROR(fmt , ## args); \ ERROR(fmt , ## args); \
return 0; \ return 0; \
...@@ -232,6 +245,7 @@ static int phram_setup(const char *val, struct kernel_param *kp) ...@@ -232,6 +245,7 @@ static int phram_setup(const char *val, struct kernel_param *kp)
parse_err("parameter too long\n"); parse_err("parameter too long\n");
strcpy(str, val); strcpy(str, val);
kill_final_newline(str);
for (i=0; i<3; i++) for (i=0; i<3; i++)
token[i] = strsep(&str, ","); token[i] = strsep(&str, ",");
......
/*====================================================================== /*======================================================================
$Id: slram.c,v 1.33 2005/01/05 18:05:13 dwmw2 Exp $ $Id: slram.c,v 1.34 2005/01/06 21:16:42 jwboyer Exp $
This driver provides a method to access memory not used by the kernel This driver provides a method to access memory not used by the kernel
itself (i.e. if the kernel commandline mem=xxx is used). To actually itself (i.e. if the kernel commandline mem=xxx is used). To actually
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#define SLRAM_MAX_DEVICES_PARAMS 6 /* 3 parameters / device */ #define SLRAM_MAX_DEVICES_PARAMS 6 /* 3 parameters / device */
#define SLRAM_BLK_SZ 0x4000
#define T(fmt, args...) printk(KERN_DEBUG fmt, ## args) #define T(fmt, args...) printk(KERN_DEBUG fmt, ## args)
#define E(fmt, args...) printk(KERN_NOTICE fmt, ## args) #define E(fmt, args...) printk(KERN_NOTICE fmt, ## args)
...@@ -108,6 +109,9 @@ static int slram_point(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -108,6 +109,9 @@ static int slram_point(struct mtd_info *mtd, loff_t from, size_t len,
{ {
slram_priv_t *priv = mtd->priv; slram_priv_t *priv = mtd->priv;
if (from + len > mtd->size)
return -EINVAL;
*mtdbuf = priv->start + from; *mtdbuf = priv->start + from;
*retlen = len; *retlen = len;
return(0); return(0);
...@@ -121,7 +125,13 @@ static int slram_read(struct mtd_info *mtd, loff_t from, size_t len, ...@@ -121,7 +125,13 @@ static int slram_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf) size_t *retlen, u_char *buf)
{ {
slram_priv_t *priv = mtd->priv; slram_priv_t *priv = mtd->priv;
if (from > mtd->size)
return -EINVAL;
if (from + len > mtd->size)
len = mtd->size - from;
memcpy(buf, priv->start + from, len); memcpy(buf, priv->start + from, len);
*retlen = len; *retlen = len;
...@@ -133,6 +143,9 @@ static int slram_write(struct mtd_info *mtd, loff_t to, size_t len, ...@@ -133,6 +143,9 @@ static int slram_write(struct mtd_info *mtd, loff_t to, size_t len,
{ {
slram_priv_t *priv = mtd->priv; slram_priv_t *priv = mtd->priv;
if (to + len > mtd->size)
return -EINVAL;
memcpy(priv->start + to, buf, len); memcpy(priv->start + to, buf, len);
*retlen = len; *retlen = len;
...@@ -188,7 +201,7 @@ static int register_device(char *name, unsigned long start, unsigned long length ...@@ -188,7 +201,7 @@ static int register_device(char *name, unsigned long start, unsigned long length
(*curmtd)->mtdinfo->name = name; (*curmtd)->mtdinfo->name = name;
(*curmtd)->mtdinfo->size = length; (*curmtd)->mtdinfo->size = length;
(*curmtd)->mtdinfo->flags = MTD_CLEAR_BITS | MTD_SET_BITS | (*curmtd)->mtdinfo->flags = MTD_CLEAR_BITS | MTD_SET_BITS |
MTD_WRITEB_WRITEABLE | MTD_VOLATILE; MTD_WRITEB_WRITEABLE | MTD_VOLATILE | MTD_CAP_RAM;
(*curmtd)->mtdinfo->erase = slram_erase; (*curmtd)->mtdinfo->erase = slram_erase;
(*curmtd)->mtdinfo->point = slram_point; (*curmtd)->mtdinfo->point = slram_point;
(*curmtd)->mtdinfo->unpoint = slram_unpoint; (*curmtd)->mtdinfo->unpoint = slram_unpoint;
...@@ -196,7 +209,7 @@ static int register_device(char *name, unsigned long start, unsigned long length ...@@ -196,7 +209,7 @@ static int register_device(char *name, unsigned long start, unsigned long length
(*curmtd)->mtdinfo->write = slram_write; (*curmtd)->mtdinfo->write = slram_write;
(*curmtd)->mtdinfo->owner = THIS_MODULE; (*curmtd)->mtdinfo->owner = THIS_MODULE;
(*curmtd)->mtdinfo->type = MTD_RAM; (*curmtd)->mtdinfo->type = MTD_RAM;
(*curmtd)->mtdinfo->erasesize = 0x0; (*curmtd)->mtdinfo->erasesize = SLRAM_BLK_SZ;
if (add_mtd_device((*curmtd)->mtdinfo)) { if (add_mtd_device((*curmtd)->mtdinfo)) {
E("slram: Failed to register new device\n"); E("slram: Failed to register new device\n");
...@@ -261,7 +274,7 @@ static int parse_cmdline(char *devname, char *szstart, char *szlength) ...@@ -261,7 +274,7 @@ static int parse_cmdline(char *devname, char *szstart, char *szlength)
} }
T("slram: devname=%s, devstart=0x%lx, devlength=0x%lx\n", T("slram: devname=%s, devstart=0x%lx, devlength=0x%lx\n",
devname, devstart, devlength); devname, devstart, devlength);
if ((devstart < 0) || (devlength < 0)) { if ((devstart < 0) || (devlength < 0) || (devlength % SLRAM_BLK_SZ != 0)) {
E("slram: Illegal start / length parameter.\n"); E("slram: Illegal start / length parameter.\n");
return(-EINVAL); return(-EINVAL);
} }
......
/* This version ported to the Linux-MTD system by dwmw2@infradead.org /* This version ported to the Linux-MTD system by dwmw2@infradead.org
* $Id: ftl.c,v 1.54 2004/11/16 18:33:15 dwmw2 Exp $ * $Id: ftl.c,v 1.55 2005/01/17 13:47:21 hvr Exp $
* *
* Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br> * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
* - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
...@@ -357,6 +357,7 @@ static int erase_xfer(partition_t *part, ...@@ -357,6 +357,7 @@ static int erase_xfer(partition_t *part,
if (!erase) if (!erase)
return -ENOMEM; return -ENOMEM;
erase->mtd = part->mbd.mtd;
erase->callback = ftl_erase_callback; erase->callback = ftl_erase_callback;
erase->addr = xfer->Offset; erase->addr = xfer->Offset;
erase->len = 1 << part->header.EraseUnitSize; erase->len = 1 << part->header.EraseUnitSize;
...@@ -1096,7 +1097,7 @@ struct mtd_blktrans_ops ftl_tr = { ...@@ -1096,7 +1097,7 @@ struct mtd_blktrans_ops ftl_tr = {
int init_ftl(void) int init_ftl(void)
{ {
DEBUG(0, "$Id: ftl.c,v 1.54 2004/11/16 18:33:15 dwmw2 Exp $\n"); DEBUG(0, "$Id: ftl.c,v 1.55 2005/01/17 13:47:21 hvr Exp $\n");
return register_mtd_blktrans(&ftl_tr); return register_mtd_blktrans(&ftl_tr);
} }
......
# drivers/mtd/maps/Kconfig # drivers/mtd/maps/Kconfig
# $Id: Kconfig,v 1.42 2005/01/05 16:59:50 dwmw2 Exp $ # $Id: Kconfig,v 1.55 2005/07/02 01:53:24 tpoynor Exp $
menu "Mapping drivers for chip access" menu "Mapping drivers for chip access"
depends on MTD!=n depends on MTD!=n
...@@ -122,16 +122,6 @@ config MTD_SBC_GXX ...@@ -122,16 +122,6 @@ config MTD_SBC_GXX
More info at More info at
<http://www.arcomcontrols.com/products/icp/pc104/processors/SBC_GX1.htm>. <http://www.arcomcontrols.com/products/icp/pc104/processors/SBC_GX1.htm>.
config MTD_ELAN_104NC
tristate "CFI Flash device mapped on Arcom ELAN-104NC"
depends on X86 && MTD_CFI_INTELEXT && MTD_PARTITIONS && MTD_COMPLEX_MAPPINGS
help
This provides a driver for the on-board flash of the Arcom Control
System's ELAN-104NC development board. By default the flash
is split into 3 partitions which are accessed as separate MTD
devices. This board utilizes Intel StrataFlash. More info at
<http://www.arcomcontrols.com/products/icp/pc104/processors/ELAN104NC.htm>.
config MTD_LUBBOCK config MTD_LUBBOCK
tristate "CFI Flash device mapped on Intel Lubbock XScale eval board" tristate "CFI Flash device mapped on Intel Lubbock XScale eval board"
depends on ARCH_LUBBOCK && MTD_CFI_INTELEXT && MTD_PARTITIONS depends on ARCH_LUBBOCK && MTD_CFI_INTELEXT && MTD_PARTITIONS
...@@ -139,6 +129,14 @@ config MTD_LUBBOCK ...@@ -139,6 +129,14 @@ config MTD_LUBBOCK
This provides a driver for the on-board flash of the Intel This provides a driver for the on-board flash of the Intel
'Lubbock' XScale evaluation board. 'Lubbock' XScale evaluation board.
config MTD_MAINSTONE
tristate "CFI Flash device mapped on Intel Mainstone XScale eval board"
depends on MACH_MAINSTONE && MTD_CFI_INTELEXT
select MTD_PARTITIONS
help
This provides a driver for the on-board flash of the Intel
'Mainstone PXA27x evaluation board.
config MTD_OCTAGON config MTD_OCTAGON
tristate "JEDEC Flash device mapped on Octagon 5066 SBC" tristate "JEDEC Flash device mapped on Octagon 5066 SBC"
depends on X86 && MTD_JEDEC && MTD_COMPLEX_MAPPINGS depends on X86 && MTD_JEDEC && MTD_COMPLEX_MAPPINGS
...@@ -213,74 +211,11 @@ config MTD_NETtel ...@@ -213,74 +211,11 @@ config MTD_NETtel
help help
Support for flash chips on NETtel/SecureEdge/SnapGear boards. Support for flash chips on NETtel/SecureEdge/SnapGear boards.
config MTD_PB1XXX config MTD_ALCHEMY
tristate "Flash devices on Alchemy PB1xxx boards" tristate ' AMD Alchemy Pb1xxx/Db1xxx/RDK MTD support'
depends on MIPS && ( MIPS_PB1000 || MIPS_PB1100 || MIPS_PB1500 ) depends on MIPS && SOC_AU1X00
help
Flash memory access on Alchemy Pb1000/Pb1100/Pb1500 boards
config MTD_PB1XXX_BOOT
bool "PB1x00 boot flash device"
depends on MTD_PB1XXX && ( MIPS_PB1100 || MIPS_PB1500 )
help
Use the first of the two 32MiB flash banks on Pb1100/Pb1500 board.
You can say 'Y' to both this and 'MTD_PB1XXX_USER' below, to use
both banks.
config MTD_PB1XXX_USER
bool "PB1x00 user flash device"
depends on MTD_PB1XXX && ( MIPS_PB1100 || MIPS_PB1500 )
default y if MTD_PB1XX_BOOT = n
help
Use the second of the two 32MiB flash banks on Pb1100/Pb1500 board.
You can say 'Y' to both this and 'MTD_PB1XXX_BOOT' above, to use
both banks.
config MTD_PB1550
tristate "Flash devices on Alchemy PB1550 board"
depends on MIPS && MIPS_PB1550
help
Flash memory access on Alchemy Pb1550 board
config MTD_PB1550_BOOT
bool "PB1550 boot flash device"
depends on MTD_PB1550
help help
Use the first of the two 64MiB flash banks on Pb1550 board. Flash memory access on AMD Alchemy Pb/Db/RDK Reference Boards
You can say 'Y' to both this and 'MTD_PB1550_USER' below, to use
both banks.
config MTD_PB1550_USER
bool "PB1550 user flash device"
depends on MTD_PB1550
default y if MTD_PB1550_BOOT = n
help
Use the second of the two 64MiB flash banks on Pb1550 board.
You can say 'Y' to both this and 'MTD_PB1550_BOOT' above, to use
both banks.
config MTD_DB1550
tristate "Flash devices on Alchemy DB1550 board"
depends on MIPS && MIPS_DB1550
help
Flash memory access on Alchemy Db1550 board
config MTD_DB1550_BOOT
bool "DB1550 boot flash device"
depends on MTD_DB1550
help
Use the first of the two 64MiB flash banks on Db1550 board.
You can say 'Y' to both this and 'MTD_DB1550_USER' below, to use
both banks.
config MTD_DB1550_USER
bool "DB1550 user flash device"
depends on MTD_DB1550
default y if MTD_DB1550_BOOT = n
help
Use the second of the two 64MiB flash banks on Db1550 board.
You can say 'Y' to both this and 'MTD_DB1550_BOOT' above, to use
both banks.
config MTD_DILNETPC config MTD_DILNETPC
tristate "CFI Flash device mapped on DIL/Net PC" tristate "CFI Flash device mapped on DIL/Net PC"
...@@ -588,6 +523,15 @@ config MTD_MPC1211 ...@@ -588,6 +523,15 @@ config MTD_MPC1211
This enables access to the flash chips on the Interface MPC-1211(CTP/PCI/MPC-SH02). This enables access to the flash chips on the Interface MPC-1211(CTP/PCI/MPC-SH02).
If you have such a board, say 'Y'. If you have such a board, say 'Y'.
config MTD_OMAP_NOR
tristate "TI OMAP board mappings"
depends on MTD_CFI && ARCH_OMAP
help
This enables access to the NOR flash chips on TI OMAP-based
boards defining flash platform devices and flash platform data.
These boards include the Innovator, H2, H3, OSK, Perseus2, and
more. If you have such a board, say 'Y'.
# This needs CFI or JEDEC, depending on the cards found. # This needs CFI or JEDEC, depending on the cards found.
config MTD_PCI config MTD_PCI
tristate "PCI MTD driver" tristate "PCI MTD driver"
...@@ -647,13 +591,14 @@ config MTD_DMV182 ...@@ -647,13 +591,14 @@ config MTD_DMV182
Map driver for Dy-4 SVME/DMV-182 board. Map driver for Dy-4 SVME/DMV-182 board.
config MTD_BAST config MTD_BAST
tristate "Map driver for Simtec BAST (EB2410ITX)" tristate "Map driver for Simtec BAST (EB2410ITX) or Thorcom VR1000"
depends on ARCH_BAST depends on ARCH_BAST || MACH_VR1000
select MTD_PARTITIONS select MTD_PARTITIONS
select MTD_MAP_BANK_WIDTH_16 select MTD_MAP_BANK_WIDTH_16
select MTD_JEDECPROBE select MTD_JEDECPROBE
help help
Map driver for NOR flash on the Simtec BAST (EB2410ITX). Map driver for NOR flash on the Simtec BAST (EB2410ITX), or the
Thorcom VR1000
Note, this driver *cannot* over-ride the WP link on the Note, this driver *cannot* over-ride the WP link on the
board, or currently detect the state of the link. board, or currently detect the state of the link.
...@@ -669,5 +614,15 @@ config MTD_SHARP_SL ...@@ -669,5 +614,15 @@ config MTD_SHARP_SL
help help
This enables access to the flash chip on the Sharp SL Series of PDAs. This enables access to the flash chip on the Sharp SL Series of PDAs.
config MTD_PLATRAM
tristate "Map driver for platform device RAM (mtd-ram)"
depends on MTD
select MTD_RAM
help
Map driver for RAM areas described via the platform device
system.
This selection automatically selects the map_ram driver.
endmenu endmenu
# #
# linux/drivers/maps/Makefile # linux/drivers/maps/Makefile
# #
# $Id: Makefile.common,v 1.23 2005/01/05 17:06:36 dwmw2 Exp $ # $Id: Makefile.common,v 1.30 2005/07/02 01:53:24 tpoynor Exp $
ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y) ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y)
obj-$(CONFIG_MTD) += map_funcs.o obj-$(CONFIG_MTD) += map_funcs.o
...@@ -15,7 +15,6 @@ obj-$(CONFIG_MTD_CFI_FLAGADM) += cfi_flagadm.o ...@@ -15,7 +15,6 @@ obj-$(CONFIG_MTD_CFI_FLAGADM) += cfi_flagadm.o
obj-$(CONFIG_MTD_CSTM_MIPS_IXX) += cstm_mips_ixx.o obj-$(CONFIG_MTD_CSTM_MIPS_IXX) += cstm_mips_ixx.o
obj-$(CONFIG_MTD_DC21285) += dc21285.o obj-$(CONFIG_MTD_DC21285) += dc21285.o
obj-$(CONFIG_MTD_DILNETPC) += dilnetpc.o obj-$(CONFIG_MTD_DILNETPC) += dilnetpc.o
obj-$(CONFIG_MTD_ELAN_104NC) += elan-104nc.o
obj-$(CONFIG_MTD_EPXA10DB) += epxa10db-flash.o obj-$(CONFIG_MTD_EPXA10DB) += epxa10db-flash.o
obj-$(CONFIG_MTD_IQ80310) += iq80310.o obj-$(CONFIG_MTD_IQ80310) += iq80310.o
obj-$(CONFIG_MTD_L440GX) += l440gx.o obj-$(CONFIG_MTD_L440GX) += l440gx.o
...@@ -23,6 +22,7 @@ obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o ...@@ -23,6 +22,7 @@ obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o
obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o
obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o
obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o
obj-$(CONFIG_MTD_MAINSTONE) += mainstone-flash.o
obj-$(CONFIG_MTD_MBX860) += mbx860.o obj-$(CONFIG_MTD_MBX860) += mbx860.o
obj-$(CONFIG_MTD_CEIVA) += ceiva.o obj-$(CONFIG_MTD_CEIVA) += ceiva.o
obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o
...@@ -44,10 +44,7 @@ obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o ...@@ -44,10 +44,7 @@ obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o
obj-$(CONFIG_MTD_OCELOT) += ocelot.o obj-$(CONFIG_MTD_OCELOT) += ocelot.o
obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
obj-$(CONFIG_MTD_PCI) += pci.o obj-$(CONFIG_MTD_PCI) += pci.o
obj-$(CONFIG_MTD_PB1XXX) += pb1xxx-flash.o obj-$(CONFIG_MTD_ALCHEMY) += alchemy-flash.o
obj-$(CONFIG_MTD_DB1X00) += db1x00-flash.o
obj-$(CONFIG_MTD_PB1550) += pb1550-flash.o
obj-$(CONFIG_MTD_DB1550) += db1550-flash.o
obj-$(CONFIG_MTD_LASAT) += lasat.o obj-$(CONFIG_MTD_LASAT) += lasat.o
obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o
obj-$(CONFIG_MTD_EDB7312) += edb7312.o obj-$(CONFIG_MTD_EDB7312) += edb7312.o
...@@ -71,3 +68,5 @@ obj-$(CONFIG_MTD_IXP2000) += ixp2000.o ...@@ -71,3 +68,5 @@ obj-$(CONFIG_MTD_IXP2000) += ixp2000.o
obj-$(CONFIG_MTD_WRSBC8260) += wr_sbc82xx_flash.o obj-$(CONFIG_MTD_WRSBC8260) += wr_sbc82xx_flash.o
obj-$(CONFIG_MTD_DMV182) += dmv182.o obj-$(CONFIG_MTD_DMV182) += dmv182.o
obj-$(CONFIG_MTD_SHARP_SL) += sharpsl-flash.o obj-$(CONFIG_MTD_SHARP_SL) += sharpsl-flash.o
obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o
/*
* Flash memory access on AMD Alchemy evaluation boards
*
* $Id: alchemy-flash.c,v 1.1 2005/02/27 21:50:21 ppopov Exp $
*
* (C) 2003, 2004 Pete Popov <ppopov@embeddedalley.com>
*
*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#ifdef DEBUG_RW
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif
#ifdef CONFIG_MIPS_PB1000
#define BOARD_MAP_NAME "Pb1000 Flash"
#define BOARD_FLASH_SIZE 0x00800000 /* 8MB */
#define BOARD_FLASH_WIDTH 4 /* 32-bits */
#endif
#ifdef CONFIG_MIPS_PB1500
#define BOARD_MAP_NAME "Pb1500 Flash"
#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */
#define BOARD_FLASH_WIDTH 4 /* 32-bits */
#endif
#ifdef CONFIG_MIPS_PB1100
#define BOARD_MAP_NAME "Pb1100 Flash"
#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */
#define BOARD_FLASH_WIDTH 4 /* 32-bits */
#endif
#ifdef CONFIG_MIPS_PB1550
#define BOARD_MAP_NAME "Pb1550 Flash"
#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */
#define BOARD_FLASH_WIDTH 4 /* 32-bits */
#endif
#ifdef CONFIG_MIPS_PB1200
#define BOARD_MAP_NAME "Pb1200 Flash"
#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */
#define BOARD_FLASH_WIDTH 2 /* 16-bits */
#endif
#ifdef CONFIG_MIPS_DB1000
#define BOARD_MAP_NAME "Db1000 Flash"
#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */
#define BOARD_FLASH_WIDTH 4 /* 32-bits */
#endif
#ifdef CONFIG_MIPS_DB1500
#define BOARD_MAP_NAME "Db1500 Flash"
#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */
#define BOARD_FLASH_WIDTH 4 /* 32-bits */
#endif
#ifdef CONFIG_MIPS_DB1100
#define BOARD_MAP_NAME "Db1100 Flash"
#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */
#define BOARD_FLASH_WIDTH 4 /* 32-bits */
#endif
#ifdef CONFIG_MIPS_DB1550
#define BOARD_MAP_NAME "Db1550 Flash"
#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */
#define BOARD_FLASH_WIDTH 4 /* 32-bits */
#endif
#ifdef CONFIG_MIPS_DB1200
#define BOARD_MAP_NAME "Db1200 Flash"
#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */
#define BOARD_FLASH_WIDTH 2 /* 16-bits */
#endif
#ifdef CONFIG_MIPS_HYDROGEN3
#define BOARD_MAP_NAME "Hydrogen3 Flash"
#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */
#define BOARD_FLASH_WIDTH 4 /* 32-bits */
#define USE_LOCAL_ACCESSORS /* why? */
#endif
#ifdef CONFIG_MIPS_BOSPORUS
#define BOARD_MAP_NAME "Bosporus Flash"
#define BOARD_FLASH_SIZE 0x01000000 /* 16MB */
#define BOARD_FLASH_WIDTH 2 /* 16-bits */
#endif
#ifdef CONFIG_MIPS_MIRAGE
#define BOARD_MAP_NAME "Mirage Flash"
#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */
#define BOARD_FLASH_WIDTH 4 /* 32-bits */
#define USE_LOCAL_ACCESSORS /* why? */
#endif
static struct map_info alchemy_map = {
.name = BOARD_MAP_NAME,
};
static struct mtd_partition alchemy_partitions[] = {
{
.name = "User FS",
.size = BOARD_FLASH_SIZE - 0x00400000,
.offset = 0x0000000
},{
.name = "YAMON",
.size = 0x0100000,
.offset = MTDPART_OFS_APPEND,
.mask_flags = MTD_WRITEABLE
},{
.name = "raw kernel",
.size = (0x300000 - 0x40000), /* last 256KB is yamon env */
.offset = MTDPART_OFS_APPEND,
}
};
#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
static struct mtd_info *mymtd;
int __init alchemy_mtd_init(void)
{
struct mtd_partition *parts;
int nb_parts = 0;
unsigned long window_addr;
unsigned long window_size;
/* Default flash buswidth */
alchemy_map.bankwidth = BOARD_FLASH_WIDTH;
window_addr = 0x20000000 - BOARD_FLASH_SIZE;
window_size = BOARD_FLASH_SIZE;
#ifdef CONFIG_MIPS_MIRAGE_WHY
/* Boot ROM flash bank only; no user bank */
window_addr = 0x1C000000;
window_size = 0x04000000;
/* USERFS from 0x1C00 0000 to 0x1FC00000 */
alchemy_partitions[0].size = 0x03C00000;
#endif
/*
* Static partition definition selection
*/
parts = alchemy_partitions;
nb_parts = NB_OF(alchemy_partitions);
alchemy_map.size = window_size;
/*
* Now let's probe for the actual flash. Do it here since
* specific machine settings might have been set above.
*/
printk(KERN_NOTICE BOARD_MAP_NAME ": probing %d-bit flash bus\n",
alchemy_map.bankwidth*8);
alchemy_map.virt = ioremap(window_addr, window_size);
mymtd = do_map_probe("cfi_probe", &alchemy_map);
if (!mymtd) {
iounmap(alchemy_map.virt);
return -ENXIO;
}
mymtd->owner = THIS_MODULE;
add_mtd_partitions(mymtd, parts, nb_parts);
return 0;
}
static void __exit alchemy_mtd_cleanup(void)
{
if (mymtd) {
del_mtd_partitions(mymtd);
map_destroy(mymtd);
iounmap(alchemy_map.virt);
}
}
module_init(alchemy_mtd_init);
module_exit(alchemy_mtd_cleanup);
MODULE_AUTHOR("Embedded Alley Solutions, Inc");
MODULE_DESCRIPTION(BOARD_MAP_NAME " MTD driver");
MODULE_LICENSE("GPL");
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* amd76xrom.c * amd76xrom.c
* *
* Normal mappings of chips in physical memory * Normal mappings of chips in physical memory
* $Id: amd76xrom.c,v 1.19 2004/11/28 09:40:39 dwmw2 Exp $ * $Id: amd76xrom.c,v 1.20 2005/03/18 14:04:35 gleixner Exp $
*/ */
#include <linux/module.h> #include <linux/module.h>
...@@ -314,7 +314,7 @@ static int __init init_amd76xrom(void) ...@@ -314,7 +314,7 @@ static int __init init_amd76xrom(void)
} }
return -ENXIO; return -ENXIO;
#if 0 #if 0
return pci_module_init(&amd76xrom_driver); return pci_register_driver(&amd76xrom_driver);
#endif #endif
} }
......
/* linux/drivers/mtd/maps/bast_flash.c /* linux/drivers/mtd/maps/bast_flash.c
* *
* Copyright (c) 2004 Simtec Electronics * Copyright (c) 2004-2005 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk> * Ben Dooks <ben@simtec.co.uk>
* *
* Simtec Bast (EB2410ITX) NOR MTD Mapping driver * Simtec Bast (EB2410ITX) NOR MTD Mapping driver
* *
* Changelog: * Changelog:
* 20-Sep-2004 BJD Initial version * 20-Sep-2004 BJD Initial version
* 17-Jan-2005 BJD Add whole device if no partitions found
* *
* $Id: bast-flash.c,v 1.1 2004/09/21 14:29:04 bjd Exp $ * $Id: bast-flash.c,v 1.2 2005/01/18 11:13:47 bjd Exp $
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -46,9 +47,9 @@ ...@@ -46,9 +47,9 @@
#include <asm/arch/bast-cpld.h> #include <asm/arch/bast-cpld.h>
#ifdef CONFIG_MTD_BAST_MAXSIZE #ifdef CONFIG_MTD_BAST_MAXSIZE
#define AREA_MAXSIZE (CONFIG_MTD_BAST_MAXSIZE * (1024*1024)) #define AREA_MAXSIZE (CONFIG_MTD_BAST_MAXSIZE * SZ_1M)
#else #else
#define AREA_MAXSIZE (32*1024*1024) #define AREA_MAXSIZE (32 * SZ_1M)
#endif #endif
#define PFX "bast-flash: " #define PFX "bast-flash: "
...@@ -189,6 +190,8 @@ static int bast_flash_probe(struct device *dev) ...@@ -189,6 +190,8 @@ static int bast_flash_probe(struct device *dev)
err = add_mtd_partitions(info->mtd, info->partitions, err); err = add_mtd_partitions(info->mtd, info->partitions, err);
if (err) if (err)
printk(KERN_ERR PFX "cannot add/parse partitions\n"); printk(KERN_ERR PFX "cannot add/parse partitions\n");
} else {
err = add_mtd_device(info->mtd);
} }
if (err == 0) if (err == 0)
......
/*
* Flash memory access on Alchemy Db1550 board
*
* $Id: db1550-flash.c,v 1.7 2004/11/04 13:24:14 gleixner Exp $
*
* (C) 2004 Embedded Edge, LLC, based on db1550-flash.c:
* (C) 2003, 2004 Pete Popov <ppopov@embeddedalley.com>
*
*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#ifdef DEBUG_RW
#define DBG(x...) printk(x)
#else
#define DBG(x...)
#endif
static unsigned long window_addr;
static unsigned long window_size;
static struct map_info db1550_map = {
.name = "Db1550 flash",
};
static unsigned char flash_bankwidth = 4;
/*
* Support only 64MB NOR Flash parts
*/
#if defined(CONFIG_MTD_DB1550_BOOT) && defined(CONFIG_MTD_DB1550_USER)
#define DB1550_BOTH_BANKS
#elif defined(CONFIG_MTD_DB1550_BOOT) && !defined(CONFIG_MTD_DB1550_USER)
#define DB1550_BOOT_ONLY
#elif !defined(CONFIG_MTD_DB1550_BOOT) && defined(CONFIG_MTD_DB1550_USER)
#define DB1550_USER_ONLY
#endif
#ifdef DB1550_BOTH_BANKS
/* both banks will be used. Combine the first bank and the first
* part of the second bank together into a single jffs/jffs2
* partition.
*/
static struct mtd_partition db1550_partitions[] = {
/* assume boot[2:0]:swap is '0000' or '1000', which translates to:
* 1C00 0000 1FFF FFFF CE0 64MB Boot NOR Flash
* 1800 0000 1BFF FFFF CE0 64MB Param NOR Flash
*/
{
.name = "User FS",
.size = (0x1FC00000 - 0x18000000),
.offset = 0x0000000
},{
.name = "yamon",
.size = 0x0100000,
.offset = MTDPART_OFS_APPEND,
.mask_flags = MTD_WRITEABLE
},{
.name = "raw kernel",
.size = (0x300000 - 0x40000), /* last 256KB is yamon env */
.offset = MTDPART_OFS_APPEND,
}
};
#elif defined(DB1550_BOOT_ONLY)
static struct mtd_partition db1550_partitions[] = {
/* assume boot[2:0]:swap is '0000' or '1000', which translates to:
* 1C00 0000 1FFF FFFF CE0 64MB Boot NOR Flash
*/
{
.name = "User FS",
.size = 0x03c00000,
.offset = 0x0000000
},{
.name = "yamon",
.size = 0x0100000,
.offset = MTDPART_OFS_APPEND,
.mask_flags = MTD_WRITEABLE
},{
.name = "raw kernel",
.size = (0x300000-0x40000), /* last 256KB is yamon env */
.offset = MTDPART_OFS_APPEND,
}
};
#elif defined(DB1550_USER_ONLY)
static struct mtd_partition db1550_partitions[] = {
/* assume boot[2:0]:swap is '0000' or '1000', which translates to:
* 1800 0000 1BFF FFFF CE0 64MB Param NOR Flash
*/
{
.name = "User FS",
.size = (0x4000000 - 0x200000), /* reserve 2MB for raw kernel */
.offset = 0x0000000
},{
.name = "raw kernel",
.size = MTDPART_SIZ_FULL,
.offset = MTDPART_OFS_APPEND,
}
};
#else
#error MTD_DB1550 define combo error /* should never happen */
#endif
#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
static struct mtd_info *mymtd;
/*
* Probe the flash density and setup window address and size
* based on user CONFIG options. There are times when we don't
* want the MTD driver to be probing the boot or user flash,
* so having the option to enable only one bank is important.
*/
int setup_flash_params(void)
{
#if defined(DB1550_BOTH_BANKS)
window_addr = 0x18000000;
window_size = 0x8000000;
#elif defined(DB1550_BOOT_ONLY)
window_addr = 0x1C000000;
window_size = 0x4000000;
#else /* USER ONLY */
window_addr = 0x18000000;
window_size = 0x4000000;
#endif
return 0;
}
int __init db1550_mtd_init(void)
{
struct mtd_partition *parts;
int nb_parts = 0;
/* Default flash bankwidth */
db1550_map.bankwidth = flash_bankwidth;
if (setup_flash_params())
return -ENXIO;
/*
* Static partition definition selection
*/
parts = db1550_partitions;
nb_parts = NB_OF(db1550_partitions);
db1550_map.size = window_size;
/*
* Now let's probe for the actual flash. Do it here since
* specific machine settings might have been set above.
*/
printk(KERN_NOTICE "Db1550 flash: probing %d-bit flash bus\n",
db1550_map.bankwidth*8);
db1550_map.virt = ioremap(window_addr, window_size);
mymtd = do_map_probe("cfi_probe", &db1550_map);
if (!mymtd) return -ENXIO;
mymtd->owner = THIS_MODULE;
add_mtd_partitions(mymtd, parts, nb_parts);
return 0;
}
static void __exit db1550_mtd_cleanup(void)
{
if (mymtd) {
del_mtd_partitions(mymtd);
map_destroy(mymtd);
iounmap((void *) db1550_map.virt);
}
}
module_init(db1550_mtd_init);
module_exit(db1550_mtd_cleanup);
MODULE_AUTHOR("Embedded Edge, LLC");
MODULE_DESCRIPTION("Db1550 mtd map driver");
MODULE_LICENSE("GPL");
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment