Merge master.kernel.org:/pub/scm/linux/kernel/git/tglx/mtd-2.6
This commit is contained in:
commit
200d481f28
|
@ -1,5 +1,5 @@
|
|||
# drivers/mtd/chips/Kconfig
|
||||
# $Id: Kconfig,v 1.13 2004/12/01 15:49:10 nico Exp $
|
||||
# $Id: Kconfig,v 1.15 2005/06/06 23:04:35 tpoynor Exp $
|
||||
|
||||
menu "RAM/ROM/Flash chip drivers"
|
||||
depends on MTD!=n
|
||||
|
@ -155,6 +155,31 @@ config MTD_CFI_I8
|
|||
If your flash chips are interleaved in eights - i.e. you have eight
|
||||
flash chips addressed by each bus cycle, then say 'Y'.
|
||||
|
||||
config MTD_OTP
|
||||
bool "Protection Registers aka one-time programmable (OTP) bits"
|
||||
depends on MTD_CFI_ADV_OPTIONS
|
||||
default n
|
||||
help
|
||||
This enables support for reading, writing and locking so called
|
||||
"Protection Registers" present on some flash chips.
|
||||
A subset of them are pre-programmed at the factory with a
|
||||
unique set of values. The rest is user-programmable.
|
||||
|
||||
The user-programmable Protection Registers contain one-time
|
||||
programmable (OTP) bits; when programmed, register bits cannot be
|
||||
erased. Each Protection Register can be accessed multiple times to
|
||||
program individual bits, as long as the register remains unlocked.
|
||||
|
||||
Each Protection Register has an associated Lock Register bit. When a
|
||||
Lock Register bit is programmed, the associated Protection Register
|
||||
can only be read; it can no longer be programmed. Additionally,
|
||||
because the Lock Register bits themselves are OTP, when programmed,
|
||||
Lock Register bits cannot be erased. Therefore, when a Protection
|
||||
Register is locked, it cannot be unlocked.
|
||||
|
||||
This feature should therefore be used with extreme care. Any mistake
|
||||
in the programming of OTP bits will waste them.
|
||||
|
||||
config MTD_CFI_INTELEXT
|
||||
tristate "Support for Intel/Sharp flash chips"
|
||||
depends on MTD_GEN_PROBE
|
||||
|
@ -275,7 +300,7 @@ config MTD_JEDEC
|
|||
|
||||
config MTD_XIP
|
||||
bool "XIP aware MTD support"
|
||||
depends on !SMP && MTD_CFI_INTELEXT && EXPERIMENTAL
|
||||
depends on !SMP && (MTD_CFI_INTELEXT || MTD_CFI_AMDSTD) && EXPERIMENTAL
|
||||
default y if XIP_KERNEL
|
||||
help
|
||||
This allows MTD support to work with flash memory which is also
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Author: Jonas Holmberg <jonas.holmberg@axis.com>
|
||||
*
|
||||
* $Id: amd_flash.c,v 1.26 2004/11/20 12:49:04 dwmw2 Exp $
|
||||
* $Id: amd_flash.c,v 1.27 2005/02/04 07:43:09 jonashg Exp $
|
||||
*
|
||||
* Copyright (c) 2001 Axis Communications AB
|
||||
*
|
||||
|
@ -67,7 +67,6 @@
|
|||
#define AM29LV160DT 0x22C4
|
||||
#define AM29LV160DB 0x2249
|
||||
#define AM29BDS323D 0x22D1
|
||||
#define AM29BDS643D 0x227E
|
||||
|
||||
/* Atmel */
|
||||
#define AT49xV16x 0x00C0
|
||||
|
@ -617,17 +616,6 @@ static struct mtd_info *amd_flash_probe(struct map_info *map)
|
|||
{ .offset = 0x300000, .erasesize = 0x10000, .numblocks = 15 },
|
||||
{ .offset = 0x3f0000, .erasesize = 0x02000, .numblocks = 8 },
|
||||
}
|
||||
}, {
|
||||
.mfr_id = MANUFACTURER_AMD,
|
||||
.dev_id = AM29BDS643D,
|
||||
.name = "AMD AM29BDS643D",
|
||||
.size = 0x00800000,
|
||||
.numeraseregions = 3,
|
||||
.regions = {
|
||||
{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 96 },
|
||||
{ .offset = 0x600000, .erasesize = 0x10000, .numblocks = 31 },
|
||||
{ .offset = 0x7f0000, .erasesize = 0x02000, .numblocks = 8 },
|
||||
}
|
||||
}, {
|
||||
.mfr_id = MANUFACTURER_ATMEL,
|
||||
.dev_id = AT49xV16x,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*
|
||||
* (C) 2000 Red Hat. GPL'd
|
||||
*
|
||||
* $Id: cfi_cmdset_0001.c,v 1.164 2004/11/16 18:29:00 dwmw2 Exp $
|
||||
* $Id: cfi_cmdset_0001.c,v 1.178 2005/05/19 17:05:43 nico Exp $
|
||||
*
|
||||
*
|
||||
* 10/10/2000 Nicolas Pitre <nico@cam.org>
|
||||
|
@ -29,6 +29,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/mtd/xip.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
|
@ -48,16 +49,25 @@
|
|||
#define M50LPW080 0x002F
|
||||
|
||||
static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
|
||||
//static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
|
||||
//static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
|
||||
static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
|
||||
static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
|
||||
static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *);
|
||||
static void cfi_intelext_sync (struct mtd_info *);
|
||||
static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len);
|
||||
static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len);
|
||||
#ifdef CONFIG_MTD_OTP
|
||||
static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
|
||||
static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
|
||||
static int cfi_intelext_write_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
|
||||
static int cfi_intelext_lock_user_prot_reg (struct mtd_info *, loff_t, size_t);
|
||||
static int cfi_intelext_get_fact_prot_info (struct mtd_info *,
|
||||
struct otp_info *, size_t);
|
||||
static int cfi_intelext_get_user_prot_info (struct mtd_info *,
|
||||
struct otp_info *, size_t);
|
||||
#endif
|
||||
static int cfi_intelext_suspend (struct mtd_info *);
|
||||
static void cfi_intelext_resume (struct mtd_info *);
|
||||
static int cfi_intelext_reboot (struct notifier_block *, unsigned long, void *);
|
||||
|
||||
static void cfi_intelext_destroy(struct mtd_info *);
|
||||
|
||||
|
@ -252,7 +262,8 @@ read_pri_intelext(struct map_info *map, __u16 adr)
|
|||
int nb_parts, i;
|
||||
|
||||
/* Protection Register info */
|
||||
extra_size += (extp->NumProtectionFields - 1) * (4 + 6);
|
||||
extra_size += (extp->NumProtectionFields - 1) *
|
||||
sizeof(struct cfi_intelext_otpinfo);
|
||||
|
||||
/* Burst Read info */
|
||||
extra_size += 6;
|
||||
|
@ -324,7 +335,9 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
|
|||
mtd->resume = cfi_intelext_resume;
|
||||
mtd->flags = MTD_CAP_NORFLASH;
|
||||
mtd->name = map->name;
|
||||
|
||||
|
||||
mtd->reboot_notifier.notifier_call = cfi_intelext_reboot;
|
||||
|
||||
if (cfi->cfi_mode == CFI_MODE_CFI) {
|
||||
/*
|
||||
* It's a real CFI chip, not one for which the probe
|
||||
|
@ -422,9 +435,13 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
|
|||
mtd->eraseregions[i].numblocks);
|
||||
}
|
||||
|
||||
#if 0
|
||||
mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg;
|
||||
#ifdef CONFIG_MTD_OTP
|
||||
mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg;
|
||||
mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg;
|
||||
mtd->write_user_prot_reg = cfi_intelext_write_user_prot_reg;
|
||||
mtd->lock_user_prot_reg = cfi_intelext_lock_user_prot_reg;
|
||||
mtd->get_fact_prot_info = cfi_intelext_get_fact_prot_info;
|
||||
mtd->get_user_prot_info = cfi_intelext_get_user_prot_info;
|
||||
#endif
|
||||
|
||||
/* This function has the potential to distort the reality
|
||||
|
@ -433,6 +450,7 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
|
|||
goto setup_err;
|
||||
|
||||
__module_get(THIS_MODULE);
|
||||
register_reboot_notifier(&mtd->reboot_notifier);
|
||||
return mtd;
|
||||
|
||||
setup_err:
|
||||
|
@ -471,7 +489,8 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd,
|
|||
int offs, numregions, numparts, partshift, numvirtchips, i, j;
|
||||
|
||||
/* Protection Register info */
|
||||
offs = (extp->NumProtectionFields - 1) * (4 + 6);
|
||||
offs = (extp->NumProtectionFields - 1) *
|
||||
sizeof(struct cfi_intelext_otpinfo);
|
||||
|
||||
/* Burst Read info */
|
||||
offs += 6;
|
||||
|
@ -563,7 +582,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
|
|||
resettime:
|
||||
timeo = jiffies + HZ;
|
||||
retry:
|
||||
if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING)) {
|
||||
if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE)) {
|
||||
/*
|
||||
* OK. We have possibility for contension on the write/erase
|
||||
* operations which are global to the real chip and not per
|
||||
|
@ -807,10 +826,6 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
|
|||
* assembly to make sure inline functions were actually inlined and that gcc
|
||||
* didn't emit calls to its own support functions). Also configuring MTD CFI
|
||||
* support to a single buswidth and a single interleave is also recommended.
|
||||
* Note that not only IRQs are disabled but the preemption count is also
|
||||
* increased to prevent other locking primitives (namely spin_unlock) from
|
||||
* decrementing the preempt count to zero and scheduling the CPU away while
|
||||
* not in array mode.
|
||||
*/
|
||||
|
||||
static void xip_disable(struct map_info *map, struct flchip *chip,
|
||||
|
@ -818,7 +833,6 @@ static void xip_disable(struct map_info *map, struct flchip *chip,
|
|||
{
|
||||
/* TODO: chips with no XIP use should ignore and return */
|
||||
(void) map_read(map, adr); /* ensure mmu mapping is up to date */
|
||||
preempt_disable();
|
||||
local_irq_disable();
|
||||
}
|
||||
|
||||
|
@ -831,9 +845,8 @@ static void __xipram xip_enable(struct map_info *map, struct flchip *chip,
|
|||
chip->state = FL_READY;
|
||||
}
|
||||
(void) map_read(map, adr);
|
||||
asm volatile (".rep 8; nop; .endr"); /* fill instruction prefetch */
|
||||
xip_iprefetch();
|
||||
local_irq_enable();
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -909,7 +922,7 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
|
|||
(void) map_read(map, adr);
|
||||
asm volatile (".rep 8; nop; .endr");
|
||||
local_irq_enable();
|
||||
preempt_enable();
|
||||
spin_unlock(chip->mutex);
|
||||
asm volatile (".rep 8; nop; .endr");
|
||||
cond_resched();
|
||||
|
||||
|
@ -919,15 +932,15 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
|
|||
* a suspended erase state. If so let's wait
|
||||
* until it's done.
|
||||
*/
|
||||
preempt_disable();
|
||||
spin_lock(chip->mutex);
|
||||
while (chip->state != newstate) {
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(&chip->wq, &wait);
|
||||
preempt_enable();
|
||||
spin_unlock(chip->mutex);
|
||||
schedule();
|
||||
remove_wait_queue(&chip->wq, &wait);
|
||||
preempt_disable();
|
||||
spin_lock(chip->mutex);
|
||||
}
|
||||
/* Disallow XIP again */
|
||||
local_irq_disable();
|
||||
|
@ -956,12 +969,14 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
|
|||
* The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while
|
||||
* the flash is actively programming or erasing since we have to poll for
|
||||
* the operation to complete anyway. We can't do that in a generic way with
|
||||
* a XIP setup so do it before the actual flash operation in this case.
|
||||
* a XIP setup so do it before the actual flash operation in this case
|
||||
* and stub it out from INVALIDATE_CACHE_UDELAY.
|
||||
*/
|
||||
#undef INVALIDATE_CACHED_RANGE
|
||||
#define INVALIDATE_CACHED_RANGE(x...)
|
||||
#define XIP_INVAL_CACHED_RANGE(map, from, size) \
|
||||
do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0)
|
||||
#define XIP_INVAL_CACHED_RANGE(map, from, size) \
|
||||
INVALIDATE_CACHED_RANGE(map, from, size)
|
||||
|
||||
#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \
|
||||
UDELAY(map, chip, adr, usec)
|
||||
|
||||
/*
|
||||
* Extra notes:
|
||||
|
@ -984,11 +999,23 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
|
|||
|
||||
#define xip_disable(map, chip, adr)
|
||||
#define xip_enable(map, chip, adr)
|
||||
|
||||
#define UDELAY(map, chip, adr, usec) cfi_udelay(usec)
|
||||
|
||||
#define XIP_INVAL_CACHED_RANGE(x...)
|
||||
|
||||
#define UDELAY(map, chip, adr, usec) \
|
||||
do { \
|
||||
spin_unlock(chip->mutex); \
|
||||
cfi_udelay(usec); \
|
||||
spin_lock(chip->mutex); \
|
||||
} while (0)
|
||||
|
||||
#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \
|
||||
do { \
|
||||
spin_unlock(chip->mutex); \
|
||||
INVALIDATE_CACHED_RANGE(map, adr, len); \
|
||||
cfi_udelay(usec); \
|
||||
spin_lock(chip->mutex); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len)
|
||||
|
@ -1176,111 +1203,11 @@ static int cfi_intelext_read (struct mtd_info *mtd, loff_t from, size_t len, siz
|
|||
return ret;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int __xipram cfi_intelext_read_prot_reg (struct mtd_info *mtd,
|
||||
loff_t from, size_t len,
|
||||
size_t *retlen,
|
||||
u_char *buf,
|
||||
int base_offst, int reg_sz)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
struct cfi_pri_intelext *extp = cfi->cmdset_priv;
|
||||
struct flchip *chip;
|
||||
int ofs_factor = cfi->interleave * cfi->device_type;
|
||||
int count = len;
|
||||
int chip_num, offst;
|
||||
int ret;
|
||||
|
||||
chip_num = ((unsigned int)from/reg_sz);
|
||||
offst = from - (reg_sz*chip_num)+base_offst;
|
||||
|
||||
while (count) {
|
||||
/* Calculate which chip & protection register offset we need */
|
||||
|
||||
if (chip_num >= cfi->numchips)
|
||||
goto out;
|
||||
|
||||
chip = &cfi->chips[chip_num];
|
||||
|
||||
spin_lock(chip->mutex);
|
||||
ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY);
|
||||
if (ret) {
|
||||
spin_unlock(chip->mutex);
|
||||
return (len-count)?:ret;
|
||||
}
|
||||
|
||||
xip_disable(map, chip, chip->start);
|
||||
|
||||
if (chip->state != FL_JEDEC_QUERY) {
|
||||
map_write(map, CMD(0x90), chip->start);
|
||||
chip->state = FL_JEDEC_QUERY;
|
||||
}
|
||||
|
||||
while (count && ((offst-base_offst) < reg_sz)) {
|
||||
*buf = map_read8(map,(chip->start+((extp->ProtRegAddr+1)*ofs_factor)+offst));
|
||||
buf++;
|
||||
offst++;
|
||||
count--;
|
||||
}
|
||||
|
||||
xip_enable(map, chip, chip->start);
|
||||
put_chip(map, chip, chip->start);
|
||||
spin_unlock(chip->mutex);
|
||||
|
||||
/* Move on to the next chip */
|
||||
chip_num++;
|
||||
offst = base_offst;
|
||||
}
|
||||
|
||||
out:
|
||||
return len-count;
|
||||
}
|
||||
|
||||
static int cfi_intelext_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
struct cfi_pri_intelext *extp=cfi->cmdset_priv;
|
||||
int base_offst,reg_sz;
|
||||
|
||||
/* Check that we actually have some protection registers */
|
||||
if(!extp || !(extp->FeatureSupport&64)){
|
||||
printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
base_offst=(1<<extp->FactProtRegSize);
|
||||
reg_sz=(1<<extp->UserProtRegSize);
|
||||
|
||||
return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz);
|
||||
}
|
||||
|
||||
static int cfi_intelext_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
struct cfi_pri_intelext *extp=cfi->cmdset_priv;
|
||||
int base_offst,reg_sz;
|
||||
|
||||
/* Check that we actually have some protection registers */
|
||||
if(!extp || !(extp->FeatureSupport&64)){
|
||||
printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
base_offst=0;
|
||||
reg_sz=(1<<extp->FactProtRegSize);
|
||||
|
||||
return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
|
||||
unsigned long adr, map_word datum)
|
||||
unsigned long adr, map_word datum, int mode)
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
map_word status, status_OK;
|
||||
map_word status, status_OK, write_cmd;
|
||||
unsigned long timeo;
|
||||
int z, ret=0;
|
||||
|
||||
|
@ -1288,9 +1215,14 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
|
|||
|
||||
/* Let's determine this according to the interleave only once */
|
||||
status_OK = CMD(0x80);
|
||||
switch (mode) {
|
||||
case FL_WRITING: write_cmd = CMD(0x40); break;
|
||||
case FL_OTP_WRITE: write_cmd = CMD(0xc0); break;
|
||||
default: return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock(chip->mutex);
|
||||
ret = get_chip(map, chip, adr, FL_WRITING);
|
||||
ret = get_chip(map, chip, adr, mode);
|
||||
if (ret) {
|
||||
spin_unlock(chip->mutex);
|
||||
return ret;
|
||||
|
@ -1299,19 +1231,18 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
|
|||
XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map));
|
||||
ENABLE_VPP(map);
|
||||
xip_disable(map, chip, adr);
|
||||
map_write(map, CMD(0x40), adr);
|
||||
map_write(map, write_cmd, adr);
|
||||
map_write(map, datum, adr);
|
||||
chip->state = FL_WRITING;
|
||||
chip->state = mode;
|
||||
|
||||
spin_unlock(chip->mutex);
|
||||
INVALIDATE_CACHED_RANGE(map, adr, map_bankwidth(map));
|
||||
UDELAY(map, chip, adr, chip->word_write_time);
|
||||
spin_lock(chip->mutex);
|
||||
INVALIDATE_CACHE_UDELAY(map, chip,
|
||||
adr, map_bankwidth(map),
|
||||
chip->word_write_time);
|
||||
|
||||
timeo = jiffies + (HZ/2);
|
||||
z = 0;
|
||||
for (;;) {
|
||||
if (chip->state != FL_WRITING) {
|
||||
if (chip->state != mode) {
|
||||
/* Someone's suspended the write. Sleep */
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
||||
|
@ -1339,10 +1270,8 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
|
|||
}
|
||||
|
||||
/* Latency issues. Drop the lock, wait a while and retry */
|
||||
spin_unlock(chip->mutex);
|
||||
z++;
|
||||
UDELAY(map, chip, adr, 1);
|
||||
spin_lock(chip->mutex);
|
||||
}
|
||||
if (!z) {
|
||||
chip->word_write_time--;
|
||||
|
@ -1399,7 +1328,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le
|
|||
datum = map_word_load_partial(map, datum, buf, gap, n);
|
||||
|
||||
ret = do_write_oneword(map, &cfi->chips[chipnum],
|
||||
bus_ofs, datum);
|
||||
bus_ofs, datum, FL_WRITING);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -1420,7 +1349,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le
|
|||
map_word datum = map_word_load(map, buf);
|
||||
|
||||
ret = do_write_oneword(map, &cfi->chips[chipnum],
|
||||
ofs, datum);
|
||||
ofs, datum, FL_WRITING);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -1444,7 +1373,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le
|
|||
datum = map_word_load_partial(map, datum, buf, 0, len);
|
||||
|
||||
ret = do_write_oneword(map, &cfi->chips[chipnum],
|
||||
ofs, datum);
|
||||
ofs, datum, FL_WRITING);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -1506,9 +1435,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
|
|||
if (map_word_andequal(map, status, status_OK, status_OK))
|
||||
break;
|
||||
|
||||
spin_unlock(chip->mutex);
|
||||
UDELAY(map, chip, cmd_adr, 1);
|
||||
spin_lock(chip->mutex);
|
||||
|
||||
if (++z > 20) {
|
||||
/* Argh. Not ready for write to buffer */
|
||||
|
@ -1554,10 +1481,9 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
|
|||
map_write(map, CMD(0xd0), cmd_adr);
|
||||
chip->state = FL_WRITING;
|
||||
|
||||
spin_unlock(chip->mutex);
|
||||
INVALIDATE_CACHED_RANGE(map, adr, len);
|
||||
UDELAY(map, chip, cmd_adr, chip->buffer_write_time);
|
||||
spin_lock(chip->mutex);
|
||||
INVALIDATE_CACHE_UDELAY(map, chip,
|
||||
cmd_adr, len,
|
||||
chip->buffer_write_time);
|
||||
|
||||
timeo = jiffies + (HZ/2);
|
||||
z = 0;
|
||||
|
@ -1589,10 +1515,8 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
|
|||
}
|
||||
|
||||
/* Latency issues. Drop the lock, wait a while and retry */
|
||||
spin_unlock(chip->mutex);
|
||||
UDELAY(map, chip, cmd_adr, 1);
|
||||
z++;
|
||||
spin_lock(chip->mutex);
|
||||
UDELAY(map, chip, cmd_adr, 1);
|
||||
}
|
||||
if (!z) {
|
||||
chip->buffer_write_time--;
|
||||
|
@ -1720,10 +1644,9 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
|
|||
chip->state = FL_ERASING;
|
||||
chip->erase_suspended = 0;
|
||||
|
||||
spin_unlock(chip->mutex);
|
||||
INVALIDATE_CACHED_RANGE(map, adr, len);
|
||||
UDELAY(map, chip, adr, chip->erase_time*1000/2);
|
||||
spin_lock(chip->mutex);
|
||||
INVALIDATE_CACHE_UDELAY(map, chip,
|
||||
adr, len,
|
||||
chip->erase_time*1000/2);
|
||||
|
||||
/* FIXME. Use a timer to check this, and return immediately. */
|
||||
/* Once the state machine's known to be working I'll do that */
|
||||
|
@ -1768,9 +1691,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
|
|||
}
|
||||
|
||||
/* Latency issues. Drop the lock, wait a while and retry */
|
||||
spin_unlock(chip->mutex);
|
||||
UDELAY(map, chip, adr, 1000000/HZ);
|
||||
spin_lock(chip->mutex);
|
||||
}
|
||||
|
||||
/* We've broken this before. It doesn't hurt to be safe */
|
||||
|
@ -1780,44 +1701,34 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
|
|||
|
||||
/* check for lock bit */
|
||||
if (map_word_bitsset(map, status, CMD(0x3a))) {
|
||||
unsigned char chipstatus;
|
||||
unsigned long chipstatus;
|
||||
|
||||
/* Reset the error bits */
|
||||
map_write(map, CMD(0x50), adr);
|
||||
map_write(map, CMD(0x70), adr);
|
||||
xip_enable(map, chip, adr);
|
||||
|
||||
chipstatus = status.x[0];
|
||||
if (!map_word_equal(map, status, CMD(chipstatus))) {
|
||||
int i, w;
|
||||
for (w=0; w<map_words(map); w++) {
|
||||
for (i = 0; i<cfi_interleave(cfi); i++) {
|
||||
chipstatus |= status.x[w] >> (cfi->device_type * 8);
|
||||
}
|
||||
}
|
||||
printk(KERN_WARNING "Status is not identical for all chips: 0x%lx. Merging to give 0x%02x\n",
|
||||
status.x[0], chipstatus);
|
||||
}
|
||||
chipstatus = MERGESTATUS(status);
|
||||
|
||||
if ((chipstatus & 0x30) == 0x30) {
|
||||
printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", chipstatus);
|
||||
printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%lx\n", chipstatus);
|
||||
ret = -EIO;
|
||||
} else if (chipstatus & 0x02) {
|
||||
/* Protection bit set */
|
||||
ret = -EROFS;
|
||||
} else if (chipstatus & 0x8) {
|
||||
/* Voltage */
|
||||
printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", chipstatus);
|
||||
printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%lx\n", chipstatus);
|
||||
ret = -EIO;
|
||||
} else if (chipstatus & 0x20) {
|
||||
if (retries--) {
|
||||
printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus);
|
||||
printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%lx. Retrying...\n", adr, chipstatus);
|
||||
timeo = jiffies + HZ;
|
||||
put_chip(map, chip, adr);
|
||||
spin_unlock(chip->mutex);
|
||||
goto retry;
|
||||
}
|
||||
printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus);
|
||||
printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%lx\n", adr, chipstatus);
|
||||
ret = -EIO;
|
||||
}
|
||||
} else {
|
||||
|
@ -1882,6 +1793,7 @@ static void cfi_intelext_sync (struct mtd_info *mtd)
|
|||
|
||||
if (chip->state == FL_SYNCING) {
|
||||
chip->state = chip->oldstate;
|
||||
chip->oldstate = FL_READY;
|
||||
wake_up(&chip->wq);
|
||||
}
|
||||
spin_unlock(chip->mutex);
|
||||
|
@ -1897,8 +1809,9 @@ static int __xipram do_printlockstatus_oneblock(struct map_info *map,
|
|||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
int status, ofs_factor = cfi->interleave * cfi->device_type;
|
||||
|
||||
adr += chip->start;
|
||||
xip_disable(map, chip, adr+(2*ofs_factor));
|
||||
cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL);
|
||||
map_write(map, CMD(0x90), adr+(2*ofs_factor));
|
||||
chip->state = FL_JEDEC_QUERY;
|
||||
status = cfi_read_query(map, adr+(2*ofs_factor));
|
||||
xip_enable(map, chip, 0);
|
||||
|
@ -1915,6 +1828,7 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip
|
|||
unsigned long adr, int len, void *thunk)
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
struct cfi_pri_intelext *extp = cfi->cmdset_priv;
|
||||
map_word status, status_OK;
|
||||
unsigned long timeo = jiffies + HZ;
|
||||
int ret;
|
||||
|
@ -1944,9 +1858,13 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip
|
|||
} else
|
||||
BUG();
|
||||
|
||||
spin_unlock(chip->mutex);
|
||||
UDELAY(map, chip, adr, 1000000/HZ);
|
||||
spin_lock(chip->mutex);
|
||||
/*
|
||||
* If Instant Individual Block Locking supported then no need
|
||||
* to delay.
|
||||
*/
|
||||
|
||||
if (!extp || !(extp->FeatureSupport & (1 << 5)))
|
||||
UDELAY(map, chip, adr, 1000000/HZ);
|
||||
|
||||
/* FIXME. Use a timer to check this, and return immediately. */
|
||||
/* Once the state machine's known to be working I'll do that */
|
||||
|
@ -1973,9 +1891,7 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip
|
|||
}
|
||||
|
||||
/* Latency issues. Drop the lock, wait a while and retry */
|
||||
spin_unlock(chip->mutex);
|
||||
UDELAY(map, chip, adr, 1);
|
||||
spin_lock(chip->mutex);
|
||||
}
|
||||
|
||||
/* Done and happy. */
|
||||
|
@ -2034,6 +1950,274 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_OTP
|
||||
|
||||
typedef int (*otp_op_t)(struct map_info *map, struct flchip *chip,
|
||||
u_long data_offset, u_char *buf, u_int size,
|
||||
u_long prot_offset, u_int groupno, u_int groupsize);
|
||||
|
||||
static int __xipram
|
||||
do_otp_read(struct map_info *map, struct flchip *chip, u_long offset,
|
||||
u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz)
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
int ret;
|
||||
|
||||
spin_lock(chip->mutex);
|
||||
ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY);
|
||||
if (ret) {
|
||||
spin_unlock(chip->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* let's ensure we're not reading back cached data from array mode */
|
||||
INVALIDATE_CACHED_RANGE(map, chip->start + offset, size);
|
||||
|
||||
xip_disable(map, chip, chip->start);
|
||||
if (chip->state != FL_JEDEC_QUERY) {
|
||||
map_write(map, CMD(0x90), chip->start);
|
||||
chip->state = FL_JEDEC_QUERY;
|
||||
}
|
||||
map_copy_from(map, buf, chip->start + offset, size);
|
||||
xip_enable(map, chip, chip->start);
|
||||
|
||||
/* then ensure we don't keep OTP data in the cache */
|
||||
INVALIDATE_CACHED_RANGE(map, chip->start + offset, size);
|
||||
|
||||
put_chip(map, chip, chip->start);
|
||||
spin_unlock(chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
do_otp_write(struct map_info *map, struct flchip *chip, u_long offset,
|
||||
u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz)
|
||||
{
|
||||
int ret;
|
||||
|
||||
while (size) {
|
||||
unsigned long bus_ofs = offset & ~(map_bankwidth(map)-1);
|
||||
int gap = offset - bus_ofs;
|
||||
int n = min_t(int, size, map_bankwidth(map)-gap);
|
||||
map_word datum = map_word_ff(map);
|
||||
|
||||
datum = map_word_load_partial(map, datum, buf, gap, n);
|
||||
ret = do_write_oneword(map, chip, bus_ofs, datum, FL_OTP_WRITE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
offset += n;
|
||||
buf += n;
|
||||
size -= n;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
do_otp_lock(struct map_info *map, struct flchip *chip, u_long offset,
|
||||
u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz)
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
map_word datum;
|
||||
|
||||
/* make sure area matches group boundaries */
|
||||
if (size != grpsz)
|
||||
return -EXDEV;
|
||||
|
||||
datum = map_word_ff(map);
|
||||
datum = map_word_clr(map, datum, CMD(1 << grpno));
|
||||
return do_write_oneword(map, chip, prot, datum, FL_OTP_WRITE);
|
||||
}
|
||||
|
||||
static int cfi_intelext_otp_walk(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf,
|
||||
otp_op_t action, int user_regs)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
struct cfi_pri_intelext *extp = cfi->cmdset_priv;
|
||||
struct flchip *chip;
|
||||
struct cfi_intelext_otpinfo *otp;
|
||||
u_long devsize, reg_prot_offset, data_offset;
|
||||
u_int chip_num, chip_step, field, reg_fact_size, reg_user_size;
|
||||
u_int groups, groupno, groupsize, reg_fact_groups, reg_user_groups;
|
||||
int ret;
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
/* Check that we actually have some OTP registers */
|
||||
if (!extp || !(extp->FeatureSupport & 64) || !extp->NumProtectionFields)
|
||||
return -ENODATA;
|
||||
|
||||
/* we need real chips here not virtual ones */
|
||||
devsize = (1 << cfi->cfiq->DevSize) * cfi->interleave;
|
||||
chip_step = devsize >> cfi->chipshift;
|
||||
chip_num = 0;
|
||||
|
||||
/* Some chips have OTP located in the _top_ partition only.
|
||||
For example: Intel 28F256L18T (T means top-parameter device) */
|
||||
if (cfi->mfr == MANUFACTURER_INTEL) {
|
||||
switch (cfi->id) {
|
||||
case 0x880b:
|
||||
case 0x880c:
|
||||
case 0x880d:
|
||||
chip_num = chip_step - 1;
|
||||
}
|
||||
}
|
||||
|
||||
for ( ; chip_num < cfi->numchips; chip_num += chip_step) {
|
||||
chip = &cfi->chips[chip_num];
|
||||
otp = (struct cfi_intelext_otpinfo *)&extp->extra[0];
|
||||
|
||||
/* first OTP region */
|
||||
field = 0;
|
||||
reg_prot_offset = extp->ProtRegAddr;
|
||||
reg_fact_groups = 1;
|
||||
reg_fact_size = 1 << extp->FactProtRegSize;
|
||||
reg_user_groups = 1;
|
||||
reg_user_size = 1 << extp->UserProtRegSize;
|
||||
|
||||
while (len > 0) {
|
||||
/* flash geometry fixup */
|
||||
data_offset = reg_prot_offset + 1;
|
||||
data_offset *= cfi->interleave * cfi->device_type;
|
||||
reg_prot_offset *= cfi->interleave * cfi->device_type;
|
||||
reg_fact_size *= cfi->interleave;
|
||||
reg_user_size *= cfi->interleave;
|
||||
|
||||
if (user_regs) {
|
||||
groups = reg_user_groups;
|
||||
groupsize = reg_user_size;
|
||||
/* skip over factory reg area */
|
||||
groupno = reg_fact_groups;
|
||||
data_offset += reg_fact_groups * reg_fact_size;
|
||||
} else {
|
||||
groups = reg_fact_groups;
|
||||
groupsize = reg_fact_size;
|
||||
groupno = 0;
|
||||
}
|
||||
|
||||
while (len > 0 && groups > 0) {
|
||||
if (!action) {
|
||||
/*
|
||||
* Special case: if action is NULL
|
||||
* we fill buf with otp_info records.
|
||||
*/
|
||||
struct otp_info *otpinfo;
|
||||
map_word lockword;
|
||||
len -= sizeof(struct otp_info);
|
||||
if (len <= 0)
|
||||
return -ENOSPC;
|
||||
ret = do_otp_read(map, chip,
|
||||
reg_prot_offset,
|
||||
(u_char *)&lockword,
|
||||
map_bankwidth(map),
|
||||
0, 0, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
otpinfo = (struct otp_info *)buf;
|
||||
otpinfo->start = from;
|
||||
otpinfo->length = groupsize;
|
||||
otpinfo->locked =
|
||||
!map_word_bitsset(map, lockword,
|
||||
CMD(1 << groupno));
|
||||
from += groupsize;
|
||||
buf += sizeof(*otpinfo);
|
||||
*retlen += sizeof(*otpinfo);
|
||||
} else if (from >= groupsize) {
|
||||
from -= groupsize;
|
||||
data_offset += groupsize;
|
||||
} else {
|
||||
int size = groupsize;
|
||||
data_offset += from;
|
||||
size -= from;
|
||||
from = 0;
|
||||
if (size > len)
|
||||
size = len;
|
||||
ret = action(map, chip, data_offset,
|
||||
buf, size, reg_prot_offset,
|
||||
groupno, groupsize);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
buf += size;
|
||||
len -= size;
|
||||
*retlen += size;
|
||||
data_offset += size;
|
||||
}
|
||||
groupno++;
|
||||
groups--;
|
||||
}
|
||||
|
||||
/* next OTP region */
|
||||
if (++field == extp->NumProtectionFields)
|
||||
break;
|
||||
reg_prot_offset = otp->ProtRegAddr;
|
||||
reg_fact_groups = otp->FactGroups;
|
||||
reg_fact_size = 1 << otp->FactProtRegSize;
|
||||
reg_user_groups = otp->UserGroups;
|
||||
reg_user_size = 1 << otp->UserProtRegSize;
|
||||
otp++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cfi_intelext_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
|
||||
size_t len, size_t *retlen,
|
||||
u_char *buf)
|
||||
{
|
||||
return cfi_intelext_otp_walk(mtd, from, len, retlen,
|
||||
buf, do_otp_read, 0);
|
||||
}
|
||||
|
||||
static int cfi_intelext_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
|
||||
size_t len, size_t *retlen,
|
||||
u_char *buf)
|
||||
{
|
||||
return cfi_intelext_otp_walk(mtd, from, len, retlen,
|
||||
buf, do_otp_read, 1);
|
||||
}
|
||||
|
||||
static int cfi_intelext_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
|
||||
size_t len, size_t *retlen,
|
||||
u_char *buf)
|
||||
{
|
||||
return cfi_intelext_otp_walk(mtd, from, len, retlen,
|
||||
buf, do_otp_write, 1);
|
||||
}
|
||||
|
||||
static int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd,
|
||||
loff_t from, size_t len)
|
||||
{
|
||||
size_t retlen;
|
||||
return cfi_intelext_otp_walk(mtd, from, len, &retlen,
|
||||
NULL, do_otp_lock, 1);
|
||||
}
|
||||
|
||||
static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd,
|
||||
struct otp_info *buf, size_t len)
|
||||
{
|
||||
size_t retlen;
|
||||
int ret;
|
||||
|
||||
ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 0);
|
||||
return ret ? : retlen;
|
||||
}
|
||||
|
||||
static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd,
|
||||
struct otp_info *buf, size_t len)
|
||||
{
|
||||
size_t retlen;
|
||||
int ret;
|
||||
|
||||
ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 1);
|
||||
return ret ? : retlen;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int cfi_intelext_suspend(struct mtd_info *mtd)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
|
@ -2125,10 +2309,46 @@ static void cfi_intelext_resume(struct mtd_info *mtd)
|
|||
}
|
||||
}
|
||||
|
||||
static int cfi_intelext_reset(struct mtd_info *mtd)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
int i, ret;
|
||||
|
||||
for (i=0; i < cfi->numchips; i++) {
|
||||
struct flchip *chip = &cfi->chips[i];
|
||||
|
||||
/* force the completion of any ongoing operation
|
||||
and switch to array mode so any bootloader in
|
||||
flash is accessible for soft reboot. */
|
||||
spin_lock(chip->mutex);
|
||||
ret = get_chip(map, chip, chip->start, FL_SYNCING);
|
||||
if (!ret) {
|
||||
map_write(map, CMD(0xff), chip->start);
|
||||
chip->state = FL_READY;
|
||||
}
|
||||
spin_unlock(chip->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cfi_intelext_reboot(struct notifier_block *nb, unsigned long val,
|
||||
void *v)
|
||||
{
|
||||
struct mtd_info *mtd;
|
||||
|
||||
mtd = container_of(nb, struct mtd_info, reboot_notifier);
|
||||
cfi_intelext_reset(mtd);
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static void cfi_intelext_destroy(struct mtd_info *mtd)
|
||||
{
|
||||
struct map_info *map = mtd->priv;
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
cfi_intelext_reset(mtd);
|
||||
unregister_reboot_notifier(&mtd->reboot_notifier);
|
||||
kfree(cfi->cmdset_priv);
|
||||
kfree(cfi->cfiq);
|
||||
kfree(cfi->chips[0].priv);
|
||||
|
|
|
@ -4,16 +4,20 @@
|
|||
*
|
||||
* Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp>
|
||||
* Copyright (C) 2004 Arcom Control Systems Ltd <linux@arcom.com>
|
||||
* Copyright (C) 2005 MontaVista Software Inc. <source@mvista.com>
|
||||
*
|
||||
* 2_by_8 routines added by Simon Munton
|
||||
*
|
||||
* 4_by_16 work by Carolyn J. Smith
|
||||
*
|
||||
* XIP support hooks by Vitaly Wool (based on code for Intel flash
|
||||
* by Nicolas Pitre)
|
||||
*
|
||||
* Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com
|
||||
*
|
||||
* This code is GPL
|
||||
*
|
||||
* $Id: cfi_cmdset_0002.c,v 1.114 2004/12/11 15:43:53 dedekind Exp $
|
||||
* $Id: cfi_cmdset_0002.c,v 1.118 2005/07/04 22:34:29 gleixner Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -34,6 +38,7 @@
|
|||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/cfi.h>
|
||||
#include <linux/mtd/xip.h>
|
||||
|
||||
#define AMD_BOOTLOC_BUG
|
||||
#define FORCE_WORD_WRITE 0
|
||||
|
@ -43,6 +48,7 @@
|
|||
#define MANUFACTURER_AMD 0x0001
|
||||
#define MANUFACTURER_SST 0x00BF
|
||||
#define SST49LF004B 0x0060
|
||||
#define SST49LF008A 0x005a
|
||||
|
||||
static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
|
||||
static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
|
||||
|
@ -191,6 +197,7 @@ static struct cfi_fixup cfi_fixup_table[] = {
|
|||
};
|
||||
static struct cfi_fixup jedec_fixup_table[] = {
|
||||
{ MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, },
|
||||
{ MANUFACTURER_SST, SST49LF008A, fixup_use_fwh_lock, NULL, },
|
||||
{ 0, 0, NULL, NULL }
|
||||
};
|
||||
|
||||
|
@ -391,7 +398,7 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
|
|||
* correctly and is therefore not done (particulary with interleaved chips
|
||||
* as each chip must be checked independantly of the others).
|
||||
*/
|
||||
static int chip_ready(struct map_info *map, unsigned long addr)
|
||||
static int __xipram chip_ready(struct map_info *map, unsigned long addr)
|
||||
{
|
||||
map_word d, t;
|
||||
|
||||
|
@ -401,6 +408,32 @@ static int chip_ready(struct map_info *map, unsigned long addr)
|
|||
return map_word_equal(map, d, t);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if the chip is ready and has the correct value.
|
||||
*
|
||||
* Ready is one of: read mode, query mode, erase-suspend-read mode (in any
|
||||
* non-suspended sector) and it is indicated by no bits toggling.
|
||||
*
|
||||
* Error are indicated by toggling bits or bits held with the wrong value,
|
||||
* or with bits toggling.
|
||||
*
|
||||
* Note that anything more complicated than checking if no bits are toggling
|
||||
* (including checking DQ5 for an error status) is tricky to get working
|
||||
* correctly and is therefore not done (particulary with interleaved chips
|
||||
* as each chip must be checked independantly of the others).
|
||||
*
|
||||
*/
|
||||
static int __xipram chip_good(struct map_info *map, unsigned long addr, map_word expected)
|
||||
{
|
||||
map_word oldd, curd;
|
||||
|
||||
oldd = map_read(map, addr);
|
||||
curd = map_read(map, addr);
|
||||
|
||||
return map_word_equal(map, oldd, curd) &&
|
||||
map_word_equal(map, curd, expected);
|
||||
}
|
||||
|
||||
static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
|
||||
{
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
|
@ -420,12 +453,12 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
|
|||
|
||||
if (time_after(jiffies, timeo)) {
|
||||
printk(KERN_ERR "Waiting for chip to be ready timed out.\n");
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
return -EIO;
|
||||
}
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
cfi_udelay(1);
|
||||
cfi_spin_lock(chip->mutex);
|
||||
spin_lock(chip->mutex);
|
||||
/* Someone else might have been playing with it. */
|
||||
goto retry;
|
||||
}
|
||||
|
@ -473,15 +506,23 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
cfi_udelay(1);
|
||||
cfi_spin_lock(chip->mutex);
|
||||
spin_lock(chip->mutex);
|
||||
/* Nobody will touch it while it's in state FL_ERASE_SUSPENDING.
|
||||
So we can just loop here. */
|
||||
}
|
||||
chip->state = FL_READY;
|
||||
return 0;
|
||||
|
||||
case FL_XIP_WHILE_ERASING:
|
||||
if (mode != FL_READY && mode != FL_POINT &&
|
||||
(!cfip || !(cfip->EraseSuspend&2)))
|
||||
goto sleep;
|
||||
chip->oldstate = chip->state;
|
||||
chip->state = FL_READY;
|
||||
return 0;
|
||||
|
||||
case FL_POINT:
|
||||
/* Only if there's no operation suspended... */
|
||||
if (mode == FL_READY && chip->oldstate == FL_READY)
|
||||
|
@ -491,10 +532,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr
|
|||
sleep:
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(&chip->wq, &wait);
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
schedule();
|
||||
remove_wait_queue(&chip->wq, &wait);
|
||||
cfi_spin_lock(chip->mutex);
|
||||
spin_lock(chip->mutex);
|
||||
goto resettime;
|
||||
}
|
||||
}
|
||||
|
@ -512,6 +553,11 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
|
|||
chip->state = FL_ERASING;
|
||||
break;
|
||||
|
||||
case FL_XIP_WHILE_ERASING:
|
||||
chip->state = chip->oldstate;
|
||||
chip->oldstate = FL_READY;
|
||||
break;
|
||||
|
||||
case FL_READY:
|
||||
case FL_STATUS:
|
||||
/* We should really make set_vpp() count, rather than doing this */
|
||||
|
@ -523,6 +569,198 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad
|
|||
wake_up(&chip->wq);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_XIP
|
||||
|
||||
/*
|
||||
* No interrupt what so ever can be serviced while the flash isn't in array
|
||||
* mode. This is ensured by the xip_disable() and xip_enable() functions
|
||||
* enclosing any code path where the flash is known not to be in array mode.
|
||||
* And within a XIP disabled code path, only functions marked with __xipram
|
||||
* may be called and nothing else (it's a good thing to inspect generated
|
||||
* assembly to make sure inline functions were actually inlined and that gcc
|
||||
* didn't emit calls to its own support functions). Also configuring MTD CFI
|
||||
* support to a single buswidth and a single interleave is also recommended.
|
||||
*/
|
||||
|
||||
static void xip_disable(struct map_info *map, struct flchip *chip,
|
||||
unsigned long adr)
|
||||
{
|
||||
/* TODO: chips with no XIP use should ignore and return */
|
||||
(void) map_read(map, adr); /* ensure mmu mapping is up to date */
|
||||
local_irq_disable();
|
||||
}
|
||||
|
||||
static void __xipram xip_enable(struct map_info *map, struct flchip *chip,
|
||||
unsigned long adr)
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
|
||||
if (chip->state != FL_POINT && chip->state != FL_READY) {
|
||||
map_write(map, CMD(0xf0), adr);
|
||||
chip->state = FL_READY;
|
||||
}
|
||||
(void) map_read(map, adr);
|
||||
xip_iprefetch();
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
/*
|
||||
* When a delay is required for the flash operation to complete, the
|
||||
* xip_udelay() function is polling for both the given timeout and pending
|
||||
* (but still masked) hardware interrupts. Whenever there is an interrupt
|
||||
* pending then the flash erase operation is suspended, array mode restored
|
||||
* and interrupts unmasked. Task scheduling might also happen at that
|
||||
* point. The CPU eventually returns from the interrupt or the call to
|
||||
* schedule() and the suspended flash operation is resumed for the remaining
|
||||
* of the delay period.
|
||||
*
|
||||
* Warning: this function _will_ fool interrupt latency tracing tools.
|
||||
*/
|
||||
|
||||
static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
|
||||
unsigned long adr, int usec)
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
|
||||
map_word status, OK = CMD(0x80);
|
||||
unsigned long suspended, start = xip_currtime();
|
||||
flstate_t oldstate;
|
||||
|
||||
do {
|
||||
cpu_relax();
|
||||
if (xip_irqpending() && extp &&
|
||||
((chip->state == FL_ERASING && (extp->EraseSuspend & 2))) &&
|
||||
(cfi_interleave_is_1(cfi) || chip->oldstate == FL_READY)) {
|
||||
/*
|
||||
* Let's suspend the erase operation when supported.
|
||||
* Note that we currently don't try to suspend
|
||||
* interleaved chips if there is already another
|
||||
* operation suspended (imagine what happens
|
||||
* when one chip was already done with the current
|
||||
* operation while another chip suspended it, then
|
||||
* we resume the whole thing at once). Yes, it
|
||||
* can happen!
|
||||
*/
|
||||
map_write(map, CMD(0xb0), adr);
|
||||
usec -= xip_elapsed_since(start);
|
||||
suspended = xip_currtime();
|
||||
do {
|
||||
if (xip_elapsed_since(suspended) > 100000) {
|
||||
/*
|
||||
* The chip doesn't want to suspend
|
||||
* after waiting for 100 msecs.
|
||||
* This is a critical error but there
|
||||
* is not much we can do here.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
status = map_read(map, adr);
|
||||
} while (!map_word_andequal(map, status, OK, OK));
|
||||
|
||||
/* Suspend succeeded */
|
||||
oldstate = chip->state;
|
||||
if (!map_word_bitsset(map, status, CMD(0x40)))
|
||||
break;
|
||||
chip->state = FL_XIP_WHILE_ERASING;
|
||||
chip->erase_suspended = 1;
|
||||
map_write(map, CMD(0xf0), adr);
|
||||
(void) map_read(map, adr);
|
||||
asm volatile (".rep 8; nop; .endr");
|
||||
local_irq_enable();
|
||||
spin_unlock(chip->mutex);
|
||||
asm volatile (".rep 8; nop; .endr");
|
||||
cond_resched();
|
||||
|
||||
/*
|
||||
* We're back. However someone else might have
|
||||
* decided to go write to the chip if we are in
|
||||
* a suspended erase state. If so let's wait
|
||||
* until it's done.
|
||||
*/
|
||||
spin_lock(chip->mutex);
|
||||
while (chip->state != FL_XIP_WHILE_ERASING) {
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(&chip->wq, &wait);
|
||||
spin_unlock(chip->mutex);
|
||||
schedule();
|
||||
remove_wait_queue(&chip->wq, &wait);
|
||||
spin_lock(chip->mutex);
|
||||
}
|
||||
/* Disallow XIP again */
|
||||
local_irq_disable();
|
||||
|
||||
/* Resume the write or erase operation */
|
||||
map_write(map, CMD(0x30), adr);
|
||||
chip->state = oldstate;
|
||||
start = xip_currtime();
|
||||
} else if (usec >= 1000000/HZ) {
|
||||
/*
|
||||
* Try to save on CPU power when waiting delay
|
||||
* is at least a system timer tick period.
|
||||
* No need to be extremely accurate here.
|
||||
*/
|
||||
xip_cpu_idle();
|
||||
}
|
||||
status = map_read(map, adr);
|
||||
} while (!map_word_andequal(map, status, OK, OK)
|
||||
&& xip_elapsed_since(start) < usec);
|
||||
}
|
||||
|
||||
#define UDELAY(map, chip, adr, usec) xip_udelay(map, chip, adr, usec)
|
||||
|
||||
/*
|
||||
* The INVALIDATE_CACHED_RANGE() macro is normally used in parallel while
|
||||
* the flash is actively programming or erasing since we have to poll for
|
||||
* the operation to complete anyway. We can't do that in a generic way with
|
||||
* a XIP setup so do it before the actual flash operation in this case
|
||||
* and stub it out from INVALIDATE_CACHE_UDELAY.
|
||||
*/
|
||||
#define XIP_INVAL_CACHED_RANGE(map, from, size) \
|
||||
INVALIDATE_CACHED_RANGE(map, from, size)
|
||||
|
||||
#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \
|
||||
UDELAY(map, chip, adr, usec)
|
||||
|
||||
/*
|
||||
* Extra notes:
|
||||
*
|
||||
* Activating this XIP support changes the way the code works a bit. For
|
||||
* example the code to suspend the current process when concurrent access
|
||||
* happens is never executed because xip_udelay() will always return with the
|
||||
* same chip state as it was entered with. This is why there is no care for
|
||||
* the presence of add_wait_queue() or schedule() calls from within a couple
|
||||
* xip_disable()'d areas of code, like in do_erase_oneblock for example.
|
||||
* The queueing and scheduling are always happening within xip_udelay().
|
||||
*
|
||||
* Similarly, get_chip() and put_chip() just happen to always be executed
|
||||
* with chip->state set to FL_READY (or FL_XIP_WHILE_*) where flash state
|
||||
* is in array mode, therefore never executing many cases therein and not
|
||||
* causing any problem with XIP.
|
||||
*/
|
||||
|
||||
#else
|
||||
|
||||
#define xip_disable(map, chip, adr)
|
||||
#define xip_enable(map, chip, adr)
|
||||
#define XIP_INVAL_CACHED_RANGE(x...)
|
||||
|
||||
#define UDELAY(map, chip, adr, usec) \
|
||||
do { \
|
||||
spin_unlock(chip->mutex); \
|
||||
cfi_udelay(usec); \
|
||||
spin_lock(chip->mutex); \
|
||||
} while (0)
|
||||
|
||||
#define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \
|
||||
do { \
|
||||
spin_unlock(chip->mutex); \
|
||||
INVALIDATE_CACHED_RANGE(map, adr, len); \
|
||||
cfi_udelay(usec); \
|
||||
spin_lock(chip->mutex); \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
|
||||
{
|
||||
|
@ -535,10 +773,10 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof
|
|||
/* Ensure cmd read/writes are aligned. */
|
||||
cmd_addr = adr & ~(map_bankwidth(map)-1);
|
||||
|
||||
cfi_spin_lock(chip->mutex);
|
||||
spin_lock(chip->mutex);
|
||||
ret = get_chip(map, chip, cmd_addr, FL_READY);
|
||||
if (ret) {
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -551,7 +789,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof
|
|||
|
||||
put_chip(map, chip, cmd_addr);
|
||||
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -605,7 +843,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
|
|||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
|
||||
retry:
|
||||
cfi_spin_lock(chip->mutex);
|
||||
spin_lock(chip->mutex);
|
||||
|
||||
if (chip->state != FL_READY){
|
||||
#if 0
|
||||
|
@ -614,7 +852,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
|
|||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(&chip->wq, &wait);
|
||||
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
|
||||
schedule();
|
||||
remove_wait_queue(&chip->wq, &wait);
|
||||
|
@ -643,7 +881,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi
|
|||
cfi_send_gen_cmd(0x00, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
|
||||
wake_up(&chip->wq);
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -692,7 +930,7 @@ static int cfi_amdstd_secsi_read (struct mtd_info *mtd, loff_t from, size_t len,
|
|||
}
|
||||
|
||||
|
||||
static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum)
|
||||
static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum)
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
unsigned long timeo = jiffies + HZ;
|
||||
|
@ -712,10 +950,10 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
|
|||
|
||||
adr += chip->start;
|
||||
|
||||
cfi_spin_lock(chip->mutex);
|
||||
spin_lock(chip->mutex);
|
||||
ret = get_chip(map, chip, adr, FL_WRITING);
|
||||
if (ret) {
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -735,7 +973,9 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
|
|||
goto op_done;
|
||||
}
|
||||
|
||||
XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map));
|
||||
ENABLE_VPP(map);
|
||||
xip_disable(map, chip, adr);
|
||||
retry:
|
||||
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
|
@ -743,9 +983,9 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
|
|||
map_write(map, datum, adr);
|
||||
chip->state = FL_WRITING;
|
||||
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
cfi_udelay(chip->word_write_time);
|
||||
cfi_spin_lock(chip->mutex);
|
||||
INVALIDATE_CACHE_UDELAY(map, chip,
|
||||
adr, map_bankwidth(map),
|
||||
chip->word_write_time);
|
||||
|
||||
/* See comment above for timeout value. */
|
||||
timeo = jiffies + uWriteTimeout;
|
||||
|
@ -756,39 +996,43 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned
|
|||
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(&chip->wq, &wait);
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
schedule();
|
||||
remove_wait_queue(&chip->wq, &wait);
|
||||
timeo = jiffies + (HZ / 2); /* FIXME */
|
||||
cfi_spin_lock(chip->mutex);
|
||||
spin_lock(chip->mutex);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chip_ready(map, adr))
|
||||
goto op_done;
|
||||
break;
|
||||
|
||||
if (time_after(jiffies, timeo))
|
||||
if (time_after(jiffies, timeo)) {
|
||||
xip_enable(map, chip, adr);
|
||||
printk(KERN_WARNING "MTD %s(): software timeout\n", __func__);
|
||||
xip_disable(map, chip, adr);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Latency issues. Drop the lock, wait a while and retry */
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
cfi_udelay(1);
|
||||
cfi_spin_lock(chip->mutex);
|
||||
UDELAY(map, chip, adr, 1);
|
||||
}
|
||||
/* Did we succeed? */
|
||||
if (!chip_good(map, adr, datum)) {
|
||||
/* reset on all failures. */
|
||||
map_write( map, CMD(0xF0), chip->start );
|
||||
/* FIXME - should have reset delay before continuing */
|
||||
|
||||
printk(KERN_WARNING "MTD %s(): software timeout\n", __func__);
|
||||
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:
|
||||
chip->state = FL_READY;
|
||||
put_chip(map, chip, adr);
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -820,7 +1064,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
map_word tmp_buf;
|
||||
|
||||
retry:
|
||||
cfi_spin_lock(cfi->chips[chipnum].mutex);
|
||||
spin_lock(cfi->chips[chipnum].mutex);
|
||||
|
||||
if (cfi->chips[chipnum].state != FL_READY) {
|
||||
#if 0
|
||||
|
@ -829,7 +1073,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(&cfi->chips[chipnum].wq, &wait);
|
||||
|
||||
cfi_spin_unlock(cfi->chips[chipnum].mutex);
|
||||
spin_unlock(cfi->chips[chipnum].mutex);
|
||||
|
||||
schedule();
|
||||
remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
|
||||
|
@ -843,7 +1087,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
/* Load 'tmp_buf' with old contents of flash */
|
||||
tmp_buf = map_read(map, bus_ofs+chipstart);
|
||||
|
||||
cfi_spin_unlock(cfi->chips[chipnum].mutex);
|
||||
spin_unlock(cfi->chips[chipnum].mutex);
|
||||
|
||||
/* Number of bytes to copy from buffer */
|
||||
n = min_t(int, len, map_bankwidth(map)-i);
|
||||
|
@ -898,7 +1142,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
map_word tmp_buf;
|
||||
|
||||
retry1:
|
||||
cfi_spin_lock(cfi->chips[chipnum].mutex);
|
||||
spin_lock(cfi->chips[chipnum].mutex);
|
||||
|
||||
if (cfi->chips[chipnum].state != FL_READY) {
|
||||
#if 0
|
||||
|
@ -907,7 +1151,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(&cfi->chips[chipnum].wq, &wait);
|
||||
|
||||
cfi_spin_unlock(cfi->chips[chipnum].mutex);
|
||||
spin_unlock(cfi->chips[chipnum].mutex);
|
||||
|
||||
schedule();
|
||||
remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
|
||||
|
@ -920,7 +1164,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
|
||||
tmp_buf = map_read(map, ofs + chipstart);
|
||||
|
||||
cfi_spin_unlock(cfi->chips[chipnum].mutex);
|
||||
spin_unlock(cfi->chips[chipnum].mutex);
|
||||
|
||||
tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len);
|
||||
|
||||
|
@ -939,8 +1183,9 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
/*
|
||||
* FIXME: interleaved mode not tested, and probably not supported!
|
||||
*/
|
||||
static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
|
||||
unsigned long adr, const u_char *buf, int len)
|
||||
static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
|
||||
unsigned long adr, const u_char *buf,
|
||||
int len)
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
unsigned long timeo = jiffies + HZ;
|
||||
|
@ -954,10 +1199,10 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
|
|||
adr += chip->start;
|
||||
cmd_adr = adr;
|
||||
|
||||
cfi_spin_lock(chip->mutex);
|
||||
spin_lock(chip->mutex);
|
||||
ret = get_chip(map, chip, adr, FL_WRITING);
|
||||
if (ret) {
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -966,7 +1211,10 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
|
|||
DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
|
||||
__func__, adr, datum.x[0] );
|
||||
|
||||
XIP_INVAL_CACHED_RANGE(map, adr, len);
|
||||
ENABLE_VPP(map);
|
||||
xip_disable(map, chip, cmd_adr);
|
||||
|
||||
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
//cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
|
@ -996,9 +1244,9 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
|
|||
map_write(map, CMD(0x29), cmd_adr);
|
||||
chip->state = FL_WRITING;
|
||||
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
cfi_udelay(chip->buffer_write_time);
|
||||
cfi_spin_lock(chip->mutex);
|
||||
INVALIDATE_CACHE_UDELAY(map, chip,
|
||||
adr, map_bankwidth(map),
|
||||
chip->word_write_time);
|
||||
|
||||
timeo = jiffies + uWriteTimeout;
|
||||
|
||||
|
@ -1009,38 +1257,39 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip,
|
|||
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(&chip->wq, &wait);
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
schedule();
|
||||
remove_wait_queue(&chip->wq, &wait);
|
||||
timeo = jiffies + (HZ / 2); /* FIXME */
|
||||
cfi_spin_lock(chip->mutex);
|
||||
spin_lock(chip->mutex);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chip_ready(map, adr))
|
||||
if (chip_ready(map, adr)) {
|
||||
xip_enable(map, chip, adr);
|
||||
goto op_done;
|
||||
}
|
||||
|
||||
if( time_after(jiffies, timeo))
|
||||
break;
|
||||
|
||||
/* Latency issues. Drop the lock, wait a while and retry */
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
cfi_udelay(1);
|
||||
cfi_spin_lock(chip->mutex);
|
||||
UDELAY(map, chip, adr, 1);
|
||||
}
|
||||
|
||||
printk(KERN_WARNING "MTD %s(): software timeout\n",
|
||||
__func__ );
|
||||
|
||||
/* reset on all failures. */
|
||||
map_write( map, CMD(0xF0), chip->start );
|
||||
xip_enable(map, chip, adr);
|
||||
/* FIXME - should have reset delay before continuing */
|
||||
|
||||
printk(KERN_WARNING "MTD %s(): software timeout\n",
|
||||
__func__ );
|
||||
|
||||
ret = -EIO;
|
||||
op_done:
|
||||
chip->state = FL_READY;
|
||||
put_chip(map, chip, adr);
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1130,7 +1379,7 @@ static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
* Handle devices with one erase region, that only implement
|
||||
* the chip erase command.
|
||||
*/
|
||||
static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
|
||||
static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip)
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
unsigned long timeo = jiffies + HZ;
|
||||
|
@ -1140,17 +1389,20 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
|
|||
|
||||
adr = cfi->addr_unlock1;
|
||||
|
||||
cfi_spin_lock(chip->mutex);
|
||||
spin_lock(chip->mutex);
|
||||
ret = get_chip(map, chip, adr, FL_WRITING);
|
||||
if (ret) {
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
|
||||
__func__, chip->start );
|
||||
|
||||
XIP_INVAL_CACHED_RANGE(map, adr, map->size);
|
||||
ENABLE_VPP(map);
|
||||
xip_disable(map, chip, adr);
|
||||
|
||||
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
|
@ -1162,9 +1414,9 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
|
|||
chip->erase_suspended = 0;
|
||||
chip->in_progress_block_addr = adr;
|
||||
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
msleep(chip->erase_time/2);
|
||||
cfi_spin_lock(chip->mutex);
|
||||
INVALIDATE_CACHE_UDELAY(map, chip,
|
||||
adr, map->size,
|
||||
chip->erase_time*500);
|
||||
|
||||
timeo = jiffies + (HZ*20);
|
||||
|
||||
|
@ -1173,10 +1425,10 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
|
|||
/* Someone's suspended the erase. Sleep */
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(&chip->wq, &wait);
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
schedule();
|
||||
remove_wait_queue(&chip->wq, &wait);
|
||||
cfi_spin_lock(chip->mutex);
|
||||
spin_lock(chip->mutex);
|
||||
continue;
|
||||
}
|
||||
if (chip->erase_suspended) {
|
||||
|
@ -1187,36 +1439,36 @@ static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
|
|||
}
|
||||
|
||||
if (chip_ready(map, adr))
|
||||
goto op_done;
|
||||
|
||||
if (time_after(jiffies, timeo))
|
||||
break;
|
||||
|
||||
if (time_after(jiffies, timeo)) {
|
||||
printk(KERN_WARNING "MTD %s(): software timeout\n",
|
||||
__func__ );
|
||||
break;
|
||||
}
|
||||
|
||||
/* Latency issues. Drop the lock, wait a while and retry */
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
schedule_timeout(1);
|
||||
cfi_spin_lock(chip->mutex);
|
||||
UDELAY(map, chip, adr, 1000000/HZ);
|
||||
}
|
||||
/* Did we succeed? */
|
||||
if (!chip_good(map, adr, map_word_ff(map))) {
|
||||
/* reset on all failures. */
|
||||
map_write( map, CMD(0xF0), chip->start );
|
||||
/* FIXME - should have reset delay before continuing */
|
||||
|
||||
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;
|
||||
xip_enable(map, chip, adr);
|
||||
put_chip(map, chip, adr);
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk)
|
||||
static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk)
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
unsigned long timeo = jiffies + HZ;
|
||||
|
@ -1225,17 +1477,20 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u
|
|||
|
||||
adr += chip->start;
|
||||
|
||||
cfi_spin_lock(chip->mutex);
|
||||
spin_lock(chip->mutex);
|
||||
ret = get_chip(map, chip, adr, FL_ERASING);
|
||||
if (ret) {
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
|
||||
__func__, adr );
|
||||
|
||||
XIP_INVAL_CACHED_RANGE(map, adr, len);
|
||||
ENABLE_VPP(map);
|
||||
xip_disable(map, chip, adr);
|
||||
|
||||
cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL);
|
||||
|
@ -1246,10 +1501,10 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u
|
|||
chip->state = FL_ERASING;
|
||||
chip->erase_suspended = 0;
|
||||
chip->in_progress_block_addr = adr;
|
||||
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
msleep(chip->erase_time/2);
|
||||
cfi_spin_lock(chip->mutex);
|
||||
|
||||
INVALIDATE_CACHE_UDELAY(map, chip,
|
||||
adr, len,
|
||||
chip->erase_time*500);
|
||||
|
||||
timeo = jiffies + (HZ*20);
|
||||
|
||||
|
@ -1258,10 +1513,10 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u
|
|||
/* Someone's suspended the erase. Sleep */
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(&chip->wq, &wait);
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
schedule();
|
||||
remove_wait_queue(&chip->wq, &wait);
|
||||
cfi_spin_lock(chip->mutex);
|
||||
spin_lock(chip->mutex);
|
||||
continue;
|
||||
}
|
||||
if (chip->erase_suspended) {
|
||||
|
@ -1271,31 +1526,33 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u
|
|||
chip->erase_suspended = 0;
|
||||
}
|
||||
|
||||
if (chip_ready(map, adr))
|
||||
goto op_done;
|
||||
|
||||
if (time_after(jiffies, timeo))
|
||||
if (chip_ready(map, adr)) {
|
||||
xip_enable(map, chip, adr);
|
||||
break;
|
||||
}
|
||||
|
||||
if (time_after(jiffies, timeo)) {
|
||||
xip_enable(map, chip, adr);
|
||||
printk(KERN_WARNING "MTD %s(): software timeout\n",
|
||||
__func__ );
|
||||
break;
|
||||
}
|
||||
|
||||
/* Latency issues. Drop the lock, wait a while and retry */
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
schedule_timeout(1);
|
||||
cfi_spin_lock(chip->mutex);
|
||||
UDELAY(map, chip, adr, 1000000/HZ);
|
||||
}
|
||||
/* Did we succeed? */
|
||||
if (!chip_good(map, adr, map_word_ff(map))) {
|
||||
/* reset on all failures. */
|
||||
map_write( map, CMD(0xF0), chip->start );
|
||||
/* FIXME - should have reset delay before continuing */
|
||||
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
printk(KERN_WARNING "MTD %s(): software timeout\n",
|
||||
__func__ );
|
||||
|
||||
/* reset on all failures. */
|
||||
map_write( map, CMD(0xF0), chip->start );
|
||||
/* FIXME - should have reset delay before continuing */
|
||||
|
||||
ret = -EIO;
|
||||
op_done:
|
||||
chip->state = FL_READY;
|
||||
put_chip(map, chip, adr);
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1355,7 +1612,7 @@ static void cfi_amdstd_sync (struct mtd_info *mtd)
|
|||
chip = &cfi->chips[i];
|
||||
|
||||
retry:
|
||||
cfi_spin_lock(chip->mutex);
|
||||
spin_lock(chip->mutex);
|
||||
|
||||
switch(chip->state) {
|
||||
case FL_READY:
|
||||
|
@ -1369,14 +1626,14 @@ static void cfi_amdstd_sync (struct mtd_info *mtd)
|
|||
* with the chip now anyway.
|
||||
*/
|
||||
case FL_SYNCING:
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Not an idle state */
|
||||
add_wait_queue(&chip->wq, &wait);
|
||||
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
|
||||
schedule();
|
||||
|
||||
|
@ -1391,13 +1648,13 @@ static void cfi_amdstd_sync (struct mtd_info *mtd)
|
|||
for (i--; i >=0; i--) {
|
||||
chip = &cfi->chips[i];
|
||||
|
||||
cfi_spin_lock(chip->mutex);
|
||||
spin_lock(chip->mutex);
|
||||
|
||||
if (chip->state == FL_SYNCING) {
|
||||
chip->state = chip->oldstate;
|
||||
wake_up(&chip->wq);
|
||||
}
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1413,7 +1670,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd)
|
|||
for (i=0; !ret && i<cfi->numchips; i++) {
|
||||
chip = &cfi->chips[i];
|
||||
|
||||
cfi_spin_lock(chip->mutex);
|
||||
spin_lock(chip->mutex);
|
||||
|
||||
switch(chip->state) {
|
||||
case FL_READY:
|
||||
|
@ -1433,7 +1690,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd)
|
|||
ret = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
}
|
||||
|
||||
/* Unlock the chips again */
|
||||
|
@ -1442,13 +1699,13 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd)
|
|||
for (i--; i >=0; i--) {
|
||||
chip = &cfi->chips[i];
|
||||
|
||||
cfi_spin_lock(chip->mutex);
|
||||
spin_lock(chip->mutex);
|
||||
|
||||
if (chip->state == FL_PM_SUSPENDED) {
|
||||
chip->state = chip->oldstate;
|
||||
wake_up(&chip->wq);
|
||||
}
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1467,7 +1724,7 @@ static void cfi_amdstd_resume(struct mtd_info *mtd)
|
|||
|
||||
chip = &cfi->chips[i];
|
||||
|
||||
cfi_spin_lock(chip->mutex);
|
||||
spin_lock(chip->mutex);
|
||||
|
||||
if (chip->state == FL_PM_SUSPENDED) {
|
||||
chip->state = FL_READY;
|
||||
|
@ -1477,7 +1734,7 @@ static void cfi_amdstd_resume(struct mtd_info *mtd)
|
|||
else
|
||||
printk(KERN_ERR "Argh. Chip not in PM_SUSPENDED state upon resume()\n");
|
||||
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,10 +58,10 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip,
|
|||
* to flash memory - that means that we don't have to check status
|
||||
* and timeout.
|
||||
*/
|
||||
cfi_spin_lock(chip->mutex);
|
||||
spin_lock(chip->mutex);
|
||||
ret = get_chip(map, chip, adr, FL_LOCKING);
|
||||
if (ret) {
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip,
|
|||
/* Done and happy. */
|
||||
chip->state = FL_READY;
|
||||
put_chip(map, chip, adr);
|
||||
cfi_spin_unlock(chip->mutex);
|
||||
spin_unlock(chip->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* Routines common to all CFI-type probes.
|
||||
* (C) 2001-2003 Red Hat, Inc.
|
||||
* GPL'd
|
||||
* $Id: gen_probe.c,v 1.21 2004/08/14 15:14:05 dwmw2 Exp $
|
||||
* $Id: gen_probe.c,v 1.22 2005/01/24 23:49:50 rmk Exp $
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
@ -162,7 +162,7 @@ static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp,
|
|||
int max_chips = map_bankwidth(map); /* And minimum 1 */
|
||||
int nr_chips, type;
|
||||
|
||||
for (nr_chips = min_chips; nr_chips <= max_chips; nr_chips <<= 1) {
|
||||
for (nr_chips = max_chips; nr_chips >= min_chips; nr_chips >>= 1) {
|
||||
|
||||
if (!cfi_interleave_supported(nr_chips))
|
||||
continue;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
Common Flash Interface probe code.
|
||||
(C) 2000 Red Hat. GPL'd.
|
||||
$Id: jedec_probe.c,v 1.61 2004/11/19 20:52:16 thayne Exp $
|
||||
$Id: jedec_probe.c,v 1.63 2005/02/14 16:30:32 bjd Exp $
|
||||
See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5)
|
||||
for the standard this probe goes back to.
|
||||
|
||||
|
@ -142,6 +142,7 @@
|
|||
#define SST29LE512 0x003d
|
||||
#define SST39LF800 0x2781
|
||||
#define SST39LF160 0x2782
|
||||
#define SST39VF1601 0x234b
|
||||
#define SST39LF512 0x00D4
|
||||
#define SST39LF010 0x00D5
|
||||
#define SST39LF020 0x00D6
|
||||
|
@ -1448,6 +1449,21 @@ static const struct amd_flash_info jedec_table[] = {
|
|||
ERASEINFO(0x1000,256),
|
||||
ERASEINFO(0x1000,256)
|
||||
}
|
||||
}, {
|
||||
.mfr_id = MANUFACTURER_SST, /* should be CFI */
|
||||
.dev_id = SST39VF1601,
|
||||
.name = "SST 39VF1601",
|
||||
.uaddr = {
|
||||
[0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */
|
||||
[1] = MTD_UADDR_0x5555_0x2AAA /* x16 */
|
||||
},
|
||||
.DevSize = SIZE_2MiB,
|
||||
.CmdSet = P_ID_AMD_STD,
|
||||
.NumEraseRegions= 2,
|
||||
.regions = {
|
||||
ERASEINFO(0x1000,256),
|
||||
ERASEINFO(0x1000,256)
|
||||
}
|
||||
|
||||
}, {
|
||||
.mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */
|
||||
|
@ -1856,6 +1872,16 @@ static inline int jedec_match( __u32 base,
|
|||
case CFI_DEVICETYPE_X8:
|
||||
mfr = (__u8)finfo->mfr_id;
|
||||
id = (__u8)finfo->dev_id;
|
||||
|
||||
/* bjd: it seems that if we do this, we can end up
|
||||
* detecting 16bit flashes as an 8bit device, even though
|
||||
* there aren't.
|
||||
*/
|
||||
if (finfo->dev_id > 0xff) {
|
||||
DEBUG( MTD_DEBUG_LEVEL3, "%s(): ID is not 8bit\n",
|
||||
__func__);
|
||||
goto match_done;
|
||||
}
|
||||
break;
|
||||
case CFI_DEVICETYPE_X16:
|
||||
mfr = (__u16)finfo->mfr_id;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $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
|
||||
*
|
||||
|
@ -239,7 +239,8 @@ static int mtdpart_setup_real(char *s)
|
|||
&num_parts, /* out: number of parts */
|
||||
0, /* first partition */
|
||||
(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)
|
||||
{
|
||||
/*
|
||||
|
@ -252,6 +253,9 @@ static int mtdpart_setup_real(char *s)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* align this_mtd */
|
||||
this_mtd = (struct cmdline_mtd_partition *)
|
||||
ALIGN((unsigned long)this_mtd, sizeof(void*));
|
||||
/* enter results */
|
||||
this_mtd->parts = parts;
|
||||
this_mtd->num_parts = num_parts;
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
/*
|
||||
* $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
|
||||
*
|
||||
* Copyright (C) 2001,2002 Simon Evans <spse@secret.org.uk>
|
||||
* Copyright (C) 2004 Gareth Bult <Gareth@Encryptec.net>
|
||||
* Copyright (C) 2004,2005 Jörn Engel <joern@wh.fh-wedel.de>
|
||||
*
|
||||
* Licence: GPL
|
||||
|
@ -20,7 +19,7 @@
|
|||
#include <linux/mtd/mtd.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)
|
||||
|
@ -89,7 +88,6 @@ void cache_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;
|
||||
//do_page_cache_readahead(mapping, index, XXX, 64);
|
||||
cache_readahead(mapping, index);
|
||||
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,
|
|||
struct block2mtd_dev *dev = mtd->priv;
|
||||
struct page *page;
|
||||
int index = from >> PAGE_SHIFT;
|
||||
int offset = from & (PAGE_SHIFT-1);
|
||||
int offset = from & (PAGE_SIZE-1);
|
||||
int cpylen;
|
||||
|
||||
if (from > mtd->size)
|
||||
|
@ -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;
|
||||
unsigned long n;
|
||||
size_t n;
|
||||
|
||||
n = ustrtoul(token, &endp, 0);
|
||||
n = (size_t) ustrtoul(token, &endp, 0);
|
||||
if (*endp)
|
||||
return -EINVAL;
|
||||
|
||||
*num32 = n;
|
||||
*num = n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -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 *token[2];
|
||||
char *name;
|
||||
u32 erase_size = PAGE_SIZE;
|
||||
size_t erase_size = PAGE_SIZE;
|
||||
int i, ret;
|
||||
|
||||
if (strnlen(val, sizeof(buf)) >= sizeof(buf))
|
||||
|
@ -449,7 +447,7 @@ static int block2mtd_setup(const char *val, struct kernel_param *kp)
|
|||
return 0;
|
||||
|
||||
if (token[1]) {
|
||||
ret = parse_num32(&erase_size, token[1]);
|
||||
ret = parse_num(&erase_size, token[1]);
|
||||
if (ret)
|
||||
parse_err("illegal erase size");
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* as published by the Free Software Foundation; either 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>
|
||||
|
@ -99,8 +99,8 @@ static inline uint ms02nv_probe_one(ulong addr)
|
|||
* The firmware writes MS02NV_ID at MS02NV_MAGIC and also
|
||||
* a diagnostic status at MS02NV_DIAG.
|
||||
*/
|
||||
ms02nv_diagp = (ms02nv_uint *)(KSEG1ADDR(addr + MS02NV_DIAG));
|
||||
ms02nv_magicp = (ms02nv_uint *)(KSEG1ADDR(addr + MS02NV_MAGIC));
|
||||
ms02nv_diagp = (ms02nv_uint *)(CKSEG1ADDR(addr + MS02NV_DIAG));
|
||||
ms02nv_magicp = (ms02nv_uint *)(CKSEG1ADDR(addr + MS02NV_MAGIC));
|
||||
err = get_dbe(ms02nv_magic, ms02nv_magicp);
|
||||
if (err)
|
||||
return 0;
|
||||
|
@ -233,7 +233,7 @@ static int __init ms02nv_init_one(ulong addr)
|
|||
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);
|
||||
|
||||
mp->next = root_ms02nv_mtd;
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
/*
|
||||
* 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>
|
||||
*
|
||||
* Copyright (c) 1999 Alexander Larsson <alex@cendio.se>
|
||||
* Copyright (c) 2005 Joern Engel <joern@wh.fh-wedel.de>
|
||||
*
|
||||
* This code is GPL
|
||||
*
|
||||
|
@ -18,213 +19,140 @@
|
|||
#include <linux/mtd/compatmac.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 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_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..
|
||||
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) {
|
||||
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;
|
||||
mtd_erase_callback(instr);
|
||||
if (instr->addr + instr->len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
memset((char *)mtd->priv + instr->addr, 0xff, instr->len);
|
||||
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
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)
|
||||
return -EINVAL;
|
||||
|
||||
*mtdbuf = mtd->priv + from;
|
||||
*retlen = len;
|
||||
return 0;
|
||||
if (from + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
*mtdbuf = mtd->priv + from;
|
||||
*retlen = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ram_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from,
|
||||
size_t len)
|
||||
static void ram_unpoint(struct mtd_info *mtd, u_char * addr, loff_t from,
|
||||
size_t len)
|
||||
{
|
||||
DEBUG(MTD_DEBUG_LEVEL2, "ram_unpoint\n");
|
||||
}
|
||||
|
||||
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) {
|
||||
DEBUG(MTD_DEBUG_LEVEL1, "ram_read() out of bounds (%ld > %ld)\n", (long)(from + len), (long)mtd->size);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (from + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(buf, mtd->priv + from, len);
|
||||
memcpy(buf, mtd->priv + from, len);
|
||||
|
||||
*retlen=len;
|
||||
return 0;
|
||||
*retlen = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
DEBUG(MTD_DEBUG_LEVEL1, "ram_write() out of bounds (%ld > %ld)\n", (long)(to + len), (long)mtd->size);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (to + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy ((char *)mtd->priv + to, buf, len);
|
||||
memcpy((char *)mtd->priv + to, buf, len);
|
||||
|
||||
*retlen=len;
|
||||
return 0;
|
||||
*retlen = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cleanup_mtdram(void)
|
||||
{
|
||||
if (mtd_info) {
|
||||
del_mtd_device(mtd_info);
|
||||
#if CONFIG_MTDRAM_TOTAL_SIZE > 0
|
||||
if (mtd_info->priv)
|
||||
#if CONFIG_MTDRAM_ABS_POS > 0
|
||||
iounmap(mtd_info->priv);
|
||||
#else
|
||||
vfree(mtd_info->priv);
|
||||
#endif
|
||||
#endif
|
||||
kfree(mtd_info);
|
||||
}
|
||||
if (mtd_info) {
|
||||
del_mtd_device(mtd_info);
|
||||
if (mtd_info->priv)
|
||||
vfree(mtd_info->priv);
|
||||
kfree(mtd_info);
|
||||
}
|
||||
}
|
||||
|
||||
int mtdram_init_device(struct mtd_info *mtd, void *mapped_address,
|
||||
unsigned long size, char *name)
|
||||
int mtdram_init_device(struct mtd_info *mtd, void *mapped_address,
|
||||
unsigned long size, char *name)
|
||||
{
|
||||
memset(mtd, 0, sizeof(*mtd));
|
||||
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;
|
||||
/* 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;
|
||||
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 (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 */
|
||||
|
||||
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;
|
||||
void *addr;
|
||||
int err;
|
||||
|
||||
addr = vmalloc(MTDRAM_TOTAL_SIZE);
|
||||
if (!addr) {
|
||||
DEBUG(MTD_DEBUG_LEVEL1,
|
||||
"Failed to vmalloc memory region of size %ld\n",
|
||||
(long)MTDRAM_TOTAL_SIZE);
|
||||
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;
|
||||
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_ABS_POS > 0) */
|
||||
|
||||
#else /* CONFIG_MTDRAM_TOTAL_SIZE > 0 */
|
||||
|
||||
static int __init init_mtdram(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* !(CONFIG_MTDRAM_TOTAL_SIZE > 0) */
|
||||
|
||||
module_init(init_mtdram);
|
||||
module_exit(cleanup_mtdram);
|
||||
|
@ -232,4 +160,3 @@ module_exit(cleanup_mtdram);
|
|||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Alexander Larsson <alexl@redhat.com>");
|
||||
MODULE_DESCRIPTION("Simulated MTD driver for testing");
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/**
|
||||
* $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 Schäuble <psionic@psionic.de>
|
||||
* Copyright (c) 2003-2004 Jörn Engel <joern@wh.fh-wedel.de>
|
||||
|
@ -15,9 +15,7 @@
|
|||
*
|
||||
* Example:
|
||||
* phram=swap,64Mi,128Mi phram=test,900Mi,1Mi
|
||||
*
|
||||
*/
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
|
@ -36,7 +34,6 @@ struct phram_mtd_list {
|
|||
static LIST_HEAD(phram_list);
|
||||
|
||||
|
||||
|
||||
static int phram_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
{
|
||||
u_char *start = mtd->priv;
|
||||
|
@ -71,7 +68,8 @@ static int phram_point(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
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,
|
|||
{
|
||||
u_char *start = mtd->priv;
|
||||
|
||||
if (from + len > mtd->size)
|
||||
if (from >= mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
if (len > mtd->size - from)
|
||||
len = mtd->size - from;
|
||||
|
||||
memcpy(buf, start + from, len);
|
||||
|
||||
|
@ -94,8 +95,11 @@ static int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
{
|
||||
u_char *start = mtd->priv;
|
||||
|
||||
if (to + len > mtd->size)
|
||||
if (to >= mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
if (len > mtd->size - to)
|
||||
len = mtd->size - to;
|
||||
|
||||
memcpy(start + to, buf, len);
|
||||
|
||||
|
@ -107,9 +111,9 @@ static int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
|
|||
|
||||
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);
|
||||
iounmap(this->mtd.priv);
|
||||
kfree(this);
|
||||
|
@ -145,7 +149,7 @@ static int register_device(char *name, unsigned long start, unsigned long len)
|
|||
new->mtd.write = phram_write;
|
||||
new->mtd.owner = THIS_MODULE;
|
||||
new->mtd.type = MTD_RAM;
|
||||
new->mtd.erasesize = 0;
|
||||
new->mtd.erasesize = PAGE_SIZE;
|
||||
|
||||
ret = -EAGAIN;
|
||||
if (add_mtd_device(&new->mtd)) {
|
||||
|
@ -214,6 +218,15 @@ static int parse_name(char **pname, const char *token)
|
|||
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 { \
|
||||
ERROR(fmt , ## args); \
|
||||
return 0; \
|
||||
|
@ -232,6 +245,7 @@ static int phram_setup(const char *val, struct kernel_param *kp)
|
|||
parse_err("parameter too long\n");
|
||||
|
||||
strcpy(str, val);
|
||||
kill_final_newline(str);
|
||||
|
||||
for (i=0; i<3; i++)
|
||||
token[i] = strsep(&str, ",");
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*======================================================================
|
||||
|
||||
$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
|
||||
itself (i.e. if the kernel commandline mem=xxx is used). To actually
|
||||
|
@ -50,6 +50,7 @@
|
|||
#include <linux/mtd/mtd.h>
|
||||
|
||||
#define SLRAM_MAX_DEVICES_PARAMS 6 /* 3 parameters / device */
|
||||
#define SLRAM_BLK_SZ 0x4000
|
||||
|
||||
#define T(fmt, args...) printk(KERN_DEBUG 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,
|
|||
{
|
||||
slram_priv_t *priv = mtd->priv;
|
||||
|
||||
if (from + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
*mtdbuf = priv->start + from;
|
||||
*retlen = len;
|
||||
return(0);
|
||||
|
@ -121,7 +125,13 @@ static int slram_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|||
size_t *retlen, u_char *buf)
|
||||
{
|
||||
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);
|
||||
|
||||
*retlen = 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;
|
||||
|
||||
if (to + len > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(priv->start + to, buf, len);
|
||||
|
||||
*retlen = len;
|
||||
|
@ -188,7 +201,7 @@ static int register_device(char *name, unsigned long start, unsigned long length
|
|||
(*curmtd)->mtdinfo->name = name;
|
||||
(*curmtd)->mtdinfo->size = length;
|
||||
(*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->point = slram_point;
|
||||
(*curmtd)->mtdinfo->unpoint = slram_unpoint;
|
||||
|
@ -196,7 +209,7 @@ static int register_device(char *name, unsigned long start, unsigned long length
|
|||
(*curmtd)->mtdinfo->write = slram_write;
|
||||
(*curmtd)->mtdinfo->owner = THIS_MODULE;
|
||||
(*curmtd)->mtdinfo->type = MTD_RAM;
|
||||
(*curmtd)->mtdinfo->erasesize = 0x0;
|
||||
(*curmtd)->mtdinfo->erasesize = SLRAM_BLK_SZ;
|
||||
|
||||
if (add_mtd_device((*curmtd)->mtdinfo)) {
|
||||
E("slram: Failed to register new device\n");
|
||||
|
@ -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",
|
||||
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");
|
||||
return(-EINVAL);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* 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 some leaks on failure in build_maps and ftl_notify_add, cleanups
|
||||
|
@ -357,6 +357,7 @@ static int erase_xfer(partition_t *part,
|
|||
if (!erase)
|
||||
return -ENOMEM;
|
||||
|
||||
erase->mtd = part->mbd.mtd;
|
||||
erase->callback = ftl_erase_callback;
|
||||
erase->addr = xfer->Offset;
|
||||
erase->len = 1 << part->header.EraseUnitSize;
|
||||
|
@ -1096,7 +1097,7 @@ struct mtd_blktrans_ops ftl_tr = {
|
|||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# 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"
|
||||
depends on MTD!=n
|
||||
|
@ -122,16 +122,6 @@ config MTD_SBC_GXX
|
|||
More info at
|
||||
<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
|
||||
tristate "CFI Flash device mapped on Intel Lubbock XScale eval board"
|
||||
depends on ARCH_LUBBOCK && MTD_CFI_INTELEXT && MTD_PARTITIONS
|
||||
|
@ -139,6 +129,14 @@ config MTD_LUBBOCK
|
|||
This provides a driver for the on-board flash of the Intel
|
||||
'Lubbock' XScale evaluation board.
|
||||
|
||||
config MTD_MAINSTONE
|
||||
tristate "CFI Flash device mapped on Intel Mainstone XScale eval board"
|
||||
depends on MACH_MAINSTONE && MTD_CFI_INTELEXT
|
||||
select MTD_PARTITIONS
|
||||
help
|
||||
This provides a driver for the on-board flash of the Intel
|
||||
'Mainstone PXA27x evaluation board.
|
||||
|
||||
config MTD_OCTAGON
|
||||
tristate "JEDEC Flash device mapped on Octagon 5066 SBC"
|
||||
depends on X86 && MTD_JEDEC && MTD_COMPLEX_MAPPINGS
|
||||
|
@ -213,74 +211,11 @@ config MTD_NETtel
|
|||
help
|
||||
Support for flash chips on NETtel/SecureEdge/SnapGear boards.
|
||||
|
||||
config MTD_PB1XXX
|
||||
tristate "Flash devices on Alchemy PB1xxx boards"
|
||||
depends on MIPS && ( MIPS_PB1000 || MIPS_PB1100 || MIPS_PB1500 )
|
||||
config MTD_ALCHEMY
|
||||
tristate ' AMD Alchemy Pb1xxx/Db1xxx/RDK MTD support'
|
||||
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
|
||||
Use the first of the two 64MiB flash banks on Pb1550 board.
|
||||
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.
|
||||
Flash memory access on AMD Alchemy Pb/Db/RDK Reference Boards
|
||||
|
||||
config MTD_DILNETPC
|
||||
tristate "CFI Flash device mapped on DIL/Net PC"
|
||||
|
@ -588,6 +523,15 @@ config MTD_MPC1211
|
|||
This enables access to the flash chips on the Interface MPC-1211(CTP/PCI/MPC-SH02).
|
||||
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.
|
||||
config MTD_PCI
|
||||
tristate "PCI MTD driver"
|
||||
|
@ -647,13 +591,14 @@ config MTD_DMV182
|
|||
Map driver for Dy-4 SVME/DMV-182 board.
|
||||
|
||||
config MTD_BAST
|
||||
tristate "Map driver for Simtec BAST (EB2410ITX)"
|
||||
depends on ARCH_BAST
|
||||
tristate "Map driver for Simtec BAST (EB2410ITX) or Thorcom VR1000"
|
||||
depends on ARCH_BAST || MACH_VR1000
|
||||
select MTD_PARTITIONS
|
||||
select MTD_MAP_BANK_WIDTH_16
|
||||
select MTD_JEDECPROBE
|
||||
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
|
||||
board, or currently detect the state of the link.
|
||||
|
@ -669,5 +614,15 @@ config MTD_SHARP_SL
|
|||
help
|
||||
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
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
# 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)
|
||||
obj-$(CONFIG_MTD) += map_funcs.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_DC21285) += dc21285.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_IQ80310) += iq80310.o
|
||||
obj-$(CONFIG_MTD_L440GX) += l440gx.o
|
||||
|
@ -23,6 +22,7 @@ obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o
|
|||
obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o
|
||||
obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o
|
||||
obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o
|
||||
obj-$(CONFIG_MTD_MAINSTONE) += mainstone-flash.o
|
||||
obj-$(CONFIG_MTD_MBX860) += mbx860.o
|
||||
obj-$(CONFIG_MTD_CEIVA) += ceiva.o
|
||||
obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o
|
||||
|
@ -44,10 +44,7 @@ obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o
|
|||
obj-$(CONFIG_MTD_OCELOT) += ocelot.o
|
||||
obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
|
||||
obj-$(CONFIG_MTD_PCI) += pci.o
|
||||
obj-$(CONFIG_MTD_PB1XXX) += pb1xxx-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_ALCHEMY) += alchemy-flash.o
|
||||
obj-$(CONFIG_MTD_LASAT) += lasat.o
|
||||
obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o
|
||||
obj-$(CONFIG_MTD_EDB7312) += edb7312.o
|
||||
|
@ -71,3 +68,5 @@ obj-$(CONFIG_MTD_IXP2000) += ixp2000.o
|
|||
obj-$(CONFIG_MTD_WRSBC8260) += wr_sbc82xx_flash.o
|
||||
obj-$(CONFIG_MTD_DMV182) += dmv182.o
|
||||
obj-$(CONFIG_MTD_SHARP_SL) += sharpsl-flash.o
|
||||
obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
|
||||
obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* 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 @@
|
|||
* amd76xrom.c
|
||||
*
|
||||
* 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>
|
||||
|
@ -314,7 +314,7 @@ static int __init init_amd76xrom(void)
|
|||
}
|
||||
return -ENXIO;
|
||||
#if 0
|
||||
return pci_module_init(&amd76xrom_driver);
|
||||
return pci_register_driver(&amd76xrom_driver);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
/* linux/drivers/mtd/maps/bast_flash.c
|
||||
*
|
||||
* Copyright (c) 2004 Simtec Electronics
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
* Copyright (c) 2004-2005 Simtec Electronics
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*
|
||||
* Simtec Bast (EB2410ITX) NOR MTD Mapping driver
|
||||
*
|
||||
* Changelog:
|
||||
* 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
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -46,9 +47,9 @@
|
|||
#include <asm/arch/bast-cpld.h>
|
||||
|
||||
#ifdef CONFIG_MTD_BAST_MAXSIZE
|
||||
#define AREA_MAXSIZE (CONFIG_MTD_BAST_MAXSIZE * (1024*1024))
|
||||
#define AREA_MAXSIZE (CONFIG_MTD_BAST_MAXSIZE * SZ_1M)
|
||||
#else
|
||||
#define AREA_MAXSIZE (32*1024*1024)
|
||||
#define AREA_MAXSIZE (32 * SZ_1M)
|
||||
#endif
|
||||
|
||||
#define PFX "bast-flash: "
|
||||
|
@ -189,6 +190,8 @@ static int bast_flash_probe(struct device *dev)
|
|||
err = add_mtd_partitions(info->mtd, info->partitions, err);
|
||||
if (err)
|
||||
printk(KERN_ERR PFX "cannot add/parse partitions\n");
|
||||
} else {
|
||||
err = add_mtd_device(info->mtd);
|
||||
}
|
||||
|
||||
if (err == 0)
|
||||
|
|
|
@ -1,187 +0,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");
|
|
@ -1,226 +0,0 @@
|
|||
/*
|
||||
* Flash memory access on Alchemy Db1xxx boards
|
||||
*
|
||||
* $Id: db1x00-flash.c,v 1.6 2004/11/04 13:24:14 gleixner Exp $
|
||||
*
|
||||
* (C) 2003 Pete Popov <ppopov@embeddedalley.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.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
|
||||
|
||||
/* MTD CONFIG OPTIONS */
|
||||
#if defined(CONFIG_MTD_DB1X00_BOOT) && defined(CONFIG_MTD_DB1X00_USER)
|
||||
#define DB1X00_BOTH_BANKS
|
||||
#elif defined(CONFIG_MTD_DB1X00_BOOT) && !defined(CONFIG_MTD_DB1X00_USER)
|
||||
#define DB1X00_BOOT_ONLY
|
||||
#elif !defined(CONFIG_MTD_DB1X00_BOOT) && defined(CONFIG_MTD_DB1X00_USER)
|
||||
#define DB1X00_USER_ONLY
|
||||
#endif
|
||||
|
||||
static unsigned long window_addr;
|
||||
static unsigned long window_size;
|
||||
static unsigned long flash_size;
|
||||
|
||||
static unsigned short *bcsr = (unsigned short *)0xAE000000;
|
||||
static unsigned char flash_bankwidth = 4;
|
||||
|
||||
/*
|
||||
* The Db1x boards support different flash densities. We setup
|
||||
* the mtd_partition structures below for default of 64Mbit
|
||||
* flash densities, and override the partitions sizes, if
|
||||
* necessary, after we check the board status register.
|
||||
*/
|
||||
|
||||
#ifdef DB1X00_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 db1x00_partitions[] = {
|
||||
{
|
||||
.name = "User FS",
|
||||
.size = 0x1c00000,
|
||||
.offset = 0x0000000
|
||||
},{
|
||||
.name = "yamon",
|
||||
.size = 0x0100000,
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
.mask_flags = MTD_WRITEABLE
|
||||
},{
|
||||
.name = "raw kernel",
|
||||
.size = (0x300000-0x40000), /* last 256KB is env */
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
}
|
||||
};
|
||||
#elif defined(DB1X00_BOOT_ONLY)
|
||||
static struct mtd_partition db1x00_partitions[] = {
|
||||
{
|
||||
.name = "User FS",
|
||||
.size = 0x00c00000,
|
||||
.offset = 0x0000000
|
||||
},{
|
||||
.name = "yamon",
|
||||
.size = 0x0100000,
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
.mask_flags = MTD_WRITEABLE
|
||||
},{
|
||||
.name = "raw kernel",
|
||||
.size = (0x300000-0x40000), /* last 256KB is env */
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
}
|
||||
};
|
||||
#elif defined(DB1X00_USER_ONLY)
|
||||
static struct mtd_partition db1x00_partitions[] = {
|
||||
{
|
||||
.name = "User FS",
|
||||
.size = 0x0e00000,
|
||||
.offset = 0x0000000
|
||||
},{
|
||||
.name = "raw kernel",
|
||||
.size = MTDPART_SIZ_FULL,
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
}
|
||||
};
|
||||
#else
|
||||
#error MTD_DB1X00 define combo error /* should never happen */
|
||||
#endif
|
||||
#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
|
||||
|
||||
#define NAME "Db1x00 Linux Flash"
|
||||
|
||||
static struct map_info db1xxx_mtd_map = {
|
||||
.name = NAME,
|
||||
};
|
||||
|
||||
static struct mtd_partition *parsed_parts;
|
||||
static struct mtd_info *db1xxx_mtd;
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
switch ((bcsr[2] >> 14) & 0x3) {
|
||||
case 0: /* 64Mbit devices */
|
||||
flash_size = 0x800000; /* 8MB per part */
|
||||
#if defined(DB1X00_BOTH_BANKS)
|
||||
window_addr = 0x1E000000;
|
||||
window_size = 0x2000000;
|
||||
#elif defined(DB1X00_BOOT_ONLY)
|
||||
window_addr = 0x1F000000;
|
||||
window_size = 0x1000000;
|
||||
#else /* USER ONLY */
|
||||
window_addr = 0x1E000000;
|
||||
window_size = 0x1000000;
|
||||
#endif
|
||||
break;
|
||||
case 1:
|
||||
/* 128 Mbit devices */
|
||||
flash_size = 0x1000000; /* 16MB per part */
|
||||
#if defined(DB1X00_BOTH_BANKS)
|
||||
window_addr = 0x1C000000;
|
||||
window_size = 0x4000000;
|
||||
/* USERFS from 0x1C00 0000 to 0x1FC0 0000 */
|
||||
db1x00_partitions[0].size = 0x3C00000;
|
||||
#elif defined(DB1X00_BOOT_ONLY)
|
||||
window_addr = 0x1E000000;
|
||||
window_size = 0x2000000;
|
||||
/* USERFS from 0x1E00 0000 to 0x1FC0 0000 */
|
||||
db1x00_partitions[0].size = 0x1C00000;
|
||||
#else /* USER ONLY */
|
||||
window_addr = 0x1C000000;
|
||||
window_size = 0x2000000;
|
||||
/* USERFS from 0x1C00 0000 to 0x1DE00000 */
|
||||
db1x00_partitions[0].size = 0x1DE0000;
|
||||
#endif
|
||||
break;
|
||||
case 2:
|
||||
/* 256 Mbit devices */
|
||||
flash_size = 0x4000000; /* 64MB per part */
|
||||
#if defined(DB1X00_BOTH_BANKS)
|
||||
return 1;
|
||||
#elif defined(DB1X00_BOOT_ONLY)
|
||||
/* Boot ROM flash bank only; no user bank */
|
||||
window_addr = 0x1C000000;
|
||||
window_size = 0x4000000;
|
||||
/* USERFS from 0x1C00 0000 to 0x1FC00000 */
|
||||
db1x00_partitions[0].size = 0x3C00000;
|
||||
#else /* USER ONLY */
|
||||
return 1;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
db1xxx_mtd_map.size = window_size;
|
||||
db1xxx_mtd_map.bankwidth = flash_bankwidth;
|
||||
db1xxx_mtd_map.phys = window_addr;
|
||||
db1xxx_mtd_map.bankwidth = flash_bankwidth;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init db1x00_mtd_init(void)
|
||||
{
|
||||
struct mtd_partition *parts;
|
||||
int nb_parts = 0;
|
||||
|
||||
if (setup_flash_params())
|
||||
return -ENXIO;
|
||||
|
||||
/*
|
||||
* Static partition definition selection
|
||||
*/
|
||||
parts = db1x00_partitions;
|
||||
nb_parts = NB_OF(db1x00_partitions);
|
||||
|
||||
/*
|
||||
* Now let's probe for the actual flash. Do it here since
|
||||
* specific machine settings might have been set above.
|
||||
*/
|
||||
printk(KERN_NOTICE "Db1xxx flash: probing %d-bit flash bus\n",
|
||||
db1xxx_mtd_map.bankwidth*8);
|
||||
db1xxx_mtd_map.virt = ioremap(window_addr, window_size);
|
||||
db1xxx_mtd = do_map_probe("cfi_probe", &db1xxx_mtd_map);
|
||||
if (!db1xxx_mtd) return -ENXIO;
|
||||
db1xxx_mtd->owner = THIS_MODULE;
|
||||
|
||||
add_mtd_partitions(db1xxx_mtd, parts, nb_parts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit db1x00_mtd_cleanup(void)
|
||||
{
|
||||
if (db1xxx_mtd) {
|
||||
del_mtd_partitions(db1xxx_mtd);
|
||||
map_destroy(db1xxx_mtd);
|
||||
if (parsed_parts)
|
||||
kfree(parsed_parts);
|
||||
}
|
||||
}
|
||||
|
||||
module_init(db1x00_mtd_init);
|
||||
module_exit(db1x00_mtd_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Pete Popov");
|
||||
MODULE_DESCRIPTION("Db1x00 mtd map driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,228 +0,0 @@
|
|||
/* elan-104nc.c -- MTD map driver for Arcom Control Systems ELAN-104NC
|
||||
|
||||
Copyright (C) 2000 Arcom Control System Ltd
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
||||
|
||||
$Id: elan-104nc.c,v 1.25 2004/11/28 09:40:39 dwmw2 Exp $
|
||||
|
||||
The ELAN-104NC has up to 8 Mibyte of Intel StrataFlash (28F320/28F640) in x16
|
||||
mode. This drivers uses the CFI probe and Intel Extended Command Set drivers.
|
||||
|
||||
The flash is accessed as follows:
|
||||
|
||||
32 kbyte memory window at 0xb0000-0xb7fff
|
||||
|
||||
16 bit I/O port (0x22) for some sort of paging.
|
||||
|
||||
The single flash device is divided into 3 partition which appear as separate
|
||||
MTD devices.
|
||||
|
||||
Linux thinks that the I/O port is used by the PIC and hence check_region() will
|
||||
always fail. So we don't do it. I just hope it doesn't break anything.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#define WINDOW_START 0xb0000
|
||||
/* Number of bits in offset. */
|
||||
#define WINDOW_SHIFT 15
|
||||
#define WINDOW_LENGTH (1 << WINDOW_SHIFT)
|
||||
/* The bits for the offset into the window. */
|
||||
#define WINDOW_MASK (WINDOW_LENGTH-1)
|
||||
#define PAGE_IO 0x22
|
||||
#define PAGE_IO_SIZE 2
|
||||
|
||||
static volatile int page_in_window = -1; // Current page in window.
|
||||
static void __iomem *iomapadr;
|
||||
static DEFINE_SPINLOCK(elan_104nc_spin);
|
||||
|
||||
/* partition_info gives details on the logical partitions that the split the
|
||||
* single flash device into. If the size if zero we use up to the end of the
|
||||
* device. */
|
||||
static struct mtd_partition partition_info[]={
|
||||
{ .name = "ELAN-104NC flash boot partition",
|
||||
.offset = 0,
|
||||
.size = 640*1024 },
|
||||
{ .name = "ELAN-104NC flash partition 1",
|
||||
.offset = 640*1024,
|
||||
.size = 896*1024 },
|
||||
{ .name = "ELAN-104NC flash partition 2",
|
||||
.offset = (640+896)*1024 }
|
||||
};
|
||||
#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0]))
|
||||
|
||||
/*
|
||||
* If no idea what is going on here. This is taken from the FlashFX stuff.
|
||||
*/
|
||||
#define ROMCS 1
|
||||
|
||||
static inline void elan_104nc_setup(void)
|
||||
{
|
||||
u16 t;
|
||||
|
||||
outw( 0x0023 + ROMCS*2, PAGE_IO );
|
||||
t=inb( PAGE_IO+1 );
|
||||
|
||||
t=(t & 0xf9) | 0x04;
|
||||
|
||||
outw( ((0x0023 + ROMCS*2) | (t << 8)), PAGE_IO );
|
||||
}
|
||||
|
||||
static inline void elan_104nc_page(struct map_info *map, unsigned long ofs)
|
||||
{
|
||||
unsigned long page = ofs >> WINDOW_SHIFT;
|
||||
|
||||
if( page!=page_in_window ) {
|
||||
int cmd1;
|
||||
int cmd2;
|
||||
|
||||
cmd1=(page & 0x700) + 0x0833 + ROMCS*0x4000;
|
||||
cmd2=((page & 0xff) << 8) + 0x0032;
|
||||
|
||||
outw( cmd1, PAGE_IO );
|
||||
outw( cmd2, PAGE_IO );
|
||||
|
||||
page_in_window = page;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static map_word elan_104nc_read16(struct map_info *map, unsigned long ofs)
|
||||
{
|
||||
map_word ret;
|
||||
spin_lock(&elan_104nc_spin);
|
||||
elan_104nc_page(map, ofs);
|
||||
ret.x[0] = readw(iomapadr + (ofs & WINDOW_MASK));
|
||||
spin_unlock(&elan_104nc_spin);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void elan_104nc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
|
||||
{
|
||||
while (len) {
|
||||
unsigned long thislen = len;
|
||||
if (len > (WINDOW_LENGTH - (from & WINDOW_MASK)))
|
||||
thislen = WINDOW_LENGTH-(from & WINDOW_MASK);
|
||||
|
||||
spin_lock(&elan_104nc_spin);
|
||||
elan_104nc_page(map, from);
|
||||
memcpy_fromio(to, iomapadr + (from & WINDOW_MASK), thislen);
|
||||
spin_unlock(&elan_104nc_spin);
|
||||
to += thislen;
|
||||
from += thislen;
|
||||
len -= thislen;
|
||||
}
|
||||
}
|
||||
|
||||
static void elan_104nc_write16(struct map_info *map, map_word d, unsigned long adr)
|
||||
{
|
||||
spin_lock(&elan_104nc_spin);
|
||||
elan_104nc_page(map, adr);
|
||||
writew(d.x[0], iomapadr + (adr & WINDOW_MASK));
|
||||
spin_unlock(&elan_104nc_spin);
|
||||
}
|
||||
|
||||
static void elan_104nc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
|
||||
{
|
||||
while(len) {
|
||||
unsigned long thislen = len;
|
||||
if (len > (WINDOW_LENGTH - (to & WINDOW_MASK)))
|
||||
thislen = WINDOW_LENGTH-(to & WINDOW_MASK);
|
||||
|
||||
spin_lock(&elan_104nc_spin);
|
||||
elan_104nc_page(map, to);
|
||||
memcpy_toio(iomapadr + (to & WINDOW_MASK), from, thislen);
|
||||
spin_unlock(&elan_104nc_spin);
|
||||
to += thislen;
|
||||
from += thislen;
|
||||
len -= thislen;
|
||||
}
|
||||
}
|
||||
|
||||
static struct map_info elan_104nc_map = {
|
||||
.name = "ELAN-104NC flash",
|
||||
.phys = NO_XIP,
|
||||
.size = 8*1024*1024, /* this must be set to a maximum possible amount
|
||||
of flash so the cfi probe routines find all
|
||||
the chips */
|
||||
.bankwidth = 2,
|
||||
.read = elan_104nc_read16,
|
||||
.copy_from = elan_104nc_copy_from,
|
||||
.write = elan_104nc_write16,
|
||||
.copy_to = elan_104nc_copy_to
|
||||
};
|
||||
|
||||
/* MTD device for all of the flash. */
|
||||
static struct mtd_info *all_mtd;
|
||||
|
||||
static void cleanup_elan_104nc(void)
|
||||
{
|
||||
if( all_mtd ) {
|
||||
del_mtd_partitions( all_mtd );
|
||||
map_destroy( all_mtd );
|
||||
}
|
||||
|
||||
iounmap(iomapadr);
|
||||
}
|
||||
|
||||
static int __init init_elan_104nc(void)
|
||||
{
|
||||
/* Urg! We use I/O port 0x22 without request_region()ing it,
|
||||
because it's already allocated to the PIC. */
|
||||
|
||||
iomapadr = ioremap(WINDOW_START, WINDOW_LENGTH);
|
||||
if (!iomapadr) {
|
||||
printk( KERN_ERR"%s: failed to ioremap memory region\n",
|
||||
elan_104nc_map.name );
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n",
|
||||
elan_104nc_map.name,
|
||||
PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1,
|
||||
WINDOW_START, WINDOW_START+WINDOW_LENGTH-1 );
|
||||
|
||||
elan_104nc_setup();
|
||||
|
||||
/* Probe for chip. */
|
||||
all_mtd = do_map_probe("cfi_probe", &elan_104nc_map );
|
||||
if( !all_mtd ) {
|
||||
cleanup_elan_104nc();
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
all_mtd->owner = THIS_MODULE;
|
||||
|
||||
/* Create MTD devices for each partition. */
|
||||
add_mtd_partitions( all_mtd, partition_info, NUM_PARTITIONS );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(init_elan_104nc);
|
||||
module_exit(cleanup_elan_104nc);
|
||||
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Arcom Control Systems Ltd.");
|
||||
MODULE_DESCRIPTION("MTD map driver for Arcom Control Systems ELAN-104NC");
|
|
@ -2,7 +2,7 @@
|
|||
* ichxrom.c
|
||||
*
|
||||
* Normal mappings of chips in physical memory
|
||||
* $Id: ichxrom.c,v 1.16 2004/11/28 09:40:39 dwmw2 Exp $
|
||||
* $Id: ichxrom.c,v 1.18 2005/07/07 10:26:20 dwmw2 Exp $
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -338,9 +338,9 @@ static struct pci_device_id ichxrom_pci_tbl[] __devinitdata = {
|
|||
{ 0, },
|
||||
};
|
||||
|
||||
#if 0
|
||||
MODULE_DEVICE_TABLE(pci, ichxrom_pci_tbl);
|
||||
|
||||
#if 0
|
||||
static struct pci_driver ichxrom_driver = {
|
||||
.name = MOD_NAME,
|
||||
.id_table = ichxrom_pci_tbl,
|
||||
|
@ -366,7 +366,7 @@ static int __init init_ichxrom(void)
|
|||
}
|
||||
return -ENXIO;
|
||||
#if 0
|
||||
return pci_module_init(&ichxrom_driver);
|
||||
return pci_register_driver(&ichxrom_driver);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: ixp2000.c,v 1.5 2004/11/16 17:15:48 dsaxena Exp $
|
||||
* $Id: ixp2000.c,v 1.6 2005/03/18 14:07:46 gleixner Exp $
|
||||
*
|
||||
* drivers/mtd/maps/ixp2000.c
|
||||
*
|
||||
|
@ -216,11 +216,6 @@ static int ixp2000_flash_probe(struct device *_dev)
|
|||
goto Error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup read mode for FLASH
|
||||
*/
|
||||
*IXP2000_SLOWPORT_FRM = 1;
|
||||
|
||||
#if defined(__ARMEB__)
|
||||
/*
|
||||
* Enable erratum 44 workaround for NPUs with broken slowport
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
* $Id: $
|
||||
*
|
||||
* Map driver for the Mainstone developer platform.
|
||||
*
|
||||
* Author: Nicolas Pitre
|
||||
* Copyright: (C) 2001 MontaVista Software Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/arch/pxa-regs.h>
|
||||
#include <asm/arch/mainstone.h>
|
||||
|
||||
|
||||
#define ROM_ADDR 0x00000000
|
||||
#define FLASH_ADDR 0x04000000
|
||||
|
||||
#define WINDOW_SIZE 0x04000000
|
||||
|
||||
static void mainstone_map_inval_cache(struct map_info *map, unsigned long from,
|
||||
ssize_t len)
|
||||
{
|
||||
consistent_sync((char *)map->cached + from, len, DMA_FROM_DEVICE);
|
||||
}
|
||||
|
||||
static struct map_info mainstone_maps[2] = { {
|
||||
.size = WINDOW_SIZE,
|
||||
.phys = PXA_CS0_PHYS,
|
||||
.inval_cache = mainstone_map_inval_cache,
|
||||
}, {
|
||||
.size = WINDOW_SIZE,
|
||||
.phys = PXA_CS1_PHYS,
|
||||
.inval_cache = mainstone_map_inval_cache,
|
||||
} };
|
||||
|
||||
static struct mtd_partition mainstone_partitions[] = {
|
||||
{
|
||||
.name = "Bootloader",
|
||||
.size = 0x00040000,
|
||||
.offset = 0,
|
||||
.mask_flags = MTD_WRITEABLE /* force read-only */
|
||||
},{
|
||||
.name = "Kernel",
|
||||
.size = 0x00400000,
|
||||
.offset = 0x00040000,
|
||||
},{
|
||||
.name = "Filesystem",
|
||||
.size = MTDPART_SIZ_FULL,
|
||||
.offset = 0x00440000
|
||||
}
|
||||
};
|
||||
|
||||
static struct mtd_info *mymtds[2];
|
||||
static struct mtd_partition *parsed_parts[2];
|
||||
static int nr_parsed_parts[2];
|
||||
|
||||
static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
|
||||
|
||||
static int __init init_mainstone(void)
|
||||
{
|
||||
int SW7 = 0; /* FIXME: get from SCR (Mst doc section 3.2.1.1) */
|
||||
int ret = 0, i;
|
||||
|
||||
mainstone_maps[0].bankwidth = (BOOT_DEF & 1) ? 2 : 4;
|
||||
mainstone_maps[1].bankwidth = 4;
|
||||
|
||||
/* Compensate for SW7 which swaps the flash banks */
|
||||
mainstone_maps[SW7].name = "processor flash";
|
||||
mainstone_maps[SW7 ^ 1].name = "main board flash";
|
||||
|
||||
printk(KERN_NOTICE "Mainstone configured to boot from %s\n",
|
||||
mainstone_maps[0].name);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
mainstone_maps[i].virt = ioremap(mainstone_maps[i].phys,
|
||||
WINDOW_SIZE);
|
||||
if (!mainstone_maps[i].virt) {
|
||||
printk(KERN_WARNING "Failed to ioremap %s\n",
|
||||
mainstone_maps[i].name);
|
||||
if (!ret)
|
||||
ret = -ENOMEM;
|
||||
continue;
|
||||
}
|
||||
mainstone_maps[i].cached =
|
||||
ioremap_cached(mainstone_maps[i].phys, WINDOW_SIZE);
|
||||
if (!mainstone_maps[i].cached)
|
||||
printk(KERN_WARNING "Failed to ioremap cached %s\n",
|
||||
mainstone_maps[i].name);
|
||||
simple_map_init(&mainstone_maps[i]);
|
||||
|
||||
printk(KERN_NOTICE
|
||||
"Probing %s at physical address 0x%08lx"
|
||||
" (%d-bit bankwidth)\n",
|
||||
mainstone_maps[i].name, mainstone_maps[i].phys,
|
||||
mainstone_maps[i].bankwidth * 8);
|
||||
|
||||
mymtds[i] = do_map_probe("cfi_probe", &mainstone_maps[i]);
|
||||
|
||||
if (!mymtds[i]) {
|
||||
iounmap((void *)mainstone_maps[i].virt);
|
||||
if (mainstone_maps[i].cached)
|
||||
iounmap(mainstone_maps[i].cached);
|
||||
if (!ret)
|
||||
ret = -EIO;
|
||||
continue;
|
||||
}
|
||||
mymtds[i]->owner = THIS_MODULE;
|
||||
|
||||
ret = parse_mtd_partitions(mymtds[i], probes,
|
||||
&parsed_parts[i], 0);
|
||||
|
||||
if (ret > 0)
|
||||
nr_parsed_parts[i] = ret;
|
||||
}
|
||||
|
||||
if (!mymtds[0] && !mymtds[1])
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!mymtds[i]) {
|
||||
printk(KERN_WARNING "%s is absent. Skipping\n",
|
||||
mainstone_maps[i].name);
|
||||
} else if (nr_parsed_parts[i]) {
|
||||
add_mtd_partitions(mymtds[i], parsed_parts[i],
|
||||
nr_parsed_parts[i]);
|
||||
} else if (!i) {
|
||||
printk("Using static partitions on %s\n",
|
||||
mainstone_maps[i].name);
|
||||
add_mtd_partitions(mymtds[i], mainstone_partitions,
|
||||
ARRAY_SIZE(mainstone_partitions));
|
||||
} else {
|
||||
printk("Registering %s as whole device\n",
|
||||
mainstone_maps[i].name);
|
||||
add_mtd_device(mymtds[i]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cleanup_mainstone(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!mymtds[i])
|
||||
continue;
|
||||
|
||||
if (nr_parsed_parts[i] || !i)
|
||||
del_mtd_partitions(mymtds[i]);
|
||||
else
|
||||
del_mtd_device(mymtds[i]);
|
||||
|
||||
map_destroy(mymtds[i]);
|
||||
iounmap((void *)mainstone_maps[i].virt);
|
||||
if (mainstone_maps[i].cached)
|
||||
iounmap(mainstone_maps[i].cached);
|
||||
kfree(parsed_parts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
module_init(init_mainstone);
|
||||
module_exit(cleanup_mainstone);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
|
||||
MODULE_DESCRIPTION("MTD map driver for Intel Mainstone");
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: map_funcs.c,v 1.9 2004/07/13 22:33:15 dwmw2 Exp $
|
||||
* $Id: map_funcs.c,v 1.10 2005/06/06 23:04:36 tpoynor Exp $
|
||||
*
|
||||
* Out-of-line map I/O functions for simple maps when CONFIG_COMPLEX_MAPPINGS
|
||||
* is enabled.
|
||||
|
@ -9,23 +9,24 @@
|
|||
#include <linux/module.h>
|
||||
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/xip.h>
|
||||
|
||||
static map_word simple_map_read(struct map_info *map, unsigned long ofs)
|
||||
static map_word __xipram simple_map_read(struct map_info *map, unsigned long ofs)
|
||||
{
|
||||
return inline_map_read(map, ofs);
|
||||
}
|
||||
|
||||
static void simple_map_write(struct map_info *map, const map_word datum, unsigned long ofs)
|
||||
static void __xipram simple_map_write(struct map_info *map, const map_word datum, unsigned long ofs)
|
||||
{
|
||||
inline_map_write(map, datum, ofs);
|
||||
}
|
||||
|
||||
static void simple_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
|
||||
static void __xipram simple_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
|
||||
{
|
||||
inline_map_copy_from(map, to, from, len);
|
||||
}
|
||||
|
||||
static void simple_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
|
||||
static void __xipram simple_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
|
||||
{
|
||||
inline_map_copy_to(map, to, from, len);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Flash memory support for various TI OMAP boards
|
||||
*
|
||||
* Copyright (C) 2001-2002 MontaVista Software Inc.
|
||||
* Copyright (C) 2003-2004 Texas Instruments
|
||||
* Copyright (C) 2004 Nokia Corporation
|
||||
*
|
||||
* Assembled using driver code copyright the companies above
|
||||
* and written by David Brownell, Jian Zhang <jzhang@ti.com>,
|
||||
* Tony Lindgren <tony@atomide.com> and others.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
|
||||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
||||
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/mach/flash.h>
|
||||
#include <asm/arch/tc.h>
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
static const char *part_probes[] = { /* "RedBoot", */ "cmdlinepart", NULL };
|
||||
#endif
|
||||
|
||||
struct omapflash_info {
|
||||
struct mtd_partition *parts;
|
||||
struct mtd_info *mtd;
|
||||
struct map_info map;
|
||||
};
|
||||
|
||||
static void omap_set_vpp(struct map_info *map, int enable)
|
||||
{
|
||||
static int count;
|
||||
|
||||
if (enable) {
|
||||
if (count++ == 0)
|
||||
OMAP_EMIFS_CONFIG_REG |= OMAP_EMIFS_CONFIG_WP;
|
||||
} else {
|
||||
if (count && (--count == 0))
|
||||
OMAP_EMIFS_CONFIG_REG &= ~OMAP_EMIFS_CONFIG_WP;
|
||||
}
|
||||
}
|
||||
|
||||
static int __devinit omapflash_probe(struct device *dev)
|
||||
{
|
||||
int err;
|
||||
struct omapflash_info *info;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct flash_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct resource *res = pdev->resource;
|
||||
unsigned long size = res->end - res->start + 1;
|
||||
|
||||
info = kmalloc(sizeof(struct omapflash_info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(info, 0, sizeof(struct omapflash_info));
|
||||
|
||||
if (!request_mem_region(res->start, size, "flash")) {
|
||||
err = -EBUSY;
|
||||
goto out_free_info;
|
||||
}
|
||||
|
||||
info->map.virt = ioremap(res->start, size);
|
||||
if (!info->map.virt) {
|
||||
err = -ENOMEM;
|
||||
goto out_release_mem_region;
|
||||
}
|
||||
info->map.name = pdev->dev.bus_id;
|
||||
info->map.phys = res->start;
|
||||
info->map.size = size;
|
||||
info->map.bankwidth = pdata->width;
|
||||
info->map.set_vpp = omap_set_vpp;
|
||||
|
||||
simple_map_init(&info->map);
|
||||
info->mtd = do_map_probe(pdata->map_name, &info->map);
|
||||
if (!info->mtd) {
|
||||
err = -EIO;
|
||||
goto out_iounmap;
|
||||
}
|
||||
info->mtd->owner = THIS_MODULE;
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
err = parse_mtd_partitions(info->mtd, part_probes, &info->parts, 0);
|
||||
if (err > 0)
|
||||
add_mtd_partitions(info->mtd, info->parts, err);
|
||||
else if (err < 0 && pdata->parts)
|
||||
add_mtd_partitions(info->mtd, pdata->parts, pdata->nr_parts);
|
||||
else
|
||||
#endif
|
||||
add_mtd_device(info->mtd);
|
||||
|
||||
dev_set_drvdata(&pdev->dev, info);
|
||||
|
||||
return 0;
|
||||
|
||||
out_iounmap:
|
||||
iounmap(info->map.virt);
|
||||
out_release_mem_region:
|
||||
release_mem_region(res->start, size);
|
||||
out_free_info:
|
||||
kfree(info);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit omapflash_remove(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct omapflash_info *info = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
|
||||
if (info) {
|
||||
if (info->parts) {
|
||||
del_mtd_partitions(info->mtd);
|
||||
kfree(info->parts);
|
||||
} else
|
||||
del_mtd_device(info->mtd);
|
||||
map_destroy(info->mtd);
|
||||
release_mem_region(info->map.phys, info->map.size);
|
||||
iounmap((void __iomem *) info->map.virt);
|
||||
kfree(info);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct device_driver omapflash_driver = {
|
||||
.name = "omapflash",
|
||||
.bus = &platform_bus_type,
|
||||
.probe = omapflash_probe,
|
||||
.remove = __devexit_p(omapflash_remove),
|
||||
};
|
||||
|
||||
static int __init omapflash_init(void)
|
||||
{
|
||||
return driver_register(&omapflash_driver);
|
||||
}
|
||||
|
||||
static void __exit omapflash_exit(void)
|
||||
{
|
||||
driver_unregister(&omapflash_driver);
|
||||
}
|
||||
|
||||
module_init(omapflash_init);
|
||||
module_exit(omapflash_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("MTD NOR map driver for TI OMAP boards");
|
||||
|
|
@ -1,203 +0,0 @@
|
|||
/*
|
||||
* Flash memory access on Alchemy Pb1550 board
|
||||
*
|
||||
* $Id: pb1550-flash.c,v 1.6 2004/11/04 13:24:15 gleixner Exp $
|
||||
*
|
||||
* (C) 2004 Embedded Edge, LLC, based on pb1550-flash.c:
|
||||
* (C) 2003 Pete Popov <ppopov@pacbell.net>
|
||||
*
|
||||
*/
|
||||
|
||||
#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>
|
||||
#include <asm/au1000.h>
|
||||
#include <asm/pb1550.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 pb1550_map = {
|
||||
.name = "Pb1550 flash",
|
||||
};
|
||||
|
||||
static unsigned char flash_bankwidth = 4;
|
||||
|
||||
/*
|
||||
* Support only 64MB NOR Flash parts
|
||||
*/
|
||||
|
||||
#ifdef PB1550_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 pb1550_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(PB1550_BOOT_ONLY)
|
||||
static struct mtd_partition pb1550_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(PB1550_USER_ONLY)
|
||||
static struct mtd_partition pb1550_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_PB1550 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)
|
||||
{
|
||||
u16 boot_swapboot;
|
||||
boot_swapboot = (au_readl(MEM_STSTAT) & (0x7<<1)) |
|
||||
((bcsr->status >> 6) & 0x1);
|
||||
printk("Pb1550 MTD: boot:swap %d\n", boot_swapboot);
|
||||
|
||||
switch (boot_swapboot) {
|
||||
case 0: /* 512Mbit devices, both enabled */
|
||||
case 1:
|
||||
case 8:
|
||||
case 9:
|
||||
#if defined(PB1550_BOTH_BANKS)
|
||||
window_addr = 0x18000000;
|
||||
window_size = 0x8000000;
|
||||
#elif defined(PB1550_BOOT_ONLY)
|
||||
window_addr = 0x1C000000;
|
||||
window_size = 0x4000000;
|
||||
#else /* USER ONLY */
|
||||
window_addr = 0x1E000000;
|
||||
window_size = 0x4000000;
|
||||
#endif
|
||||
break;
|
||||
case 0xC:
|
||||
case 0xD:
|
||||
case 0xE:
|
||||
case 0xF:
|
||||
/* 64 MB Boot NOR Flash is disabled */
|
||||
/* and the start address is moved to 0x0C00000 */
|
||||
window_addr = 0x0C000000;
|
||||
window_size = 0x4000000;
|
||||
default:
|
||||
printk("Pb1550 MTD: unsupported boot:swap setting\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init pb1550_mtd_init(void)
|
||||
{
|
||||
struct mtd_partition *parts;
|
||||
int nb_parts = 0;
|
||||
|
||||
/* Default flash bankwidth */
|
||||
pb1550_map.bankwidth = flash_bankwidth;
|
||||
|
||||
if (setup_flash_params())
|
||||
return -ENXIO;
|
||||
|
||||
/*
|
||||
* Static partition definition selection
|
||||
*/
|
||||
parts = pb1550_partitions;
|
||||
nb_parts = NB_OF(pb1550_partitions);
|
||||
pb1550_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 "Pb1550 flash: probing %d-bit flash bus\n",
|
||||
pb1550_map.bankwidth*8);
|
||||
pb1550_map.virt = ioremap(window_addr, window_size);
|
||||
mymtd = do_map_probe("cfi_probe", &pb1550_map);
|
||||
if (!mymtd) return -ENXIO;
|
||||
mymtd->owner = THIS_MODULE;
|
||||
|
||||
add_mtd_partitions(mymtd, parts, nb_parts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit pb1550_mtd_cleanup(void)
|
||||
{
|
||||
if (mymtd) {
|
||||
del_mtd_partitions(mymtd);
|
||||
map_destroy(mymtd);
|
||||
}
|
||||
}
|
||||
|
||||
module_init(pb1550_mtd_init);
|
||||
module_exit(pb1550_mtd_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Embedded Edge, LLC");
|
||||
MODULE_DESCRIPTION("Pb1550 mtd map driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,178 +0,0 @@
|
|||
/*
|
||||
* Flash memory access on Alchemy Pb1xxx boards
|
||||
*
|
||||
* (C) 2001 Pete Popov <ppopov@mvista.com>
|
||||
*
|
||||
* $Id: pb1xxx-flash.c,v 1.14 2004/11/04 13:24:15 gleixner Exp $
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.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 WINDOW_ADDR 0x1F800000
|
||||
#define WINDOW_SIZE 0x800000
|
||||
|
||||
static struct mtd_partition pb1xxx_partitions[] = {
|
||||
{
|
||||
.name = "yamon env",
|
||||
.size = 0x00020000,
|
||||
.offset = 0,
|
||||
.mask_flags = MTD_WRITEABLE},
|
||||
{
|
||||
.name = "User FS",
|
||||
.size = 0x003e0000,
|
||||
.offset = 0x20000,},
|
||||
{
|
||||
.name = "boot code",
|
||||
.size = 0x100000,
|
||||
.offset = 0x400000,
|
||||
.mask_flags = MTD_WRITEABLE},
|
||||
{
|
||||
.name = "raw/kernel",
|
||||
.size = 0x300000,
|
||||
.offset = 0x500000}
|
||||
};
|
||||
|
||||
#elif defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100)
|
||||
|
||||
#if defined(CONFIG_MTD_PB1500_BOOT) && defined(CONFIG_MTD_PB1500_USER)
|
||||
/* both 32MB banks will be used. Combine the first 32MB bank and the
|
||||
* first 28MB of the second bank together into a single jffs/jffs2
|
||||
* partition.
|
||||
*/
|
||||
#define WINDOW_ADDR 0x1C000000
|
||||
#define WINDOW_SIZE 0x4000000
|
||||
static struct mtd_partition pb1xxx_partitions[] = {
|
||||
{
|
||||
.name = "User FS",
|
||||
.size = 0x3c00000,
|
||||
.offset = 0x0000000
|
||||
},{
|
||||
.name = "yamon",
|
||||
.size = 0x0100000,
|
||||
.offset = 0x3c00000,
|
||||
.mask_flags = MTD_WRITEABLE
|
||||
},{
|
||||
.name = "raw kernel",
|
||||
.size = 0x02c0000,
|
||||
.offset = 0x3d00000
|
||||
}
|
||||
};
|
||||
#elif defined(CONFIG_MTD_PB1500_BOOT) && !defined(CONFIG_MTD_PB1500_USER)
|
||||
#define WINDOW_ADDR 0x1E000000
|
||||
#define WINDOW_SIZE 0x2000000
|
||||
static struct mtd_partition pb1xxx_partitions[] = {
|
||||
{
|
||||
.name = "User FS",
|
||||
.size = 0x1c00000,
|
||||
.offset = 0x0000000
|
||||
},{
|
||||
.name = "yamon",
|
||||
.size = 0x0100000,
|
||||
.offset = 0x1c00000,
|
||||
.mask_flags = MTD_WRITEABLE
|
||||
},{
|
||||
.name = "raw kernel",
|
||||
.size = 0x02c0000,
|
||||
.offset = 0x1d00000
|
||||
}
|
||||
};
|
||||
#elif !defined(CONFIG_MTD_PB1500_BOOT) && defined(CONFIG_MTD_PB1500_USER)
|
||||
#define WINDOW_ADDR 0x1C000000
|
||||
#define WINDOW_SIZE 0x2000000
|
||||
static struct mtd_partition pb1xxx_partitions[] = {
|
||||
{
|
||||
.name = "User FS",
|
||||
.size = 0x1e00000,
|
||||
.offset = 0x0000000
|
||||
},{
|
||||
.name = "raw kernel",
|
||||
.size = 0x0200000,
|
||||
.offset = 0x1e00000,
|
||||
}
|
||||
};
|
||||
#else
|
||||
#error MTD_PB1500 define combo error /* should never happen */
|
||||
#endif
|
||||
#else
|
||||
#error Unsupported board
|
||||
#endif
|
||||
|
||||
#define NAME "Pb1x00 Linux Flash"
|
||||
#define PADDR WINDOW_ADDR
|
||||
#define BUSWIDTH 4
|
||||
#define SIZE WINDOW_SIZE
|
||||
#define PARTITIONS 4
|
||||
|
||||
static struct map_info pb1xxx_mtd_map = {
|
||||
.name = NAME,
|
||||
.size = SIZE,
|
||||
.bankwidth = BUSWIDTH,
|
||||
.phys = PADDR,
|
||||
};
|
||||
|
||||
static struct mtd_info *pb1xxx_mtd;
|
||||
|
||||
int __init pb1xxx_mtd_init(void)
|
||||
{
|
||||
struct mtd_partition *parts;
|
||||
int nb_parts = 0;
|
||||
char *part_type;
|
||||
|
||||
/*
|
||||
* Static partition definition selection
|
||||
*/
|
||||
part_type = "static";
|
||||
parts = pb1xxx_partitions;
|
||||
nb_parts = ARRAY_SIZE(pb1xxx_partitions);
|
||||
|
||||
/*
|
||||
* Now let's probe for the actual flash. Do it here since
|
||||
* specific machine settings might have been set above.
|
||||
*/
|
||||
printk(KERN_NOTICE "Pb1xxx flash: probing %d-bit flash bus\n",
|
||||
BUSWIDTH*8);
|
||||
pb1xxx_mtd_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE);
|
||||
|
||||
simple_map_init(&pb1xxx_mtd_map);
|
||||
|
||||
pb1xxx_mtd = do_map_probe("cfi_probe", &pb1xxx_mtd_map);
|
||||
if (!pb1xxx_mtd) return -ENXIO;
|
||||
pb1xxx_mtd->owner = THIS_MODULE;
|
||||
|
||||
add_mtd_partitions(pb1xxx_mtd, parts, nb_parts);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit pb1xxx_mtd_cleanup(void)
|
||||
{
|
||||
if (pb1xxx_mtd) {
|
||||
del_mtd_partitions(pb1xxx_mtd);
|
||||
map_destroy(pb1xxx_mtd);
|
||||
iounmap((void *) pb1xxx_mtd_map.virt);
|
||||
}
|
||||
}
|
||||
|
||||
module_init(pb1xxx_mtd_init);
|
||||
module_exit(pb1xxx_mtd_cleanup);
|
||||
|
||||
MODULE_AUTHOR("Pete Popov");
|
||||
MODULE_DESCRIPTION("Pb1xxx CFI map driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -7,7 +7,7 @@
|
|||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* $Id: pci.c,v 1.9 2004/11/28 09:40:40 dwmw2 Exp $
|
||||
* $Id: pci.c,v 1.10 2005/03/18 14:04:35 gleixner Exp $
|
||||
*
|
||||
* Generic PCI memory map driver. We support the following boards:
|
||||
* - Intel IQ80310 ATU.
|
||||
|
@ -370,7 +370,7 @@ static struct pci_driver mtd_pci_driver = {
|
|||
|
||||
static int __init mtd_pci_maps_init(void)
|
||||
{
|
||||
return pci_module_init(&mtd_pci_driver);
|
||||
return pci_register_driver(&mtd_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit mtd_pci_maps_exit(void)
|
||||
|
|
|
@ -0,0 +1,278 @@
|
|||
/* drivers/mtd/maps/plat-ram.c
|
||||
*
|
||||
* (c) 2004-2005 Simtec Electronics
|
||||
* http://www.simtec.co.uk/products/SWLINUX/
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*
|
||||
* Generic platfrom device based RAM map
|
||||
*
|
||||
* $Id: plat-ram.c,v 1.3 2005/03/19 22:41:27 gleixner Exp $
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/plat-ram.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
/* private structure for each mtd platform ram device created */
|
||||
|
||||
struct platram_info {
|
||||
struct device *dev;
|
||||
struct mtd_info *mtd;
|
||||
struct map_info map;
|
||||
struct mtd_partition *partitions;
|
||||
struct resource *area;
|
||||
struct platdata_mtd_ram *pdata;
|
||||
};
|
||||
|
||||
/* to_platram_info()
|
||||
*
|
||||
* device private data to struct platram_info conversion
|
||||
*/
|
||||
|
||||
static inline struct platram_info *to_platram_info(struct device *dev)
|
||||
{
|
||||
return (struct platram_info *)dev_get_drvdata(dev);
|
||||
}
|
||||
|
||||
/* platram_setrw
|
||||
*
|
||||
* call the platform device's set rw/ro control
|
||||
*
|
||||
* to = 0 => read-only
|
||||
* = 1 => read-write
|
||||
*/
|
||||
|
||||
static inline void platram_setrw(struct platram_info *info, int to)
|
||||
{
|
||||
if (info->pdata == NULL)
|
||||
return;
|
||||
|
||||
if (info->pdata->set_rw != NULL)
|
||||
(info->pdata->set_rw)(info->dev, to);
|
||||
}
|
||||
|
||||
/* platram_remove
|
||||
*
|
||||
* called to remove the device from the driver's control
|
||||
*/
|
||||
|
||||
static int platram_remove(struct device *dev)
|
||||
{
|
||||
struct platram_info *info = to_platram_info(dev);
|
||||
|
||||
dev_set_drvdata(dev, NULL);
|
||||
|
||||
dev_dbg(dev, "removing device\n");
|
||||
|
||||
if (info == NULL)
|
||||
return 0;
|
||||
|
||||
if (info->mtd) {
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
if (info->partitions) {
|
||||
del_mtd_partitions(info->mtd);
|
||||
kfree(info->partitions);
|
||||
}
|
||||
#endif
|
||||
del_mtd_device(info->mtd);
|
||||
map_destroy(info->mtd);
|
||||
}
|
||||
|
||||
/* ensure ram is left read-only */
|
||||
|
||||
platram_setrw(info, PLATRAM_RO);
|
||||
|
||||
/* release resources */
|
||||
|
||||
if (info->area) {
|
||||
release_resource(info->area);
|
||||
kfree(info->area);
|
||||
}
|
||||
|
||||
if (info->map.virt != NULL)
|
||||
iounmap(info->map.virt);
|
||||
|
||||
kfree(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* platram_probe
|
||||
*
|
||||
* called from device drive system when a device matching our
|
||||
* driver is found.
|
||||
*/
|
||||
|
||||
static int platram_probe(struct device *dev)
|
||||
{
|
||||
struct platform_device *pd = to_platform_device(dev);
|
||||
struct platdata_mtd_ram *pdata;
|
||||
struct platram_info *info;
|
||||
struct resource *res;
|
||||
int err = 0;
|
||||
|
||||
dev_dbg(dev, "probe entered\n");
|
||||
|
||||
if (dev->platform_data == NULL) {
|
||||
dev_err(dev, "no platform data supplied\n");
|
||||
err = -ENOENT;
|
||||
goto exit_error;
|
||||
}
|
||||
|
||||
pdata = dev->platform_data;
|
||||
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (info == NULL) {
|
||||
dev_err(dev, "no memory for flash info\n");
|
||||
err = -ENOMEM;
|
||||
goto exit_error;
|
||||
}
|
||||
|
||||
memset(info, 0, sizeof(*info));
|
||||
dev_set_drvdata(dev, info);
|
||||
|
||||
info->dev = dev;
|
||||
info->pdata = pdata;
|
||||
|
||||
/* get the resource for the memory mapping */
|
||||
|
||||
res = platform_get_resource(pd, IORESOURCE_MEM, 0);
|
||||
|
||||
if (res == NULL) {
|
||||
dev_err(dev, "no memory resource specified\n");
|
||||
err = -ENOENT;
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "got platform resource %p (0x%lx)\n", res, res->start);
|
||||
|
||||
/* setup map parameters */
|
||||
|
||||
info->map.phys = res->start;
|
||||
info->map.size = (res->end - res->start) + 1;
|
||||
info->map.name = pdata->mapname != NULL ? pdata->mapname : pd->name;
|
||||
info->map.bankwidth = pdata->bankwidth;
|
||||
|
||||
/* register our usage of the memory area */
|
||||
|
||||
info->area = request_mem_region(res->start, info->map.size, pd->name);
|
||||
if (info->area == NULL) {
|
||||
dev_err(dev, "failed to request memory region\n");
|
||||
err = -EIO;
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
/* remap the memory area */
|
||||
|
||||
info->map.virt = ioremap(res->start, info->map.size);
|
||||
dev_dbg(dev, "virt %p, %lu bytes\n", info->map.virt, info->map.size);
|
||||
|
||||
if (info->map.virt == NULL) {
|
||||
dev_err(dev, "failed to ioremap() region\n");
|
||||
err = -EIO;
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
simple_map_init(&info->map);
|
||||
|
||||
dev_dbg(dev, "initialised map, probing for mtd\n");
|
||||
|
||||
/* probe for the right mtd map driver */
|
||||
|
||||
info->mtd = do_map_probe("map_ram" , &info->map);
|
||||
if (info->mtd == NULL) {
|
||||
dev_err(dev, "failed to probe for map_ram\n");
|
||||
err = -ENOMEM;
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
info->mtd->owner = THIS_MODULE;
|
||||
|
||||
platram_setrw(info, PLATRAM_RW);
|
||||
|
||||
/* check to see if there are any available partitions, or wether
|
||||
* to add this device whole */
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
if (pdata->nr_partitions > 0) {
|
||||
const char **probes = { NULL };
|
||||
|
||||
if (pdata->probes)
|
||||
probes = (const char **)pdata->probes;
|
||||
|
||||
err = parse_mtd_partitions(info->mtd, probes,
|
||||
&info->partitions, 0);
|
||||
if (err > 0) {
|
||||
err = add_mtd_partitions(info->mtd, info->partitions,
|
||||
err);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_MTD_PARTITIONS */
|
||||
|
||||
if (add_mtd_device(info->mtd)) {
|
||||
dev_err(dev, "add_mtd_device() failed\n");
|
||||
err = -ENOMEM;
|
||||
}
|
||||
|
||||
dev_info(dev, "registered mtd device\n");
|
||||
return err;
|
||||
|
||||
exit_free:
|
||||
platram_remove(dev);
|
||||
exit_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
/* device driver info */
|
||||
|
||||
static struct device_driver platram_driver = {
|
||||
.name = "mtd-ram",
|
||||
.bus = &platform_bus_type,
|
||||
.probe = platram_probe,
|
||||
.remove = platram_remove,
|
||||
};
|
||||
|
||||
/* module init/exit */
|
||||
|
||||
static int __init platram_init(void)
|
||||
{
|
||||
printk("Generic platform RAM MTD, (c) 2004 Simtec Electronics\n");
|
||||
return driver_register(&platram_driver);
|
||||
}
|
||||
|
||||
static void __exit platram_exit(void)
|
||||
{
|
||||
driver_unregister(&platram_driver);
|
||||
}
|
||||
|
||||
module_init(platram_init);
|
||||
module_exit(platram_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
|
||||
MODULE_DESCRIPTION("MTD platform RAM map driver");
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* MTD map driver for BIOS Flash on Intel SCB2 boards
|
||||
* $Id: scb2_flash.c,v 1.11 2004/11/28 09:40:40 dwmw2 Exp $
|
||||
* $Id: scb2_flash.c,v 1.12 2005/03/18 14:04:35 gleixner Exp $
|
||||
* Copyright (C) 2002 Sun Microsystems, Inc.
|
||||
* Tim Hockin <thockin@sun.com>
|
||||
*
|
||||
|
@ -238,7 +238,7 @@ static struct pci_driver scb2_flash_driver = {
|
|||
static int __init
|
||||
scb2_flash_init(void)
|
||||
{
|
||||
return pci_module_init(&scb2_flash_driver);
|
||||
return pci_register_driver(&scb2_flash_driver);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* Copyright (C) 2001 Lineo Japan, Inc.
|
||||
* Copyright (C) 2002 SHARP
|
||||
*
|
||||
* $Id: sharpsl-flash.c,v 1.2 2004/11/24 20:38:06 rpurdie Exp $
|
||||
* $Id: sharpsl-flash.c,v 1.5 2005/03/21 08:42:11 rpurdie Exp $
|
||||
*
|
||||
* based on rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp
|
||||
* Handle mapping of the flash on the RPX Lite and CLLF boards
|
||||
|
@ -24,13 +24,14 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
#define WINDOW_ADDR 0x00000000
|
||||
#define WINDOW_SIZE 0x01000000
|
||||
#define WINDOW_SIZE 0x00800000
|
||||
#define BANK_WIDTH 2
|
||||
|
||||
static struct mtd_info *mymtd;
|
||||
|
@ -44,9 +45,7 @@ struct map_info sharpsl_map = {
|
|||
|
||||
static struct mtd_partition sharpsl_partitions[1] = {
|
||||
{
|
||||
name: "Filesystem",
|
||||
size: 0x006d0000,
|
||||
offset: 0x00120000
|
||||
name: "Boot PROM Filesystem",
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -58,12 +57,16 @@ int __init init_sharpsl(void)
|
|||
int nb_parts = 0;
|
||||
char *part_type = "static";
|
||||
|
||||
printk(KERN_NOTICE "Sharp SL series flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
|
||||
printk(KERN_NOTICE "Sharp SL series flash device: %x at %x\n",
|
||||
WINDOW_SIZE, WINDOW_ADDR);
|
||||
sharpsl_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE);
|
||||
if (!sharpsl_map.virt) {
|
||||
printk("Failed to ioremap\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
simple_map_init(&sharpsl_map);
|
||||
|
||||
mymtd = do_map_probe("map_rom", &sharpsl_map);
|
||||
if (!mymtd) {
|
||||
iounmap(sharpsl_map.virt);
|
||||
|
@ -72,6 +75,22 @@ int __init init_sharpsl(void)
|
|||
|
||||
mymtd->owner = THIS_MODULE;
|
||||
|
||||
if (machine_is_corgi() || machine_is_shepherd() || machine_is_husky()
|
||||
|| machine_is_poodle()) {
|
||||
sharpsl_partitions[0].size=0x006d0000;
|
||||
sharpsl_partitions[0].offset=0x00120000;
|
||||
} else if (machine_is_tosa()) {
|
||||
sharpsl_partitions[0].size=0x006a0000;
|
||||
sharpsl_partitions[0].offset=0x00160000;
|
||||
} else if (machine_is_spitz()) {
|
||||
sharpsl_partitions[0].size=0x006b0000;
|
||||
sharpsl_partitions[0].offset=0x00140000;
|
||||
} else {
|
||||
map_destroy(mymtd);
|
||||
iounmap(sharpsl_map.virt);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
parts = sharpsl_partitions;
|
||||
nb_parts = NB_OF(sharpsl_partitions);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: mtdchar.c,v 1.66 2005/01/05 18:05:11 dwmw2 Exp $
|
||||
* $Id: mtdchar.c,v 1.73 2005/07/04 17:36:41 gleixner Exp $
|
||||
*
|
||||
* Character-device access to raw MTD devices.
|
||||
*
|
||||
|
@ -15,27 +15,30 @@
|
|||
#include <linux/fs.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#ifdef CONFIG_DEVFS_FS
|
||||
#include <linux/devfs_fs_kernel.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
static struct class *mtd_class;
|
||||
|
||||
static void mtd_notify_add(struct mtd_info* mtd)
|
||||
{
|
||||
if (!mtd)
|
||||
return;
|
||||
|
||||
devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
|
||||
S_IFCHR | S_IRUGO | S_IWUGO, "mtd/%d", mtd->index);
|
||||
|
||||
devfs_mk_cdev(MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
|
||||
S_IFCHR | S_IRUGO, "mtd/%dro", mtd->index);
|
||||
class_device_create(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
|
||||
NULL, "mtd%d", mtd->index);
|
||||
|
||||
class_device_create(mtd_class,
|
||||
MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
|
||||
NULL, "mtd%dro", mtd->index);
|
||||
}
|
||||
|
||||
static void mtd_notify_remove(struct mtd_info* mtd)
|
||||
{
|
||||
if (!mtd)
|
||||
return;
|
||||
devfs_remove("mtd/%d", mtd->index);
|
||||
devfs_remove("mtd/%dro", mtd->index);
|
||||
|
||||
class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2));
|
||||
class_device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1));
|
||||
}
|
||||
|
||||
static struct mtd_notifier notifier = {
|
||||
|
@ -43,25 +46,25 @@ static struct mtd_notifier notifier = {
|
|||
.remove = mtd_notify_remove,
|
||||
};
|
||||
|
||||
static inline void mtdchar_devfs_init(void)
|
||||
{
|
||||
devfs_mk_dir("mtd");
|
||||
register_mtd_user(¬ifier);
|
||||
}
|
||||
/*
|
||||
* We use file->private_data to store a pointer to the MTDdevice.
|
||||
* Since alighment is at least 32 bits, we have 2 bits free for OTP
|
||||
* modes as well.
|
||||
*/
|
||||
|
||||
static inline void mtdchar_devfs_exit(void)
|
||||
{
|
||||
unregister_mtd_user(¬ifier);
|
||||
devfs_remove("mtd");
|
||||
}
|
||||
#else /* !DEVFS */
|
||||
#define mtdchar_devfs_init() do { } while(0)
|
||||
#define mtdchar_devfs_exit() do { } while(0)
|
||||
#endif
|
||||
#define TO_MTD(file) (struct mtd_info *)((long)((file)->private_data) & ~3L)
|
||||
|
||||
#define MTD_MODE_OTP_FACT 1
|
||||
#define MTD_MODE_OTP_USER 2
|
||||
#define MTD_MODE(file) ((long)((file)->private_data) & 3)
|
||||
|
||||
#define SET_MTD_MODE(file, mode) \
|
||||
do { long __p = (long)((file)->private_data); \
|
||||
(file)->private_data = (void *)((__p & ~3L) | mode); } while (0)
|
||||
|
||||
static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
|
||||
{
|
||||
struct mtd_info *mtd = file->private_data;
|
||||
struct mtd_info *mtd = TO_MTD(file);
|
||||
|
||||
switch (orig) {
|
||||
case 0:
|
||||
|
@ -134,7 +137,7 @@ static int mtd_close(struct inode *inode, struct file *file)
|
|||
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
|
||||
|
||||
mtd = file->private_data;
|
||||
mtd = TO_MTD(file);
|
||||
|
||||
if (mtd->sync)
|
||||
mtd->sync(mtd);
|
||||
|
@ -151,7 +154,7 @@ static int mtd_close(struct inode *inode, struct file *file)
|
|||
|
||||
static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
|
||||
{
|
||||
struct mtd_info *mtd = file->private_data;
|
||||
struct mtd_info *mtd = TO_MTD(file);
|
||||
size_t retlen=0;
|
||||
size_t total_retlen=0;
|
||||
int ret=0;
|
||||
|
@ -178,7 +181,16 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t
|
|||
if (!kbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf);
|
||||
switch (MTD_MODE(file)) {
|
||||
case MTD_MODE_OTP_FACT:
|
||||
ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf);
|
||||
break;
|
||||
case MTD_MODE_OTP_USER:
|
||||
ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
|
||||
break;
|
||||
default:
|
||||
ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf);
|
||||
}
|
||||
/* Nand returns -EBADMSG on ecc errors, but it returns
|
||||
* the data. For our userspace tools it is important
|
||||
* to dump areas with ecc errors !
|
||||
|
@ -196,6 +208,8 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t
|
|||
|
||||
count -= retlen;
|
||||
buf += retlen;
|
||||
if (retlen == 0)
|
||||
count = 0;
|
||||
}
|
||||
else {
|
||||
kfree(kbuf);
|
||||
|
@ -210,7 +224,7 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t
|
|||
|
||||
static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos)
|
||||
{
|
||||
struct mtd_info *mtd = file->private_data;
|
||||
struct mtd_info *mtd = TO_MTD(file);
|
||||
char *kbuf;
|
||||
size_t retlen;
|
||||
size_t total_retlen=0;
|
||||
|
@ -245,7 +259,20 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count
|
|||
return -EFAULT;
|
||||
}
|
||||
|
||||
ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);
|
||||
switch (MTD_MODE(file)) {
|
||||
case MTD_MODE_OTP_FACT:
|
||||
ret = -EROFS;
|
||||
break;
|
||||
case MTD_MODE_OTP_USER:
|
||||
if (!mtd->write_user_prot_reg) {
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
|
||||
break;
|
||||
default:
|
||||
ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);
|
||||
}
|
||||
if (!ret) {
|
||||
*ppos += retlen;
|
||||
total_retlen += retlen;
|
||||
|
@ -276,7 +303,7 @@ static void mtdchar_erase_callback (struct erase_info *instr)
|
|||
static int mtd_ioctl(struct inode *inode, struct file *file,
|
||||
u_int cmd, u_long arg)
|
||||
{
|
||||
struct mtd_info *mtd = file->private_data;
|
||||
struct mtd_info *mtd = TO_MTD(file);
|
||||
void __user *argp = (void __user *)arg;
|
||||
int ret = 0;
|
||||
u_long size;
|
||||
|
@ -518,6 +545,80 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
|
|||
break;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_OTP
|
||||
case OTPSELECT:
|
||||
{
|
||||
int mode;
|
||||
if (copy_from_user(&mode, argp, sizeof(int)))
|
||||
return -EFAULT;
|
||||
SET_MTD_MODE(file, 0);
|
||||
switch (mode) {
|
||||
case MTD_OTP_FACTORY:
|
||||
if (!mtd->read_fact_prot_reg)
|
||||
ret = -EOPNOTSUPP;
|
||||
else
|
||||
SET_MTD_MODE(file, MTD_MODE_OTP_FACT);
|
||||
break;
|
||||
case MTD_OTP_USER:
|
||||
if (!mtd->read_fact_prot_reg)
|
||||
ret = -EOPNOTSUPP;
|
||||
else
|
||||
SET_MTD_MODE(file, MTD_MODE_OTP_USER);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
case MTD_OTP_OFF:
|
||||
break;
|
||||
}
|
||||
file->f_pos = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case OTPGETREGIONCOUNT:
|
||||
case OTPGETREGIONINFO:
|
||||
{
|
||||
struct otp_info *buf = kmalloc(4096, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
ret = -EOPNOTSUPP;
|
||||
switch (MTD_MODE(file)) {
|
||||
case MTD_MODE_OTP_FACT:
|
||||
if (mtd->get_fact_prot_info)
|
||||
ret = mtd->get_fact_prot_info(mtd, buf, 4096);
|
||||
break;
|
||||
case MTD_MODE_OTP_USER:
|
||||
if (mtd->get_user_prot_info)
|
||||
ret = mtd->get_user_prot_info(mtd, buf, 4096);
|
||||
break;
|
||||
}
|
||||
if (ret >= 0) {
|
||||
if (cmd == OTPGETREGIONCOUNT) {
|
||||
int nbr = ret / sizeof(struct otp_info);
|
||||
ret = copy_to_user(argp, &nbr, sizeof(int));
|
||||
} else
|
||||
ret = copy_to_user(argp, buf, ret);
|
||||
if (ret)
|
||||
ret = -EFAULT;
|
||||
}
|
||||
kfree(buf);
|
||||
break;
|
||||
}
|
||||
|
||||
case OTPLOCK:
|
||||
{
|
||||
struct otp_info info;
|
||||
|
||||
if (MTD_MODE(file) != MTD_MODE_OTP_USER)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&info, argp, sizeof(info)))
|
||||
return -EFAULT;
|
||||
if (!mtd->lock_user_prot_reg)
|
||||
return -EOPNOTSUPP;
|
||||
ret = mtd->lock_user_prot_reg(mtd, info.start, info.length);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
ret = -ENOTTY;
|
||||
}
|
||||
|
@ -543,13 +644,22 @@ static int __init init_mtdchar(void)
|
|||
return -EAGAIN;
|
||||
}
|
||||
|
||||
mtdchar_devfs_init();
|
||||
mtd_class = class_create(THIS_MODULE, "mtd");
|
||||
|
||||
if (IS_ERR(mtd_class)) {
|
||||
printk(KERN_ERR "Error creating mtd class.\n");
|
||||
unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
|
||||
return PTR_ERR(mtd_class);
|
||||
}
|
||||
|
||||
register_mtd_user(¬ifier);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cleanup_mtdchar(void)
|
||||
{
|
||||
mtdchar_devfs_exit();
|
||||
unregister_mtd_user(¬ifier);
|
||||
class_destroy(mtd_class);
|
||||
unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: mtdcore.c,v 1.44 2004/11/16 18:28:59 dwmw2 Exp $
|
||||
* $Id: mtdcore.c,v 1.45 2005/02/18 14:34:50 dedekind Exp $
|
||||
*
|
||||
* Core registration and callback routines for MTD
|
||||
* drivers and users.
|
||||
|
@ -149,8 +149,8 @@ void register_mtd_user (struct mtd_notifier *new)
|
|||
}
|
||||
|
||||
/**
|
||||
* register_mtd_user - unregister a 'user' of MTD devices.
|
||||
* @new: pointer to notifier info structure
|
||||
* unregister_mtd_user - unregister a 'user' of MTD devices.
|
||||
* @old: pointer to notifier info structure
|
||||
*
|
||||
* Removes a callback function pair from the list of 'users' to be
|
||||
* notified upon addition or removal of MTD devices. Causes the
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*
|
||||
* This code is GPL
|
||||
*
|
||||
* $Id: mtdpart.c,v 1.51 2004/11/16 18:28:59 dwmw2 Exp $
|
||||
* $Id: mtdpart.c,v 1.53 2005/02/08 17:11:13 nico Exp $
|
||||
*
|
||||
* 02-21-2002 Thomas Gleixner <gleixner@autronix.de>
|
||||
* added support for read_oob, write_oob
|
||||
|
@ -116,6 +116,13 @@ static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t le
|
|||
len, retlen, buf);
|
||||
}
|
||||
|
||||
static int part_get_user_prot_info (struct mtd_info *mtd,
|
||||
struct otp_info *buf, size_t len)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return part->master->get_user_prot_info (part->master, buf, len);
|
||||
}
|
||||
|
||||
static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
{
|
||||
|
@ -124,6 +131,13 @@ static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t le
|
|||
len, retlen, buf);
|
||||
}
|
||||
|
||||
static int part_get_fact_prot_info (struct mtd_info *mtd,
|
||||
struct otp_info *buf, size_t len)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return part->master->get_fact_prot_info (part->master, buf, len);
|
||||
}
|
||||
|
||||
static int part_write (struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
|
@ -182,6 +196,12 @@ static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t l
|
|||
len, retlen, buf);
|
||||
}
|
||||
|
||||
static int part_lock_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
return part->master->lock_user_prot_reg (part->master, from, len);
|
||||
}
|
||||
|
||||
static int part_writev (struct mtd_info *mtd, const struct kvec *vecs,
|
||||
unsigned long count, loff_t to, size_t *retlen)
|
||||
{
|
||||
|
@ -409,6 +429,12 @@ int add_mtd_partitions(struct mtd_info *master,
|
|||
slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
|
||||
if(master->write_user_prot_reg)
|
||||
slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
|
||||
if(master->lock_user_prot_reg)
|
||||
slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg;
|
||||
if(master->get_user_prot_info)
|
||||
slave->mtd.get_user_prot_info = part_get_user_prot_info;
|
||||
if(master->get_fact_prot_info)
|
||||
slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
|
||||
if (master->sync)
|
||||
slave->mtd.sync = part_sync;
|
||||
if (!i && master->suspend && master->resume) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# drivers/mtd/nand/Kconfig
|
||||
# $Id: Kconfig,v 1.26 2005/01/05 12:42:24 dwmw2 Exp $
|
||||
# $Id: Kconfig,v 1.31 2005/06/20 12:03:21 bjd Exp $
|
||||
|
||||
menu "NAND Flash Device Drivers"
|
||||
depends on MTD!=n
|
||||
|
@ -58,20 +58,6 @@ config MTD_NAND_TOTO
|
|||
config MTD_NAND_IDS
|
||||
tristate
|
||||
|
||||
config MTD_NAND_TX4925NDFMC
|
||||
tristate "SmartMedia Card on Toshiba RBTX4925 reference board"
|
||||
depends on TOSHIBA_RBTX4925 && MTD_NAND && TOSHIBA_RBTX4925_MPLEX_NAND
|
||||
help
|
||||
This enables the driver for the NAND flash device found on the
|
||||
Toshiba RBTX4925 reference board, which is a SmartMediaCard.
|
||||
|
||||
config MTD_NAND_TX4938NDFMC
|
||||
tristate "NAND Flash device on Toshiba RBTX4938 reference board"
|
||||
depends on TOSHIBA_RBTX4938 && MTD_NAND && TOSHIBA_RBTX4938_MPLEX_NAND
|
||||
help
|
||||
This enables the driver for the NAND flash device found on the
|
||||
Toshiba RBTX4938 reference board.
|
||||
|
||||
config MTD_NAND_AU1550
|
||||
tristate "Au1550 NAND support"
|
||||
depends on SOC_AU1550 && MTD_NAND
|
||||
|
@ -95,10 +81,11 @@ config MTD_NAND_PPCHAMELEONEVB
|
|||
This enables the NAND flash driver on the PPChameleon EVB Board.
|
||||
|
||||
config MTD_NAND_S3C2410
|
||||
tristate "NAND Flash support for S3C2410 SoC"
|
||||
tristate "NAND Flash support for S3C2410/S3C2440 SoC"
|
||||
depends on ARCH_S3C2410 && MTD_NAND
|
||||
help
|
||||
This enables the NAND flash controller on the S3C2410.
|
||||
This enables the NAND flash controller on the S3C2410 and S3C2440
|
||||
SoCs
|
||||
|
||||
No board specfic support is done by this driver, each board
|
||||
must advertise a platform_device for the driver to attach.
|
||||
|
|
|
@ -10,8 +10,6 @@ obj-$(CONFIG_MTD_NAND_SPIA) += spia.o
|
|||
obj-$(CONFIG_MTD_NAND_TOTO) += toto.o
|
||||
obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o
|
||||
obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o
|
||||
obj-$(CONFIG_MTD_NAND_TX4925NDFMC) += tx4925ndfmc.o
|
||||
obj-$(CONFIG_MTD_NAND_TX4938NDFMC) += tx4938ndfmc.o
|
||||
obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
|
||||
obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o
|
||||
obj-$(CONFIG_MTD_NAND_S3C2410) += s3c2410.o
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
*
|
||||
* Interface to generic NAND code for M-Systems DiskOnChip devices
|
||||
*
|
||||
* $Id: diskonchip.c,v 1.45 2005/01/05 18:05:14 dwmw2 Exp $
|
||||
* $Id: diskonchip.c,v 1.54 2005/04/07 14:22:55 dbrown Exp $
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
@ -35,13 +35,13 @@
|
|||
#include <linux/mtd/inftl.h>
|
||||
|
||||
/* Where to look for the devices? */
|
||||
#ifndef CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS
|
||||
#define CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS 0
|
||||
#ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS
|
||||
#define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0
|
||||
#endif
|
||||
|
||||
static unsigned long __initdata doc_locations[] = {
|
||||
#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__)
|
||||
#ifdef CONFIG_MTD_DISKONCHIP_PROBE_HIGH
|
||||
#ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH
|
||||
0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000,
|
||||
0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,
|
||||
0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
|
||||
|
@ -81,11 +81,6 @@ struct doc_priv {
|
|||
struct mtd_info *nextdoc;
|
||||
};
|
||||
|
||||
/* Max number of eraseblocks to scan (from start of device) for the (I)NFTL
|
||||
MediaHeader. The spec says to just keep going, I think, but that's just
|
||||
silly. */
|
||||
#define MAX_MEDIAHEADER_SCAN 8
|
||||
|
||||
/* This is the syndrome computed by the HW ecc generator upon reading an empty
|
||||
page, one with all 0xff for data and stored ecc code. */
|
||||
static u_char empty_read_syndrome[6] = { 0x26, 0xff, 0x6d, 0x47, 0x73, 0x7a };
|
||||
|
@ -111,10 +106,11 @@ module_param(try_dword, int, 0);
|
|||
static int no_ecc_failures=0;
|
||||
module_param(no_ecc_failures, int, 0);
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
static int no_autopart=0;
|
||||
module_param(no_autopart, int, 0);
|
||||
#endif
|
||||
|
||||
static int show_firmware_partition=0;
|
||||
module_param(show_firmware_partition, int, 0);
|
||||
|
||||
#ifdef MTD_NAND_DISKONCHIP_BBTWRITE
|
||||
static int inftl_bbt_write=1;
|
||||
|
@ -123,7 +119,7 @@ static int inftl_bbt_write=0;
|
|||
#endif
|
||||
module_param(inftl_bbt_write, int, 0);
|
||||
|
||||
static unsigned long doc_config_location = CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS;
|
||||
static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS;
|
||||
module_param(doc_config_location, ulong, 0);
|
||||
MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
|
||||
|
||||
|
@ -410,7 +406,12 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
|
|||
doc200x_hwcontrol(mtd, NAND_CTL_SETALE);
|
||||
this->write_byte(mtd, 0);
|
||||
doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
|
||||
|
||||
|
||||
/* We cant' use dev_ready here, but at least we wait for the
|
||||
* command to complete
|
||||
*/
|
||||
udelay(50);
|
||||
|
||||
ret = this->read_byte(mtd) << 8;
|
||||
ret |= this->read_byte(mtd);
|
||||
|
||||
|
@ -429,6 +430,8 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
|
|||
doc2000_write_byte(mtd, 0);
|
||||
doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
|
||||
|
||||
udelay(50);
|
||||
|
||||
ident.dword = readl(docptr + DoC_2k_CDSN_IO);
|
||||
if (((ident.byte[0] << 8) | ident.byte[1]) == ret) {
|
||||
printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n");
|
||||
|
@ -1046,11 +1049,21 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_
|
|||
|
||||
//u_char mydatabuf[528];
|
||||
|
||||
/* The strange out-of-order .oobfree list below is a (possibly unneeded)
|
||||
* attempt to retain compatibility. It used to read:
|
||||
* .oobfree = { {8, 8} }
|
||||
* Since that leaves two bytes unusable, it was changed. But the following
|
||||
* scheme might affect existing jffs2 installs by moving the cleanmarker:
|
||||
* .oobfree = { {6, 10} }
|
||||
* jffs2 seems to handle the above gracefully, but the current scheme seems
|
||||
* safer. The only problem with it is that any code that parses oobfree must
|
||||
* be able to handle out-of-order segments.
|
||||
*/
|
||||
static struct nand_oobinfo doc200x_oobinfo = {
|
||||
.useecc = MTD_NANDECC_AUTOPLACE,
|
||||
.eccbytes = 6,
|
||||
.eccpos = {0, 1, 2, 3, 4, 5},
|
||||
.oobfree = { {8, 8} }
|
||||
.oobfree = { {8, 8}, {6, 2} }
|
||||
};
|
||||
|
||||
/* Find the (I)NFTL Media Header, and optionally also the mirror media header.
|
||||
|
@ -1064,12 +1077,11 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf,
|
|||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
struct doc_priv *doc = this->priv;
|
||||
unsigned offs, end = (MAX_MEDIAHEADER_SCAN << this->phys_erase_shift);
|
||||
unsigned offs;
|
||||
int ret;
|
||||
size_t retlen;
|
||||
|
||||
end = min(end, mtd->size); // paranoia
|
||||
for (offs = 0; offs < end; offs += mtd->erasesize) {
|
||||
for (offs = 0; offs < mtd->size; offs += mtd->erasesize) {
|
||||
ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf);
|
||||
if (retlen != mtd->oobblock) continue;
|
||||
if (ret) {
|
||||
|
@ -1111,6 +1123,7 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,
|
|||
u_char *buf;
|
||||
struct NFTLMediaHeader *mh;
|
||||
const unsigned psize = 1 << this->page_shift;
|
||||
int numparts = 0;
|
||||
unsigned blocks, maxblocks;
|
||||
int offs, numheaders;
|
||||
|
||||
|
@ -1122,8 +1135,10 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,
|
|||
if (!(numheaders=find_media_headers(mtd, buf, "ANAND", 1))) goto out;
|
||||
mh = (struct NFTLMediaHeader *) buf;
|
||||
|
||||
//#ifdef CONFIG_MTD_DEBUG_VERBOSE
|
||||
// if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
|
||||
mh->NumEraseUnits = le16_to_cpu(mh->NumEraseUnits);
|
||||
mh->FirstPhysicalEUN = le16_to_cpu(mh->FirstPhysicalEUN);
|
||||
mh->FormattedSize = le32_to_cpu(mh->FormattedSize);
|
||||
|
||||
printk(KERN_INFO " DataOrgID = %s\n"
|
||||
" NumEraseUnits = %d\n"
|
||||
" FirstPhysicalEUN = %d\n"
|
||||
|
@ -1132,7 +1147,6 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,
|
|||
mh->DataOrgID, mh->NumEraseUnits,
|
||||
mh->FirstPhysicalEUN, mh->FormattedSize,
|
||||
mh->UnitSizeFactor);
|
||||
//#endif
|
||||
|
||||
blocks = mtd->size >> this->phys_erase_shift;
|
||||
maxblocks = min(32768U, mtd->erasesize - psize);
|
||||
|
@ -1175,23 +1189,28 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,
|
|||
offs <<= this->page_shift;
|
||||
offs += mtd->erasesize;
|
||||
|
||||
//parts[0].name = " DiskOnChip Boot / Media Header partition";
|
||||
//parts[0].offset = 0;
|
||||
//parts[0].size = offs;
|
||||
|
||||
parts[0].name = " DiskOnChip BDTL partition";
|
||||
parts[0].offset = offs;
|
||||
parts[0].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift;
|
||||
|
||||
offs += parts[0].size;
|
||||
if (offs < mtd->size) {
|
||||
parts[1].name = " DiskOnChip Remainder partition";
|
||||
parts[1].offset = offs;
|
||||
parts[1].size = mtd->size - offs;
|
||||
ret = 2;
|
||||
goto out;
|
||||
if (show_firmware_partition == 1) {
|
||||
parts[0].name = " DiskOnChip Firmware / Media Header partition";
|
||||
parts[0].offset = 0;
|
||||
parts[0].size = offs;
|
||||
numparts = 1;
|
||||
}
|
||||
ret = 1;
|
||||
|
||||
parts[numparts].name = " DiskOnChip BDTL partition";
|
||||
parts[numparts].offset = offs;
|
||||
parts[numparts].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift;
|
||||
|
||||
offs += parts[numparts].size;
|
||||
numparts++;
|
||||
|
||||
if (offs < mtd->size) {
|
||||
parts[numparts].name = " DiskOnChip Remainder partition";
|
||||
parts[numparts].offset = offs;
|
||||
parts[numparts].size = mtd->size - offs;
|
||||
numparts++;
|
||||
}
|
||||
|
||||
ret = numparts;
|
||||
out:
|
||||
kfree(buf);
|
||||
return ret;
|
||||
|
@ -1233,8 +1252,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
|
|||
mh->FormatFlags = le32_to_cpu(mh->FormatFlags);
|
||||
mh->PercentUsed = le32_to_cpu(mh->PercentUsed);
|
||||
|
||||
//#ifdef CONFIG_MTD_DEBUG_VERBOSE
|
||||
// if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
|
||||
printk(KERN_INFO " bootRecordID = %s\n"
|
||||
" NoOfBootImageBlocks = %d\n"
|
||||
" NoOfBinaryPartitions = %d\n"
|
||||
|
@ -1252,7 +1269,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
|
|||
((unsigned char *) &mh->OsakVersion)[2] & 0xf,
|
||||
((unsigned char *) &mh->OsakVersion)[3] & 0xf,
|
||||
mh->PercentUsed);
|
||||
//#endif
|
||||
|
||||
vshift = this->phys_erase_shift + mh->BlockMultiplierBits;
|
||||
|
||||
|
@ -1278,8 +1294,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
|
|||
ip->spareUnits = le32_to_cpu(ip->spareUnits);
|
||||
ip->Reserved0 = le32_to_cpu(ip->Reserved0);
|
||||
|
||||
//#ifdef CONFIG_MTD_DEBUG_VERBOSE
|
||||
// if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
|
||||
printk(KERN_INFO " PARTITION[%d] ->\n"
|
||||
" virtualUnits = %d\n"
|
||||
" firstUnit = %d\n"
|
||||
|
@ -1289,16 +1303,14 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
|
|||
i, ip->virtualUnits, ip->firstUnit,
|
||||
ip->lastUnit, ip->flags,
|
||||
ip->spareUnits);
|
||||
//#endif
|
||||
|
||||
/*
|
||||
if ((i == 0) && (ip->firstUnit > 0)) {
|
||||
if ((show_firmware_partition == 1) &&
|
||||
(i == 0) && (ip->firstUnit > 0)) {
|
||||
parts[0].name = " DiskOnChip IPL / Media Header partition";
|
||||
parts[0].offset = 0;
|
||||
parts[0].size = mtd->erasesize * ip->firstUnit;
|
||||
numparts = 1;
|
||||
}
|
||||
*/
|
||||
|
||||
if (ip->flags & INFTL_BINARY)
|
||||
parts[numparts].name = " DiskOnChip BDK partition";
|
||||
|
|
|
@ -28,6 +28,24 @@
|
|||
* among multiple independend devices. Suggestions and initial patch
|
||||
* from Ben Dooks <ben-mtd@fluff.org>
|
||||
*
|
||||
* 12-05-2004 dmarlin: add workaround for Renesas AG-AND chips "disturb" issue.
|
||||
* Basically, any block not rewritten may lose data when surrounding blocks
|
||||
* are rewritten many times. JFFS2 ensures this doesn't happen for blocks
|
||||
* it uses, but the Bad Block Table(s) may not be rewritten. To ensure they
|
||||
* do not lose data, force them to be rewritten when some of the surrounding
|
||||
* blocks are erased. Rather than tracking a specific nearby block (which
|
||||
* could itself go bad), use a page address 'mask' to select several blocks
|
||||
* in the same area, and rewrite the BBT when any of them are erased.
|
||||
*
|
||||
* 01-03-2005 dmarlin: added support for the device recovery command sequence for Renesas
|
||||
* AG-AND chips. If there was a sudden loss of power during an erase operation,
|
||||
* a "device recovery" operation must be performed when power is restored
|
||||
* to ensure correct operation.
|
||||
*
|
||||
* 01-20-2005 dmarlin: added support for optional hardware specific callback routine to
|
||||
* perform extra error status checks on erase and write failures. This required
|
||||
* adding a wrapper function for nand_read_ecc.
|
||||
*
|
||||
* Credits:
|
||||
* David Woodhouse for adding multichip support
|
||||
*
|
||||
|
@ -41,7 +59,7 @@
|
|||
* The AG-AND chips have nice features for speed improvement,
|
||||
* which are not supported yet. Read / program 4 pages in one go.
|
||||
*
|
||||
* $Id: nand_base.c,v 1.126 2004/12/13 11:22:25 lavinen Exp $
|
||||
* $Id: nand_base.c,v 1.146 2005/06/17 15:02:06 gleixner Exp $
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
|
@ -149,17 +167,21 @@ static void nand_release_device (struct mtd_info *mtd)
|
|||
|
||||
/* De-select the NAND device */
|
||||
this->select_chip(mtd, -1);
|
||||
/* Do we have a hardware controller ? */
|
||||
|
||||
if (this->controller) {
|
||||
/* Release the controller and the chip */
|
||||
spin_lock(&this->controller->lock);
|
||||
this->controller->active = NULL;
|
||||
this->state = FL_READY;
|
||||
wake_up(&this->controller->wq);
|
||||
spin_unlock(&this->controller->lock);
|
||||
} else {
|
||||
/* Release the chip */
|
||||
spin_lock(&this->chip_lock);
|
||||
this->state = FL_READY;
|
||||
wake_up(&this->wq);
|
||||
spin_unlock(&this->chip_lock);
|
||||
}
|
||||
/* Release the chip */
|
||||
spin_lock (&this->chip_lock);
|
||||
this->state = FL_READY;
|
||||
wake_up (&this->wq);
|
||||
spin_unlock (&this->chip_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -443,7 +465,8 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
|||
|
||||
/* Get block number */
|
||||
block = ((int) ofs) >> this->bbt_erase_shift;
|
||||
this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
|
||||
if (this->bbt)
|
||||
this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
|
||||
|
||||
/* Do we have a flash based bad block table ? */
|
||||
if (this->options & NAND_USE_FLASH_BBT)
|
||||
|
@ -466,7 +489,7 @@ static int nand_check_wp (struct mtd_info *mtd)
|
|||
struct nand_chip *this = mtd->priv;
|
||||
/* Check the WP bit */
|
||||
this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
|
||||
return (this->read_byte(mtd) & 0x80) ? 0 : 1;
|
||||
return (this->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -490,6 +513,22 @@ static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, i
|
|||
return nand_isbad_bbt (mtd, ofs, allowbbt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for the ready pin, after a command
|
||||
* The timeout is catched later.
|
||||
*/
|
||||
static void nand_wait_ready(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
unsigned long timeo = jiffies + 2;
|
||||
|
||||
/* wait until command is processed or timeout occures */
|
||||
do {
|
||||
if (this->dev_ready(mtd))
|
||||
return;
|
||||
} while (time_before(jiffies, timeo));
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_command - [DEFAULT] Send command to NAND device
|
||||
* @mtd: MTD device structure
|
||||
|
@ -571,7 +610,7 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in
|
|||
this->hwcontrol(mtd, NAND_CTL_SETCLE);
|
||||
this->write_byte(mtd, NAND_CMD_STATUS);
|
||||
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
|
||||
while ( !(this->read_byte(mtd) & 0x40));
|
||||
while ( !(this->read_byte(mtd) & NAND_STATUS_READY));
|
||||
return;
|
||||
|
||||
/* This applies to read commands */
|
||||
|
@ -585,12 +624,11 @@ static void nand_command (struct mtd_info *mtd, unsigned command, int column, in
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply this short delay always to ensure that we do wait tWB in
|
||||
* any case on any machine. */
|
||||
ndelay (100);
|
||||
/* wait until command is processed */
|
||||
while (!this->dev_ready(mtd));
|
||||
|
||||
nand_wait_ready(mtd);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -619,7 +657,7 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column,
|
|||
/* Begin command latch cycle */
|
||||
this->hwcontrol(mtd, NAND_CTL_SETCLE);
|
||||
/* Write out the command to the device. */
|
||||
this->write_byte(mtd, command);
|
||||
this->write_byte(mtd, (command & 0xff));
|
||||
/* End command latch cycle */
|
||||
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
|
||||
|
||||
|
@ -647,8 +685,8 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column,
|
|||
|
||||
/*
|
||||
* program and erase have their own busy handlers
|
||||
* status and sequential in needs no delay
|
||||
*/
|
||||
* status, sequential in, and deplete1 need no delay
|
||||
*/
|
||||
switch (command) {
|
||||
|
||||
case NAND_CMD_CACHEDPROG:
|
||||
|
@ -657,8 +695,19 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column,
|
|||
case NAND_CMD_ERASE2:
|
||||
case NAND_CMD_SEQIN:
|
||||
case NAND_CMD_STATUS:
|
||||
case NAND_CMD_DEPLETE1:
|
||||
return;
|
||||
|
||||
/*
|
||||
* read error status commands require only a short delay
|
||||
*/
|
||||
case NAND_CMD_STATUS_ERROR:
|
||||
case NAND_CMD_STATUS_ERROR0:
|
||||
case NAND_CMD_STATUS_ERROR1:
|
||||
case NAND_CMD_STATUS_ERROR2:
|
||||
case NAND_CMD_STATUS_ERROR3:
|
||||
udelay(this->chip_delay);
|
||||
return;
|
||||
|
||||
case NAND_CMD_RESET:
|
||||
if (this->dev_ready)
|
||||
|
@ -667,7 +716,7 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column,
|
|||
this->hwcontrol(mtd, NAND_CTL_SETCLE);
|
||||
this->write_byte(mtd, NAND_CMD_STATUS);
|
||||
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
|
||||
while ( !(this->read_byte(mtd) & 0x40));
|
||||
while ( !(this->read_byte(mtd) & NAND_STATUS_READY));
|
||||
return;
|
||||
|
||||
case NAND_CMD_READ0:
|
||||
|
@ -690,12 +739,12 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column,
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Apply this short delay always to ensure that we do wait tWB in
|
||||
* any case on any machine. */
|
||||
ndelay (100);
|
||||
/* wait until command is processed */
|
||||
while (!this->dev_ready(mtd));
|
||||
|
||||
nand_wait_ready(mtd);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -708,37 +757,34 @@ static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column,
|
|||
*/
|
||||
static void nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state)
|
||||
{
|
||||
struct nand_chip *active = this;
|
||||
|
||||
struct nand_chip *active;
|
||||
spinlock_t *lock;
|
||||
wait_queue_head_t *wq;
|
||||
DECLARE_WAITQUEUE (wait, current);
|
||||
|
||||
/*
|
||||
* Grab the lock and see if the device is available
|
||||
*/
|
||||
lock = (this->controller) ? &this->controller->lock : &this->chip_lock;
|
||||
wq = (this->controller) ? &this->controller->wq : &this->wq;
|
||||
retry:
|
||||
active = this;
|
||||
spin_lock(lock);
|
||||
|
||||
/* Hardware controller shared among independend devices */
|
||||
if (this->controller) {
|
||||
spin_lock (&this->controller->lock);
|
||||
if (this->controller->active)
|
||||
active = this->controller->active;
|
||||
else
|
||||
this->controller->active = this;
|
||||
spin_unlock (&this->controller->lock);
|
||||
}
|
||||
|
||||
if (active == this) {
|
||||
spin_lock (&this->chip_lock);
|
||||
if (this->state == FL_READY) {
|
||||
this->state = new_state;
|
||||
spin_unlock (&this->chip_lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
set_current_state (TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue (&active->wq, &wait);
|
||||
spin_unlock (&active->chip_lock);
|
||||
schedule ();
|
||||
remove_wait_queue (&active->wq, &wait);
|
||||
if (active == this && this->state == FL_READY) {
|
||||
this->state = new_state;
|
||||
spin_unlock(lock);
|
||||
return;
|
||||
}
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(wq, &wait);
|
||||
spin_unlock(lock);
|
||||
schedule();
|
||||
remove_wait_queue(wq, &wait);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
|
@ -785,7 +831,7 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
|
|||
if (this->read_byte(mtd) & NAND_STATUS_READY)
|
||||
break;
|
||||
}
|
||||
yield ();
|
||||
cond_resched();
|
||||
}
|
||||
status = (int) this->read_byte(mtd);
|
||||
return status;
|
||||
|
@ -871,8 +917,14 @@ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int pa
|
|||
if (!cached) {
|
||||
/* call wait ready function */
|
||||
status = this->waitfunc (mtd, this, FL_WRITING);
|
||||
|
||||
/* See if operation failed and additional status checks are available */
|
||||
if ((status & NAND_STATUS_FAIL) && (this->errstat)) {
|
||||
status = this->errstat(mtd, this, FL_WRITING, status, page);
|
||||
}
|
||||
|
||||
/* See if device thinks it succeeded */
|
||||
if (status & 0x01) {
|
||||
if (status & NAND_STATUS_FAIL) {
|
||||
DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page);
|
||||
return -EIO;
|
||||
}
|
||||
|
@ -975,7 +1027,7 @@ static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int
|
|||
if (!this->dev_ready)
|
||||
udelay (this->chip_delay);
|
||||
else
|
||||
while (!this->dev_ready(mtd));
|
||||
nand_wait_ready(mtd);
|
||||
|
||||
/* All done, return happy */
|
||||
if (!numpages)
|
||||
|
@ -997,23 +1049,24 @@ out:
|
|||
#endif
|
||||
|
||||
/**
|
||||
* nand_read - [MTD Interface] MTD compability function for nand_read_ecc
|
||||
* nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc
|
||||
* @mtd: MTD device structure
|
||||
* @from: offset to read from
|
||||
* @len: number of bytes to read
|
||||
* @retlen: pointer to variable to store the number of read bytes
|
||||
* @buf: the databuffer to put data
|
||||
*
|
||||
* This function simply calls nand_read_ecc with oob buffer and oobsel = NULL
|
||||
*/
|
||||
* This function simply calls nand_do_read_ecc with oob buffer and oobsel = NULL
|
||||
* and flags = 0xff
|
||||
*/
|
||||
static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
|
||||
{
|
||||
return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL);
|
||||
}
|
||||
return nand_do_read_ecc (mtd, from, len, retlen, buf, NULL, &mtd->oobinfo, 0xff);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* nand_read_ecc - [MTD Interface] Read data with ECC
|
||||
* nand_read_ecc - [MTD Interface] MTD compability function for nand_do_read_ecc
|
||||
* @mtd: MTD device structure
|
||||
* @from: offset to read from
|
||||
* @len: number of bytes to read
|
||||
|
@ -1022,11 +1075,39 @@ static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * re
|
|||
* @oob_buf: filesystem supplied oob data buffer
|
||||
* @oobsel: oob selection structure
|
||||
*
|
||||
* NAND read with ECC
|
||||
* This function simply calls nand_do_read_ecc with flags = 0xff
|
||||
*/
|
||||
static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
|
||||
{
|
||||
/* use userspace supplied oobinfo, if zero */
|
||||
if (oobsel == NULL)
|
||||
oobsel = &mtd->oobinfo;
|
||||
return nand_do_read_ecc(mtd, from, len, retlen, buf, oob_buf, oobsel, 0xff);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* nand_do_read_ecc - [MTD Interface] Read data with ECC
|
||||
* @mtd: MTD device structure
|
||||
* @from: offset to read from
|
||||
* @len: number of bytes to read
|
||||
* @retlen: pointer to variable to store the number of read bytes
|
||||
* @buf: the databuffer to put data
|
||||
* @oob_buf: filesystem supplied oob data buffer (can be NULL)
|
||||
* @oobsel: oob selection structure
|
||||
* @flags: flag to indicate if nand_get_device/nand_release_device should be preformed
|
||||
* and how many corrected error bits are acceptable:
|
||||
* bits 0..7 - number of tolerable errors
|
||||
* bit 8 - 0 == do not get/release chip, 1 == get/release chip
|
||||
*
|
||||
* NAND read with ECC
|
||||
*/
|
||||
int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t * retlen, u_char * buf, u_char * oob_buf,
|
||||
struct nand_oobinfo *oobsel, int flags)
|
||||
{
|
||||
|
||||
int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1;
|
||||
int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
@ -1051,12 +1132,9 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
|
|||
}
|
||||
|
||||
/* Grab the lock and see if the device is available */
|
||||
nand_get_device (this, mtd ,FL_READING);
|
||||
if (flags & NAND_GET_DEVICE)
|
||||
nand_get_device (this, mtd, FL_READING);
|
||||
|
||||
/* use userspace supplied oobinfo, if zero */
|
||||
if (oobsel == NULL)
|
||||
oobsel = &mtd->oobinfo;
|
||||
|
||||
/* Autoplace of oob data ? Use the default placement scheme */
|
||||
if (oobsel->useecc == MTD_NANDECC_AUTOPLACE)
|
||||
oobsel = this->autooob;
|
||||
|
@ -1118,7 +1196,8 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
|
|||
}
|
||||
|
||||
/* get oob area, if we have no oob buffer from fs-driver */
|
||||
if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE)
|
||||
if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE ||
|
||||
oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
|
||||
oob_data = &this->data_buf[end];
|
||||
|
||||
eccsteps = this->eccsteps;
|
||||
|
@ -1155,7 +1234,8 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
|
|||
/* We calc error correction directly, it checks the hw
|
||||
* generator for an error, reads back the syndrome and
|
||||
* does the error correction on the fly */
|
||||
if (this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]) == -1) {
|
||||
ecc_status = this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]);
|
||||
if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {
|
||||
DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: "
|
||||
"Failed ECC read, page 0x%08x on chip %d\n", page, chipnr);
|
||||
ecc_failed++;
|
||||
|
@ -1194,7 +1274,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
|
|||
p[i] = ecc_status;
|
||||
}
|
||||
|
||||
if (ecc_status == -1) {
|
||||
if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {
|
||||
DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);
|
||||
ecc_failed++;
|
||||
}
|
||||
|
@ -1206,14 +1286,14 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
|
|||
/* without autoplace. Legacy mode used by YAFFS1 */
|
||||
switch(oobsel->useecc) {
|
||||
case MTD_NANDECC_AUTOPLACE:
|
||||
case MTD_NANDECC_AUTOPL_USR:
|
||||
/* Walk through the autoplace chunks */
|
||||
for (i = 0, j = 0; j < mtd->oobavail; i++) {
|
||||
for (i = 0; oobsel->oobfree[i][1]; i++) {
|
||||
int from = oobsel->oobfree[i][0];
|
||||
int num = oobsel->oobfree[i][1];
|
||||
memcpy(&oob_buf[oob], &oob_data[from], num);
|
||||
j+= num;
|
||||
oob += num;
|
||||
}
|
||||
oob += mtd->oobavail;
|
||||
break;
|
||||
case MTD_NANDECC_PLACE:
|
||||
/* YAFFS1 legacy mode */
|
||||
|
@ -1239,7 +1319,7 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
|
|||
if (!this->dev_ready)
|
||||
udelay (this->chip_delay);
|
||||
else
|
||||
while (!this->dev_ready(mtd));
|
||||
nand_wait_ready(mtd);
|
||||
|
||||
if (read == len)
|
||||
break;
|
||||
|
@ -1264,7 +1344,8 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
|
|||
}
|
||||
|
||||
/* Deselect and wake up anyone waiting on the device */
|
||||
nand_release_device(mtd);
|
||||
if (flags & NAND_GET_DEVICE)
|
||||
nand_release_device(mtd);
|
||||
|
||||
/*
|
||||
* Return success, if no ECC failures, else -EBADMSG
|
||||
|
@ -1337,7 +1418,7 @@ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t
|
|||
if (!this->dev_ready)
|
||||
udelay (this->chip_delay);
|
||||
else
|
||||
while (!this->dev_ready(mtd));
|
||||
nand_wait_ready(mtd);
|
||||
|
||||
/* Read more ? */
|
||||
if (i < len) {
|
||||
|
@ -1417,7 +1498,7 @@ int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len,
|
|||
if (!this->dev_ready)
|
||||
udelay (this->chip_delay);
|
||||
else
|
||||
while (!this->dev_ready(mtd));
|
||||
nand_wait_ready(mtd);
|
||||
|
||||
/* Check, if the chip supports auto page increment */
|
||||
if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
|
||||
|
@ -1567,6 +1648,8 @@ static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
|
|||
oobsel = this->autooob;
|
||||
autoplace = 1;
|
||||
}
|
||||
if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
|
||||
autoplace = 1;
|
||||
|
||||
/* Setup variables and oob buffer */
|
||||
totalpages = len >> this->page_shift;
|
||||
|
@ -1733,7 +1816,7 @@ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t *
|
|||
status = this->waitfunc (mtd, this, FL_WRITING);
|
||||
|
||||
/* See if device thinks it succeeded */
|
||||
if (status & 0x01) {
|
||||
if (status & NAND_STATUS_FAIL) {
|
||||
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
|
@ -1841,6 +1924,8 @@ static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsig
|
|||
oobsel = this->autooob;
|
||||
autoplace = 1;
|
||||
}
|
||||
if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
|
||||
autoplace = 1;
|
||||
|
||||
/* Setup start page */
|
||||
page = (int) (to >> this->page_shift);
|
||||
|
@ -1987,6 +2072,7 @@ static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
|
|||
return nand_erase_nand (mtd, instr, 0);
|
||||
}
|
||||
|
||||
#define BBT_PAGE_MASK 0xffffff3f
|
||||
/**
|
||||
* nand_erase_intern - [NAND Interface] erase block(s)
|
||||
* @mtd: MTD device structure
|
||||
|
@ -1999,6 +2085,10 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb
|
|||
{
|
||||
int page, len, status, pages_per_block, ret, chipnr;
|
||||
struct nand_chip *this = mtd->priv;
|
||||
int rewrite_bbt[NAND_MAX_CHIPS]={0}; /* flags to indicate the page, if bbt needs to be rewritten. */
|
||||
unsigned int bbt_masked_page; /* bbt mask to compare to page being erased. */
|
||||
/* It is used to see if the current page is in the same */
|
||||
/* 256 block group and the same bank as the bbt. */
|
||||
|
||||
DEBUG (MTD_DEBUG_LEVEL3,
|
||||
"nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len);
|
||||
|
@ -2044,6 +2134,13 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb
|
|||
goto erase_exit;
|
||||
}
|
||||
|
||||
/* if BBT requires refresh, set the BBT page mask to see if the BBT should be rewritten */
|
||||
if (this->options & BBT_AUTO_REFRESH) {
|
||||
bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
|
||||
} else {
|
||||
bbt_masked_page = 0xffffffff; /* should not match anything */
|
||||
}
|
||||
|
||||
/* Loop through the pages */
|
||||
len = instr->len;
|
||||
|
||||
|
@ -2066,13 +2163,26 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb
|
|||
|
||||
status = this->waitfunc (mtd, this, FL_ERASING);
|
||||
|
||||
/* See if operation failed and additional status checks are available */
|
||||
if ((status & NAND_STATUS_FAIL) && (this->errstat)) {
|
||||
status = this->errstat(mtd, this, FL_ERASING, status, page);
|
||||
}
|
||||
|
||||
/* See if block erase succeeded */
|
||||
if (status & 0x01) {
|
||||
if (status & NAND_STATUS_FAIL) {
|
||||
DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page);
|
||||
instr->state = MTD_ERASE_FAILED;
|
||||
instr->fail_addr = (page << this->page_shift);
|
||||
goto erase_exit;
|
||||
}
|
||||
|
||||
/* if BBT requires refresh, set the BBT rewrite flag to the page being erased */
|
||||
if (this->options & BBT_AUTO_REFRESH) {
|
||||
if (((page & BBT_PAGE_MASK) == bbt_masked_page) &&
|
||||
(page != this->bbt_td->pages[chipnr])) {
|
||||
rewrite_bbt[chipnr] = (page << this->page_shift);
|
||||
}
|
||||
}
|
||||
|
||||
/* Increment page address and decrement length */
|
||||
len -= (1 << this->phys_erase_shift);
|
||||
|
@ -2083,6 +2193,13 @@ int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbb
|
|||
chipnr++;
|
||||
this->select_chip(mtd, -1);
|
||||
this->select_chip(mtd, chipnr);
|
||||
|
||||
/* if BBT requires refresh and BBT-PERCHIP,
|
||||
* set the BBT page mask to see if this BBT should be rewritten */
|
||||
if ((this->options & BBT_AUTO_REFRESH) && (this->bbt_td->options & NAND_BBT_PERCHIP)) {
|
||||
bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
instr->state = MTD_ERASE_DONE;
|
||||
|
@ -2097,6 +2214,18 @@ erase_exit:
|
|||
/* Deselect and wake up anyone waiting on the device */
|
||||
nand_release_device(mtd);
|
||||
|
||||
/* if BBT requires refresh and erase was successful, rewrite any selected bad block tables */
|
||||
if ((this->options & BBT_AUTO_REFRESH) && (!ret)) {
|
||||
for (chipnr = 0; chipnr < this->numchips; chipnr++) {
|
||||
if (rewrite_bbt[chipnr]) {
|
||||
/* update the BBT for chip */
|
||||
DEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt (%d:0x%0x 0x%0x)\n",
|
||||
chipnr, rewrite_bbt[chipnr], this->bbt_td->pages[chipnr]);
|
||||
nand_update_bbt (mtd, rewrite_bbt[chipnr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return more or less happy */
|
||||
return ret;
|
||||
}
|
||||
|
@ -2168,7 +2297,7 @@ static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs)
|
|||
*/
|
||||
int nand_scan (struct mtd_info *mtd, int maxchips)
|
||||
{
|
||||
int i, j, nand_maf_id, nand_dev_id, busw;
|
||||
int i, nand_maf_id, nand_dev_id, busw, maf_id;
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
/* Get buswidth to select the correct functions*/
|
||||
|
@ -2256,12 +2385,18 @@ int nand_scan (struct mtd_info *mtd, int maxchips)
|
|||
busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;
|
||||
}
|
||||
|
||||
/* Try to identify manufacturer */
|
||||
for (maf_id = 0; nand_manuf_ids[maf_id].id != 0x0; maf_id++) {
|
||||
if (nand_manuf_ids[maf_id].id == nand_maf_id)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check, if buswidth is correct. Hardware drivers should set
|
||||
* this correct ! */
|
||||
if (busw != (this->options & NAND_BUSWIDTH_16)) {
|
||||
printk (KERN_INFO "NAND device: Manufacturer ID:"
|
||||
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
|
||||
nand_manuf_ids[i].name , mtd->name);
|
||||
nand_manuf_ids[maf_id].name , mtd->name);
|
||||
printk (KERN_WARNING
|
||||
"NAND bus width %d instead %d bit\n",
|
||||
(this->options & NAND_BUSWIDTH_16) ? 16 : 8,
|
||||
|
@ -2300,14 +2435,9 @@ int nand_scan (struct mtd_info *mtd, int maxchips)
|
|||
if (mtd->oobblock > 512 && this->cmdfunc == nand_command)
|
||||
this->cmdfunc = nand_command_lp;
|
||||
|
||||
/* Try to identify manufacturer */
|
||||
for (j = 0; nand_manuf_ids[j].id != 0x0; j++) {
|
||||
if (nand_manuf_ids[j].id == nand_maf_id)
|
||||
break;
|
||||
}
|
||||
printk (KERN_INFO "NAND device: Manufacturer ID:"
|
||||
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
|
||||
nand_manuf_ids[j].name , nand_flash_ids[i].name);
|
||||
nand_manuf_ids[maf_id].name , nand_flash_ids[i].name);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2388,12 +2518,9 @@ int nand_scan (struct mtd_info *mtd, int maxchips)
|
|||
|
||||
/* The number of bytes available for the filesystem to place fs dependend
|
||||
* oob data */
|
||||
if (this->options & NAND_BUSWIDTH_16) {
|
||||
mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 2);
|
||||
if (this->autooob->eccbytes & 0x01)
|
||||
mtd->oobavail--;
|
||||
} else
|
||||
mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 1);
|
||||
mtd->oobavail = 0;
|
||||
for (i = 0; this->autooob->oobfree[i][1]; i++)
|
||||
mtd->oobavail += this->autooob->oobfree[i][1];
|
||||
|
||||
/*
|
||||
* check ECC mode, default to software
|
||||
|
@ -2524,6 +2651,10 @@ int nand_scan (struct mtd_info *mtd, int maxchips)
|
|||
memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo));
|
||||
|
||||
mtd->owner = THIS_MODULE;
|
||||
|
||||
/* Check, if we should skip the bad block table scan */
|
||||
if (this->options & NAND_SKIP_BBTSCAN)
|
||||
return 0;
|
||||
|
||||
/* Build bad block table */
|
||||
return this->scan_bbt (mtd);
|
||||
|
@ -2555,8 +2686,8 @@ void nand_release (struct mtd_info *mtd)
|
|||
kfree (this->data_buf);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL (nand_scan);
|
||||
EXPORT_SYMBOL (nand_release);
|
||||
EXPORT_SYMBOL_GPL (nand_scan);
|
||||
EXPORT_SYMBOL_GPL (nand_release);
|
||||
|
||||
MODULE_LICENSE ("GPL");
|
||||
MODULE_AUTHOR ("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*
|
||||
* Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
|
||||
*
|
||||
* $Id: nand_bbt.c,v 1.28 2004/11/13 10:19:09 gleixner Exp $
|
||||
* $Id: nand_bbt.c,v 1.33 2005/06/14 15:47:56 gleixner Exp $
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
|
@ -77,7 +77,7 @@
|
|||
*/
|
||||
static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
|
||||
{
|
||||
int i, end;
|
||||
int i, end = 0;
|
||||
uint8_t *p = buf;
|
||||
|
||||
end = paglen + td->offs;
|
||||
|
@ -95,9 +95,9 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des
|
|||
return -1;
|
||||
}
|
||||
|
||||
p += td->len;
|
||||
end += td->len;
|
||||
if (td->options & NAND_BBT_SCANEMPTY) {
|
||||
p += td->len;
|
||||
end += td->len;
|
||||
for (i = end; i < len; i++) {
|
||||
if (*p++ != 0xff)
|
||||
return -1;
|
||||
|
@ -106,6 +106,32 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* check_short_pattern - [GENERIC] check if a pattern is in the buffer
|
||||
* @buf: the buffer to search
|
||||
* @len: the length of buffer to search
|
||||
* @paglen: the pagelength
|
||||
* @td: search pattern descriptor
|
||||
*
|
||||
* Check for a pattern at the given place. Used to search bad block
|
||||
* tables and good / bad block identifiers. Same as check_pattern, but
|
||||
* no optional empty check and the pattern is expected to start
|
||||
* at offset 0.
|
||||
*
|
||||
*/
|
||||
static int check_short_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
|
||||
{
|
||||
int i;
|
||||
uint8_t *p = buf;
|
||||
|
||||
/* Compare the pattern */
|
||||
for (i = 0; i < td->len; i++) {
|
||||
if (p[i] != td->pattern[i])
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* read_bbt - [GENERIC] Read the bad block table starting from page
|
||||
* @mtd: MTD device structure
|
||||
|
@ -252,7 +278,7 @@ static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_de
|
|||
* Create a bad block table by scanning the device
|
||||
* for the given good/bad block identify pattern
|
||||
*/
|
||||
static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)
|
||||
static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
int i, j, numblocks, len, scanlen;
|
||||
|
@ -270,9 +296,17 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
|
|||
else
|
||||
len = 1;
|
||||
}
|
||||
scanlen = mtd->oobblock + mtd->oobsize;
|
||||
readlen = len * mtd->oobblock;
|
||||
ooblen = len * mtd->oobsize;
|
||||
|
||||
if (!(bd->options & NAND_BBT_SCANEMPTY)) {
|
||||
/* We need only read few bytes from the OOB area */
|
||||
scanlen = ooblen = 0;
|
||||
readlen = bd->len;
|
||||
} else {
|
||||
/* Full page content should be read */
|
||||
scanlen = mtd->oobblock + mtd->oobsize;
|
||||
readlen = len * mtd->oobblock;
|
||||
ooblen = len * mtd->oobsize;
|
||||
}
|
||||
|
||||
if (chip == -1) {
|
||||
/* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it
|
||||
|
@ -284,7 +318,7 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
|
|||
if (chip >= this->numchips) {
|
||||
printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n",
|
||||
chip + 1, this->numchips);
|
||||
return;
|
||||
return -EINVAL;
|
||||
}
|
||||
numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
|
||||
startblock = chip * numblocks;
|
||||
|
@ -293,18 +327,41 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
|
|||
}
|
||||
|
||||
for (i = startblock; i < numblocks;) {
|
||||
nand_read_raw (mtd, buf, from, readlen, ooblen);
|
||||
int ret;
|
||||
|
||||
if (bd->options & NAND_BBT_SCANEMPTY)
|
||||
if ((ret = nand_read_raw (mtd, buf, from, readlen, ooblen)))
|
||||
return ret;
|
||||
|
||||
for (j = 0; j < len; j++) {
|
||||
if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
|
||||
this->bbt[i >> 3] |= 0x03 << (i & 0x6);
|
||||
printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
|
||||
i >> 1, (unsigned int) from);
|
||||
break;
|
||||
if (!(bd->options & NAND_BBT_SCANEMPTY)) {
|
||||
size_t retlen;
|
||||
|
||||
/* No need to read pages fully, just read required OOB bytes */
|
||||
ret = mtd->read_oob(mtd, from + j * mtd->oobblock + bd->offs,
|
||||
readlen, &retlen, &buf[0]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (check_short_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
|
||||
this->bbt[i >> 3] |= 0x03 << (i & 0x6);
|
||||
printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
|
||||
i >> 1, (unsigned int) from);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
|
||||
this->bbt[i >> 3] |= 0x03 << (i & 0x6);
|
||||
printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
|
||||
i >> 1, (unsigned int) from);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
i += 2;
|
||||
from += (1 << this->bbt_erase_shift);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -589,14 +646,12 @@ write:
|
|||
* The function creates a memory based bbt by scanning the device
|
||||
* for manufacturer / software marked good / bad blocks
|
||||
*/
|
||||
static int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||
static inline int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
/* Ensure that we only scan for the pattern and nothing else */
|
||||
bd->options = 0;
|
||||
create_bbt (mtd, this->data_buf, bd, -1);
|
||||
return 0;
|
||||
bd->options &= ~NAND_BBT_SCANEMPTY;
|
||||
return create_bbt (mtd, this->data_buf, bd, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -808,8 +863,14 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
|||
/* If no primary table decriptor is given, scan the device
|
||||
* to build a memory based bad block table
|
||||
*/
|
||||
if (!td)
|
||||
return nand_memory_bbt(mtd, bd);
|
||||
if (!td) {
|
||||
if ((res = nand_memory_bbt(mtd, bd))) {
|
||||
printk (KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n");
|
||||
kfree (this->bbt);
|
||||
this->bbt = NULL;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Allocate a temporary buffer for one eraseblock incl. oob */
|
||||
len = (1 << this->bbt_erase_shift);
|
||||
|
@ -904,14 +965,11 @@ out:
|
|||
}
|
||||
|
||||
/* Define some generic bad / good block scan pattern which are used
|
||||
* while scanning a device for factory marked good / bad blocks
|
||||
*
|
||||
* The memory based patterns just
|
||||
*/
|
||||
* while scanning a device for factory marked good / bad blocks. */
|
||||
static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
|
||||
|
||||
static struct nand_bbt_descr smallpage_memorybased = {
|
||||
.options = 0,
|
||||
.options = NAND_BBT_SCAN2NDPAGE,
|
||||
.offs = 5,
|
||||
.len = 1,
|
||||
.pattern = scan_ff_pattern
|
||||
|
@ -1042,7 +1100,7 @@ int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt)
|
|||
res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
|
||||
|
||||
DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
|
||||
(unsigned int)offs, res, block >> 1);
|
||||
(unsigned int)offs, block >> 1, res);
|
||||
|
||||
switch ((int)res) {
|
||||
case 0x00: return 0;
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
* drivers/mtd/nandids.c
|
||||
*
|
||||
* Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
|
||||
*
|
||||
* $Id: nand_ids.c,v 1.10 2004/05/26 13:40:12 gleixner Exp $
|
||||
*
|
||||
* $Id: nand_ids.c,v 1.14 2005/06/23 09:38:50 gleixner Exp $
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
|
@ -56,17 +56,24 @@ struct nand_flash_dev nand_flash_ids[] = {
|
|||
{"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16},
|
||||
|
||||
{"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0},
|
||||
{"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0},
|
||||
{"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0},
|
||||
{"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16},
|
||||
{"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16},
|
||||
{"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16},
|
||||
{"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16},
|
||||
|
||||
{"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0},
|
||||
|
||||
{"NAND 512MiB 3,3V 8-bit", 0xDC, 512, 512, 0x4000, 0},
|
||||
|
||||
/* These are the new chips with large page size. The pagesize
|
||||
* and the erasesize is determined from the extended id bytes
|
||||
*/
|
||||
/*512 Megabit */
|
||||
{"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||
{"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||
{"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||
{"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||
|
||||
/* 1 Gigabit */
|
||||
{"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||
{"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||
|
@ -103,7 +110,7 @@ struct nand_flash_dev nand_flash_ids[] = {
|
|||
* Anyway JFFS2 would increase the eraseblock size so we chose a combined one which can be erased in one go
|
||||
* There are more speed improvements for reads and writes possible, but not implemented now
|
||||
*/
|
||||
{"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY},
|
||||
{"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH},
|
||||
|
||||
{NULL,}
|
||||
};
|
||||
|
@ -118,6 +125,7 @@ struct nand_manufacturers nand_manuf_ids[] = {
|
|||
{NAND_MFR_NATIONAL, "National"},
|
||||
{NAND_MFR_RENESAS, "Renesas"},
|
||||
{NAND_MFR_STMICRO, "ST Micro"},
|
||||
{NAND_MFR_HYNIX, "Hynix"},
|
||||
{0x0, "Unknown"}
|
||||
};
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
|
||||
*
|
||||
* $Id: nandsim.c,v 1.7 2004/12/06 11:53:06 dedekind Exp $
|
||||
* $Id: nandsim.c,v 1.8 2005/03/19 15:33:56 dedekind Exp $
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
|
@ -1483,33 +1483,6 @@ ns_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Having only NAND chip IDs we call nand_scan which detects NAND flash
|
||||
* parameters and then calls scan_bbt in order to scan/find/build the
|
||||
* NAND flash bad block table. But since at that moment the NAND flash
|
||||
* image isn't allocated in the simulator, errors arise. To avoid this
|
||||
* we redefine the scan_bbt callback and initialize the nandsim structure
|
||||
* before the flash media scanning.
|
||||
*/
|
||||
int ns_scan_bbt(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *chip = (struct nand_chip *)mtd->priv;
|
||||
struct nandsim *ns = (struct nandsim *)(chip->priv);
|
||||
int retval;
|
||||
|
||||
if (!NS_IS_INITIALIZED(ns))
|
||||
if ((retval = init_nandsim(mtd)) != 0) {
|
||||
NS_ERR("scan_bbt: can't initialize the nandsim structure\n");
|
||||
return retval;
|
||||
}
|
||||
if ((retval = nand_default_bbt(mtd)) != 0) {
|
||||
free_nandsim(ns);
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module initialization function
|
||||
*/
|
||||
|
@ -1544,7 +1517,6 @@ int __init ns_init_module(void)
|
|||
chip->hwcontrol = ns_hwcontrol;
|
||||
chip->read_byte = ns_nand_read_byte;
|
||||
chip->dev_ready = ns_device_ready;
|
||||
chip->scan_bbt = ns_scan_bbt;
|
||||
chip->write_byte = ns_nand_write_byte;
|
||||
chip->write_buf = ns_nand_write_buf;
|
||||
chip->read_buf = ns_nand_read_buf;
|
||||
|
@ -1552,6 +1524,7 @@ int __init ns_init_module(void)
|
|||
chip->write_word = ns_nand_write_word;
|
||||
chip->read_word = ns_nand_read_word;
|
||||
chip->eccmode = NAND_ECC_SOFT;
|
||||
chip->options |= NAND_SKIP_BBTSCAN;
|
||||
|
||||
/*
|
||||
* Perform minimum nandsim structure initialization to handle
|
||||
|
@ -1580,6 +1553,16 @@ int __init ns_init_module(void)
|
|||
goto error;
|
||||
}
|
||||
|
||||
if ((retval = init_nandsim(nsmtd)) != 0) {
|
||||
NS_ERR("scan_bbt: can't initialize the nandsim structure\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((retval = nand_default_bbt(nsmtd)) != 0) {
|
||||
free_nandsim(nand);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Register NAND as one big partition */
|
||||
add_mtd_partitions(nsmtd, &nand->part, 1);
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Derived from drivers/mtd/nand/spia.c
|
||||
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
|
||||
*
|
||||
* $Id: rtc_from4.c,v 1.7 2004/11/04 12:53:10 gleixner Exp $
|
||||
* $Id: rtc_from4.c,v 1.9 2005/01/24 20:40:11 dmarlin Exp $
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
|
@ -83,13 +83,18 @@ static struct mtd_info *rtc_from4_mtd = NULL;
|
|||
#define RTC_FROM4_RS_ECC_CHK (RTC_FROM4_NAND_ADDR_FPGA | 0x00000070)
|
||||
#define RTC_FROM4_RS_ECC_CHK_ERROR (1 << 7)
|
||||
|
||||
#define ERR_STAT_ECC_AVAILABLE 0x20
|
||||
|
||||
/* Undefine for software ECC */
|
||||
#define RTC_FROM4_HWECC 1
|
||||
|
||||
/* Define as 1 for no virtual erase blocks (in JFFS2) */
|
||||
#define RTC_FROM4_NO_VIRTBLOCKS 0
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
static void __iomem *rtc_from4_fio_base = P2SEGADDR(RTC_FROM4_FIO_BASE);
|
||||
static void __iomem *rtc_from4_fio_base = (void *)P2SEGADDR(RTC_FROM4_FIO_BASE);
|
||||
|
||||
const static struct mtd_partition partition_info[] = {
|
||||
{
|
||||
|
@ -267,7 +272,6 @@ static void rtc_from4_nand_select_chip(struct mtd_info *mtd, int chip)
|
|||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* rtc_from4_nand_device_ready - hardware specific ready/busy check
|
||||
* @mtd: MTD device structure
|
||||
|
@ -286,6 +290,40 @@ static int rtc_from4_nand_device_ready(struct mtd_info *mtd)
|
|||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* deplete - code to perform device recovery in case there was a power loss
|
||||
* @mtd: MTD device structure
|
||||
* @chip: Chip to select (0 == slot 3, 1 == slot 4)
|
||||
*
|
||||
* If there was a sudden loss of power during an erase operation, a
|
||||
* "device recovery" operation must be performed when power is restored
|
||||
* to ensure correct operation. This routine performs the required steps
|
||||
* for the requested chip.
|
||||
*
|
||||
* See page 86 of the data sheet for details.
|
||||
*
|
||||
*/
|
||||
static void deplete(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
/* wait until device is ready */
|
||||
while (!this->dev_ready(mtd));
|
||||
|
||||
this->select_chip(mtd, chip);
|
||||
|
||||
/* Send the commands for device recovery, phase 1 */
|
||||
this->cmdfunc (mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0000);
|
||||
this->cmdfunc (mtd, NAND_CMD_DEPLETE2, -1, -1);
|
||||
|
||||
/* Send the commands for device recovery, phase 2 */
|
||||
this->cmdfunc (mtd, NAND_CMD_DEPLETE1, 0x0000, 0x0004);
|
||||
this->cmdfunc (mtd, NAND_CMD_DEPLETE2, -1, -1);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#ifdef RTC_FROM4_HWECC
|
||||
/*
|
||||
* rtc_from4_enable_hwecc - hardware specific hardware ECC enable function
|
||||
|
@ -329,6 +367,7 @@ static void rtc_from4_enable_hwecc(struct mtd_info *mtd, int mode)
|
|||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* rtc_from4_calculate_ecc - hardware specific code to read ECC code
|
||||
* @mtd: MTD device structure
|
||||
|
@ -356,6 +395,7 @@ static void rtc_from4_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_c
|
|||
ecc_code[7] |= 0x0f; /* set the last four bits (not used) */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* rtc_from4_correct_data - hardware specific code to correct data using ECC code
|
||||
* @mtd: MTD device structure
|
||||
|
@ -365,16 +405,14 @@ static void rtc_from4_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_c
|
|||
*
|
||||
* The FPGA tells us fast, if there's an error or not. If no, we go back happy
|
||||
* else we read the ecc results from the fpga and call the rs library to decode
|
||||
* and hopefully correct the error
|
||||
* and hopefully correct the error.
|
||||
*
|
||||
* For now I use the code, which we read from the FLASH to use the RS lib,
|
||||
* as the syndrom conversion has a unresolved issue.
|
||||
*/
|
||||
static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_char *ecc1, u_char *ecc2)
|
||||
{
|
||||
int i, j, res;
|
||||
unsigned short status;
|
||||
uint16_t par[6], syn[6], tmp;
|
||||
uint16_t par[6], syn[6];
|
||||
uint8_t ecc[8];
|
||||
volatile unsigned short *rs_ecc;
|
||||
|
||||
|
@ -416,15 +454,86 @@ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_cha
|
|||
}
|
||||
|
||||
/* Let the library code do its magic.*/
|
||||
res = decode_rs8(rs_decoder, buf, par, 512, syn, 0, NULL, 0xff, NULL);
|
||||
res = decode_rs8(rs_decoder, (uint8_t *)buf, par, 512, syn, 0, NULL, 0xff, NULL);
|
||||
if (res > 0) {
|
||||
DEBUG (MTD_DEBUG_LEVEL0, "rtc_from4_correct_data: "
|
||||
"ECC corrected %d errors on read\n", res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* rtc_from4_errstat - perform additional error status checks
|
||||
* @mtd: MTD device structure
|
||||
* @this: NAND chip structure
|
||||
* @state: state or the operation
|
||||
* @status: status code returned from read status
|
||||
* @page: startpage inside the chip, must be called with (page & this->pagemask)
|
||||
*
|
||||
* Perform additional error status checks on erase and write failures
|
||||
* to determine if errors are correctable. For this device, correctable
|
||||
* 1-bit errors on erase and write are considered acceptable.
|
||||
*
|
||||
* note: see pages 34..37 of data sheet for details.
|
||||
*
|
||||
*/
|
||||
static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page)
|
||||
{
|
||||
int er_stat=0;
|
||||
int rtn, retlen;
|
||||
size_t len;
|
||||
uint8_t *buf;
|
||||
int i;
|
||||
|
||||
this->cmdfunc (mtd, NAND_CMD_STATUS_CLEAR, -1, -1);
|
||||
|
||||
if (state == FL_ERASING) {
|
||||
for (i=0; i<4; i++) {
|
||||
if (status & 1<<(i+1)) {
|
||||
this->cmdfunc (mtd, (NAND_CMD_STATUS_ERROR + i + 1), -1, -1);
|
||||
rtn = this->read_byte(mtd);
|
||||
this->cmdfunc (mtd, NAND_CMD_STATUS_RESET, -1, -1);
|
||||
if (!(rtn & ERR_STAT_ECC_AVAILABLE)) {
|
||||
er_stat |= 1<<(i+1); /* err_ecc_not_avail */
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (state == FL_WRITING) {
|
||||
/* single bank write logic */
|
||||
this->cmdfunc (mtd, NAND_CMD_STATUS_ERROR, -1, -1);
|
||||
rtn = this->read_byte(mtd);
|
||||
this->cmdfunc (mtd, NAND_CMD_STATUS_RESET, -1, -1);
|
||||
if (!(rtn & ERR_STAT_ECC_AVAILABLE)) {
|
||||
er_stat |= 1<<1; /* err_ecc_not_avail */
|
||||
} else {
|
||||
len = mtd->oobblock;
|
||||
buf = kmalloc (len, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
printk (KERN_ERR "rtc_from4_errstat: Out of memory!\n");
|
||||
er_stat = 1; /* if we can't check, assume failed */
|
||||
} else {
|
||||
/* recovery read */
|
||||
/* page read */
|
||||
rtn = nand_do_read_ecc (mtd, page, len, &retlen, buf, NULL, this->autooob, 1);
|
||||
if (rtn) { /* if read failed or > 1-bit error corrected */
|
||||
er_stat |= 1<<1; /* ECC read failed */
|
||||
}
|
||||
kfree(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rtn = status;
|
||||
if (er_stat == 0) { /* if ECC is available */
|
||||
rtn = (status & ~NAND_STATUS_FAIL); /* clear the error bit */
|
||||
}
|
||||
|
||||
return rtn;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Main initialization routine
|
||||
*/
|
||||
|
@ -432,6 +541,7 @@ int __init rtc_from4_init (void)
|
|||
{
|
||||
struct nand_chip *this;
|
||||
unsigned short bcr1, bcr2, wcr2;
|
||||
int i;
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
rtc_from4_mtd = kmalloc(sizeof(struct mtd_info) + sizeof (struct nand_chip),
|
||||
|
@ -483,6 +593,8 @@ int __init rtc_from4_init (void)
|
|||
|
||||
this->eccmode = NAND_ECC_HW8_512;
|
||||
this->options |= NAND_HWECC_SYNDROME;
|
||||
/* return the status of extra status and ECC checks */
|
||||
this->errstat = rtc_from4_errstat;
|
||||
/* set the nand_oobinfo to support FPGA H/W error detection */
|
||||
this->autooob = &rtc_from4_nand_oobinfo;
|
||||
this->enable_hwecc = rtc_from4_enable_hwecc;
|
||||
|
@ -504,6 +616,18 @@ int __init rtc_from4_init (void)
|
|||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* Perform 'device recovery' for each chip in case there was a power loss. */
|
||||
for (i=0; i < this->numchips; i++) {
|
||||
deplete(rtc_from4_mtd, i);
|
||||
}
|
||||
|
||||
#if RTC_FROM4_NO_VIRTBLOCKS
|
||||
/* use a smaller erase block to minimize wasted space when a block is bad */
|
||||
/* note: this uses eight times as much RAM as using the default and makes */
|
||||
/* mounts take four times as long. */
|
||||
rtc_from4_mtd->flags |= MTD_NO_VIRTBLOCKS;
|
||||
#endif
|
||||
|
||||
/* Register the partitions */
|
||||
add_mtd_partitions(rtc_from4_mtd, partition_info, NUM_PARTITIONS);
|
||||
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
/* linux/drivers/mtd/nand/s3c2410.c
|
||||
*
|
||||
* Copyright (c) 2004 Simtec Electronics
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
* Copyright (c) 2004,2005 Simtec Electronics
|
||||
* http://www.simtec.co.uk/products/SWLINUX/
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*
|
||||
* Samsung S3C2410 NAND driver
|
||||
* Samsung S3C2410/S3C240 NAND driver
|
||||
*
|
||||
* Changelog:
|
||||
* 21-Sep-2004 BJD Initial version
|
||||
* 23-Sep-2004 BJD Mulitple device support
|
||||
* 28-Sep-2004 BJD Fixed ECC placement for Hardware mode
|
||||
* 12-Oct-2004 BJD Fixed errors in use of platform data
|
||||
* 18-Feb-2005 BJD Fix sparse errors
|
||||
* 14-Mar-2005 BJD Applied tglx's code reduction patch
|
||||
* 02-May-2005 BJD Fixed s3c2440 support
|
||||
* 02-May-2005 BJD Reduced hwcontrol decode
|
||||
* 20-Jun-2005 BJD Updated s3c2440 support, fixed timing bug
|
||||
* 08-Jul-2005 BJD Fix OOPS when no platform data supplied
|
||||
*
|
||||
* $Id: s3c2410.c,v 1.7 2005/01/05 18:05:14 dwmw2 Exp $
|
||||
* $Id: s3c2410.c,v 1.14 2005/07/06 20:05:06 bjd Exp $
|
||||
*
|
||||
* 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
|
||||
|
@ -69,10 +76,10 @@ static int hardware_ecc = 0;
|
|||
*/
|
||||
|
||||
static struct nand_oobinfo nand_hw_eccoob = {
|
||||
.useecc = MTD_NANDECC_AUTOPLACE,
|
||||
.eccbytes = 3,
|
||||
.eccpos = {0, 1, 2 },
|
||||
.oobfree = { {8, 8} }
|
||||
.useecc = MTD_NANDECC_AUTOPLACE,
|
||||
.eccbytes = 3,
|
||||
.eccpos = {0, 1, 2 },
|
||||
.oobfree = { {8, 8} }
|
||||
};
|
||||
|
||||
/* controller and mtd information */
|
||||
|
@ -99,8 +106,10 @@ struct s3c2410_nand_info {
|
|||
struct device *device;
|
||||
struct resource *area;
|
||||
struct clk *clk;
|
||||
void *regs;
|
||||
void __iomem *regs;
|
||||
int mtd_count;
|
||||
|
||||
unsigned char is_s3c2440;
|
||||
};
|
||||
|
||||
/* conversion functions */
|
||||
|
@ -165,12 +174,12 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
|
|||
/* calculate the timing information for the controller */
|
||||
|
||||
if (plat != NULL) {
|
||||
tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 8);
|
||||
tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 4);
|
||||
twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8);
|
||||
twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8);
|
||||
} else {
|
||||
/* default timings */
|
||||
tacls = 8;
|
||||
tacls = 4;
|
||||
twrph0 = 8;
|
||||
twrph1 = 8;
|
||||
}
|
||||
|
@ -185,10 +194,16 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
|
|||
to_ns(twrph0, clkrate),
|
||||
to_ns(twrph1, clkrate));
|
||||
|
||||
cfg = S3C2410_NFCONF_EN;
|
||||
cfg |= S3C2410_NFCONF_TACLS(tacls-1);
|
||||
cfg |= S3C2410_NFCONF_TWRPH0(twrph0-1);
|
||||
cfg |= S3C2410_NFCONF_TWRPH1(twrph1-1);
|
||||
if (!info->is_s3c2440) {
|
||||
cfg = S3C2410_NFCONF_EN;
|
||||
cfg |= S3C2410_NFCONF_TACLS(tacls-1);
|
||||
cfg |= S3C2410_NFCONF_TWRPH0(twrph0-1);
|
||||
cfg |= S3C2410_NFCONF_TWRPH1(twrph1-1);
|
||||
} else {
|
||||
cfg = S3C2440_NFCONF_TACLS(tacls-1);
|
||||
cfg |= S3C2440_NFCONF_TWRPH0(twrph0-1);
|
||||
cfg |= S3C2440_NFCONF_TWRPH1(twrph1-1);
|
||||
}
|
||||
|
||||
pr_debug(PFX "NF_CONF is 0x%lx\n", cfg);
|
||||
|
||||
|
@ -203,17 +218,22 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
|
|||
struct s3c2410_nand_info *info;
|
||||
struct s3c2410_nand_mtd *nmtd;
|
||||
struct nand_chip *this = mtd->priv;
|
||||
void __iomem *reg;
|
||||
unsigned long cur;
|
||||
unsigned long bit;
|
||||
|
||||
nmtd = this->priv;
|
||||
info = nmtd->info;
|
||||
|
||||
cur = readl(info->regs + S3C2410_NFCONF);
|
||||
bit = (info->is_s3c2440) ? S3C2440_NFCONT_nFCE : S3C2410_NFCONF_nFCE;
|
||||
reg = info->regs+((info->is_s3c2440) ? S3C2440_NFCONT:S3C2410_NFCONF);
|
||||
|
||||
cur = readl(reg);
|
||||
|
||||
if (chip == -1) {
|
||||
cur |= S3C2410_NFCONF_nFCE;
|
||||
cur |= bit;
|
||||
} else {
|
||||
if (chip > nmtd->set->nr_chips) {
|
||||
if (nmtd->set != NULL && chip > nmtd->set->nr_chips) {
|
||||
printk(KERN_ERR PFX "chip %d out of range\n", chip);
|
||||
return;
|
||||
}
|
||||
|
@ -223,143 +243,76 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
|
|||
(info->platform->select_chip)(nmtd->set, chip);
|
||||
}
|
||||
|
||||
cur &= ~S3C2410_NFCONF_nFCE;
|
||||
cur &= ~bit;
|
||||
}
|
||||
|
||||
writel(cur, info->regs + S3C2410_NFCONF);
|
||||
writel(cur, reg);
|
||||
}
|
||||
|
||||
/* command and control functions */
|
||||
/* command and control functions
|
||||
*
|
||||
* Note, these all use tglx's method of changing the IO_ADDR_W field
|
||||
* to make the code simpler, and use the nand layer's code to issue the
|
||||
* command and address sequences via the proper IO ports.
|
||||
*
|
||||
*/
|
||||
|
||||
static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd)
|
||||
{
|
||||
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
|
||||
unsigned long cur;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
switch (cmd) {
|
||||
case NAND_CTL_SETNCE:
|
||||
cur = readl(info->regs + S3C2410_NFCONF);
|
||||
cur &= ~S3C2410_NFCONF_nFCE;
|
||||
writel(cur, info->regs + S3C2410_NFCONF);
|
||||
break;
|
||||
|
||||
case NAND_CTL_CLRNCE:
|
||||
cur = readl(info->regs + S3C2410_NFCONF);
|
||||
cur |= S3C2410_NFCONF_nFCE;
|
||||
writel(cur, info->regs + S3C2410_NFCONF);
|
||||
printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__);
|
||||
break;
|
||||
|
||||
/* we don't need to implement these */
|
||||
case NAND_CTL_SETCLE:
|
||||
case NAND_CTL_CLRCLE:
|
||||
chip->IO_ADDR_W = info->regs + S3C2410_NFCMD;
|
||||
break;
|
||||
|
||||
case NAND_CTL_SETALE:
|
||||
case NAND_CTL_CLRALE:
|
||||
pr_debug(PFX "s3c2410_nand_hwcontrol(%d) unusedn", cmd);
|
||||
chip->IO_ADDR_W = info->regs + S3C2410_NFADDR;
|
||||
break;
|
||||
|
||||
/* NAND_CTL_CLRCLE: */
|
||||
/* NAND_CTL_CLRALE: */
|
||||
default:
|
||||
chip->IO_ADDR_W = info->regs + S3C2410_NFDATA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* s3c2410_nand_command
|
||||
*
|
||||
* This function implements sending commands and the relevant address
|
||||
* information to the chip, via the hardware controller. Since the
|
||||
* S3C2410 generates the correct ALE/CLE signaling automatically, we
|
||||
* do not need to use hwcontrol.
|
||||
*/
|
||||
/* command and control functions */
|
||||
|
||||
static void s3c2410_nand_command (struct mtd_info *mtd, unsigned command,
|
||||
int column, int page_addr)
|
||||
static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd)
|
||||
{
|
||||
register struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
|
||||
register struct nand_chip *this = mtd->priv;
|
||||
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
/*
|
||||
* Write out the command to the device.
|
||||
*/
|
||||
if (command == NAND_CMD_SEQIN) {
|
||||
int readcmd;
|
||||
switch (cmd) {
|
||||
case NAND_CTL_SETNCE:
|
||||
case NAND_CTL_CLRNCE:
|
||||
printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__);
|
||||
break;
|
||||
|
||||
if (column >= mtd->oobblock) {
|
||||
/* OOB area */
|
||||
column -= mtd->oobblock;
|
||||
readcmd = NAND_CMD_READOOB;
|
||||
} else if (column < 256) {
|
||||
/* First 256 bytes --> READ0 */
|
||||
readcmd = NAND_CMD_READ0;
|
||||
} else {
|
||||
column -= 256;
|
||||
readcmd = NAND_CMD_READ1;
|
||||
}
|
||||
|
||||
writeb(readcmd, info->regs + S3C2410_NFCMD);
|
||||
}
|
||||
writeb(command, info->regs + S3C2410_NFCMD);
|
||||
case NAND_CTL_SETCLE:
|
||||
chip->IO_ADDR_W = info->regs + S3C2440_NFCMD;
|
||||
break;
|
||||
|
||||
/* Set ALE and clear CLE to start address cycle */
|
||||
case NAND_CTL_SETALE:
|
||||
chip->IO_ADDR_W = info->regs + S3C2440_NFADDR;
|
||||
break;
|
||||
|
||||
if (column != -1 || page_addr != -1) {
|
||||
|
||||
/* Serially input address */
|
||||
if (column != -1) {
|
||||
/* Adjust columns for 16 bit buswidth */
|
||||
if (this->options & NAND_BUSWIDTH_16)
|
||||
column >>= 1;
|
||||
writeb(column, info->regs + S3C2410_NFADDR);
|
||||
}
|
||||
if (page_addr != -1) {
|
||||
writeb((unsigned char) (page_addr), info->regs + S3C2410_NFADDR);
|
||||
writeb((unsigned char) (page_addr >> 8), info->regs + S3C2410_NFADDR);
|
||||
/* One more address cycle for higher density devices */
|
||||
if (this->chipsize & 0x0c000000)
|
||||
writeb((unsigned char) ((page_addr >> 16) & 0x0f),
|
||||
info->regs + S3C2410_NFADDR);
|
||||
}
|
||||
/* Latch in address */
|
||||
}
|
||||
|
||||
/*
|
||||
* program and erase have their own busy handlers
|
||||
* status and sequential in needs no delay
|
||||
*/
|
||||
switch (command) {
|
||||
|
||||
case NAND_CMD_PAGEPROG:
|
||||
case NAND_CMD_ERASE1:
|
||||
case NAND_CMD_ERASE2:
|
||||
case NAND_CMD_SEQIN:
|
||||
case NAND_CMD_STATUS:
|
||||
return;
|
||||
|
||||
case NAND_CMD_RESET:
|
||||
if (this->dev_ready)
|
||||
break;
|
||||
|
||||
udelay(this->chip_delay);
|
||||
writeb(NAND_CMD_STATUS, info->regs + S3C2410_NFCMD);
|
||||
|
||||
while ( !(this->read_byte(mtd) & 0x40));
|
||||
return;
|
||||
|
||||
/* This applies to read commands */
|
||||
/* NAND_CTL_CLRCLE: */
|
||||
/* NAND_CTL_CLRALE: */
|
||||
default:
|
||||
/*
|
||||
* If we don't have access to the busy pin, we apply the given
|
||||
* command delay
|
||||
*/
|
||||
if (!this->dev_ready) {
|
||||
udelay (this->chip_delay);
|
||||
return;
|
||||
}
|
||||
chip->IO_ADDR_W = info->regs + S3C2440_NFDATA;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Apply this short delay always to ensure that we do wait tWB in
|
||||
* any case on any machine. */
|
||||
ndelay (100);
|
||||
/* wait until command is processed */
|
||||
while (!this->dev_ready(mtd));
|
||||
}
|
||||
|
||||
|
||||
/* s3c2410_nand_devready()
|
||||
*
|
||||
* returns 0 if the nand is busy, 1 if it is ready
|
||||
|
@ -369,9 +322,12 @@ static int s3c2410_nand_devready(struct mtd_info *mtd)
|
|||
{
|
||||
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
|
||||
|
||||
if (info->is_s3c2440)
|
||||
return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY;
|
||||
return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY;
|
||||
}
|
||||
|
||||
|
||||
/* ECC handling functions */
|
||||
|
||||
static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
|
||||
|
@ -394,6 +350,12 @@ static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* ECC functions
|
||||
*
|
||||
* These allow the s3c2410 and s3c2440 to use the controller's ECC
|
||||
* generator block to ECC the data as it passes through]
|
||||
*/
|
||||
|
||||
static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
|
||||
|
@ -404,6 +366,15 @@ static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
|
|||
writel(ctrl, info->regs + S3C2410_NFCONF);
|
||||
}
|
||||
|
||||
static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
|
||||
unsigned long ctrl;
|
||||
|
||||
ctrl = readl(info->regs + S3C2440_NFCONT);
|
||||
writel(ctrl | S3C2440_NFCONT_INITECC, info->regs + S3C2440_NFCONT);
|
||||
}
|
||||
|
||||
static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd,
|
||||
const u_char *dat, u_char *ecc_code)
|
||||
{
|
||||
|
@ -420,7 +391,26 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd,
|
|||
}
|
||||
|
||||
|
||||
/* over-ride the standard functions for a little more speed? */
|
||||
static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd,
|
||||
const u_char *dat, u_char *ecc_code)
|
||||
{
|
||||
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
|
||||
unsigned long ecc = readl(info->regs + S3C2440_NFMECC0);
|
||||
|
||||
ecc_code[0] = ecc;
|
||||
ecc_code[1] = ecc >> 8;
|
||||
ecc_code[2] = ecc >> 16;
|
||||
|
||||
pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n",
|
||||
ecc_code[0], ecc_code[1], ecc_code[2]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* over-ride the standard functions for a little more speed. We can
|
||||
* use read/write block to move the data buffers to/from the controller
|
||||
*/
|
||||
|
||||
static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
||||
{
|
||||
|
@ -523,11 +513,10 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
|
|||
{
|
||||
struct nand_chip *chip = &nmtd->chip;
|
||||
|
||||
chip->IO_ADDR_R = (char *)info->regs + S3C2410_NFDATA;
|
||||
chip->IO_ADDR_W = (char *)info->regs + S3C2410_NFDATA;
|
||||
chip->IO_ADDR_R = info->regs + S3C2410_NFDATA;
|
||||
chip->IO_ADDR_W = info->regs + S3C2410_NFDATA;
|
||||
chip->hwcontrol = s3c2410_nand_hwcontrol;
|
||||
chip->dev_ready = s3c2410_nand_devready;
|
||||
chip->cmdfunc = s3c2410_nand_command;
|
||||
chip->write_buf = s3c2410_nand_write_buf;
|
||||
chip->read_buf = s3c2410_nand_read_buf;
|
||||
chip->select_chip = s3c2410_nand_select_chip;
|
||||
|
@ -536,6 +525,12 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
|
|||
chip->options = 0;
|
||||
chip->controller = &info->controller;
|
||||
|
||||
if (info->is_s3c2440) {
|
||||
chip->IO_ADDR_R = info->regs + S3C2440_NFDATA;
|
||||
chip->IO_ADDR_W = info->regs + S3C2440_NFDATA;
|
||||
chip->hwcontrol = s3c2440_nand_hwcontrol;
|
||||
}
|
||||
|
||||
nmtd->info = info;
|
||||
nmtd->mtd.priv = chip;
|
||||
nmtd->set = set;
|
||||
|
@ -546,6 +541,11 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
|
|||
chip->calculate_ecc = s3c2410_nand_calculate_ecc;
|
||||
chip->eccmode = NAND_ECC_HW3_512;
|
||||
chip->autooob = &nand_hw_eccoob;
|
||||
|
||||
if (info->is_s3c2440) {
|
||||
chip->enable_hwecc = s3c2440_nand_enable_hwecc;
|
||||
chip->calculate_ecc = s3c2440_nand_calculate_ecc;
|
||||
}
|
||||
} else {
|
||||
chip->eccmode = NAND_ECC_SOFT;
|
||||
}
|
||||
|
@ -559,7 +559,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
|
|||
* nand layer to look for devices
|
||||
*/
|
||||
|
||||
static int s3c2410_nand_probe(struct device *dev)
|
||||
static int s3c24xx_nand_probe(struct device *dev, int is_s3c2440)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct s3c2410_platform_nand *plat = to_nand_plat(dev);
|
||||
|
@ -585,6 +585,7 @@ static int s3c2410_nand_probe(struct device *dev)
|
|||
dev_set_drvdata(dev, info);
|
||||
|
||||
spin_lock_init(&info->controller.lock);
|
||||
init_waitqueue_head(&info->controller.wq);
|
||||
|
||||
/* get the clock source and enable it */
|
||||
|
||||
|
@ -600,7 +601,8 @@ static int s3c2410_nand_probe(struct device *dev)
|
|||
|
||||
/* allocate and map the resource */
|
||||
|
||||
res = pdev->resource; /* assume that the flash has one resource */
|
||||
/* currently we assume we have the one resource */
|
||||
res = pdev->resource;
|
||||
size = res->end - res->start + 1;
|
||||
|
||||
info->area = request_mem_region(res->start, size, pdev->name);
|
||||
|
@ -611,9 +613,10 @@ static int s3c2410_nand_probe(struct device *dev)
|
|||
goto exit_error;
|
||||
}
|
||||
|
||||
info->device = dev;
|
||||
info->platform = plat;
|
||||
info->regs = ioremap(res->start, size);
|
||||
info->device = dev;
|
||||
info->platform = plat;
|
||||
info->regs = ioremap(res->start, size);
|
||||
info->is_s3c2440 = is_s3c2440;
|
||||
|
||||
if (info->regs == NULL) {
|
||||
printk(KERN_ERR PFX "cannot reserve register region\n");
|
||||
|
@ -678,6 +681,18 @@ static int s3c2410_nand_probe(struct device *dev)
|
|||
return err;
|
||||
}
|
||||
|
||||
/* driver device registration */
|
||||
|
||||
static int s3c2410_nand_probe(struct device *dev)
|
||||
{
|
||||
return s3c24xx_nand_probe(dev, 0);
|
||||
}
|
||||
|
||||
static int s3c2440_nand_probe(struct device *dev)
|
||||
{
|
||||
return s3c24xx_nand_probe(dev, 1);
|
||||
}
|
||||
|
||||
static struct device_driver s3c2410_nand_driver = {
|
||||
.name = "s3c2410-nand",
|
||||
.bus = &platform_bus_type,
|
||||
|
@ -685,14 +700,24 @@ static struct device_driver s3c2410_nand_driver = {
|
|||
.remove = s3c2410_nand_remove,
|
||||
};
|
||||
|
||||
static struct device_driver s3c2440_nand_driver = {
|
||||
.name = "s3c2440-nand",
|
||||
.bus = &platform_bus_type,
|
||||
.probe = s3c2440_nand_probe,
|
||||
.remove = s3c2410_nand_remove,
|
||||
};
|
||||
|
||||
static int __init s3c2410_nand_init(void)
|
||||
{
|
||||
printk("S3C2410 NAND Driver, (c) 2004 Simtec Electronics\n");
|
||||
printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
|
||||
|
||||
driver_register(&s3c2440_nand_driver);
|
||||
return driver_register(&s3c2410_nand_driver);
|
||||
}
|
||||
|
||||
static void __exit s3c2410_nand_exit(void)
|
||||
{
|
||||
driver_unregister(&s3c2440_nand_driver);
|
||||
driver_unregister(&s3c2410_nand_driver);
|
||||
}
|
||||
|
||||
|
@ -701,4 +726,4 @@ module_exit(s3c2410_nand_exit);
|
|||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
|
||||
MODULE_DESCRIPTION("S3C2410 MTD NAND driver");
|
||||
MODULE_DESCRIPTION("S3C24XX MTD NAND driver");
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Copyright (C) 2004 Richard Purdie
|
||||
*
|
||||
* $Id: sharpsl.c,v 1.3 2005/01/03 14:53:50 rpurdie Exp $
|
||||
* $Id: sharpsl.c,v 1.4 2005/01/23 11:09:19 rpurdie Exp $
|
||||
*
|
||||
* Based on Sharp's NAND driver sharp_sl.c
|
||||
*
|
||||
|
@ -216,7 +216,7 @@ sharpsl_nand_init(void)
|
|||
nr_partitions = DEFAULT_NUM_PARTITIONS;
|
||||
sharpsl_partition_info = sharpsl_nand_default_partition_info;
|
||||
if (machine_is_poodle()) {
|
||||
sharpsl_partition_info[1].size=22 * 1024 * 1024;
|
||||
sharpsl_partition_info[1].size=30 * 1024 * 1024;
|
||||
} else if (machine_is_corgi() || machine_is_shepherd()) {
|
||||
sharpsl_partition_info[1].size=25 * 1024 * 1024;
|
||||
} else if (machine_is_husky()) {
|
||||
|
|
|
@ -1,416 +0,0 @@
|
|||
/*
|
||||
* drivers/mtd/tx4925ndfmc.c
|
||||
*
|
||||
* Overview:
|
||||
* This is a device driver for the NAND flash device found on the
|
||||
* Toshiba RBTX4925 reference board, which is a SmartMediaCard. It supports
|
||||
* 16MiB, 32MiB and 64MiB cards.
|
||||
*
|
||||
* Author: MontaVista Software, Inc. source@mvista.com
|
||||
*
|
||||
* Derived from drivers/mtd/autcpu12.c
|
||||
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
|
||||
*
|
||||
* $Id: tx4925ndfmc.c,v 1.5 2004/10/05 13:50:20 gleixner Exp $
|
||||
*
|
||||
* Copyright (C) 2001 Toshiba Corporation
|
||||
*
|
||||
* 2003 (c) MontaVista Software, Inc. This file is licensed under
|
||||
* the terms of the GNU General Public License version 2. This program
|
||||
* is licensed "as is" without any warranty of any kind, whether express
|
||||
* or implied.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/tx4925/tx4925_nand.h>
|
||||
|
||||
extern struct nand_oobinfo jffs2_oobinfo;
|
||||
|
||||
/*
|
||||
* MTD structure for RBTX4925 board
|
||||
*/
|
||||
static struct mtd_info *tx4925ndfmc_mtd = NULL;
|
||||
|
||||
/*
|
||||
* Define partitions for flash devices
|
||||
*/
|
||||
|
||||
static struct mtd_partition partition_info16k[] = {
|
||||
{ .name = "RBTX4925 flash partition 1",
|
||||
.offset = 0,
|
||||
.size = 8 * 0x00100000 },
|
||||
{ .name = "RBTX4925 flash partition 2",
|
||||
.offset = 8 * 0x00100000,
|
||||
.size = 8 * 0x00100000 },
|
||||
};
|
||||
|
||||
static struct mtd_partition partition_info32k[] = {
|
||||
{ .name = "RBTX4925 flash partition 1",
|
||||
.offset = 0,
|
||||
.size = 8 * 0x00100000 },
|
||||
{ .name = "RBTX4925 flash partition 2",
|
||||
.offset = 8 * 0x00100000,
|
||||
.size = 24 * 0x00100000 },
|
||||
};
|
||||
|
||||
static struct mtd_partition partition_info64k[] = {
|
||||
{ .name = "User FS",
|
||||
.offset = 0,
|
||||
.size = 16 * 0x00100000 },
|
||||
{ .name = "RBTX4925 flash partition 2",
|
||||
.offset = 16 * 0x00100000,
|
||||
.size = 48 * 0x00100000},
|
||||
};
|
||||
|
||||
static struct mtd_partition partition_info128k[] = {
|
||||
{ .name = "Skip bad section",
|
||||
.offset = 0,
|
||||
.size = 16 * 0x00100000 },
|
||||
{ .name = "User FS",
|
||||
.offset = 16 * 0x00100000,
|
||||
.size = 112 * 0x00100000 },
|
||||
};
|
||||
#define NUM_PARTITIONS16K 2
|
||||
#define NUM_PARTITIONS32K 2
|
||||
#define NUM_PARTITIONS64K 2
|
||||
#define NUM_PARTITIONS128K 2
|
||||
|
||||
/*
|
||||
* hardware specific access to control-lines
|
||||
*/
|
||||
static void tx4925ndfmc_hwcontrol(struct mtd_info *mtd, int cmd)
|
||||
{
|
||||
|
||||
switch(cmd){
|
||||
|
||||
case NAND_CTL_SETCLE:
|
||||
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CLE;
|
||||
break;
|
||||
case NAND_CTL_CLRCLE:
|
||||
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CLE;
|
||||
break;
|
||||
case NAND_CTL_SETALE:
|
||||
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ALE;
|
||||
break;
|
||||
case NAND_CTL_CLRALE:
|
||||
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ALE;
|
||||
break;
|
||||
case NAND_CTL_SETNCE:
|
||||
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CE;
|
||||
break;
|
||||
case NAND_CTL_CLRNCE:
|
||||
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CE;
|
||||
break;
|
||||
case NAND_CTL_SETWP:
|
||||
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_WE;
|
||||
break;
|
||||
case NAND_CTL_CLRWP:
|
||||
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_WE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* read device ready pin
|
||||
*/
|
||||
static int tx4925ndfmc_device_ready(struct mtd_info *mtd)
|
||||
{
|
||||
int ready;
|
||||
ready = (tx4925_ndfmcptr->sr & TX4925_NDSFR_BUSY) ? 0 : 1;
|
||||
return ready;
|
||||
}
|
||||
void tx4925ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
/* reset first */
|
||||
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_MASK;
|
||||
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
|
||||
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_ENAB;
|
||||
}
|
||||
static void tx4925ndfmc_disable_ecc(void)
|
||||
{
|
||||
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
|
||||
}
|
||||
static void tx4925ndfmc_enable_read_ecc(void)
|
||||
{
|
||||
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
|
||||
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_READ;
|
||||
}
|
||||
void tx4925ndfmc_readecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code){
|
||||
int i;
|
||||
u_char *ecc = ecc_code;
|
||||
tx4925ndfmc_enable_read_ecc();
|
||||
for (i = 0;i < 6;i++,ecc++)
|
||||
*ecc = tx4925_read_nfmc(&(tx4925_ndfmcptr->dtr));
|
||||
tx4925ndfmc_disable_ecc();
|
||||
}
|
||||
void tx4925ndfmc_device_setup(void)
|
||||
{
|
||||
|
||||
*(unsigned char *)0xbb005000 &= ~0x08;
|
||||
|
||||
/* reset NDFMC */
|
||||
tx4925_ndfmcptr->rstr |= TX4925_NDFRSTR_RST;
|
||||
while (tx4925_ndfmcptr->rstr & TX4925_NDFRSTR_RST);
|
||||
|
||||
/* setup BusSeparete, Hold Time, Strobe Pulse Width */
|
||||
tx4925_ndfmcptr->mcr = TX4925_BSPRT ? TX4925_NDFMCR_BSPRT : 0;
|
||||
tx4925_ndfmcptr->spr = TX4925_HOLD << 4 | TX4925_SPW;
|
||||
}
|
||||
static u_char tx4925ndfmc_nand_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
return tx4925_read_nfmc(this->IO_ADDR_R);
|
||||
}
|
||||
|
||||
static void tx4925ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
tx4925_write_nfmc(byte, this->IO_ADDR_W);
|
||||
}
|
||||
|
||||
static void tx4925ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
for (i=0; i<len; i++)
|
||||
tx4925_write_nfmc(buf[i], this->IO_ADDR_W);
|
||||
}
|
||||
|
||||
static void tx4925ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
for (i=0; i<len; i++)
|
||||
buf[i] = tx4925_read_nfmc(this->IO_ADDR_R);
|
||||
}
|
||||
|
||||
static int tx4925ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
for (i=0; i<len; i++)
|
||||
if (buf[i] != tx4925_read_nfmc(this->IO_ADDR_R))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send command to NAND device
|
||||
*/
|
||||
static void tx4925ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
|
||||
{
|
||||
register struct nand_chip *this = mtd->priv;
|
||||
|
||||
/* Begin command latch cycle */
|
||||
this->hwcontrol(mtd, NAND_CTL_SETCLE);
|
||||
/*
|
||||
* Write out the command to the device.
|
||||
*/
|
||||
if (command == NAND_CMD_SEQIN) {
|
||||
int readcmd;
|
||||
|
||||
if (column >= mtd->oobblock) {
|
||||
/* OOB area */
|
||||
column -= mtd->oobblock;
|
||||
readcmd = NAND_CMD_READOOB;
|
||||
} else if (column < 256) {
|
||||
/* First 256 bytes --> READ0 */
|
||||
readcmd = NAND_CMD_READ0;
|
||||
} else {
|
||||
column -= 256;
|
||||
readcmd = NAND_CMD_READ1;
|
||||
}
|
||||
this->write_byte(mtd, readcmd);
|
||||
}
|
||||
this->write_byte(mtd, command);
|
||||
|
||||
/* Set ALE and clear CLE to start address cycle */
|
||||
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
|
||||
|
||||
if (column != -1 || page_addr != -1) {
|
||||
this->hwcontrol(mtd, NAND_CTL_SETALE);
|
||||
|
||||
/* Serially input address */
|
||||
if (column != -1)
|
||||
this->write_byte(mtd, column);
|
||||
if (page_addr != -1) {
|
||||
this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
|
||||
this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
|
||||
/* One more address cycle for higher density devices */
|
||||
if (mtd->size & 0x0c000000)
|
||||
this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
|
||||
}
|
||||
/* Latch in address */
|
||||
this->hwcontrol(mtd, NAND_CTL_CLRALE);
|
||||
}
|
||||
|
||||
/*
|
||||
* program and erase have their own busy handlers
|
||||
* status and sequential in needs no delay
|
||||
*/
|
||||
switch (command) {
|
||||
|
||||
case NAND_CMD_PAGEPROG:
|
||||
/* Turn off WE */
|
||||
this->hwcontrol (mtd, NAND_CTL_CLRWP);
|
||||
return;
|
||||
|
||||
case NAND_CMD_SEQIN:
|
||||
/* Turn on WE */
|
||||
this->hwcontrol (mtd, NAND_CTL_SETWP);
|
||||
return;
|
||||
|
||||
case NAND_CMD_ERASE1:
|
||||
case NAND_CMD_ERASE2:
|
||||
case NAND_CMD_STATUS:
|
||||
return;
|
||||
|
||||
case NAND_CMD_RESET:
|
||||
if (this->dev_ready)
|
||||
break;
|
||||
this->hwcontrol(mtd, NAND_CTL_SETCLE);
|
||||
this->write_byte(mtd, NAND_CMD_STATUS);
|
||||
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
|
||||
while ( !(this->read_byte(mtd) & 0x40));
|
||||
return;
|
||||
|
||||
/* This applies to read commands */
|
||||
default:
|
||||
/*
|
||||
* If we don't have access to the busy pin, we apply the given
|
||||
* command delay
|
||||
*/
|
||||
if (!this->dev_ready) {
|
||||
udelay (this->chip_delay);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* wait until command is processed */
|
||||
while (!this->dev_ready(mtd));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_CMDLINE_PARTS
|
||||
extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partitio
|
||||
n **pparts, char *);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Main initialization routine
|
||||
*/
|
||||
extern int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
|
||||
int __init tx4925ndfmc_init (void)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
int err = 0;
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
tx4925ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
|
||||
GFP_KERNEL);
|
||||
if (!tx4925ndfmc_mtd) {
|
||||
printk ("Unable to allocate RBTX4925 NAND MTD device structure.\n");
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
tx4925ndfmc_device_setup();
|
||||
|
||||
/* io is indirect via a register so don't need to ioremap address */
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *) (&tx4925ndfmc_mtd[1]);
|
||||
|
||||
/* Initialize structures */
|
||||
memset((char *) tx4925ndfmc_mtd, 0, sizeof(struct mtd_info));
|
||||
memset((char *) this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
tx4925ndfmc_mtd->priv = this;
|
||||
|
||||
/* Set address of NAND IO lines */
|
||||
this->IO_ADDR_R = (void __iomem *)&(tx4925_ndfmcptr->dtr);
|
||||
this->IO_ADDR_W = (void __iomem *)&(tx4925_ndfmcptr->dtr);
|
||||
this->hwcontrol = tx4925ndfmc_hwcontrol;
|
||||
this->enable_hwecc = tx4925ndfmc_enable_hwecc;
|
||||
this->calculate_ecc = tx4925ndfmc_readecc;
|
||||
this->correct_data = nand_correct_data;
|
||||
this->eccmode = NAND_ECC_HW6_512;
|
||||
this->dev_ready = tx4925ndfmc_device_ready;
|
||||
/* 20 us command delay time */
|
||||
this->chip_delay = 20;
|
||||
this->read_byte = tx4925ndfmc_nand_read_byte;
|
||||
this->write_byte = tx4925ndfmc_nand_write_byte;
|
||||
this->cmdfunc = tx4925ndfmc_nand_command;
|
||||
this->write_buf = tx4925ndfmc_nand_write_buf;
|
||||
this->read_buf = tx4925ndfmc_nand_read_buf;
|
||||
this->verify_buf = tx4925ndfmc_nand_verify_buf;
|
||||
|
||||
/* Scan to find existance of the device */
|
||||
if (nand_scan (tx4925ndfmc_mtd, 1)) {
|
||||
err = -ENXIO;
|
||||
goto out_ior;
|
||||
}
|
||||
|
||||
/* Register the partitions */
|
||||
#ifdef CONFIG_MTD_CMDLINE_PARTS
|
||||
{
|
||||
int mtd_parts_nb = 0;
|
||||
struct mtd_partition *mtd_parts = 0;
|
||||
mtd_parts_nb = parse_cmdline_partitions(tx4925ndfmc_mtd, &mtd_parts, "tx4925ndfmc");
|
||||
if (mtd_parts_nb > 0)
|
||||
add_mtd_partitions(tx4925ndfmc_mtd, mtd_parts, mtd_parts_nb);
|
||||
else
|
||||
add_mtd_device(tx4925ndfmc_mtd);
|
||||
}
|
||||
#else /* ifdef CONFIG_MTD_CMDLINE_PARTS */
|
||||
switch(tx4925ndfmc_mtd->size){
|
||||
case 0x01000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info16k, NUM_PARTITIONS16K); break;
|
||||
case 0x02000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info32k, NUM_PARTITIONS32K); break;
|
||||
case 0x04000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info64k, NUM_PARTITIONS64K); break;
|
||||
case 0x08000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info128k, NUM_PARTITIONS128K); break;
|
||||
default: {
|
||||
printk ("Unsupported SmartMedia device\n");
|
||||
err = -ENXIO;
|
||||
goto out_ior;
|
||||
}
|
||||
}
|
||||
#endif /* ifdef CONFIG_MTD_CMDLINE_PARTS */
|
||||
goto out;
|
||||
|
||||
out_ior:
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
module_init(tx4925ndfmc_init);
|
||||
|
||||
/*
|
||||
* Clean up routine
|
||||
*/
|
||||
#ifdef MODULE
|
||||
static void __exit tx4925ndfmc_cleanup (void)
|
||||
{
|
||||
/* Release resources, unregister device */
|
||||
nand_release (tx4925ndfmc_mtd);
|
||||
|
||||
/* Free the MTD device structure */
|
||||
kfree (tx4925ndfmc_mtd);
|
||||
}
|
||||
module_exit(tx4925ndfmc_cleanup);
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>");
|
||||
MODULE_DESCRIPTION("Glue layer for SmartMediaCard on Toshiba RBTX4925");
|
|
@ -1,406 +0,0 @@
|
|||
/*
|
||||
* drivers/mtd/nand/tx4938ndfmc.c
|
||||
*
|
||||
* Overview:
|
||||
* This is a device driver for the NAND flash device connected to
|
||||
* TX4938 internal NAND Memory Controller.
|
||||
* TX4938 NDFMC is almost same as TX4925 NDFMC, but register size are 64 bit.
|
||||
*
|
||||
* Author: source@mvista.com
|
||||
*
|
||||
* Based on spia.c by Steven J. Hill
|
||||
*
|
||||
* $Id: tx4938ndfmc.c,v 1.4 2004/10/05 13:50:20 gleixner Exp $
|
||||
*
|
||||
* Copyright (C) 2000-2001 Toshiba Corporation
|
||||
*
|
||||
* 2003 (c) MontaVista Software, Inc. This file is licensed under the
|
||||
* terms of the GNU General Public License version 2. This program is
|
||||
* licensed "as is" without any warranty of any kind, whether express
|
||||
* or implied.
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/bootinfo.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/tx4938/rbtx4938.h>
|
||||
|
||||
extern struct nand_oobinfo jffs2_oobinfo;
|
||||
|
||||
/*
|
||||
* MTD structure for TX4938 NDFMC
|
||||
*/
|
||||
static struct mtd_info *tx4938ndfmc_mtd;
|
||||
|
||||
/*
|
||||
* Define partitions for flash device
|
||||
*/
|
||||
#define flush_wb() (void)tx4938_ndfmcptr->mcr;
|
||||
|
||||
#define NUM_PARTITIONS 3
|
||||
#define NUMBER_OF_CIS_BLOCKS 24
|
||||
#define SIZE_OF_BLOCK 0x00004000
|
||||
#define NUMBER_OF_BLOCK_PER_ZONE 1024
|
||||
#define SIZE_OF_ZONE (NUMBER_OF_BLOCK_PER_ZONE * SIZE_OF_BLOCK)
|
||||
#ifndef CONFIG_MTD_CMDLINE_PARTS
|
||||
/*
|
||||
* You can use the following sample of MTD partitions
|
||||
* on the NAND Flash Memory 32MB or more.
|
||||
*
|
||||
* The following figure shows the image of the sample partition on
|
||||
* the 32MB NAND Flash Memory.
|
||||
*
|
||||
* Block No.
|
||||
* 0 +-----------------------------+ ------
|
||||
* | CIS | ^
|
||||
* 24 +-----------------------------+ |
|
||||
* | kernel image | | Zone 0
|
||||
* | | |
|
||||
* +-----------------------------+ |
|
||||
* 1023 | unused area | v
|
||||
* +-----------------------------+ ------
|
||||
* 1024 | JFFS2 | ^
|
||||
* | | |
|
||||
* | | | Zone 1
|
||||
* | | |
|
||||
* | | |
|
||||
* | | v
|
||||
* 2047 +-----------------------------+ ------
|
||||
*
|
||||
*/
|
||||
static struct mtd_partition partition_info[NUM_PARTITIONS] = {
|
||||
{
|
||||
.name = "RBTX4938 CIS Area",
|
||||
.offset = 0,
|
||||
.size = (NUMBER_OF_CIS_BLOCKS * SIZE_OF_BLOCK),
|
||||
.mask_flags = MTD_WRITEABLE /* This partition is NOT writable */
|
||||
},
|
||||
{
|
||||
.name = "RBTX4938 kernel image",
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
.size = 8 * 0x00100000, /* 8MB (Depends on size of kernel image) */
|
||||
.mask_flags = MTD_WRITEABLE /* This partition is NOT writable */
|
||||
},
|
||||
{
|
||||
.name = "Root FS (JFFS2)",
|
||||
.offset = (0 + SIZE_OF_ZONE), /* start address of next zone */
|
||||
.size = MTDPART_SIZ_FULL
|
||||
},
|
||||
};
|
||||
#endif
|
||||
|
||||
static void tx4938ndfmc_hwcontrol(struct mtd_info *mtd, int cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case NAND_CTL_SETCLE:
|
||||
tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CLE;
|
||||
break;
|
||||
case NAND_CTL_CLRCLE:
|
||||
tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CLE;
|
||||
break;
|
||||
case NAND_CTL_SETALE:
|
||||
tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_ALE;
|
||||
break;
|
||||
case NAND_CTL_CLRALE:
|
||||
tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_ALE;
|
||||
break;
|
||||
/* TX4938_NDFMCR_CE bit is 0:high 1:low */
|
||||
case NAND_CTL_SETNCE:
|
||||
tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CE;
|
||||
break;
|
||||
case NAND_CTL_CLRNCE:
|
||||
tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CE;
|
||||
break;
|
||||
case NAND_CTL_SETWP:
|
||||
tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_WE;
|
||||
break;
|
||||
case NAND_CTL_CLRWP:
|
||||
tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_WE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
static int tx4938ndfmc_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
flush_wb();
|
||||
return !(tx4938_ndfmcptr->sr & TX4938_NDFSR_BUSY);
|
||||
}
|
||||
static void tx4938ndfmc_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
|
||||
{
|
||||
u32 mcr = tx4938_ndfmcptr->mcr;
|
||||
mcr &= ~TX4938_NDFMCR_ECC_ALL;
|
||||
tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
|
||||
tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_READ;
|
||||
ecc_code[1] = tx4938_ndfmcptr->dtr;
|
||||
ecc_code[0] = tx4938_ndfmcptr->dtr;
|
||||
ecc_code[2] = tx4938_ndfmcptr->dtr;
|
||||
tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
|
||||
}
|
||||
static void tx4938ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
u32 mcr = tx4938_ndfmcptr->mcr;
|
||||
mcr &= ~TX4938_NDFMCR_ECC_ALL;
|
||||
tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_RESET;
|
||||
tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
|
||||
tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_ON;
|
||||
}
|
||||
|
||||
static u_char tx4938ndfmc_nand_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
return tx4938_read_nfmc(this->IO_ADDR_R);
|
||||
}
|
||||
|
||||
static void tx4938ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
tx4938_write_nfmc(byte, this->IO_ADDR_W);
|
||||
}
|
||||
|
||||
static void tx4938ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
for (i=0; i<len; i++)
|
||||
tx4938_write_nfmc(buf[i], this->IO_ADDR_W);
|
||||
}
|
||||
|
||||
static void tx4938ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
for (i=0; i<len; i++)
|
||||
buf[i] = tx4938_read_nfmc(this->IO_ADDR_R);
|
||||
}
|
||||
|
||||
static int tx4938ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
for (i=0; i<len; i++)
|
||||
if (buf[i] != tx4938_read_nfmc(this->IO_ADDR_R))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send command to NAND device
|
||||
*/
|
||||
static void tx4938ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
|
||||
{
|
||||
register struct nand_chip *this = mtd->priv;
|
||||
|
||||
/* Begin command latch cycle */
|
||||
this->hwcontrol(mtd, NAND_CTL_SETCLE);
|
||||
/*
|
||||
* Write out the command to the device.
|
||||
*/
|
||||
if (command == NAND_CMD_SEQIN) {
|
||||
int readcmd;
|
||||
|
||||
if (column >= mtd->oobblock) {
|
||||
/* OOB area */
|
||||
column -= mtd->oobblock;
|
||||
readcmd = NAND_CMD_READOOB;
|
||||
} else if (column < 256) {
|
||||
/* First 256 bytes --> READ0 */
|
||||
readcmd = NAND_CMD_READ0;
|
||||
} else {
|
||||
column -= 256;
|
||||
readcmd = NAND_CMD_READ1;
|
||||
}
|
||||
this->write_byte(mtd, readcmd);
|
||||
}
|
||||
this->write_byte(mtd, command);
|
||||
|
||||
/* Set ALE and clear CLE to start address cycle */
|
||||
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
|
||||
|
||||
if (column != -1 || page_addr != -1) {
|
||||
this->hwcontrol(mtd, NAND_CTL_SETALE);
|
||||
|
||||
/* Serially input address */
|
||||
if (column != -1)
|
||||
this->write_byte(mtd, column);
|
||||
if (page_addr != -1) {
|
||||
this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
|
||||
this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
|
||||
/* One more address cycle for higher density devices */
|
||||
if (mtd->size & 0x0c000000)
|
||||
this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
|
||||
}
|
||||
/* Latch in address */
|
||||
this->hwcontrol(mtd, NAND_CTL_CLRALE);
|
||||
}
|
||||
|
||||
/*
|
||||
* program and erase have their own busy handlers
|
||||
* status and sequential in needs no delay
|
||||
*/
|
||||
switch (command) {
|
||||
|
||||
case NAND_CMD_PAGEPROG:
|
||||
/* Turn off WE */
|
||||
this->hwcontrol (mtd, NAND_CTL_CLRWP);
|
||||
return;
|
||||
|
||||
case NAND_CMD_SEQIN:
|
||||
/* Turn on WE */
|
||||
this->hwcontrol (mtd, NAND_CTL_SETWP);
|
||||
return;
|
||||
|
||||
case NAND_CMD_ERASE1:
|
||||
case NAND_CMD_ERASE2:
|
||||
case NAND_CMD_STATUS:
|
||||
return;
|
||||
|
||||
case NAND_CMD_RESET:
|
||||
if (this->dev_ready)
|
||||
break;
|
||||
this->hwcontrol(mtd, NAND_CTL_SETCLE);
|
||||
this->write_byte(mtd, NAND_CMD_STATUS);
|
||||
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
|
||||
while ( !(this->read_byte(mtd) & 0x40));
|
||||
return;
|
||||
|
||||
/* This applies to read commands */
|
||||
default:
|
||||
/*
|
||||
* If we don't have access to the busy pin, we apply the given
|
||||
* command delay
|
||||
*/
|
||||
if (!this->dev_ready) {
|
||||
udelay (this->chip_delay);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* wait until command is processed */
|
||||
while (!this->dev_ready(mtd));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_CMDLINE_PARTS
|
||||
extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *);
|
||||
#endif
|
||||
/*
|
||||
* Main initialization routine
|
||||
*/
|
||||
int __init tx4938ndfmc_init (void)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
int bsprt = 0, hold = 0xf, spw = 0xf;
|
||||
int protected = 0;
|
||||
|
||||
if ((*rbtx4938_piosel_ptr & 0x0c) != 0x08) {
|
||||
printk("TX4938 NDFMC: disabled by IOC PIOSEL\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
bsprt = 1;
|
||||
hold = 2;
|
||||
spw = 9 - 1; /* 8 GBUSCLK = 80ns (@ GBUSCLK 100MHz) */
|
||||
|
||||
if ((tx4938_ccfgptr->pcfg &
|
||||
(TX4938_PCFG_ATA_SEL|TX4938_PCFG_ISA_SEL|TX4938_PCFG_NDF_SEL))
|
||||
!= TX4938_PCFG_NDF_SEL) {
|
||||
printk("TX4938 NDFMC: disabled by PCFG.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* reset NDFMC */
|
||||
tx4938_ndfmcptr->rstr |= TX4938_NDFRSTR_RST;
|
||||
while (tx4938_ndfmcptr->rstr & TX4938_NDFRSTR_RST)
|
||||
;
|
||||
/* setup BusSeparete, Hold Time, Strobe Pulse Width */
|
||||
tx4938_ndfmcptr->mcr = bsprt ? TX4938_NDFMCR_BSPRT : 0;
|
||||
tx4938_ndfmcptr->spr = hold << 4 | spw;
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
tx4938ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
|
||||
GFP_KERNEL);
|
||||
if (!tx4938ndfmc_mtd) {
|
||||
printk ("Unable to allocate TX4938 NDFMC MTD device structure.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *) (&tx4938ndfmc_mtd[1]);
|
||||
|
||||
/* Initialize structures */
|
||||
memset((char *) tx4938ndfmc_mtd, 0, sizeof(struct mtd_info));
|
||||
memset((char *) this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
tx4938ndfmc_mtd->priv = this;
|
||||
|
||||
/* Set address of NAND IO lines */
|
||||
this->IO_ADDR_R = (unsigned long)&tx4938_ndfmcptr->dtr;
|
||||
this->IO_ADDR_W = (unsigned long)&tx4938_ndfmcptr->dtr;
|
||||
this->hwcontrol = tx4938ndfmc_hwcontrol;
|
||||
this->dev_ready = tx4938ndfmc_dev_ready;
|
||||
this->calculate_ecc = tx4938ndfmc_calculate_ecc;
|
||||
this->correct_data = nand_correct_data;
|
||||
this->enable_hwecc = tx4938ndfmc_enable_hwecc;
|
||||
this->eccmode = NAND_ECC_HW3_256;
|
||||
this->chip_delay = 100;
|
||||
this->read_byte = tx4938ndfmc_nand_read_byte;
|
||||
this->write_byte = tx4938ndfmc_nand_write_byte;
|
||||
this->cmdfunc = tx4938ndfmc_nand_command;
|
||||
this->write_buf = tx4938ndfmc_nand_write_buf;
|
||||
this->read_buf = tx4938ndfmc_nand_read_buf;
|
||||
this->verify_buf = tx4938ndfmc_nand_verify_buf;
|
||||
|
||||
/* Scan to find existance of the device */
|
||||
if (nand_scan (tx4938ndfmc_mtd, 1)) {
|
||||
kfree (tx4938ndfmc_mtd);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
if (protected) {
|
||||
printk(KERN_INFO "TX4938 NDFMC: write protected.\n");
|
||||
tx4938ndfmc_mtd->flags &= ~(MTD_WRITEABLE | MTD_ERASEABLE);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_CMDLINE_PARTS
|
||||
{
|
||||
int mtd_parts_nb = 0;
|
||||
struct mtd_partition *mtd_parts = 0;
|
||||
mtd_parts_nb = parse_cmdline_partitions(tx4938ndfmc_mtd, &mtd_parts, "tx4938ndfmc");
|
||||
if (mtd_parts_nb > 0)
|
||||
add_mtd_partitions(tx4938ndfmc_mtd, mtd_parts, mtd_parts_nb);
|
||||
else
|
||||
add_mtd_device(tx4938ndfmc_mtd);
|
||||
}
|
||||
#else
|
||||
add_mtd_partitions(tx4938ndfmc_mtd, partition_info, NUM_PARTITIONS );
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(tx4938ndfmc_init);
|
||||
|
||||
/*
|
||||
* Clean up routine
|
||||
*/
|
||||
static void __exit tx4938ndfmc_cleanup (void)
|
||||
{
|
||||
/* Release resources, unregister device */
|
||||
nand_release (tx4938ndfmc_mtd);
|
||||
|
||||
/* Free the MTD device structure */
|
||||
kfree (tx4938ndfmc_mtd);
|
||||
}
|
||||
module_exit(tx4938ndfmc_cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>");
|
||||
MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on TX4938 NDFMC");
|
26
fs/Kconfig
26
fs/Kconfig
|
@ -1039,26 +1039,18 @@ config JFFS2_FS_DEBUG
|
|||
If reporting bugs, please try to have available a full dump of the
|
||||
messages at debug level 1 while the misbehaviour was occurring.
|
||||
|
||||
config JFFS2_FS_NAND
|
||||
bool "JFFS2 support for NAND flash"
|
||||
config JFFS2_FS_WRITEBUFFER
|
||||
bool "JFFS2 write-buffering support"
|
||||
depends on JFFS2_FS
|
||||
default n
|
||||
default y
|
||||
help
|
||||
This enables the support for NAND flash in JFFS2. NAND is a newer
|
||||
type of flash chip design than the traditional NOR flash, with
|
||||
higher density but a handful of characteristics which make it more
|
||||
interesting for the file system to use.
|
||||
This enables the write-buffering support in JFFS2.
|
||||
|
||||
Say 'N' unless you have NAND flash.
|
||||
|
||||
config JFFS2_FS_NOR_ECC
|
||||
bool "JFFS2 support for ECC'd NOR flash (EXPERIMENTAL)"
|
||||
depends on JFFS2_FS && EXPERIMENTAL
|
||||
default n
|
||||
help
|
||||
This enables the experimental support for NOR flash with transparent
|
||||
ECC for JFFS2. This type of flash chip is not common, however it is
|
||||
available from ST Microelectronics.
|
||||
This functionality is required to support JFFS2 on the following
|
||||
types of flash devices:
|
||||
- NAND flash
|
||||
- NOR flash with transparent ECC
|
||||
- DataFlash
|
||||
|
||||
config JFFS2_COMPRESSION_OPTIONS
|
||||
bool "Advanced compression options for JFFS2"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#
|
||||
# Makefile for the Linux Journalling Flash File System v2 (JFFS2)
|
||||
#
|
||||
# $Id: Makefile.common,v 1.7 2004/11/03 12:57:38 jwboyer Exp $
|
||||
# $Id: Makefile.common,v 1.9 2005/02/09 09:23:53 pavlov Exp $
|
||||
#
|
||||
|
||||
obj-$(CONFIG_JFFS2_FS) += jffs2.o
|
||||
|
@ -11,8 +11,7 @@ jffs2-y += read.o nodemgmt.o readinode.o write.o scan.o gc.o
|
|||
jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o
|
||||
jffs2-y += super.o
|
||||
|
||||
jffs2-$(CONFIG_JFFS2_FS_NAND) += wbuf.o
|
||||
jffs2-$(CONFIG_JFFS2_FS_NOR_ECC) += wbuf.o
|
||||
jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER) += wbuf.o
|
||||
jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o
|
||||
jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o
|
||||
jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
$Id: README.Locking,v 1.9 2004/11/20 10:35:40 dwmw2 Exp $
|
||||
$Id: README.Locking,v 1.12 2005/04/13 13:22:35 dwmw2 Exp $
|
||||
|
||||
JFFS2 LOCKING DOCUMENTATION
|
||||
---------------------------
|
||||
|
@ -108,6 +108,10 @@ in-core jffs2_inode_cache objects (each inode in JFFS2 has the
|
|||
correspondent jffs2_inode_cache object). So, the inocache_lock
|
||||
has to be locked while walking the c->inocache_list hash buckets.
|
||||
|
||||
This spinlock also covers allocation of new inode numbers, which is
|
||||
currently just '++->highest_ino++', but might one day get more complicated
|
||||
if we need to deal with wrapping after 4 milliard inode numbers are used.
|
||||
|
||||
Note, the f->sem guarantees that the correspondent jffs2_inode_cache
|
||||
will not be removed. So, it is allowed to access it without locking
|
||||
the inocache_lock spinlock.
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: background.c,v 1.50 2004/11/16 20:36:10 dwmw2 Exp $
|
||||
* $Id: background.c,v 1.54 2005/05/20 21:37:12 gleixner Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -37,7 +37,7 @@ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
|
|||
if (c->gc_task)
|
||||
BUG();
|
||||
|
||||
init_MUTEX_LOCKED(&c->gc_thread_start);
|
||||
init_completion(&c->gc_thread_start);
|
||||
init_completion(&c->gc_thread_exit);
|
||||
|
||||
pid = kernel_thread(jffs2_garbage_collect_thread, c, CLONE_FS|CLONE_FILES);
|
||||
|
@ -48,7 +48,7 @@ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
|
|||
} else {
|
||||
/* Wait for it... */
|
||||
D1(printk(KERN_DEBUG "JFFS2: Garbage collect thread is pid %d\n", pid));
|
||||
down(&c->gc_thread_start);
|
||||
wait_for_completion(&c->gc_thread_start);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -56,13 +56,16 @@ int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c)
|
|||
|
||||
void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c)
|
||||
{
|
||||
int wait = 0;
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
if (c->gc_task) {
|
||||
D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid));
|
||||
send_sig(SIGKILL, c->gc_task, 1);
|
||||
wait = 1;
|
||||
}
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
wait_for_completion(&c->gc_thread_exit);
|
||||
if (wait)
|
||||
wait_for_completion(&c->gc_thread_exit);
|
||||
}
|
||||
|
||||
static int jffs2_garbage_collect_thread(void *_c)
|
||||
|
@ -75,7 +78,7 @@ static int jffs2_garbage_collect_thread(void *_c)
|
|||
allow_signal(SIGCONT);
|
||||
|
||||
c->gc_task = current;
|
||||
up(&c->gc_thread_start);
|
||||
complete(&c->gc_thread_start);
|
||||
|
||||
set_user_nice(current, 10);
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: build.c,v 1.69 2004/12/16 20:22:18 dmarlin Exp $
|
||||
* $Id: build.c,v 1.70 2005/02/28 08:21:05 dedekind Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -97,14 +97,16 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
|
|||
/* First, scan the medium and build all the inode caches with
|
||||
lists of physical nodes */
|
||||
|
||||
c->flags |= JFFS2_SB_FLAG_MOUNTING;
|
||||
c->flags |= JFFS2_SB_FLAG_SCANNING;
|
||||
ret = jffs2_scan_medium(c);
|
||||
c->flags &= ~JFFS2_SB_FLAG_SCANNING;
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
D1(printk(KERN_DEBUG "Scanned flash completely\n"));
|
||||
D2(jffs2_dump_block_lists(c));
|
||||
|
||||
c->flags |= JFFS2_SB_FLAG_BUILDING;
|
||||
/* Now scan the directory tree, increasing nlink according to every dirent found. */
|
||||
for_each_inode(i, c, ic) {
|
||||
D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino));
|
||||
|
@ -116,7 +118,6 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
|
|||
cond_resched();
|
||||
}
|
||||
}
|
||||
c->flags &= ~JFFS2_SB_FLAG_MOUNTING;
|
||||
|
||||
D1(printk(KERN_DEBUG "Pass 1 complete\n"));
|
||||
|
||||
|
@ -164,6 +165,8 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
|
|||
ic->scan_dents = NULL;
|
||||
cond_resched();
|
||||
}
|
||||
c->flags &= ~JFFS2_SB_FLAG_BUILDING;
|
||||
|
||||
D1(printk(KERN_DEBUG "Pass 3 complete\n"));
|
||||
D2(jffs2_dump_block_lists(c));
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: compr_zlib.c,v 1.29 2004/11/16 20:36:11 dwmw2 Exp $
|
||||
* $Id: compr_zlib.c,v 1.31 2005/05/20 19:30:06 gleixner Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -17,10 +17,10 @@
|
|||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/zlib.h>
|
||||
#include <linux/zutil.h>
|
||||
#include <asm/semaphore.h>
|
||||
#include "nodelist.h"
|
||||
#include "compr.h"
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: dir.c,v 1.84 2004/11/16 20:36:11 dwmw2 Exp $
|
||||
* $Id: dir.c,v 1.86 2005/07/06 12:13:09 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -22,16 +22,6 @@
|
|||
#include <linux/time.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
/* Urgh. Please tell me there's a nicer way of doing these. */
|
||||
#include <linux/version.h>
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
|
||||
typedef int mknod_arg_t;
|
||||
#define NAMEI_COMPAT(x) ((void *)x)
|
||||
#else
|
||||
typedef dev_t mknod_arg_t;
|
||||
#define NAMEI_COMPAT(x) (x)
|
||||
#endif
|
||||
|
||||
static int jffs2_readdir (struct file *, void *, filldir_t);
|
||||
|
||||
static int jffs2_create (struct inode *,struct dentry *,int,
|
||||
|
@ -43,7 +33,7 @@ static int jffs2_unlink (struct inode *,struct dentry *);
|
|||
static int jffs2_symlink (struct inode *,struct dentry *,const char *);
|
||||
static int jffs2_mkdir (struct inode *,struct dentry *,int);
|
||||
static int jffs2_rmdir (struct inode *,struct dentry *);
|
||||
static int jffs2_mknod (struct inode *,struct dentry *,int,mknod_arg_t);
|
||||
static int jffs2_mknod (struct inode *,struct dentry *,int,dev_t);
|
||||
static int jffs2_rename (struct inode *, struct dentry *,
|
||||
struct inode *, struct dentry *);
|
||||
|
||||
|
@ -58,8 +48,8 @@ struct file_operations jffs2_dir_operations =
|
|||
|
||||
struct inode_operations jffs2_dir_inode_operations =
|
||||
{
|
||||
.create = NAMEI_COMPAT(jffs2_create),
|
||||
.lookup = NAMEI_COMPAT(jffs2_lookup),
|
||||
.create = jffs2_create,
|
||||
.lookup = jffs2_lookup,
|
||||
.link = jffs2_link,
|
||||
.unlink = jffs2_unlink,
|
||||
.symlink = jffs2_symlink,
|
||||
|
@ -296,11 +286,11 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
|
|||
struct jffs2_full_dirent *fd;
|
||||
int namelen;
|
||||
uint32_t alloclen, phys_ofs;
|
||||
int ret;
|
||||
int ret, targetlen = strlen(target);
|
||||
|
||||
/* FIXME: If you care. We'd need to use frags for the target
|
||||
if it grows much more than this */
|
||||
if (strlen(target) > 254)
|
||||
if (targetlen > 254)
|
||||
return -EINVAL;
|
||||
|
||||
ri = jffs2_alloc_raw_inode();
|
||||
|
@ -314,7 +304,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
|
|||
* Just the node will do for now, though
|
||||
*/
|
||||
namelen = dentry->d_name.len;
|
||||
ret = jffs2_reserve_space(c, sizeof(*ri) + strlen(target), &phys_ofs, &alloclen, ALLOC_NORMAL);
|
||||
ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &phys_ofs, &alloclen, ALLOC_NORMAL);
|
||||
|
||||
if (ret) {
|
||||
jffs2_free_raw_inode(ri);
|
||||
|
@ -333,16 +323,16 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
|
|||
|
||||
f = JFFS2_INODE_INFO(inode);
|
||||
|
||||
inode->i_size = strlen(target);
|
||||
inode->i_size = targetlen;
|
||||
ri->isize = ri->dsize = ri->csize = cpu_to_je32(inode->i_size);
|
||||
ri->totlen = cpu_to_je32(sizeof(*ri) + inode->i_size);
|
||||
ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
|
||||
|
||||
ri->compr = JFFS2_COMPR_NONE;
|
||||
ri->data_crc = cpu_to_je32(crc32(0, target, strlen(target)));
|
||||
ri->data_crc = cpu_to_je32(crc32(0, target, targetlen));
|
||||
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
|
||||
|
||||
fn = jffs2_write_dnode(c, f, ri, target, strlen(target), phys_ofs, ALLOC_NORMAL);
|
||||
fn = jffs2_write_dnode(c, f, ri, target, targetlen, phys_ofs, ALLOC_NORMAL);
|
||||
|
||||
jffs2_free_raw_inode(ri);
|
||||
|
||||
|
@ -353,6 +343,20 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
|
|||
jffs2_clear_inode(inode);
|
||||
return PTR_ERR(fn);
|
||||
}
|
||||
|
||||
/* We use f->dents field to store the target path. */
|
||||
f->dents = kmalloc(targetlen + 1, GFP_KERNEL);
|
||||
if (!f->dents) {
|
||||
printk(KERN_WARNING "Can't allocate %d bytes of memory\n", targetlen + 1);
|
||||
up(&f->sem);
|
||||
jffs2_complete_reservation(c);
|
||||
jffs2_clear_inode(inode);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(f->dents, target, targetlen + 1);
|
||||
D1(printk(KERN_DEBUG "jffs2_symlink: symlink's target '%s' cached\n", (char *)f->dents));
|
||||
|
||||
/* No data here. Only a metadata node, which will be
|
||||
obsoleted by the first data write
|
||||
*/
|
||||
|
@ -564,7 +568,7 @@ static int jffs2_rmdir (struct inode *dir_i, struct dentry *dentry)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mknod_arg_t rdev)
|
||||
static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, dev_t rdev)
|
||||
{
|
||||
struct jffs2_inode_info *f, *dir_f;
|
||||
struct jffs2_sb_info *c;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: erase.c,v 1.66 2004/11/16 20:36:11 dwmw2 Exp $
|
||||
* $Id: erase.c,v 1.76 2005/05/03 15:11:40 dedekind Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -48,6 +48,7 @@ static void jffs2_erase_block(struct jffs2_sb_info *c,
|
|||
#else /* Linux */
|
||||
struct erase_info *instr;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_erase_block(): erase block %#x (range %#x-%#x)\n", jeb->offset, jeb->offset, jeb->offset + c->sector_size));
|
||||
instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL);
|
||||
if (!instr) {
|
||||
printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n");
|
||||
|
@ -233,7 +234,7 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (((*prev)->flash_offset & ~(c->sector_size -1)) == jeb->offset) {
|
||||
if (SECTOR_ADDR((*prev)->flash_offset) == jeb->offset) {
|
||||
/* It's in the block we're erasing */
|
||||
struct jffs2_raw_node_ref *this;
|
||||
|
||||
|
@ -277,11 +278,8 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
|
|||
printk("\n");
|
||||
});
|
||||
|
||||
if (ic->nodes == (void *)ic) {
|
||||
D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino));
|
||||
if (ic->nodes == (void *)ic && ic->nlink == 0)
|
||||
jffs2_del_ino_cache(c, ic);
|
||||
jffs2_free_inode_cache(ic);
|
||||
}
|
||||
}
|
||||
|
||||
static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
|
||||
|
@ -310,7 +308,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
|
|||
int ret;
|
||||
uint32_t bad_offset;
|
||||
|
||||
if (!jffs2_cleanmarker_oob(c)) {
|
||||
if ((!jffs2_cleanmarker_oob(c)) && (c->cleanmarker_size > 0)) {
|
||||
marker_ref = jffs2_alloc_raw_node_ref();
|
||||
if (!marker_ref) {
|
||||
printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n");
|
||||
|
@ -335,7 +333,8 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
|
|||
|
||||
bad_offset = ofs;
|
||||
|
||||
ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf);
|
||||
ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf);
|
||||
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
|
||||
goto bad;
|
||||
|
@ -351,7 +350,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
|
|||
bad_offset += i;
|
||||
printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, bad_offset);
|
||||
bad:
|
||||
if (!jffs2_cleanmarker_oob(c))
|
||||
if ((!jffs2_cleanmarker_oob(c)) && (c->cleanmarker_size > 0))
|
||||
jffs2_free_raw_node_ref(marker_ref);
|
||||
kfree(ebuf);
|
||||
bad2:
|
||||
|
@ -383,6 +382,13 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
|
|||
|
||||
jeb->first_node = jeb->last_node = NULL;
|
||||
|
||||
jeb->free_size = c->sector_size;
|
||||
jeb->used_size = 0;
|
||||
jeb->dirty_size = 0;
|
||||
jeb->wasted_size = 0;
|
||||
} else if (c->cleanmarker_size == 0) {
|
||||
jeb->first_node = jeb->last_node = NULL;
|
||||
|
||||
jeb->free_size = c->sector_size;
|
||||
jeb->used_size = 0;
|
||||
jeb->dirty_size = 0;
|
||||
|
|
|
@ -7,11 +7,10 @@
|
|||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: file.c,v 1.99 2004/11/16 20:36:11 dwmw2 Exp $
|
||||
* $Id: file.c,v 1.102 2005/07/06 12:13:09 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
|
@ -51,9 +50,7 @@ struct file_operations jffs2_file_operations =
|
|||
.ioctl = jffs2_ioctl,
|
||||
.mmap = generic_file_readonly_mmap,
|
||||
.fsync = jffs2_fsync,
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,29)
|
||||
.sendfile = generic_file_sendfile
|
||||
#endif
|
||||
};
|
||||
|
||||
/* jffs2_file_inode_operations */
|
||||
|
|
|
@ -7,11 +7,10 @@
|
|||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: fs.c,v 1.51 2004/11/28 12:19:37 dedekind Exp $
|
||||
* $Id: fs.c,v 1.56 2005/07/06 12:13:09 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sched.h>
|
||||
|
@ -450,11 +449,15 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
|
|||
|
||||
c = JFFS2_SB_INFO(sb);
|
||||
|
||||
#ifndef CONFIG_JFFS2_FS_NAND
|
||||
#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
|
||||
if (c->mtd->type == MTD_NANDFLASH) {
|
||||
printk(KERN_ERR "jffs2: Cannot operate on NAND flash unless jffs2 NAND support is compiled in.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (c->mtd->type == MTD_DATAFLASH) {
|
||||
printk(KERN_ERR "jffs2: Cannot operate on DataFlash unless jffs2 DataFlash support is compiled in.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
c->flash_size = c->mtd->size;
|
||||
|
@ -522,9 +525,7 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
|
|||
if (!sb->s_root)
|
||||
goto out_root_i;
|
||||
|
||||
#if LINUX_VERSION_CODE >= 0x20403
|
||||
sb->s_maxbytes = 0xFFFFFFFF;
|
||||
#endif
|
||||
sb->s_blocksize = PAGE_CACHE_SIZE;
|
||||
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
|
||||
sb->s_magic = JFFS2_SUPER_MAGIC;
|
||||
|
@ -661,6 +662,14 @@ static int jffs2_flash_setup(struct jffs2_sb_info *c) {
|
|||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* and Dataflash */
|
||||
if (jffs2_dataflash(c)) {
|
||||
ret = jffs2_dataflash_setup(c);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -674,4 +683,9 @@ void jffs2_flash_cleanup(struct jffs2_sb_info *c) {
|
|||
if (jffs2_nor_ecc(c)) {
|
||||
jffs2_nor_ecc_flash_cleanup(c);
|
||||
}
|
||||
|
||||
/* and DataFlash */
|
||||
if (jffs2_dataflash(c)) {
|
||||
jffs2_dataflash_cleanup(c);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: gc.c,v 1.144 2004/12/21 11:18:50 dwmw2 Exp $
|
||||
* $Id: gc.c,v 1.148 2005/04/09 10:47:00 dedekind Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -50,6 +50,7 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
|
|||
put the clever wear-levelling algorithms. Eventually. */
|
||||
/* We possibly want to favour the dirtier blocks more when the
|
||||
number of free blocks is low. */
|
||||
again:
|
||||
if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > c->resv_blocks_gcbad) {
|
||||
D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n"));
|
||||
nextlist = &c->bad_used_list;
|
||||
|
@ -79,6 +80,13 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
|
|||
D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n"));
|
||||
|
||||
nextlist = &c->erasable_list;
|
||||
} else if (!list_empty(&c->erasable_pending_wbuf_list)) {
|
||||
/* There are blocks are wating for the wbuf sync */
|
||||
D1(printk(KERN_DEBUG "Synching wbuf in order to reuse erasable_pending_wbuf_list blocks\n"));
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
jffs2_flush_wbuf_pad(c);
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
goto again;
|
||||
} else {
|
||||
/* Eep. All were empty */
|
||||
D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n"));
|
||||
|
@ -661,9 +669,10 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
|
|||
{
|
||||
struct jffs2_full_dnode *new_fn;
|
||||
struct jffs2_raw_inode ri;
|
||||
struct jffs2_node_frag *last_frag;
|
||||
jint16_t dev;
|
||||
char *mdata = NULL, mdatalen = 0;
|
||||
uint32_t alloclen, phys_ofs;
|
||||
uint32_t alloclen, phys_ofs, ilen;
|
||||
int ret;
|
||||
|
||||
if (S_ISBLK(JFFS2_F_I_MODE(f)) ||
|
||||
|
@ -699,6 +708,14 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
|
|||
goto out;
|
||||
}
|
||||
|
||||
last_frag = frag_last(&f->fragtree);
|
||||
if (last_frag)
|
||||
/* Fetch the inode length from the fragtree rather then
|
||||
* from i_size since i_size may have not been updated yet */
|
||||
ilen = last_frag->ofs + last_frag->size;
|
||||
else
|
||||
ilen = JFFS2_F_I_SIZE(f);
|
||||
|
||||
memset(&ri, 0, sizeof(ri));
|
||||
ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
|
||||
ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
|
||||
|
@ -710,7 +727,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
|
|||
ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
|
||||
ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
|
||||
ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
|
||||
ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
|
||||
ri.isize = cpu_to_je32(ilen);
|
||||
ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
|
||||
ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
|
||||
ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
|
||||
|
@ -816,8 +833,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct
|
|||
|
||||
/* Doesn't matter if there's one in the same erase block. We're going to
|
||||
delete it too at the same time. */
|
||||
if ((raw->flash_offset & ~(c->sector_size-1)) ==
|
||||
(fd->raw->flash_offset & ~(c->sector_size-1)))
|
||||
if (SECTOR_ADDR(raw->flash_offset) == SECTOR_ADDR(fd->raw->flash_offset))
|
||||
continue;
|
||||
|
||||
D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw)));
|
||||
|
@ -891,7 +907,7 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
|
|||
struct jffs2_raw_inode ri;
|
||||
struct jffs2_node_frag *frag;
|
||||
struct jffs2_full_dnode *new_fn;
|
||||
uint32_t alloclen, phys_ofs;
|
||||
uint32_t alloclen, phys_ofs, ilen;
|
||||
int ret;
|
||||
|
||||
D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n",
|
||||
|
@ -951,10 +967,19 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
|
|||
ri.csize = cpu_to_je32(0);
|
||||
ri.compr = JFFS2_COMPR_ZERO;
|
||||
}
|
||||
|
||||
frag = frag_last(&f->fragtree);
|
||||
if (frag)
|
||||
/* Fetch the inode length from the fragtree rather then
|
||||
* from i_size since i_size may have not been updated yet */
|
||||
ilen = frag->ofs + frag->size;
|
||||
else
|
||||
ilen = JFFS2_F_I_SIZE(f);
|
||||
|
||||
ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
|
||||
ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
|
||||
ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
|
||||
ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
|
||||
ri.isize = cpu_to_je32(ilen);
|
||||
ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
|
||||
ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
|
||||
ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
|
||||
|
@ -1161,7 +1186,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
|
|||
D1(printk(KERN_DEBUG "Expanded dnode to write from (0x%x-0x%x) to (0x%x-0x%x)\n",
|
||||
orig_start, orig_end, start, end));
|
||||
|
||||
BUG_ON(end > JFFS2_F_I_SIZE(f));
|
||||
D1(BUG_ON(end > frag_last(&f->fragtree)->ofs + frag_last(&f->fragtree)->size));
|
||||
BUG_ON(end < orig_end);
|
||||
BUG_ON(start > orig_start);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: nodelist.c,v 1.90 2004/12/08 17:59:20 dwmw2 Exp $
|
||||
* $Id: nodelist.c,v 1.97 2005/07/06 15:18:41 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -58,27 +58,60 @@ void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new
|
|||
/* Put a new tmp_dnode_info into the list, keeping the list in
|
||||
order of increasing version
|
||||
*/
|
||||
static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list)
|
||||
|
||||
static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct rb_root *list)
|
||||
{
|
||||
struct jffs2_tmp_dnode_info **prev = list;
|
||||
|
||||
while ((*prev) && (*prev)->version < tn->version) {
|
||||
prev = &((*prev)->next);
|
||||
}
|
||||
tn->next = (*prev);
|
||||
*prev = tn;
|
||||
struct rb_node **p = &list->rb_node;
|
||||
struct rb_node * parent = NULL;
|
||||
struct jffs2_tmp_dnode_info *this;
|
||||
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
this = rb_entry(parent, struct jffs2_tmp_dnode_info, rb);
|
||||
|
||||
/* There may actually be a collision here, but it doesn't
|
||||
actually matter. As long as the two nodes with the same
|
||||
version are together, it's all fine. */
|
||||
if (tn->version < this->version)
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
}
|
||||
|
||||
rb_link_node(&tn->rb, parent, p);
|
||||
rb_insert_color(&tn->rb, list);
|
||||
}
|
||||
|
||||
static void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
|
||||
static void jffs2_free_tmp_dnode_info_list(struct rb_root *list)
|
||||
{
|
||||
struct jffs2_tmp_dnode_info *next;
|
||||
struct rb_node *this;
|
||||
struct jffs2_tmp_dnode_info *tn;
|
||||
|
||||
while (tn) {
|
||||
next = tn;
|
||||
tn = tn->next;
|
||||
jffs2_free_full_dnode(next->fn);
|
||||
jffs2_free_tmp_dnode_info(next);
|
||||
this = list->rb_node;
|
||||
|
||||
/* Now at bottom of tree */
|
||||
while (this) {
|
||||
if (this->rb_left)
|
||||
this = this->rb_left;
|
||||
else if (this->rb_right)
|
||||
this = this->rb_right;
|
||||
else {
|
||||
tn = rb_entry(this, struct jffs2_tmp_dnode_info, rb);
|
||||
jffs2_free_full_dnode(tn->fn);
|
||||
jffs2_free_tmp_dnode_info(tn);
|
||||
|
||||
this = this->rb_parent;
|
||||
if (!this)
|
||||
break;
|
||||
|
||||
if (this->rb_left == &tn->rb)
|
||||
this->rb_left = NULL;
|
||||
else if (this->rb_right == &tn->rb)
|
||||
this->rb_right = NULL;
|
||||
else BUG();
|
||||
}
|
||||
}
|
||||
list->rb_node = NULL;
|
||||
}
|
||||
|
||||
static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
|
||||
|
@ -108,12 +141,13 @@ static struct jffs2_raw_node_ref *jffs2_first_valid_node(struct jffs2_raw_node_r
|
|||
with this ino, returning the former in order of version */
|
||||
|
||||
int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
||||
struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
|
||||
struct rb_root *tnp, struct jffs2_full_dirent **fdp,
|
||||
uint32_t *highest_version, uint32_t *latest_mctime,
|
||||
uint32_t *mctime_ver)
|
||||
{
|
||||
struct jffs2_raw_node_ref *ref, *valid_ref;
|
||||
struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL;
|
||||
struct jffs2_tmp_dnode_info *tn;
|
||||
struct rb_root ret_tn = RB_ROOT;
|
||||
struct jffs2_full_dirent *fd, *ret_fd = NULL;
|
||||
union jffs2_node_union node;
|
||||
size_t retlen;
|
||||
|
@ -127,7 +161,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
|||
|
||||
valid_ref = jffs2_first_valid_node(f->inocache->nodes);
|
||||
|
||||
if (!valid_ref)
|
||||
if (!valid_ref && (f->inocache->ino != 1))
|
||||
printk(KERN_WARNING "Eep. No valid nodes for ino #%u\n", f->inocache->ino);
|
||||
|
||||
while (valid_ref) {
|
||||
|
@ -450,7 +484,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
|||
return 0;
|
||||
|
||||
free_out:
|
||||
jffs2_free_tmp_dnode_info_list(ret_tn);
|
||||
jffs2_free_tmp_dnode_info_list(&ret_tn);
|
||||
jffs2_free_full_dirent_list(ret_fd);
|
||||
return err;
|
||||
}
|
||||
|
@ -489,9 +523,13 @@ struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t
|
|||
void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new)
|
||||
{
|
||||
struct jffs2_inode_cache **prev;
|
||||
D2(printk(KERN_DEBUG "jffs2_add_ino_cache: Add %p (ino #%u)\n", new, new->ino));
|
||||
|
||||
spin_lock(&c->inocache_lock);
|
||||
|
||||
if (!new->ino)
|
||||
new->ino = ++c->highest_ino;
|
||||
|
||||
D2(printk(KERN_DEBUG "jffs2_add_ino_cache: Add %p (ino #%u)\n", new, new->ino));
|
||||
|
||||
prev = &c->inocache_list[new->ino % INOCACHE_HASHSIZE];
|
||||
|
||||
while ((*prev) && (*prev)->ino < new->ino) {
|
||||
|
@ -506,7 +544,7 @@ void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new
|
|||
void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
|
||||
{
|
||||
struct jffs2_inode_cache **prev;
|
||||
D2(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
|
||||
D1(printk(KERN_DEBUG "jffs2_del_ino_cache: Del %p (ino #%u)\n", old, old->ino));
|
||||
spin_lock(&c->inocache_lock);
|
||||
|
||||
prev = &c->inocache_list[old->ino % INOCACHE_HASHSIZE];
|
||||
|
@ -518,6 +556,14 @@ void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old)
|
|||
*prev = old->next;
|
||||
}
|
||||
|
||||
/* Free it now unless it's in READING or CLEARING state, which
|
||||
are the transitions upon read_inode() and clear_inode(). The
|
||||
rest of the time we know nobody else is looking at it, and
|
||||
if it's held by read_inode() or clear_inode() they'll free it
|
||||
for themselves. */
|
||||
if (old->state != INO_STATE_READING && old->state != INO_STATE_CLEARING)
|
||||
jffs2_free_inode_cache(old);
|
||||
|
||||
spin_unlock(&c->inocache_lock);
|
||||
}
|
||||
|
||||
|
@ -530,7 +576,6 @@ void jffs2_free_ino_caches(struct jffs2_sb_info *c)
|
|||
this = c->inocache_list[i];
|
||||
while (this) {
|
||||
next = this->next;
|
||||
D2(printk(KERN_DEBUG "jffs2_free_ino_caches: Freeing ino #%u at %p\n", this->ino, this));
|
||||
jffs2_free_inode_cache(this);
|
||||
this = next;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: nodelist.h,v 1.126 2004/11/19 15:06:29 dedekind Exp $
|
||||
* $Id: nodelist.h,v 1.131 2005/07/05 21:03:07 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -135,6 +135,7 @@ struct jffs2_inode_cache {
|
|||
#define INO_STATE_CHECKEDABSENT 3 /* Checked, cleared again */
|
||||
#define INO_STATE_GC 4 /* GCing a 'pristine' node */
|
||||
#define INO_STATE_READING 5 /* In read_inode() */
|
||||
#define INO_STATE_CLEARING 6 /* In clear_inode() */
|
||||
|
||||
#define INOCACHE_HASHSIZE 128
|
||||
|
||||
|
@ -160,7 +161,7 @@ struct jffs2_full_dnode
|
|||
*/
|
||||
struct jffs2_tmp_dnode_info
|
||||
{
|
||||
struct jffs2_tmp_dnode_info *next;
|
||||
struct rb_node rb;
|
||||
struct jffs2_full_dnode *fn;
|
||||
uint32_t version;
|
||||
};
|
||||
|
@ -362,6 +363,18 @@ static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
|
|||
node = node->rb_left;
|
||||
return rb_entry(node, struct jffs2_node_frag, rb);
|
||||
}
|
||||
|
||||
static inline struct jffs2_node_frag *frag_last(struct rb_root *root)
|
||||
{
|
||||
struct rb_node *node = root->rb_node;
|
||||
|
||||
if (!node)
|
||||
return NULL;
|
||||
while(node->rb_right)
|
||||
node = node->rb_right;
|
||||
return rb_entry(node, struct jffs2_node_frag, rb);
|
||||
}
|
||||
|
||||
#define rb_parent(rb) ((rb)->rb_parent)
|
||||
#define frag_next(frag) rb_entry(rb_next(&(frag)->rb), struct jffs2_node_frag, rb)
|
||||
#define frag_prev(frag) rb_entry(rb_prev(&(frag)->rb), struct jffs2_node_frag, rb)
|
||||
|
@ -374,7 +387,7 @@ static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
|
|||
D2(void jffs2_print_frag_list(struct jffs2_inode_info *f));
|
||||
void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list);
|
||||
int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
||||
struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
|
||||
struct rb_root *tnp, struct jffs2_full_dirent **fdp,
|
||||
uint32_t *highest_version, uint32_t *latest_mctime,
|
||||
uint32_t *mctime_ver);
|
||||
void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state);
|
||||
|
@ -462,7 +475,7 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c);
|
|||
/* erase.c */
|
||||
void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count);
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_NAND
|
||||
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
|
||||
/* wbuf.c */
|
||||
int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino);
|
||||
int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: nodemgmt.c,v 1.115 2004/11/22 11:07:21 dwmw2 Exp $
|
||||
* $Id: nodemgmt.c,v 1.122 2005/05/06 09:30:27 dedekind Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -75,7 +75,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs
|
|||
dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size;
|
||||
if (dirty < c->nospc_dirty_size) {
|
||||
if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
|
||||
printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n");
|
||||
D1(printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n"));
|
||||
break;
|
||||
}
|
||||
D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n",
|
||||
|
@ -98,7 +98,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs
|
|||
avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size;
|
||||
if ( (avail / c->sector_size) <= blocksneeded) {
|
||||
if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
|
||||
printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n");
|
||||
D1(printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n"));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -308,7 +308,10 @@ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_r
|
|||
|
||||
D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len));
|
||||
#if 1
|
||||
if (jeb != c->nextblock || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) {
|
||||
/* we could get some obsolete nodes after nextblock was refiled
|
||||
in wbuf.c */
|
||||
if ((c->nextblock || !ref_obsolete(new))
|
||||
&&(jeb != c->nextblock || ref_offset(new) != jeb->offset + (c->sector_size - jeb->free_size))) {
|
||||
printk(KERN_WARNING "argh. node added in wrong place\n");
|
||||
jffs2_free_raw_node_ref(new);
|
||||
return -EINVAL;
|
||||
|
@ -332,7 +335,7 @@ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_r
|
|||
c->used_size += len;
|
||||
}
|
||||
|
||||
if (!jeb->free_size && !jeb->dirty_size) {
|
||||
if (!jeb->free_size && !jeb->dirty_size && !ISDIRTY(jeb->wasted_size)) {
|
||||
/* If it lives on the dirty_list, jffs2_reserve_space will put it there */
|
||||
D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
|
||||
jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
|
||||
|
@ -400,7 +403,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
|
|||
jeb = &c->blocks[blocknr];
|
||||
|
||||
if (jffs2_can_mark_obsolete(c) && !jffs2_is_readonly(c) &&
|
||||
!(c->flags & JFFS2_SB_FLAG_MOUNTING)) {
|
||||
!(c->flags & (JFFS2_SB_FLAG_SCANNING | JFFS2_SB_FLAG_BUILDING))) {
|
||||
/* Hm. This may confuse static lock analysis. If any of the above
|
||||
three conditions is false, we're going to return from this
|
||||
function without actually obliterating any nodes or freeing
|
||||
|
@ -434,7 +437,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
|
|||
|
||||
// Take care, that wasted size is taken into concern
|
||||
if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) {
|
||||
D1(printk("Dirtying\n"));
|
||||
D1(printk(KERN_DEBUG "Dirtying\n"));
|
||||
addedsize = ref_totlen(c, jeb, ref);
|
||||
jeb->dirty_size += ref_totlen(c, jeb, ref);
|
||||
c->dirty_size += ref_totlen(c, jeb, ref);
|
||||
|
@ -456,7 +459,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
|
|||
}
|
||||
}
|
||||
} else {
|
||||
D1(printk("Wasting\n"));
|
||||
D1(printk(KERN_DEBUG "Wasting\n"));
|
||||
addedsize = 0;
|
||||
jeb->wasted_size += ref_totlen(c, jeb, ref);
|
||||
c->wasted_size += ref_totlen(c, jeb, ref);
|
||||
|
@ -467,8 +470,8 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
|
|||
|
||||
D1(ACCT_PARANOIA_CHECK(jeb));
|
||||
|
||||
if (c->flags & JFFS2_SB_FLAG_MOUNTING) {
|
||||
/* Mount in progress. Don't muck about with the block
|
||||
if (c->flags & JFFS2_SB_FLAG_SCANNING) {
|
||||
/* Flash scanning is in progress. Don't muck about with the block
|
||||
lists because they're not ready yet, and don't actually
|
||||
obliterate nodes that look obsolete. If they weren't
|
||||
marked obsolete on the flash at the time they _became_
|
||||
|
@ -527,7 +530,8 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
|
|||
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
if (!jffs2_can_mark_obsolete(c) || jffs2_is_readonly(c)) {
|
||||
if (!jffs2_can_mark_obsolete(c) || jffs2_is_readonly(c) ||
|
||||
(c->flags & JFFS2_SB_FLAG_BUILDING)) {
|
||||
/* We didn't lock the erase_free_sem */
|
||||
return;
|
||||
}
|
||||
|
@ -590,11 +594,8 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
|
|||
*p = ref->next_in_ino;
|
||||
ref->next_in_ino = NULL;
|
||||
|
||||
if (ic->nodes == (void *)ic) {
|
||||
D1(printk(KERN_DEBUG "inocache for ino #%u is all gone now. Freeing\n", ic->ino));
|
||||
if (ic->nodes == (void *)ic && ic->nlink == 0)
|
||||
jffs2_del_ino_cache(c, ic);
|
||||
jffs2_free_inode_cache(ic);
|
||||
}
|
||||
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
}
|
||||
|
|
|
@ -7,41 +7,24 @@
|
|||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: os-linux.h,v 1.51 2004/11/16 20:36:11 dwmw2 Exp $
|
||||
* $Id: os-linux.h,v 1.57 2005/07/06 12:13:09 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __JFFS2_OS_LINUX_H__
|
||||
#define __JFFS2_OS_LINUX_H__
|
||||
#include <linux/version.h>
|
||||
|
||||
/* JFFS2 uses Linux mode bits natively -- no need for conversion */
|
||||
#define os_to_jffs2_mode(x) (x)
|
||||
#define jffs2_to_os_mode(x) (x)
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73)
|
||||
#define kstatfs statfs
|
||||
#endif
|
||||
|
||||
struct kstatfs;
|
||||
struct kvec;
|
||||
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
|
||||
#define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode))
|
||||
#define OFNI_EDONI_2SFFJ(f) (&(f)->vfs_inode)
|
||||
#define JFFS2_SB_INFO(sb) (sb->s_fs_info)
|
||||
#define OFNI_BS_2SFFJ(c) ((struct super_block *)c->os_priv)
|
||||
#elif defined(JFFS2_OUT_OF_KERNEL)
|
||||
#define JFFS2_INODE_INFO(i) ((struct jffs2_inode_info *) &(i)->u)
|
||||
#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) )
|
||||
#define JFFS2_SB_INFO(sb) ((struct jffs2_sb_info *) &(sb)->u)
|
||||
#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
|
||||
#else
|
||||
#define JFFS2_INODE_INFO(i) (&i->u.jffs2_i)
|
||||
#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) )
|
||||
#define JFFS2_SB_INFO(sb) (&sb->u.jffs2_sb)
|
||||
#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
|
||||
#endif
|
||||
|
||||
|
||||
#define JFFS2_F_I_SIZE(f) (OFNI_EDONI_2SFFJ(f)->i_size)
|
||||
|
@ -49,28 +32,14 @@ struct kvec;
|
|||
#define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid)
|
||||
#define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid)
|
||||
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,1)
|
||||
#define JFFS2_F_I_RDEV_MIN(f) (iminor(OFNI_EDONI_2SFFJ(f)))
|
||||
#define JFFS2_F_I_RDEV_MAJ(f) (imajor(OFNI_EDONI_2SFFJ(f)))
|
||||
#else
|
||||
#define JFFS2_F_I_RDEV_MIN(f) (MINOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
|
||||
#define JFFS2_F_I_RDEV_MAJ(f) (MAJOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
|
||||
#endif
|
||||
|
||||
/* Urgh. The things we do to keep the 2.4 build working */
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,47)
|
||||
#define ITIME(sec) ((struct timespec){sec, 0})
|
||||
#define I_SEC(tv) ((tv).tv_sec)
|
||||
#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime.tv_sec)
|
||||
#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime.tv_sec)
|
||||
#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime.tv_sec)
|
||||
#else
|
||||
#define ITIME(x) (x)
|
||||
#define I_SEC(x) (x)
|
||||
#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime)
|
||||
#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime)
|
||||
#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime)
|
||||
#endif
|
||||
|
||||
#define sleep_on_spinunlock(wq, s) \
|
||||
do { \
|
||||
|
@ -84,23 +53,21 @@ struct kvec;
|
|||
|
||||
static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
|
||||
{
|
||||
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
|
||||
f->highest_version = 0;
|
||||
f->fragtree = RB_ROOT;
|
||||
f->metadata = NULL;
|
||||
f->dents = NULL;
|
||||
f->flags = 0;
|
||||
f->usercompr = 0;
|
||||
#else
|
||||
memset(f, 0, sizeof(*f));
|
||||
init_MUTEX_LOCKED(&f->sem);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
|
||||
|
||||
#if (!defined CONFIG_JFFS2_FS_NAND && !defined CONFIG_JFFS2_FS_NOR_ECC)
|
||||
#ifndef CONFIG_JFFS2_FS_WRITEBUFFER
|
||||
#define SECTOR_ADDR(x) ( ((unsigned long)(x) & ~(c->sector_size-1)) )
|
||||
#define jffs2_can_mark_obsolete(c) (1)
|
||||
#define jffs2_is_writebuffered(c) (0)
|
||||
#define jffs2_cleanmarker_oob(c) (0)
|
||||
#define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
|
||||
|
||||
|
@ -116,11 +83,14 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
|
|||
#define jffs2_wbuf_timeout NULL
|
||||
#define jffs2_wbuf_process NULL
|
||||
#define jffs2_nor_ecc(c) (0)
|
||||
#define jffs2_dataflash(c) (0)
|
||||
#define jffs2_nor_ecc_flash_setup(c) (0)
|
||||
#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0)
|
||||
|
||||
#else /* NAND and/or ECC'd NOR support present */
|
||||
|
||||
#define jffs2_is_writebuffered(c) (c->wbuf != NULL)
|
||||
#define SECTOR_ADDR(x) ( ((unsigned long)(x) / (unsigned long)(c->sector_size)) * c->sector_size )
|
||||
#define jffs2_can_mark_obsolete(c) ((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & MTD_ECC)) || c->mtd->type == MTD_RAM)
|
||||
#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
|
||||
|
||||
|
@ -142,16 +112,16 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino);
|
|||
int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c);
|
||||
int jffs2_nand_flash_setup(struct jffs2_sb_info *c);
|
||||
void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
|
||||
#ifdef CONFIG_JFFS2_FS_NOR_ECC
|
||||
|
||||
#define jffs2_nor_ecc(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_ECC))
|
||||
int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c);
|
||||
void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c);
|
||||
#else
|
||||
#define jffs2_nor_ecc(c) (0)
|
||||
#define jffs2_nor_ecc_flash_setup(c) (0)
|
||||
#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0)
|
||||
#endif /* NOR ECC */
|
||||
#endif /* NAND */
|
||||
|
||||
#define jffs2_dataflash(c) (c->mtd->type == MTD_DATAFLASH)
|
||||
int jffs2_dataflash_setup(struct jffs2_sb_info *c);
|
||||
void jffs2_dataflash_cleanup(struct jffs2_sb_info *c);
|
||||
|
||||
#endif /* WRITEBUFFER */
|
||||
|
||||
/* erase.c */
|
||||
static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: read.c,v 1.38 2004/11/16 20:36:12 dwmw2 Exp $
|
||||
* $Id: read.c,v 1.39 2005/03/01 10:34:03 dedekind Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -214,33 +214,3 @@ int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Core function to read symlink target. */
|
||||
char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
|
||||
{
|
||||
char *buf;
|
||||
int ret;
|
||||
|
||||
down(&f->sem);
|
||||
|
||||
if (!f->metadata) {
|
||||
printk(KERN_NOTICE "No metadata for symlink inode #%u\n", f->inocache->ino);
|
||||
up(&f->sem);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
buf = kmalloc(f->metadata->size+1, GFP_USER);
|
||||
if (!buf) {
|
||||
up(&f->sem);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
buf[f->metadata->size]=0;
|
||||
|
||||
ret = jffs2_read_dnode(c, f, f->metadata, buf, 0, f->metadata->size);
|
||||
|
||||
up(&f->sem);
|
||||
|
||||
if (ret) {
|
||||
kfree(buf);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: readinode.c,v 1.117 2004/11/20 18:06:54 dwmw2 Exp $
|
||||
* $Id: readinode.c,v 1.120 2005/07/05 21:03:07 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -500,7 +500,9 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
|||
struct jffs2_inode_info *f,
|
||||
struct jffs2_raw_inode *latest_node)
|
||||
{
|
||||
struct jffs2_tmp_dnode_info *tn_list, *tn;
|
||||
struct jffs2_tmp_dnode_info *tn = NULL;
|
||||
struct rb_root tn_list;
|
||||
struct rb_node *rb, *repl_rb;
|
||||
struct jffs2_full_dirent *fd_list;
|
||||
struct jffs2_full_dnode *fn = NULL;
|
||||
uint32_t crc;
|
||||
|
@ -522,9 +524,10 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
|||
}
|
||||
f->dents = fd_list;
|
||||
|
||||
while (tn_list) {
|
||||
tn = tn_list;
|
||||
rb = rb_first(&tn_list);
|
||||
|
||||
while (rb) {
|
||||
tn = rb_entry(rb, struct jffs2_tmp_dnode_info, rb);
|
||||
fn = tn->fn;
|
||||
|
||||
if (f->metadata) {
|
||||
|
@ -556,7 +559,30 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
|||
mdata_ver = tn->version;
|
||||
}
|
||||
next_tn:
|
||||
tn_list = tn->next;
|
||||
BUG_ON(rb->rb_left);
|
||||
repl_rb = NULL;
|
||||
if (rb->rb_parent && rb->rb_parent->rb_left == rb) {
|
||||
/* We were then left-hand child of our parent. We need
|
||||
to move our own right-hand child into our place. */
|
||||
repl_rb = rb->rb_right;
|
||||
if (repl_rb)
|
||||
repl_rb->rb_parent = rb->rb_parent;
|
||||
} else
|
||||
repl_rb = NULL;
|
||||
|
||||
rb = rb_next(rb);
|
||||
|
||||
/* Remove the spent tn from the tree; don't bother rebalancing
|
||||
but put our right-hand child in our own place. */
|
||||
if (tn->rb.rb_parent) {
|
||||
if (tn->rb.rb_parent->rb_left == &tn->rb)
|
||||
tn->rb.rb_parent->rb_left = repl_rb;
|
||||
else if (tn->rb.rb_parent->rb_right == &tn->rb)
|
||||
tn->rb.rb_parent->rb_right = repl_rb;
|
||||
else BUG();
|
||||
} else if (tn->rb.rb_right)
|
||||
tn->rb.rb_right->rb_parent = NULL;
|
||||
|
||||
jffs2_free_tmp_dnode_info(tn);
|
||||
}
|
||||
D1(jffs2_sanitycheck_fragtree(f));
|
||||
|
@ -623,6 +649,40 @@ static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c,
|
|||
case. */
|
||||
if (!je32_to_cpu(latest_node->isize))
|
||||
latest_node->isize = latest_node->dsize;
|
||||
|
||||
if (f->inocache->state != INO_STATE_CHECKING) {
|
||||
/* Symlink's inode data is the target path. Read it and
|
||||
* keep in RAM to facilitate quick follow symlink operation.
|
||||
* We use f->dents field to store the target path, which
|
||||
* is somewhat ugly. */
|
||||
f->dents = kmalloc(je32_to_cpu(latest_node->csize) + 1, GFP_KERNEL);
|
||||
if (!f->dents) {
|
||||
printk(KERN_WARNING "Can't allocate %d bytes of memory "
|
||||
"for the symlink target path cache\n",
|
||||
je32_to_cpu(latest_node->csize));
|
||||
up(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = jffs2_flash_read(c, ref_offset(fn->raw) + sizeof(*latest_node),
|
||||
je32_to_cpu(latest_node->csize), &retlen, (char *)f->dents);
|
||||
|
||||
if (ret || retlen != je32_to_cpu(latest_node->csize)) {
|
||||
if (retlen != je32_to_cpu(latest_node->csize))
|
||||
ret = -EIO;
|
||||
kfree(f->dents);
|
||||
f->dents = NULL;
|
||||
up(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
return -ret;
|
||||
}
|
||||
|
||||
((char *)f->dents)[je32_to_cpu(latest_node->csize)] = '\0';
|
||||
D1(printk(KERN_DEBUG "jffs2_do_read_inode(): symlink's target '%s' cached\n",
|
||||
(char *)f->dents));
|
||||
}
|
||||
|
||||
/* fall through... */
|
||||
|
||||
case S_IFBLK:
|
||||
|
@ -672,6 +732,9 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
|
|||
down(&f->sem);
|
||||
deleted = f->inocache && !f->inocache->nlink;
|
||||
|
||||
if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
|
||||
jffs2_set_inocache_state(c, f->inocache, INO_STATE_CLEARING);
|
||||
|
||||
if (f->metadata) {
|
||||
if (deleted)
|
||||
jffs2_mark_node_obsolete(c, f->metadata->raw);
|
||||
|
@ -680,16 +743,27 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
|
|||
|
||||
jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);
|
||||
|
||||
fds = f->dents;
|
||||
/* For symlink inodes we us f->dents to store the target path name */
|
||||
if (S_ISLNK(OFNI_EDONI_2SFFJ(f)->i_mode)) {
|
||||
if (f->dents) {
|
||||
kfree(f->dents);
|
||||
f->dents = NULL;
|
||||
}
|
||||
} else {
|
||||
fds = f->dents;
|
||||
|
||||
while(fds) {
|
||||
fd = fds;
|
||||
fds = fd->next;
|
||||
jffs2_free_full_dirent(fd);
|
||||
while(fds) {
|
||||
fd = fds;
|
||||
fds = fd->next;
|
||||
jffs2_free_full_dirent(fd);
|
||||
}
|
||||
}
|
||||
|
||||
if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
|
||||
if (f->inocache && f->inocache->state != INO_STATE_CHECKING) {
|
||||
jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
|
||||
if (f->inocache->nodes == (void *)f->inocache)
|
||||
jffs2_del_ino_cache(c, f->inocache);
|
||||
}
|
||||
|
||||
up(&f->sem);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: scan.c,v 1.115 2004/11/17 12:59:08 dedekind Exp $
|
||||
* $Id: scan.c,v 1.119 2005/02/17 17:51:13 dedekind Exp $
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
|
@ -19,7 +19,7 @@
|
|||
#include <linux/compiler.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
#define EMPTY_SCAN_SIZE 1024
|
||||
#define DEFAULT_EMPTY_SCAN_SIZE 1024
|
||||
|
||||
#define DIRTY_SPACE(x) do { typeof(x) _x = (x); \
|
||||
c->free_size -= _x; c->dirty_size += _x; \
|
||||
|
@ -68,13 +68,21 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
|
|||
static inline int min_free(struct jffs2_sb_info *c)
|
||||
{
|
||||
uint32_t min = 2 * sizeof(struct jffs2_raw_inode);
|
||||
#if defined CONFIG_JFFS2_FS_NAND || defined CONFIG_JFFS2_FS_NOR_ECC
|
||||
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
|
||||
if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize)
|
||||
return c->wbuf_pagesize;
|
||||
#endif
|
||||
return min;
|
||||
|
||||
}
|
||||
|
||||
static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size) {
|
||||
if (sector_size < DEFAULT_EMPTY_SCAN_SIZE)
|
||||
return sector_size;
|
||||
else
|
||||
return DEFAULT_EMPTY_SCAN_SIZE;
|
||||
}
|
||||
|
||||
int jffs2_scan_medium(struct jffs2_sb_info *c)
|
||||
{
|
||||
int i, ret;
|
||||
|
@ -220,7 +228,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
|
|||
c->dirty_size -= c->nextblock->dirty_size;
|
||||
c->nextblock->dirty_size = 0;
|
||||
}
|
||||
#if defined CONFIG_JFFS2_FS_NAND || defined CONFIG_JFFS2_FS_NOR_ECC
|
||||
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
|
||||
if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) {
|
||||
/* If we're going to start writing into a block which already
|
||||
contains data, and the end of the data isn't page-aligned,
|
||||
|
@ -286,7 +294,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
|
|||
uint32_t hdr_crc, buf_ofs, buf_len;
|
||||
int err;
|
||||
int noise = 0;
|
||||
#ifdef CONFIG_JFFS2_FS_NAND
|
||||
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
|
||||
int cleanmarkerfound = 0;
|
||||
#endif
|
||||
|
||||
|
@ -295,7 +303,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
|
|||
|
||||
D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs));
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_NAND
|
||||
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
|
||||
if (jffs2_cleanmarker_oob(c)) {
|
||||
int ret = jffs2_check_nand_cleanmarker(c, jeb);
|
||||
D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret));
|
||||
|
@ -316,7 +324,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
|
|||
if (!buf_size) {
|
||||
buf_len = c->sector_size;
|
||||
} else {
|
||||
buf_len = EMPTY_SCAN_SIZE;
|
||||
buf_len = EMPTY_SCAN_SIZE(c->sector_size);
|
||||
err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -326,11 +334,11 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
|
|||
ofs = 0;
|
||||
|
||||
/* Scan only 4KiB of 0xFF before declaring it's empty */
|
||||
while(ofs < EMPTY_SCAN_SIZE && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
|
||||
while(ofs < EMPTY_SCAN_SIZE(c->sector_size) && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
|
||||
ofs += 4;
|
||||
|
||||
if (ofs == EMPTY_SCAN_SIZE) {
|
||||
#ifdef CONFIG_JFFS2_FS_NAND
|
||||
if (ofs == EMPTY_SCAN_SIZE(c->sector_size)) {
|
||||
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
|
||||
if (jffs2_cleanmarker_oob(c)) {
|
||||
/* scan oob, take care of cleanmarker */
|
||||
int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound);
|
||||
|
@ -343,7 +351,10 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
|
|||
}
|
||||
#endif
|
||||
D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset));
|
||||
return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */
|
||||
if (c->cleanmarker_size == 0)
|
||||
return BLK_STATE_CLEANMARKER; /* don't bother with re-erase */
|
||||
else
|
||||
return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */
|
||||
}
|
||||
if (ofs) {
|
||||
D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset,
|
||||
|
@ -422,8 +433,8 @@ scan_more:
|
|||
/* If we're only checking the beginning of a block with a cleanmarker,
|
||||
bail now */
|
||||
if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) &&
|
||||
c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_in_ino) {
|
||||
D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE));
|
||||
c->cleanmarker_size && !jeb->dirty_size && !jeb->first_node->next_phys) {
|
||||
D1(printk(KERN_DEBUG "%d bytes at start of block seems clean... assuming all clean\n", EMPTY_SCAN_SIZE(c->sector_size)));
|
||||
return BLK_STATE_CLEANMARKER;
|
||||
}
|
||||
|
||||
|
@ -618,7 +629,7 @@ scan_more:
|
|||
}
|
||||
|
||||
if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size
|
||||
&& (!jeb->first_node || !jeb->first_node->next_in_ino) )
|
||||
&& (!jeb->first_node || !jeb->first_node->next_phys) )
|
||||
return BLK_STATE_CLEANMARKER;
|
||||
|
||||
/* move blocks with max 4 byte dirty space to cleanlist */
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: super.c,v 1.104 2004/11/23 15:37:31 gleixner Exp $
|
||||
* $Id: super.c,v 1.106 2005/05/18 11:37:25 dedekind Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -270,8 +270,6 @@ static void jffs2_put_super (struct super_block *sb)
|
|||
|
||||
D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n"));
|
||||
|
||||
if (!(sb->s_flags & MS_RDONLY))
|
||||
jffs2_stop_garbage_collect_thread(c);
|
||||
down(&c->alloc_sem);
|
||||
jffs2_flush_wbuf_pad(c);
|
||||
up(&c->alloc_sem);
|
||||
|
@ -292,6 +290,8 @@ static void jffs2_put_super (struct super_block *sb)
|
|||
static void jffs2_kill_sb(struct super_block *sb)
|
||||
{
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
|
||||
if (!(sb->s_flags & MS_RDONLY))
|
||||
jffs2_stop_garbage_collect_thread(c);
|
||||
generic_shutdown_super(sb);
|
||||
put_mtd_device(c->mtd);
|
||||
kfree(c);
|
||||
|
@ -309,7 +309,7 @@ static int __init init_jffs2_fs(void)
|
|||
int ret;
|
||||
|
||||
printk(KERN_INFO "JFFS2 version 2.2."
|
||||
#ifdef CONFIG_JFFS2_FS_NAND
|
||||
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
|
||||
" (NAND)"
|
||||
#endif
|
||||
" (C) 2001-2003 Red Hat, Inc.\n");
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: symlink.c,v 1.14 2004/11/16 20:36:12 dwmw2 Exp $
|
||||
* $Id: symlink.c,v 1.16 2005/03/01 10:50:48 dedekind Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -19,27 +19,45 @@
|
|||
#include "nodelist.h"
|
||||
|
||||
static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
|
||||
static void jffs2_put_link(struct dentry *dentry, struct nameidata *nd);
|
||||
|
||||
struct inode_operations jffs2_symlink_inode_operations =
|
||||
{
|
||||
.readlink = generic_readlink,
|
||||
.follow_link = jffs2_follow_link,
|
||||
.put_link = jffs2_put_link,
|
||||
.setattr = jffs2_setattr
|
||||
};
|
||||
|
||||
static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
unsigned char *buf;
|
||||
buf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode));
|
||||
nd_set_link(nd, buf);
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
|
||||
|
||||
/*
|
||||
* We don't acquire the f->sem mutex here since the only data we
|
||||
* use is f->dents which in case of the symlink inode points to the
|
||||
* symlink's target path.
|
||||
*
|
||||
* 1. If we are here the inode has already built and f->dents has
|
||||
* to point to the target path.
|
||||
* 2. Nobody uses f->dents (if the inode is symlink's inode). The
|
||||
* exception is inode freeing function which frees f->dents. But
|
||||
* it can't be called while we are here and before VFS has
|
||||
* stopped using our f->dents string which we provide by means of
|
||||
* nd_set_link() call.
|
||||
*/
|
||||
|
||||
if (!f->dents) {
|
||||
printk(KERN_ERR "jffs2_follow_link(): can't find symlink taerget\n");
|
||||
return -EIO;
|
||||
}
|
||||
D1(printk(KERN_DEBUG "jffs2_follow_link(): target path is '%s'\n", (char *) f->dents));
|
||||
|
||||
nd_set_link(nd, (char *)f->dents);
|
||||
|
||||
/*
|
||||
* We unlock the f->sem mutex but VFS will use the f->dents string. This is safe
|
||||
* since the only way that may cause f->dents to be changed is iput() operation.
|
||||
* But VFS will not use f->dents after iput() has been called.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jffs2_put_link(struct dentry *dentry, struct nameidata *nd)
|
||||
{
|
||||
char *s = nd_get_link(nd);
|
||||
if (!IS_ERR(s))
|
||||
kfree(s);
|
||||
}
|
||||
|
|
164
fs/jffs2/wbuf.c
164
fs/jffs2/wbuf.c
|
@ -9,7 +9,7 @@
|
|||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: wbuf.c,v 1.82 2004/11/20 22:08:31 dwmw2 Exp $
|
||||
* $Id: wbuf.c,v 1.92 2005/04/05 12:51:54 dedekind Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -83,7 +83,7 @@ static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino)
|
|||
struct jffs2_inodirty *new;
|
||||
|
||||
/* Mark the superblock dirty so that kupdated will flush... */
|
||||
OFNI_BS_2SFFJ(c)->s_dirt = 1;
|
||||
jffs2_erase_pending_trigger(c);
|
||||
|
||||
if (jffs2_wbuf_pending_for_ino(c, ino))
|
||||
return;
|
||||
|
@ -130,7 +130,10 @@ static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
|
|||
}
|
||||
}
|
||||
|
||||
static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
|
||||
#define REFILE_NOTEMPTY 0
|
||||
#define REFILE_ANYWAY 1
|
||||
|
||||
static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int allow_empty)
|
||||
{
|
||||
D1(printk("About to refile bad block at %08x\n", jeb->offset));
|
||||
|
||||
|
@ -144,7 +147,7 @@ static void jffs2_block_refile(struct jffs2_sb_info *c, struct jffs2_eraseblock
|
|||
D1(printk("Refiling block at %08x to bad_used_list\n", jeb->offset));
|
||||
list_add(&jeb->list, &c->bad_used_list);
|
||||
} else {
|
||||
BUG();
|
||||
BUG_ON(allow_empty == REFILE_NOTEMPTY);
|
||||
/* It has to have had some nodes or we couldn't be here */
|
||||
D1(printk("Refiling block at %08x to erase_pending_list\n", jeb->offset));
|
||||
list_add(&jeb->list, &c->erase_pending_list);
|
||||
|
@ -179,7 +182,7 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
|
|||
|
||||
jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
|
||||
|
||||
jffs2_block_refile(c, jeb);
|
||||
jffs2_block_refile(c, jeb, REFILE_NOTEMPTY);
|
||||
|
||||
/* Find the first node to be recovered, by skipping over every
|
||||
node which ends before the wbuf starts, or which is obsolete. */
|
||||
|
@ -264,17 +267,16 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
|
|||
ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len);
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n");
|
||||
if (buf)
|
||||
kfree(buf);
|
||||
kfree(buf);
|
||||
return;
|
||||
}
|
||||
if (end-start >= c->wbuf_pagesize) {
|
||||
/* Need to do another write immediately. This, btw,
|
||||
means that we'll be writing from 'buf' and not from
|
||||
the wbuf. Since if we're writing from the wbuf there
|
||||
won't be more than a wbuf full of data, now will
|
||||
there? :) */
|
||||
|
||||
/* Need to do another write immediately, but it's possible
|
||||
that this is just because the wbuf itself is completely
|
||||
full, and there's nothing earlier read back from the
|
||||
flash. Hence 'buf' isn't necessarily what we're writing
|
||||
from. */
|
||||
unsigned char *rewrite_buf = buf?:c->wbuf;
|
||||
uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize);
|
||||
|
||||
D1(printk(KERN_DEBUG "Write 0x%x bytes at 0x%08x in wbuf recover\n",
|
||||
|
@ -292,9 +294,9 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
|
|||
#endif
|
||||
if (jffs2_cleanmarker_oob(c))
|
||||
ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
|
||||
buf, NULL, c->oobinfo);
|
||||
rewrite_buf, NULL, c->oobinfo);
|
||||
else
|
||||
ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, buf);
|
||||
ret = c->mtd->write(c->mtd, ofs, towrite, &retlen, rewrite_buf);
|
||||
|
||||
if (ret || retlen != towrite) {
|
||||
/* Argh. We tried. Really we did. */
|
||||
|
@ -321,10 +323,10 @@ static void jffs2_wbuf_recover(struct jffs2_sb_info *c)
|
|||
|
||||
c->wbuf_len = (end - start) - towrite;
|
||||
c->wbuf_ofs = ofs + towrite;
|
||||
memcpy(c->wbuf, buf + towrite, c->wbuf_len);
|
||||
memmove(c->wbuf, rewrite_buf + towrite, c->wbuf_len);
|
||||
/* Don't muck about with c->wbuf_inodes. False positives are harmless. */
|
||||
|
||||
kfree(buf);
|
||||
if (buf)
|
||||
kfree(buf);
|
||||
} else {
|
||||
/* OK, now we're left with the dregs in whichever buffer we're using */
|
||||
if (buf) {
|
||||
|
@ -413,9 +415,9 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
|
|||
int ret;
|
||||
size_t retlen;
|
||||
|
||||
/* Nothing to do if not NAND flash. In particular, we shouldn't
|
||||
/* Nothing to do if not write-buffering the flash. In particular, we shouldn't
|
||||
del_timer() the timer we never initialised. */
|
||||
if (jffs2_can_mark_obsolete(c))
|
||||
if (!jffs2_is_writebuffered(c))
|
||||
return 0;
|
||||
|
||||
if (!down_trylock(&c->alloc_sem)) {
|
||||
|
@ -424,7 +426,7 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
|
|||
BUG();
|
||||
}
|
||||
|
||||
if(!c->wbuf || !c->wbuf_len)
|
||||
if (!c->wbuf_len) /* already checked c->wbuf above */
|
||||
return 0;
|
||||
|
||||
/* claim remaining space on the page
|
||||
|
@ -433,7 +435,7 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
|
|||
if we have a switch to next page, we will not have
|
||||
enough remaining space for this.
|
||||
*/
|
||||
if (pad) {
|
||||
if (pad && !jffs2_dataflash(c)) {
|
||||
c->wbuf_len = PAD(c->wbuf_len);
|
||||
|
||||
/* Pad with JFFS2_DIRTY_BITMASK initially. this helps out ECC'd NOR
|
||||
|
@ -484,7 +486,7 @@ static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad)
|
|||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
/* Adjust free size of the block if we padded. */
|
||||
if (pad) {
|
||||
if (pad && !jffs2_dataflash(c)) {
|
||||
struct jffs2_eraseblock *jeb;
|
||||
|
||||
jeb = &c->blocks[c->wbuf_ofs / c->sector_size];
|
||||
|
@ -532,6 +534,9 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
|
|||
|
||||
D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino));
|
||||
|
||||
if (!c->wbuf)
|
||||
return 0;
|
||||
|
||||
down(&c->alloc_sem);
|
||||
if (!jffs2_wbuf_pending_for_ino(c, ino)) {
|
||||
D1(printk(KERN_DEBUG "Ino #%d not pending in wbuf. Returning\n", ino));
|
||||
|
@ -547,6 +552,10 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
|
|||
D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n"));
|
||||
down_write(&c->wbuf_sem);
|
||||
ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
|
||||
/* retry flushing wbuf in case jffs2_wbuf_recover
|
||||
left some data in the wbuf */
|
||||
if (ret)
|
||||
ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
|
||||
up_write(&c->wbuf_sem);
|
||||
} else while (old_wbuf_len &&
|
||||
old_wbuf_ofs == c->wbuf_ofs) {
|
||||
|
@ -561,6 +570,10 @@ int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
|
|||
down(&c->alloc_sem);
|
||||
down_write(&c->wbuf_sem);
|
||||
ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
|
||||
/* retry flushing wbuf in case jffs2_wbuf_recover
|
||||
left some data in the wbuf */
|
||||
if (ret)
|
||||
ret = __jffs2_flush_wbuf(c, PAD_ACCOUNTING);
|
||||
up_write(&c->wbuf_sem);
|
||||
break;
|
||||
}
|
||||
|
@ -578,15 +591,27 @@ int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c)
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (!c->wbuf)
|
||||
return 0;
|
||||
|
||||
down_write(&c->wbuf_sem);
|
||||
ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
|
||||
/* retry - maybe wbuf recover left some data in wbuf. */
|
||||
if (ret)
|
||||
ret = __jffs2_flush_wbuf(c, PAD_NOACCOUNT);
|
||||
up_write(&c->wbuf_sem);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
|
||||
#define PAGE_DIV(x) ( ((unsigned long)(x) / (unsigned long)(c->wbuf_pagesize)) * (unsigned long)(c->wbuf_pagesize) )
|
||||
#define PAGE_MOD(x) ( (unsigned long)(x) % (unsigned long)(c->wbuf_pagesize) )
|
||||
#else
|
||||
#define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) )
|
||||
#define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) )
|
||||
#endif
|
||||
|
||||
int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino)
|
||||
{
|
||||
struct kvec outvecs[3];
|
||||
|
@ -601,7 +626,7 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig
|
|||
uint32_t outvec_to = to;
|
||||
|
||||
/* If not NAND flash, don't bother */
|
||||
if (!c->wbuf)
|
||||
if (!jffs2_is_writebuffered(c))
|
||||
return jffs2_flash_direct_writev(c, invecs, count, to, retlen);
|
||||
|
||||
down_write(&c->wbuf_sem);
|
||||
|
@ -630,7 +655,7 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig
|
|||
erase block. Anything else, and you die.
|
||||
New block starts at xxx000c (0-b = block header)
|
||||
*/
|
||||
if ( (to & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) {
|
||||
if (SECTOR_ADDR(to) != SECTOR_ADDR(c->wbuf_ofs)) {
|
||||
/* It's a write to a new block */
|
||||
if (c->wbuf_len) {
|
||||
D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs));
|
||||
|
@ -762,9 +787,18 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct kvec *invecs, unsig
|
|||
|
||||
if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) {
|
||||
/* At this point we have no problem,
|
||||
c->wbuf is empty.
|
||||
c->wbuf is empty. However refile nextblock to avoid
|
||||
writing again to same address.
|
||||
*/
|
||||
*retlen = donelen;
|
||||
struct jffs2_eraseblock *jeb;
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
jeb = &c->blocks[outvec_to / c->sector_size];
|
||||
jffs2_block_refile(c, jeb, REFILE_ANYWAY);
|
||||
|
||||
*retlen = 0;
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
|
@ -819,7 +853,7 @@ int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *r
|
|||
{
|
||||
struct kvec vecs[1];
|
||||
|
||||
if (jffs2_can_mark_obsolete(c))
|
||||
if (!jffs2_is_writebuffered(c))
|
||||
return c->mtd->write(c->mtd, ofs, len, retlen, buf);
|
||||
|
||||
vecs[0].iov_base = (unsigned char *) buf;
|
||||
|
@ -835,39 +869,38 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re
|
|||
loff_t orbf = 0, owbf = 0, lwbf = 0;
|
||||
int ret;
|
||||
|
||||
/* Read flash */
|
||||
if (!jffs2_can_mark_obsolete(c)) {
|
||||
down_read(&c->wbuf_sem);
|
||||
|
||||
if (jffs2_cleanmarker_oob(c))
|
||||
ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo);
|
||||
else
|
||||
ret = c->mtd->read(c->mtd, ofs, len, retlen, buf);
|
||||
|
||||
if ( (ret == -EBADMSG) && (*retlen == len) ) {
|
||||
printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n",
|
||||
len, ofs);
|
||||
/*
|
||||
* We have the raw data without ECC correction in the buffer, maybe
|
||||
* we are lucky and all data or parts are correct. We check the node.
|
||||
* If data are corrupted node check will sort it out.
|
||||
* We keep this block, it will fail on write or erase and the we
|
||||
* mark it bad. Or should we do that now? But we should give him a chance.
|
||||
* Maybe we had a system crash or power loss before the ecc write or
|
||||
* a erase was completed.
|
||||
* So we return success. :)
|
||||
*/
|
||||
ret = 0;
|
||||
}
|
||||
} else
|
||||
if (!jffs2_is_writebuffered(c))
|
||||
return c->mtd->read(c->mtd, ofs, len, retlen, buf);
|
||||
|
||||
/* Read flash */
|
||||
down_read(&c->wbuf_sem);
|
||||
if (jffs2_cleanmarker_oob(c))
|
||||
ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo);
|
||||
else
|
||||
ret = c->mtd->read(c->mtd, ofs, len, retlen, buf);
|
||||
|
||||
if ( (ret == -EBADMSG) && (*retlen == len) ) {
|
||||
printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n",
|
||||
len, ofs);
|
||||
/*
|
||||
* We have the raw data without ECC correction in the buffer, maybe
|
||||
* we are lucky and all data or parts are correct. We check the node.
|
||||
* If data are corrupted node check will sort it out.
|
||||
* We keep this block, it will fail on write or erase and the we
|
||||
* mark it bad. Or should we do that now? But we should give him a chance.
|
||||
* Maybe we had a system crash or power loss before the ecc write or
|
||||
* a erase was completed.
|
||||
* So we return success. :)
|
||||
*/
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
/* if no writebuffer available or write buffer empty, return */
|
||||
if (!c->wbuf_pagesize || !c->wbuf_len)
|
||||
goto exit;
|
||||
|
||||
/* if we read in a different block, return */
|
||||
if ( (ofs & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) )
|
||||
if (SECTOR_ADDR(ofs) != SECTOR_ADDR(c->wbuf_ofs))
|
||||
goto exit;
|
||||
|
||||
if (ofs >= c->wbuf_ofs) {
|
||||
|
@ -1161,7 +1194,27 @@ void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
|
|||
kfree(c->wbuf);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_NOR_ECC
|
||||
int jffs2_dataflash_setup(struct jffs2_sb_info *c) {
|
||||
c->cleanmarker_size = 0; /* No cleanmarkers needed */
|
||||
|
||||
/* Initialize write buffer */
|
||||
init_rwsem(&c->wbuf_sem);
|
||||
c->wbuf_pagesize = c->sector_size;
|
||||
c->wbuf_ofs = 0xFFFFFFFF;
|
||||
|
||||
c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
|
||||
if (!c->wbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
printk(KERN_INFO "JFFS2 write-buffering enabled (%i)\n", c->wbuf_pagesize);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void jffs2_dataflash_cleanup(struct jffs2_sb_info *c) {
|
||||
kfree(c->wbuf);
|
||||
}
|
||||
|
||||
int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c) {
|
||||
/* Cleanmarker is actually larger on the flashes */
|
||||
c->cleanmarker_size = 16;
|
||||
|
@ -1181,4 +1234,3 @@ int jffs2_nor_ecc_flash_setup(struct jffs2_sb_info *c) {
|
|||
void jffs2_nor_ecc_flash_cleanup(struct jffs2_sb_info *c) {
|
||||
kfree(c->wbuf);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
* $Id: write.c,v 1.87 2004/11/16 20:36:12 dwmw2 Exp $
|
||||
* $Id: write.c,v 1.92 2005/04/13 13:22:35 dwmw2 Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -35,13 +35,12 @@ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint
|
|||
f->inocache = ic;
|
||||
f->inocache->nlink = 1;
|
||||
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
|
||||
f->inocache->ino = ++c->highest_ino;
|
||||
f->inocache->state = INO_STATE_PRESENT;
|
||||
|
||||
ri->ino = cpu_to_je32(f->inocache->ino);
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
|
||||
jffs2_add_ino_cache(c, f->inocache);
|
||||
D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
|
||||
ri->ino = cpu_to_je32(f->inocache->ino);
|
||||
|
||||
ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
|
||||
ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
|
||||
|
@ -136,6 +135,15 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2
|
|||
raw->__totlen = PAD(sizeof(*ri)+datalen);
|
||||
raw->next_phys = NULL;
|
||||
|
||||
if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(ri->version) < f->highest_version)) {
|
||||
BUG_ON(!retried);
|
||||
D1(printk(KERN_DEBUG "jffs2_write_dnode : dnode_version %d, "
|
||||
"highest version %d -> updating dnode\n",
|
||||
je32_to_cpu(ri->version), f->highest_version));
|
||||
ri->version = cpu_to_je32(++f->highest_version);
|
||||
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
|
||||
}
|
||||
|
||||
ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen,
|
||||
(alloc_mode==ALLOC_GC)?0:f->inocache->ino);
|
||||
|
||||
|
@ -280,6 +288,16 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff
|
|||
raw->__totlen = PAD(sizeof(*rd)+namelen);
|
||||
raw->next_phys = NULL;
|
||||
|
||||
if ((alloc_mode!=ALLOC_GC) && (je32_to_cpu(rd->version) < f->highest_version)) {
|
||||
BUG_ON(!retried);
|
||||
D1(printk(KERN_DEBUG "jffs2_write_dirent : dirent_version %d, "
|
||||
"highest version %d -> updating dirent\n",
|
||||
je32_to_cpu(rd->version), f->highest_version));
|
||||
rd->version = cpu_to_je32(++f->highest_version);
|
||||
fd->version = je32_to_cpu(rd->version);
|
||||
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
|
||||
}
|
||||
|
||||
ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen,
|
||||
(alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino));
|
||||
if (ret || (retlen != sizeof(*rd) + namelen)) {
|
||||
|
@ -625,20 +643,23 @@ int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
|
|||
|
||||
down(&dead_f->sem);
|
||||
|
||||
while (dead_f->dents) {
|
||||
/* There can be only deleted ones */
|
||||
fd = dead_f->dents;
|
||||
|
||||
dead_f->dents = fd->next;
|
||||
|
||||
if (fd->ino) {
|
||||
printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
|
||||
dead_f->inocache->ino, fd->name, fd->ino);
|
||||
} else {
|
||||
D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, dead_f->inocache->ino));
|
||||
if (S_ISDIR(OFNI_EDONI_2SFFJ(dead_f)->i_mode)) {
|
||||
while (dead_f->dents) {
|
||||
/* There can be only deleted ones */
|
||||
fd = dead_f->dents;
|
||||
|
||||
dead_f->dents = fd->next;
|
||||
|
||||
if (fd->ino) {
|
||||
printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
|
||||
dead_f->inocache->ino, fd->name, fd->ino);
|
||||
} else {
|
||||
D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n",
|
||||
fd->name, dead_f->inocache->ino));
|
||||
}
|
||||
jffs2_mark_node_obsolete(c, fd->raw);
|
||||
jffs2_free_full_dirent(fd);
|
||||
}
|
||||
jffs2_mark_node_obsolete(c, fd->raw);
|
||||
jffs2_free_full_dirent(fd);
|
||||
}
|
||||
|
||||
dead_f->inocache->nlink--;
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* MTD primitives for XIP support. Architecture specific functions
|
||||
*
|
||||
* Do not include this file directly. It's included from linux/mtd/xip.h
|
||||
*
|
||||
* Author: Nicolas Pitre
|
||||
* Created: Nov 2, 2004
|
||||
* Copyright: (C) 2004 MontaVista Software, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* $Id: xip.h,v 1.2 2004/12/01 15:49:10 nico Exp $
|
||||
*/
|
||||
|
||||
#ifndef __ARCH_PXA_MTD_XIP_H__
|
||||
#define __ARCH_PXA_MTD_XIP_H__
|
||||
|
||||
#include <asm/arch/pxa-regs.h>
|
||||
|
||||
#define xip_irqpending() (ICIP & ICMR)
|
||||
|
||||
/* we sample OSCR and convert desired delta to usec (1/4 ~= 1000000/3686400) */
|
||||
#define xip_currtime() (OSCR)
|
||||
#define xip_elapsed_since(x) (signed)((OSCR - (x)) / 4)
|
||||
|
||||
/*
|
||||
* xip_cpu_idle() is used when waiting for a delay equal or larger than
|
||||
* the system timer tick period. This should put the CPU into idle mode
|
||||
* to save power and to be woken up only when some interrupts are pending.
|
||||
* As above, this should not rely upon standard kernel code.
|
||||
*/
|
||||
|
||||
#define xip_cpu_idle() asm volatile ("mcr p14, 0, %0, c7, c0, 0" :: "r" (1))
|
||||
|
||||
#endif /* __ARCH_PXA_MTD_XIP_H__ */
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* MTD primitives for XIP support. Architecture specific functions
|
||||
*
|
||||
* Do not include this file directly. It's included from linux/mtd/xip.h
|
||||
*
|
||||
* Author: Nicolas Pitre
|
||||
* Created: Nov 2, 2004
|
||||
* Copyright: (C) 2004 MontaVista Software, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* $Id: xip.h,v 1.2 2004/12/01 15:49:10 nico Exp $
|
||||
*/
|
||||
|
||||
#ifndef __ARCH_SA1100_MTD_XIP_H__
|
||||
#define __ARCH_SA1100_MTD_XIP_H__
|
||||
|
||||
#define xip_irqpending() (ICIP & ICMR)
|
||||
|
||||
/* we sample OSCR and convert desired delta to usec (1/4 ~= 1000000/3686400) */
|
||||
#define xip_currtime() (OSCR)
|
||||
#define xip_elapsed_since(x) (signed)((OSCR - (x)) / 4)
|
||||
|
||||
#endif /* __ARCH_SA1100_MTD_XIP_H__ */
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* MTD primitives for XIP support. Architecture specific functions
|
||||
*
|
||||
* Do not include this file directly. It's included from linux/mtd/xip.h
|
||||
*
|
||||
* Author: Nicolas Pitre
|
||||
* Created: Nov 2, 2004
|
||||
* Copyright: (C) 2004 MontaVista Software, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* $Id: xip.h,v 1.2 2004/12/01 15:49:10 nico Exp $
|
||||
*/
|
||||
|
||||
#ifndef __ARM_MTD_XIP_H__
|
||||
#define __ARM_MTD_XIP_H__
|
||||
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/arch/mtd-xip.h>
|
||||
|
||||
/* fill instruction prefetch */
|
||||
#define xip_iprefetch() do { asm volatile (".rep 8; nop; .endr"); } while (0)
|
||||
|
||||
#endif /* __ARM_MTD_XIP_H__ */
|
|
@ -1,4 +1,4 @@
|
|||
/* $Id: jffs2_fs_sb.h,v 1.48 2004/11/20 10:41:12 dwmw2 Exp $ */
|
||||
/* $Id: jffs2_fs_sb.h,v 1.52 2005/05/19 16:12:17 gleixner Exp $ */
|
||||
|
||||
#ifndef _JFFS2_FS_SB
|
||||
#define _JFFS2_FS_SB
|
||||
|
@ -14,7 +14,8 @@
|
|||
#include <linux/rwsem.h>
|
||||
|
||||
#define JFFS2_SB_FLAG_RO 1
|
||||
#define JFFS2_SB_FLAG_MOUNTING 2
|
||||
#define JFFS2_SB_FLAG_SCANNING 2 /* Flash scanning is in progress */
|
||||
#define JFFS2_SB_FLAG_BUILDING 4 /* File system building is in progress */
|
||||
|
||||
struct jffs2_inodirty;
|
||||
|
||||
|
@ -31,7 +32,7 @@ struct jffs2_sb_info {
|
|||
unsigned int flags;
|
||||
|
||||
struct task_struct *gc_task; /* GC task struct */
|
||||
struct semaphore gc_thread_start; /* GC thread start mutex */
|
||||
struct completion gc_thread_start; /* GC thread start completion */
|
||||
struct completion gc_thread_exit; /* GC thread exit completion port */
|
||||
|
||||
struct semaphore alloc_sem; /* Used to protect all the following
|
||||
|
@ -94,7 +95,7 @@ struct jffs2_sb_info {
|
|||
to an obsoleted node. I don't like this. Alternatives welcomed. */
|
||||
struct semaphore erase_free_sem;
|
||||
|
||||
#if defined CONFIG_JFFS2_FS_NAND || defined CONFIG_JFFS2_FS_NOR_ECC
|
||||
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
|
||||
/* Write-behind buffer for NAND flash */
|
||||
unsigned char *wbuf;
|
||||
uint32_t wbuf_ofs;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
/* Common Flash Interface structures
|
||||
* See http://support.intel.com/design/flash/technote/index.htm
|
||||
* $Id: cfi.h,v 1.50 2004/11/20 12:46:51 dwmw2 Exp $
|
||||
* $Id: cfi.h,v 1.54 2005/06/06 23:04:36 tpoynor Exp $
|
||||
*/
|
||||
|
||||
#ifndef __MTD_CFI_H__
|
||||
|
@ -148,6 +148,14 @@ struct cfi_pri_intelext {
|
|||
uint8_t extra[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct cfi_intelext_otpinfo {
|
||||
uint32_t ProtRegAddr;
|
||||
uint16_t FactGroups;
|
||||
uint8_t FactProtRegSize;
|
||||
uint16_t UserGroups;
|
||||
uint8_t UserProtRegSize;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct cfi_intelext_blockinfo {
|
||||
uint16_t NumIdentBlocks;
|
||||
uint16_t BlockSize;
|
||||
|
@ -244,7 +252,7 @@ static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs, int interleave, int
|
|||
* It looks too long to be inline, but in the common case it should almost all
|
||||
* get optimised away.
|
||||
*/
|
||||
static inline map_word cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi)
|
||||
static inline map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi)
|
||||
{
|
||||
map_word val = { {0} };
|
||||
int wordwidth, words_per_bus, chip_mode, chips_per_word;
|
||||
|
@ -307,6 +315,69 @@ static inline map_word cfi_build_cmd(u_char cmd, struct map_info *map, struct cf
|
|||
}
|
||||
#define CMD(x) cfi_build_cmd((x), map, cfi)
|
||||
|
||||
|
||||
static inline unsigned char cfi_merge_status(map_word val, struct map_info *map,
|
||||
struct cfi_private *cfi)
|
||||
{
|
||||
int wordwidth, words_per_bus, chip_mode, chips_per_word;
|
||||
unsigned long onestat, res = 0;
|
||||
int i;
|
||||
|
||||
/* We do it this way to give the compiler a fighting chance
|
||||
of optimising away all the crap for 'bankwidth' larger than
|
||||
an unsigned long, in the common case where that support is
|
||||
disabled */
|
||||
if (map_bankwidth_is_large(map)) {
|
||||
wordwidth = sizeof(unsigned long);
|
||||
words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
|
||||
} else {
|
||||
wordwidth = map_bankwidth(map);
|
||||
words_per_bus = 1;
|
||||
}
|
||||
|
||||
chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
|
||||
chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
|
||||
|
||||
onestat = val.x[0];
|
||||
/* Or all status words together */
|
||||
for (i=1; i < words_per_bus; i++) {
|
||||
onestat |= val.x[i];
|
||||
}
|
||||
|
||||
res = onestat;
|
||||
switch(chips_per_word) {
|
||||
default: BUG();
|
||||
#if BITS_PER_LONG >= 64
|
||||
case 8:
|
||||
res |= (onestat >> (chip_mode * 32));
|
||||
#endif
|
||||
case 4:
|
||||
res |= (onestat >> (chip_mode * 16));
|
||||
case 2:
|
||||
res |= (onestat >> (chip_mode * 8));
|
||||
case 1:
|
||||
;
|
||||
}
|
||||
|
||||
/* Last, determine what the bit-pattern should be for a single
|
||||
device, according to chip mode and endianness... */
|
||||
switch (chip_mode) {
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
res = cfi16_to_cpu(res);
|
||||
break;
|
||||
case 4:
|
||||
res = cfi32_to_cpu(res);
|
||||
break;
|
||||
default: BUG();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
#define MERGESTATUS(x) cfi_merge_status((x), map, cfi)
|
||||
|
||||
|
||||
/*
|
||||
* Sends a CFI command to a bank of flash for the given geometry.
|
||||
*
|
||||
|
@ -357,16 +428,6 @@ static inline void cfi_udelay(int us)
|
|||
}
|
||||
}
|
||||
|
||||
static inline void cfi_spin_lock(spinlock_t *mutex)
|
||||
{
|
||||
spin_lock_bh(mutex);
|
||||
}
|
||||
|
||||
static inline void cfi_spin_unlock(spinlock_t *mutex)
|
||||
{
|
||||
spin_unlock_bh(mutex);
|
||||
}
|
||||
|
||||
struct cfi_extquery *cfi_read_pri(struct map_info *map, uint16_t adr, uint16_t size,
|
||||
const char* name);
|
||||
struct cfi_fixup {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*
|
||||
* (C) 2000 Red Hat. GPLd.
|
||||
*
|
||||
* $Id: flashchip.h,v 1.15 2004/11/05 22:41:06 nico Exp $
|
||||
* $Id: flashchip.h,v 1.17 2005/03/14 18:27:15 bjd Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -29,6 +29,7 @@ typedef enum {
|
|||
FL_ERASE_SUSPENDED,
|
||||
FL_WRITING,
|
||||
FL_WRITING_TO_BUFFER,
|
||||
FL_OTP_WRITE,
|
||||
FL_WRITE_SUSPENDING,
|
||||
FL_WRITE_SUSPENDED,
|
||||
FL_PM_SUSPENDED,
|
||||
|
@ -62,8 +63,8 @@ struct flchip {
|
|||
flstate_t state;
|
||||
flstate_t oldstate;
|
||||
|
||||
int write_suspended:1;
|
||||
int erase_suspended:1;
|
||||
unsigned int write_suspended:1;
|
||||
unsigned int erase_suspended:1;
|
||||
unsigned long in_progress_block_addr;
|
||||
|
||||
spinlock_t *mutex;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
|
||||
*
|
||||
* $Id: inftl.h,v 1.6 2004/06/30 14:49:00 dbrown Exp $
|
||||
* $Id: inftl.h,v 1.7 2005/06/13 13:08:45 sean Exp $
|
||||
*/
|
||||
|
||||
#ifndef __MTD_INFTL_H__
|
||||
|
@ -20,7 +20,7 @@
|
|||
#include <mtd/inftl-user.h>
|
||||
|
||||
#ifndef INFTL_MAJOR
|
||||
#define INFTL_MAJOR 94
|
||||
#define INFTL_MAJOR 96
|
||||
#endif
|
||||
#define INFTL_PARTN_BITS 4
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
/* Overhauled routines for dealing with different mmap regions of flash */
|
||||
/* $Id: map.h,v 1.46 2005/01/05 17:09:44 dwmw2 Exp $ */
|
||||
/* $Id: map.h,v 1.52 2005/05/25 10:29:41 gleixner Exp $ */
|
||||
|
||||
#ifndef __LINUX_MTD_MAP_H__
|
||||
#define __LINUX_MTD_MAP_H__
|
||||
|
@ -263,6 +263,17 @@ static inline map_word map_word_and(struct map_info *map, map_word val1, map_wor
|
|||
return r;
|
||||
}
|
||||
|
||||
static inline map_word map_word_clr(struct map_info *map, map_word val1, map_word val2)
|
||||
{
|
||||
map_word r;
|
||||
int i;
|
||||
|
||||
for (i=0; i<map_words(map); i++) {
|
||||
r.x[i] = val1.x[i] & ~val2.x[i];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline map_word map_word_or(struct map_info *map, map_word val1, map_word val2)
|
||||
{
|
||||
map_word r;
|
||||
|
@ -273,6 +284,7 @@ static inline map_word map_word_or(struct map_info *map, map_word val1, map_word
|
|||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
#define map_word_andequal(m, a, b, z) map_word_equal(m, z, map_word_and(m, a, b))
|
||||
|
||||
static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word val2)
|
||||
|
@ -328,16 +340,27 @@ static inline map_word map_word_load_partial(struct map_info *map, map_word orig
|
|||
return orig;
|
||||
}
|
||||
|
||||
#if BITS_PER_LONG < 64
|
||||
#define MAP_FF_LIMIT 4
|
||||
#else
|
||||
#define MAP_FF_LIMIT 8
|
||||
#endif
|
||||
|
||||
static inline map_word map_word_ff(struct map_info *map)
|
||||
{
|
||||
map_word r;
|
||||
int i;
|
||||
|
||||
for (i=0; i<map_words(map); i++) {
|
||||
r.x[i] = ~0UL;
|
||||
|
||||
if (map_bankwidth(map) < MAP_FF_LIMIT) {
|
||||
int bw = 8 * map_bankwidth(map);
|
||||
r.x[0] = (1 << bw) - 1;
|
||||
} else {
|
||||
for (i=0; i<map_words(map); i++)
|
||||
r.x[i] = ~0UL;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline map_word inline_map_read(struct map_info *map, unsigned long ofs)
|
||||
{
|
||||
map_word r;
|
||||
|
@ -405,7 +428,7 @@ extern void simple_map_init(struct map_info *);
|
|||
|
||||
|
||||
#define simple_map_init(map) BUG_ON(!map_bankwidth_supported((map)->bankwidth))
|
||||
#define map_is_linear(map) (1)
|
||||
#define map_is_linear(map) ({ (void)(map); 1; })
|
||||
|
||||
#endif /* !CONFIG_MTD_COMPLEX_MAPPINGS */
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: mtd.h,v 1.56 2004/08/09 18:46:04 dmarlin Exp $
|
||||
* $Id: mtd.h,v 1.59 2005/04/11 10:19:02 gleixner Exp $
|
||||
*
|
||||
* Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al.
|
||||
*
|
||||
|
@ -18,6 +18,7 @@
|
|||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
#include <linux/mtd/compatmac.h>
|
||||
#include <mtd/mtd-abi.h>
|
||||
|
@ -69,7 +70,6 @@ struct mtd_info {
|
|||
|
||||
u_int32_t oobblock; // Size of OOB blocks (e.g. 512)
|
||||
u_int32_t oobsize; // Amount of OOB data per block (e.g. 16)
|
||||
u_int32_t oobavail; // Number of bytes in OOB area available for fs
|
||||
u_int32_t ecctype;
|
||||
u_int32_t eccsize;
|
||||
|
||||
|
@ -80,6 +80,7 @@ struct mtd_info {
|
|||
|
||||
// oobinfo is a nand_oobinfo structure, which can be set by iotcl (MEMSETOOBINFO)
|
||||
struct nand_oobinfo oobinfo;
|
||||
u_int32_t oobavail; // Number of bytes in OOB area available for fs
|
||||
|
||||
/* Data for variable erase regions. If numeraseregions is zero,
|
||||
* it means that the whole device has erasesize as given above.
|
||||
|
@ -113,12 +114,12 @@ struct mtd_info {
|
|||
* flash devices. The user data is one time programmable but the
|
||||
* factory data is read only.
|
||||
*/
|
||||
int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
|
||||
|
||||
int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
|
||||
int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
|
||||
|
||||
/* This function is not yet implemented */
|
||||
int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
|
||||
int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
|
||||
int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
|
||||
int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
|
||||
|
||||
/* kvec-based read/write methods. We need these especially for NAND flash,
|
||||
with its limited number of write cycles per erase.
|
||||
|
@ -147,6 +148,8 @@ struct mtd_info {
|
|||
int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
|
||||
int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
|
||||
|
||||
struct notifier_block reboot_notifier; /* default mode before reboot */
|
||||
|
||||
void *priv;
|
||||
|
||||
struct module *owner;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* Steven J. Hill <sjhill@realitydiluted.com>
|
||||
* Thomas Gleixner <tglx@linutronix.de>
|
||||
*
|
||||
* $Id: nand.h,v 1.68 2004/11/12 10:40:37 gleixner Exp $
|
||||
* $Id: nand.h,v 1.73 2005/05/31 19:39:17 gleixner Exp $
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
|
@ -48,6 +48,10 @@
|
|||
* 02-08-2004 tglx added option field to nand structure for chip anomalities
|
||||
* 05-25-2004 tglx added bad block table support, ST-MICRO manufacturer id
|
||||
* update of nand_chip structure description
|
||||
* 01-17-2005 dmarlin added extended commands for AG-AND device and added option
|
||||
* for BBT_AUTO_REFRESH.
|
||||
* 01-20-2005 dmarlin added optional pointer to hardware specific callback for
|
||||
* extra error status checks.
|
||||
*/
|
||||
#ifndef __LINUX_MTD_NAND_H
|
||||
#define __LINUX_MTD_NAND_H
|
||||
|
@ -115,6 +119,25 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_
|
|||
#define NAND_CMD_READSTART 0x30
|
||||
#define NAND_CMD_CACHEDPROG 0x15
|
||||
|
||||
/* Extended commands for AG-AND device */
|
||||
/*
|
||||
* Note: the command for NAND_CMD_DEPLETE1 is really 0x00 but
|
||||
* there is no way to distinguish that from NAND_CMD_READ0
|
||||
* until the remaining sequence of commands has been completed
|
||||
* so add a high order bit and mask it off in the command.
|
||||
*/
|
||||
#define NAND_CMD_DEPLETE1 0x100
|
||||
#define NAND_CMD_DEPLETE2 0x38
|
||||
#define NAND_CMD_STATUS_MULTI 0x71
|
||||
#define NAND_CMD_STATUS_ERROR 0x72
|
||||
/* multi-bank error status (banks 0-3) */
|
||||
#define NAND_CMD_STATUS_ERROR0 0x73
|
||||
#define NAND_CMD_STATUS_ERROR1 0x74
|
||||
#define NAND_CMD_STATUS_ERROR2 0x75
|
||||
#define NAND_CMD_STATUS_ERROR3 0x76
|
||||
#define NAND_CMD_STATUS_RESET 0x7f
|
||||
#define NAND_CMD_STATUS_CLEAR 0xff
|
||||
|
||||
/* Status bits */
|
||||
#define NAND_STATUS_FAIL 0x01
|
||||
#define NAND_STATUS_FAIL_N1 0x02
|
||||
|
@ -143,7 +166,7 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_
|
|||
|
||||
/*
|
||||
* Constants for Hardware ECC
|
||||
*/
|
||||
*/
|
||||
/* Reset Hardware ECC for read */
|
||||
#define NAND_ECC_READ 0
|
||||
/* Reset Hardware ECC for write */
|
||||
|
@ -151,6 +174,10 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_
|
|||
/* Enable Hardware ECC before syndrom is read back from flash */
|
||||
#define NAND_ECC_READSYN 2
|
||||
|
||||
/* Bit mask for flags passed to do_nand_read_ecc */
|
||||
#define NAND_GET_DEVICE 0x80
|
||||
|
||||
|
||||
/* Option constants for bizarre disfunctionality and real
|
||||
* features
|
||||
*/
|
||||
|
@ -170,6 +197,10 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_
|
|||
/* Chip has a array of 4 pages which can be read without
|
||||
* additional ready /busy waits */
|
||||
#define NAND_4PAGE_ARRAY 0x00000040
|
||||
/* Chip requires that BBT is periodically rewritten to prevent
|
||||
* bits from adjacent blocks from 'leaking' in altering data.
|
||||
* This happens with the Renesas AG-AND chips, possibly others. */
|
||||
#define BBT_AUTO_REFRESH 0x00000080
|
||||
|
||||
/* Options valid for Samsung large page devices */
|
||||
#define NAND_SAMSUNG_LP_OPTIONS \
|
||||
|
@ -192,7 +223,8 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_
|
|||
* This can only work if we have the ecc bytes directly behind the
|
||||
* data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */
|
||||
#define NAND_HWECC_SYNDROME 0x00020000
|
||||
|
||||
/* This option skips the bbt scan during initialization. */
|
||||
#define NAND_SKIP_BBTSCAN 0x00040000
|
||||
|
||||
/* Options set by nand scan */
|
||||
/* Nand scan has allocated oob_buf */
|
||||
|
@ -221,10 +253,13 @@ struct nand_chip;
|
|||
* struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independend devices
|
||||
* @lock: protection lock
|
||||
* @active: the mtd device which holds the controller currently
|
||||
* @wq: wait queue to sleep on if a NAND operation is in progress
|
||||
* used instead of the per chip wait queue when a hw controller is available
|
||||
*/
|
||||
struct nand_hw_control {
|
||||
spinlock_t lock;
|
||||
struct nand_chip *active;
|
||||
wait_queue_head_t wq;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -283,6 +318,8 @@ struct nand_hw_control {
|
|||
* @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan
|
||||
* @controller: [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices
|
||||
* @priv: [OPTIONAL] pointer to private chip date
|
||||
* @errstat: [OPTIONAL] hardware specific function to perform additional error status checks
|
||||
* (determine if errors are correctable)
|
||||
*/
|
||||
|
||||
struct nand_chip {
|
||||
|
@ -338,6 +375,7 @@ struct nand_chip {
|
|||
struct nand_bbt_descr *badblock_pattern;
|
||||
struct nand_hw_control *controller;
|
||||
void *priv;
|
||||
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -349,6 +387,7 @@ struct nand_chip {
|
|||
#define NAND_MFR_NATIONAL 0x8f
|
||||
#define NAND_MFR_RENESAS 0x07
|
||||
#define NAND_MFR_STMICRO 0x20
|
||||
#define NAND_MFR_HYNIX 0xad
|
||||
|
||||
/**
|
||||
* struct nand_flash_dev - NAND Flash Device ID Structure
|
||||
|
@ -459,6 +498,9 @@ extern int nand_update_bbt (struct mtd_info *mtd, loff_t offs);
|
|||
extern int nand_default_bbt (struct mtd_info *mtd);
|
||||
extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt);
|
||||
extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt);
|
||||
extern int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t * retlen, u_char * buf, u_char * oob_buf,
|
||||
struct nand_oobinfo *oobsel, int flags);
|
||||
|
||||
/*
|
||||
* Constants for oob configuration
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/* linux/include/mtd/plat-ram.h
|
||||
*
|
||||
* (c) 2004 Simtec Electronics
|
||||
* http://www.simtec.co.uk/products/SWLINUX/
|
||||
* Ben Dooks <ben@simtec.co.uk>
|
||||
*
|
||||
* Generic platform device based RAM map
|
||||
*
|
||||
* $Id: plat-ram.h,v 1.2 2005/01/24 00:37:40 bjd Exp $
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_MTD_PLATRAM_H
|
||||
#define __LINUX_MTD_PLATRAM_H __FILE__
|
||||
|
||||
#define PLATRAM_RO (0)
|
||||
#define PLATRAM_RW (1)
|
||||
|
||||
struct platdata_mtd_ram {
|
||||
char *mapname;
|
||||
char **probes;
|
||||
struct mtd_partition *partitions;
|
||||
int nr_partitions;
|
||||
int bankwidth;
|
||||
|
||||
/* control callbacks */
|
||||
|
||||
void (*set_rw)(struct device *dev, int to);
|
||||
};
|
||||
|
||||
#endif /* __LINUX_MTD_PLATRAM_H */
|
|
@ -58,22 +58,16 @@
|
|||
* returned value is <= the real elapsed time.
|
||||
* note 2: this should be able to cope with a few seconds without
|
||||
* overflowing.
|
||||
*
|
||||
* xip_iprefetch()
|
||||
*
|
||||
* Macro to fill instruction prefetch
|
||||
* e.g. a series of nops: asm volatile (".rep 8; nop; .endr");
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_ARCH_SA1100) || defined(CONFIG_ARCH_PXA)
|
||||
#include <asm/mtd-xip.h>
|
||||
|
||||
#include <asm/hardware.h>
|
||||
#ifdef CONFIG_ARCH_PXA
|
||||
#include <asm/arch/pxa-regs.h>
|
||||
#endif
|
||||
|
||||
#define xip_irqpending() (ICIP & ICMR)
|
||||
|
||||
/* we sample OSCR and convert desired delta to usec (1/4 ~= 1000000/3686400) */
|
||||
#define xip_currtime() (OSCR)
|
||||
#define xip_elapsed_since(x) (signed)((OSCR - (x)) / 4)
|
||||
|
||||
#else
|
||||
#ifndef xip_irqpending
|
||||
|
||||
#warning "missing IRQ and timer primitives for XIP MTD support"
|
||||
#warning "some of the XIP MTD support code will be disabled"
|
||||
|
@ -85,16 +79,17 @@
|
|||
|
||||
#endif
|
||||
|
||||
#ifndef xip_iprefetch
|
||||
#define xip_iprefetch() do { } while (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* xip_cpu_idle() is used when waiting for a delay equal or larger than
|
||||
* the system timer tick period. This should put the CPU into idle mode
|
||||
* to save power and to be woken up only when some interrupts are pending.
|
||||
* As above, this should not rely upon standard kernel code.
|
||||
* This should not rely upon standard kernel code.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_CPU_XSCALE)
|
||||
#define xip_cpu_idle() asm volatile ("mcr p14, 0, %0, c7, c0, 0" :: "r" (1))
|
||||
#else
|
||||
#ifndef xip_cpu_idle
|
||||
#define xip_cpu_idle() do { } while (0)
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: mtd-abi.h,v 1.7 2004/11/23 15:37:32 gleixner Exp $
|
||||
* $Id: mtd-abi.h,v 1.11 2005/05/19 16:08:58 gleixner Exp $
|
||||
*
|
||||
* Portions of MTD ABI definition which are shared by kernel and user space
|
||||
*/
|
||||
|
@ -29,6 +29,7 @@ struct mtd_oob_buf {
|
|||
#define MTD_NORFLASH 3
|
||||
#define MTD_NANDFLASH 4
|
||||
#define MTD_PEROM 5
|
||||
#define MTD_DATAFLASH 6
|
||||
#define MTD_OTHER 14
|
||||
#define MTD_UNKNOWN 15
|
||||
|
||||
|
@ -60,6 +61,12 @@ struct mtd_oob_buf {
|
|||
#define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode)
|
||||
#define MTD_NANDECC_AUTOPLACE 2 // Use the default placement scheme
|
||||
#define MTD_NANDECC_PLACEONLY 3 // Use the given placement in the structure (Do not store ecc result on read)
|
||||
#define MTD_NANDECC_AUTOPL_USR 4 // Use the given autoplacement scheme rather than using the default
|
||||
|
||||
/* OTP mode selection */
|
||||
#define MTD_OTP_OFF 0
|
||||
#define MTD_OTP_FACTORY 1
|
||||
#define MTD_OTP_USER 2
|
||||
|
||||
struct mtd_info_user {
|
||||
uint8_t type;
|
||||
|
@ -80,6 +87,12 @@ struct region_info_user {
|
|||
uint32_t regionindex;
|
||||
};
|
||||
|
||||
struct otp_info {
|
||||
uint32_t start;
|
||||
uint32_t length;
|
||||
uint32_t locked;
|
||||
};
|
||||
|
||||
#define MEMGETINFO _IOR('M', 1, struct mtd_info_user)
|
||||
#define MEMERASE _IOW('M', 2, struct erase_info_user)
|
||||
#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf)
|
||||
|
@ -92,6 +105,10 @@ struct region_info_user {
|
|||
#define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo)
|
||||
#define MEMGETBADBLOCK _IOW('M', 11, loff_t)
|
||||
#define MEMSETBADBLOCK _IOW('M', 12, loff_t)
|
||||
#define OTPSELECT _IOR('M', 13, int)
|
||||
#define OTPGETREGIONCOUNT _IOW('M', 14, int)
|
||||
#define OTPGETREGIONINFO _IOW('M', 15, struct otp_info)
|
||||
#define OTPLOCK _IOR('M', 16, struct otp_info)
|
||||
|
||||
struct nand_oobinfo {
|
||||
uint32_t useecc;
|
||||
|
|
Loading…
Reference in New Issue