Merge git://git.infradead.org/mtd-2.6
* git://git.infradead.org/mtd-2.6: (199 commits) [MTD] NAND: Fix breakage all over the place [PATCH] NAND: fix remaining OOB length calculation [MTD] NAND Fixup NDFC merge brokeness [MTD NAND] S3C2410 driver cleanup [MTD NAND] s3c24x0 board: Fix clock handling, ensure proper initialisation. [JFFS2] Check CRC32 on dirent and data nodes each time they're read [JFFS2] When retiring nextblock, allocate a node_ref for the wasted space [JFFS2] Mark XATTR support as experimental, for now [JFFS2] Don't trust node headers before the CRC is checked. [MTD] Restore MTD_ROM and MTD_RAM types [MTD] assume mtd->writesize is 1 for NOR flashes [MTD NAND] Fix s3c2410 NAND driver so it at least _looks_ like it compiles [MTD] Prepare physmap for 64-bit-resources [JFFS2] Fix more breakage caused by janitorial meddling. [JFFS2] Remove stray __exit from jffs2_compressors_exit() [MTD] Allow alternate JFFS2 mount variant for root filesystem. [MTD] Disconnect struct mtd_info from ABI [MTD] replace MTD_RAM with MTD_GENERIC_TYPE [MTD] replace MTD_ROM with MTD_GENERIC_TYPE [MTD] remove a forgotten MTD_XIP ...
This commit is contained in:
commit
be967b7e2f
@ -1843,12 +1843,12 @@ S: linux-scsi@vger.kernel.org
|
||||
W: http://megaraid.lsilogic.com
|
||||
S: Maintained
|
||||
|
||||
MEMORY TECHNOLOGY DEVICES
|
||||
MEMORY TECHNOLOGY DEVICES (MTD)
|
||||
P: David Woodhouse
|
||||
M: dwmw2@infradead.org
|
||||
W: http://www.linux-mtd.infradead.org/
|
||||
L: linux-mtd@lists.infradead.org
|
||||
T: git kernel.org:/pub/scm/linux/kernel/git/tglx/mtd-2.6.git
|
||||
T: git git://git.infradead.org/mtd-2.6.git
|
||||
S: Maintained
|
||||
|
||||
MICROTEK X6 SCANNER
|
||||
|
@ -78,7 +78,7 @@ config MTD_REDBOOT_DIRECTORY_BLOCK
|
||||
option.
|
||||
|
||||
The option specifies which Flash sectors holds the RedBoot
|
||||
partition table. A zero or positive value gives an absolete
|
||||
partition table. A zero or positive value gives an absolute
|
||||
erase block number. A negative value specifies a number of
|
||||
sectors before the end of the device.
|
||||
|
||||
@ -103,7 +103,7 @@ config MTD_CMDLINE_PARTS
|
||||
bool "Command line partition table parsing"
|
||||
depends on MTD_PARTITIONS = "y"
|
||||
---help---
|
||||
Allow generic configuration of the MTD paritition tables via the kernel
|
||||
Allow generic configuration of the MTD partition tables via the kernel
|
||||
command line. Multiple flash resources are supported for hardware where
|
||||
different kinds of flash memory are available.
|
||||
|
||||
|
@ -30,7 +30,6 @@ config MTD_JEDECPROBE
|
||||
|
||||
config MTD_GEN_PROBE
|
||||
tristate
|
||||
select OBSOLETE_INTERMODULE
|
||||
|
||||
config MTD_CFI_ADV_OPTIONS
|
||||
bool "Flash chip driver advanced configuration options"
|
||||
|
@ -3,13 +3,6 @@
|
||||
#
|
||||
# $Id: Makefile.common,v 1.5 2005/11/07 11:14:22 gleixner Exp $
|
||||
|
||||
# *** BIG UGLY NOTE ***
|
||||
#
|
||||
# The removal of get_module_symbol() and replacement with
|
||||
# inter_module_register() et al has introduced a link order dependency
|
||||
# here where previously there was none. We now have to ensure that
|
||||
# the CFI command set drivers are linked before gen_probe.o
|
||||
|
||||
obj-$(CONFIG_MTD) += chipreg.o
|
||||
obj-$(CONFIG_MTD_AMDSTD) += amd_flash.o
|
||||
obj-$(CONFIG_MTD_CFI) += cfi_probe.o
|
||||
|
@ -97,7 +97,6 @@ struct amd_flash_private {
|
||||
int interleave;
|
||||
int numchips;
|
||||
unsigned long chipshift;
|
||||
// const char *im_name;
|
||||
struct flchip chips[0];
|
||||
};
|
||||
|
||||
@ -131,12 +130,6 @@ static struct mtd_chip_driver amd_flash_chipdrv = {
|
||||
.module = THIS_MODULE
|
||||
};
|
||||
|
||||
|
||||
|
||||
static const char im_name[] = "amd_flash";
|
||||
|
||||
|
||||
|
||||
static inline __u32 wide_read(struct map_info *map, __u32 addr)
|
||||
{
|
||||
if (map->buswidth == 1) {
|
||||
@ -737,6 +730,7 @@ static struct mtd_info *amd_flash_probe(struct map_info *map)
|
||||
offset += dev_size;
|
||||
}
|
||||
mtd->type = MTD_NORFLASH;
|
||||
mtd->writesize = 1;
|
||||
mtd->flags = MTD_CAP_NORFLASH;
|
||||
mtd->name = map->name;
|
||||
mtd->erase = amd_flash_erase;
|
||||
|
@ -331,13 +331,6 @@ read_pri_intelext(struct map_info *map, __u16 adr)
|
||||
return extp;
|
||||
}
|
||||
|
||||
/* This routine is made available to other mtd code via
|
||||
* inter_module_register. It must only be accessed through
|
||||
* inter_module_get which will bump the use count of this module. The
|
||||
* addresses passed back in cfi are valid as long as the use count of
|
||||
* this module is non-zero, i.e. between inter_module_get and
|
||||
* inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000.
|
||||
*/
|
||||
struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
@ -406,7 +399,7 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
|
||||
for (i=0; i< cfi->numchips; i++) {
|
||||
cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp;
|
||||
cfi->chips[i].buffer_write_time = 1<<cfi->cfiq->BufWriteTimeoutTyp;
|
||||
cfi->chips[i].erase_time = 1<<cfi->cfiq->BlockEraseTimeoutTyp;
|
||||
cfi->chips[i].erase_time = 1000<<cfi->cfiq->BlockEraseTimeoutTyp;
|
||||
cfi->chips[i].ref_point_counter = 0;
|
||||
init_waitqueue_head(&(cfi->chips[i].wq));
|
||||
}
|
||||
@ -415,6 +408,11 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
|
||||
|
||||
return cfi_intelext_setup(mtd);
|
||||
}
|
||||
struct mtd_info *cfi_cmdset_0003(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0001")));
|
||||
struct mtd_info *cfi_cmdset_0200(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0001")));
|
||||
EXPORT_SYMBOL_GPL(cfi_cmdset_0001);
|
||||
EXPORT_SYMBOL_GPL(cfi_cmdset_0003);
|
||||
EXPORT_SYMBOL_GPL(cfi_cmdset_0200);
|
||||
|
||||
static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
|
||||
{
|
||||
@ -547,12 +545,12 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd,
|
||||
if (extp->MinorVersion >= '4') {
|
||||
struct cfi_intelext_programming_regioninfo *prinfo;
|
||||
prinfo = (struct cfi_intelext_programming_regioninfo *)&extp->extra[offs];
|
||||
MTD_PROGREGION_SIZE(mtd) = cfi->interleave << prinfo->ProgRegShift;
|
||||
mtd->writesize = cfi->interleave << prinfo->ProgRegShift;
|
||||
MTD_PROGREGION_CTRLMODE_VALID(mtd) = cfi->interleave * prinfo->ControlValid;
|
||||
MTD_PROGREGION_CTRLMODE_INVALID(mtd) = cfi->interleave * prinfo->ControlInvalid;
|
||||
mtd->flags |= MTD_PROGRAM_REGIONS;
|
||||
mtd->flags &= ~MTD_BIT_WRITEABLE;
|
||||
printk(KERN_DEBUG "%s: program region size/ctrl_valid/ctrl_inval = %d/%d/%d\n",
|
||||
map->name, MTD_PROGREGION_SIZE(mtd),
|
||||
map->name, mtd->writesize,
|
||||
MTD_PROGREGION_CTRLMODE_VALID(mtd),
|
||||
MTD_PROGREGION_CTRLMODE_INVALID(mtd));
|
||||
}
|
||||
@ -896,26 +894,33 @@ static void __xipram xip_enable(struct map_info *map, struct flchip *chip,
|
||||
|
||||
/*
|
||||
* 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 or write 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.
|
||||
* xip_wait_for_operation() 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 or write 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)
|
||||
static int __xipram xip_wait_for_operation(
|
||||
struct map_info *map, struct flchip *chip,
|
||||
unsigned long adr, int *chip_op_time )
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
|
||||
map_word status, OK = CMD(0x80);
|
||||
unsigned long suspended, start = xip_currtime();
|
||||
unsigned long usec, suspended, start, done;
|
||||
flstate_t oldstate, newstate;
|
||||
|
||||
start = xip_currtime();
|
||||
usec = *chip_op_time * 8;
|
||||
if (usec == 0)
|
||||
usec = 500000;
|
||||
done = 0;
|
||||
|
||||
do {
|
||||
cpu_relax();
|
||||
if (xip_irqpending() && cfip &&
|
||||
@ -932,9 +937,9 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
|
||||
* we resume the whole thing at once). Yes, it
|
||||
* can happen!
|
||||
*/
|
||||
usec -= done;
|
||||
map_write(map, CMD(0xb0), adr);
|
||||
map_write(map, CMD(0x70), adr);
|
||||
usec -= xip_elapsed_since(start);
|
||||
suspended = xip_currtime();
|
||||
do {
|
||||
if (xip_elapsed_since(suspended) > 100000) {
|
||||
@ -944,7 +949,7 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
|
||||
* This is a critical error but there
|
||||
* is not much we can do here.
|
||||
*/
|
||||
return;
|
||||
return -EIO;
|
||||
}
|
||||
status = map_read(map, adr);
|
||||
} while (!map_word_andequal(map, status, OK, OK));
|
||||
@ -1004,65 +1009,107 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip,
|
||||
xip_cpu_idle();
|
||||
}
|
||||
status = map_read(map, adr);
|
||||
done = xip_elapsed_since(start);
|
||||
} while (!map_word_andequal(map, status, OK, OK)
|
||||
&& xip_elapsed_since(start) < usec);
|
||||
}
|
||||
&& done < usec);
|
||||
|
||||
#define UDELAY(map, chip, adr, usec) xip_udelay(map, chip, adr, usec)
|
||||
return (done >= usec) ? -ETIME : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* and stub it out from INVAL_CACHE_AND_WAIT.
|
||||
*/
|
||||
#define XIP_INVAL_CACHED_RANGE(map, from, size) \
|
||||
INVALIDATE_CACHED_RANGE(map, from, size)
|
||||
|
||||
#define INVALIDATE_CACHE_UDELAY(map, chip, cmd_adr, adr, len, usec) \
|
||||
UDELAY(map, chip, cmd_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.
|
||||
*/
|
||||
#define INVAL_CACHE_AND_WAIT(map, chip, cmd_adr, inval_adr, inval_len, p_usec) \
|
||||
xip_wait_for_operation(map, chip, cmd_adr, p_usec)
|
||||
|
||||
#else
|
||||
|
||||
#define xip_disable(map, chip, adr)
|
||||
#define xip_enable(map, chip, adr)
|
||||
#define XIP_INVAL_CACHED_RANGE(x...)
|
||||
#define INVAL_CACHE_AND_WAIT inval_cache_and_wait_for_operation
|
||||
|
||||
#define UDELAY(map, chip, adr, usec) \
|
||||
do { \
|
||||
spin_unlock(chip->mutex); \
|
||||
cfi_udelay(usec); \
|
||||
spin_lock(chip->mutex); \
|
||||
} while (0)
|
||||
static int inval_cache_and_wait_for_operation(
|
||||
struct map_info *map, struct flchip *chip,
|
||||
unsigned long cmd_adr, unsigned long inval_adr, int inval_len,
|
||||
int *chip_op_time )
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
map_word status, status_OK = CMD(0x80);
|
||||
int z, chip_state = chip->state;
|
||||
unsigned long timeo;
|
||||
|
||||
#define INVALIDATE_CACHE_UDELAY(map, chip, cmd_adr, adr, len, usec) \
|
||||
do { \
|
||||
spin_unlock(chip->mutex); \
|
||||
INVALIDATE_CACHED_RANGE(map, adr, len); \
|
||||
cfi_udelay(usec); \
|
||||
spin_lock(chip->mutex); \
|
||||
} while (0)
|
||||
spin_unlock(chip->mutex);
|
||||
if (inval_len)
|
||||
INVALIDATE_CACHED_RANGE(map, inval_adr, inval_len);
|
||||
if (*chip_op_time)
|
||||
cfi_udelay(*chip_op_time);
|
||||
spin_lock(chip->mutex);
|
||||
|
||||
timeo = *chip_op_time * 8 * HZ / 1000000;
|
||||
if (timeo < HZ/2)
|
||||
timeo = HZ/2;
|
||||
timeo += jiffies;
|
||||
|
||||
z = 0;
|
||||
for (;;) {
|
||||
if (chip->state != chip_state) {
|
||||
/* Someone's suspended the operation: sleep */
|
||||
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);
|
||||
timeo = jiffies + (HZ / 2); /* FIXME */
|
||||
spin_lock(chip->mutex);
|
||||
continue;
|
||||
}
|
||||
|
||||
status = map_read(map, cmd_adr);
|
||||
if (map_word_andequal(map, status, status_OK, status_OK))
|
||||
break;
|
||||
|
||||
/* OK Still waiting */
|
||||
if (time_after(jiffies, timeo)) {
|
||||
map_write(map, CMD(0x70), cmd_adr);
|
||||
chip->state = FL_STATUS;
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
/* Latency issues. Drop the lock, wait a while and retry */
|
||||
z++;
|
||||
spin_unlock(chip->mutex);
|
||||
cfi_udelay(1);
|
||||
spin_lock(chip->mutex);
|
||||
}
|
||||
|
||||
if (!z) {
|
||||
if (!--(*chip_op_time))
|
||||
*chip_op_time = 1;
|
||||
} else if (z > 1)
|
||||
++(*chip_op_time);
|
||||
|
||||
/* Done and happy. */
|
||||
chip->state = FL_STATUS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define WAIT_TIMEOUT(map, chip, adr, udelay) \
|
||||
({ int __udelay = (udelay); \
|
||||
INVAL_CACHE_AND_WAIT(map, chip, adr, 0, 0, &__udelay); })
|
||||
|
||||
|
||||
static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len)
|
||||
{
|
||||
unsigned long cmd_addr;
|
||||
@ -1252,14 +1299,11 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
|
||||
unsigned long adr, map_word datum, int mode)
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
map_word status, status_OK, write_cmd;
|
||||
unsigned long timeo;
|
||||
int z, ret=0;
|
||||
map_word status, write_cmd;
|
||||
int ret=0;
|
||||
|
||||
adr += chip->start;
|
||||
|
||||
/* Let's determine those according to the interleave only once */
|
||||
status_OK = CMD(0x80);
|
||||
switch (mode) {
|
||||
case FL_WRITING:
|
||||
write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0x40) : CMD(0x41);
|
||||
@ -1285,57 +1329,17 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip,
|
||||
map_write(map, datum, adr);
|
||||
chip->state = mode;
|
||||
|
||||
INVALIDATE_CACHE_UDELAY(map, chip, adr,
|
||||
adr, map_bankwidth(map),
|
||||
chip->word_write_time);
|
||||
|
||||
timeo = jiffies + (HZ/2);
|
||||
z = 0;
|
||||
for (;;) {
|
||||
if (chip->state != mode) {
|
||||
/* Someone's suspended the write. Sleep */
|
||||
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);
|
||||
timeo = jiffies + (HZ / 2); /* FIXME */
|
||||
spin_lock(chip->mutex);
|
||||
continue;
|
||||
}
|
||||
|
||||
status = map_read(map, adr);
|
||||
if (map_word_andequal(map, status, status_OK, status_OK))
|
||||
break;
|
||||
|
||||
/* OK Still waiting */
|
||||
if (time_after(jiffies, timeo)) {
|
||||
map_write(map, CMD(0x70), adr);
|
||||
chip->state = FL_STATUS;
|
||||
xip_enable(map, chip, adr);
|
||||
printk(KERN_ERR "%s: word write error (status timeout)\n", map->name);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Latency issues. Drop the lock, wait a while and retry */
|
||||
z++;
|
||||
UDELAY(map, chip, adr, 1);
|
||||
ret = INVAL_CACHE_AND_WAIT(map, chip, adr,
|
||||
adr, map_bankwidth(map),
|
||||
&chip->word_write_time);
|
||||
if (ret) {
|
||||
xip_enable(map, chip, adr);
|
||||
printk(KERN_ERR "%s: word write error (status timeout)\n", map->name);
|
||||
goto out;
|
||||
}
|
||||
if (!z) {
|
||||
chip->word_write_time--;
|
||||
if (!chip->word_write_time)
|
||||
chip->word_write_time = 1;
|
||||
}
|
||||
if (z > 1)
|
||||
chip->word_write_time++;
|
||||
|
||||
/* Done and happy. */
|
||||
chip->state = FL_STATUS;
|
||||
|
||||
/* check for errors */
|
||||
status = map_read(map, adr);
|
||||
if (map_word_bitsset(map, status, CMD(0x1a))) {
|
||||
unsigned long chipstatus = MERGESTATUS(status);
|
||||
|
||||
@ -1452,9 +1456,9 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
|
||||
unsigned long *pvec_seek, int len)
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
map_word status, status_OK, write_cmd, datum;
|
||||
unsigned long cmd_adr, timeo;
|
||||
int wbufsize, z, ret=0, word_gap, words;
|
||||
map_word status, write_cmd, datum;
|
||||
unsigned long cmd_adr;
|
||||
int ret, wbufsize, word_gap, words;
|
||||
const struct kvec *vec;
|
||||
unsigned long vec_seek;
|
||||
|
||||
@ -1463,7 +1467,6 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
|
||||
cmd_adr = adr & ~(wbufsize-1);
|
||||
|
||||
/* Let's determine this according to the interleave only once */
|
||||
status_OK = CMD(0x80);
|
||||
write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0xe8) : CMD(0xe9);
|
||||
|
||||
spin_lock(chip->mutex);
|
||||
@ -1477,12 +1480,14 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
|
||||
ENABLE_VPP(map);
|
||||
xip_disable(map, chip, cmd_adr);
|
||||
|
||||
/* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set
|
||||
/* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set
|
||||
[...], the device will not accept any more Write to Buffer commands".
|
||||
So we must check here and reset those bits if they're set. Otherwise
|
||||
we're just pissing in the wind */
|
||||
if (chip->state != FL_STATUS)
|
||||
if (chip->state != FL_STATUS) {
|
||||
map_write(map, CMD(0x70), cmd_adr);
|
||||
chip->state = FL_STATUS;
|
||||
}
|
||||
status = map_read(map, cmd_adr);
|
||||
if (map_word_bitsset(map, status, CMD(0x30))) {
|
||||
xip_enable(map, chip, cmd_adr);
|
||||
@ -1493,32 +1498,20 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
|
||||
}
|
||||
|
||||
chip->state = FL_WRITING_TO_BUFFER;
|
||||
|
||||
z = 0;
|
||||
for (;;) {
|
||||
map_write(map, write_cmd, cmd_adr);
|
||||
|
||||
map_write(map, write_cmd, cmd_adr);
|
||||
ret = WAIT_TIMEOUT(map, chip, cmd_adr, 0);
|
||||
if (ret) {
|
||||
/* Argh. Not ready for write to buffer */
|
||||
map_word Xstatus = map_read(map, cmd_adr);
|
||||
map_write(map, CMD(0x70), cmd_adr);
|
||||
chip->state = FL_STATUS;
|
||||
status = map_read(map, cmd_adr);
|
||||
if (map_word_andequal(map, status, status_OK, status_OK))
|
||||
break;
|
||||
|
||||
UDELAY(map, chip, cmd_adr, 1);
|
||||
|
||||
if (++z > 20) {
|
||||
/* Argh. Not ready for write to buffer */
|
||||
map_word Xstatus;
|
||||
map_write(map, CMD(0x70), cmd_adr);
|
||||
chip->state = FL_STATUS;
|
||||
Xstatus = map_read(map, cmd_adr);
|
||||
/* Odd. Clear status bits */
|
||||
map_write(map, CMD(0x50), cmd_adr);
|
||||
map_write(map, CMD(0x70), cmd_adr);
|
||||
xip_enable(map, chip, cmd_adr);
|
||||
printk(KERN_ERR "%s: Chip not ready for buffer write. status = %lx, Xstatus = %lx\n",
|
||||
map->name, status.x[0], Xstatus.x[0]);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
map_write(map, CMD(0x50), cmd_adr);
|
||||
map_write(map, CMD(0x70), cmd_adr);
|
||||
xip_enable(map, chip, cmd_adr);
|
||||
printk(KERN_ERR "%s: Chip not ready for buffer write. Xstatus = %lx, status = %lx\n",
|
||||
map->name, Xstatus.x[0], status.x[0]);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Figure out the number of words to write */
|
||||
@ -1573,56 +1566,19 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip,
|
||||
map_write(map, CMD(0xd0), cmd_adr);
|
||||
chip->state = FL_WRITING;
|
||||
|
||||
INVALIDATE_CACHE_UDELAY(map, chip, cmd_adr,
|
||||
adr, len,
|
||||
chip->buffer_write_time);
|
||||
|
||||
timeo = jiffies + (HZ/2);
|
||||
z = 0;
|
||||
for (;;) {
|
||||
if (chip->state != FL_WRITING) {
|
||||
/* Someone's suspended the write. Sleep */
|
||||
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);
|
||||
timeo = jiffies + (HZ / 2); /* FIXME */
|
||||
spin_lock(chip->mutex);
|
||||
continue;
|
||||
}
|
||||
|
||||
status = map_read(map, cmd_adr);
|
||||
if (map_word_andequal(map, status, status_OK, status_OK))
|
||||
break;
|
||||
|
||||
/* OK Still waiting */
|
||||
if (time_after(jiffies, timeo)) {
|
||||
map_write(map, CMD(0x70), cmd_adr);
|
||||
chip->state = FL_STATUS;
|
||||
xip_enable(map, chip, cmd_adr);
|
||||
printk(KERN_ERR "%s: buffer write error (status timeout)\n", map->name);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Latency issues. Drop the lock, wait a while and retry */
|
||||
z++;
|
||||
UDELAY(map, chip, cmd_adr, 1);
|
||||
ret = INVAL_CACHE_AND_WAIT(map, chip, cmd_adr,
|
||||
adr, len,
|
||||
&chip->buffer_write_time);
|
||||
if (ret) {
|
||||
map_write(map, CMD(0x70), cmd_adr);
|
||||
chip->state = FL_STATUS;
|
||||
xip_enable(map, chip, cmd_adr);
|
||||
printk(KERN_ERR "%s: buffer write error (status timeout)\n", map->name);
|
||||
goto out;
|
||||
}
|
||||
if (!z) {
|
||||
chip->buffer_write_time--;
|
||||
if (!chip->buffer_write_time)
|
||||
chip->buffer_write_time = 1;
|
||||
}
|
||||
if (z > 1)
|
||||
chip->buffer_write_time++;
|
||||
|
||||
/* Done and happy. */
|
||||
chip->state = FL_STATUS;
|
||||
|
||||
/* check for errors */
|
||||
status = map_read(map, cmd_adr);
|
||||
if (map_word_bitsset(map, status, CMD(0x1a))) {
|
||||
unsigned long chipstatus = MERGESTATUS(status);
|
||||
|
||||
@ -1693,6 +1649,11 @@ static int cfi_intelext_writev (struct mtd_info *mtd, const struct kvec *vecs,
|
||||
if (chipnum == cfi->numchips)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Be nice and reschedule with the chip in a usable state for other
|
||||
processes. */
|
||||
cond_resched();
|
||||
|
||||
} while (len);
|
||||
|
||||
return 0;
|
||||
@ -1713,17 +1674,12 @@ 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;
|
||||
map_word status, status_OK;
|
||||
unsigned long timeo;
|
||||
map_word status;
|
||||
int retries = 3;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
adr += chip->start;
|
||||
|
||||
/* Let's determine this according to the interleave only once */
|
||||
status_OK = CMD(0x80);
|
||||
|
||||
retry:
|
||||
spin_lock(chip->mutex);
|
||||
ret = get_chip(map, chip, adr, FL_ERASING);
|
||||
@ -1745,48 +1701,15 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
|
||||
chip->state = FL_ERASING;
|
||||
chip->erase_suspended = 0;
|
||||
|
||||
INVALIDATE_CACHE_UDELAY(map, chip, adr,
|
||||
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 */
|
||||
|
||||
timeo = jiffies + (HZ*20);
|
||||
for (;;) {
|
||||
if (chip->state != FL_ERASING) {
|
||||
/* Someone's suspended the erase. Sleep */
|
||||
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);
|
||||
continue;
|
||||
}
|
||||
if (chip->erase_suspended) {
|
||||
/* This erase was suspended and resumed.
|
||||
Adjust the timeout */
|
||||
timeo = jiffies + (HZ*20); /* FIXME */
|
||||
chip->erase_suspended = 0;
|
||||
}
|
||||
|
||||
status = map_read(map, adr);
|
||||
if (map_word_andequal(map, status, status_OK, status_OK))
|
||||
break;
|
||||
|
||||
/* OK Still waiting */
|
||||
if (time_after(jiffies, timeo)) {
|
||||
map_write(map, CMD(0x70), adr);
|
||||
chip->state = FL_STATUS;
|
||||
xip_enable(map, chip, adr);
|
||||
printk(KERN_ERR "%s: block erase error: (status timeout)\n", map->name);
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Latency issues. Drop the lock, wait a while and retry */
|
||||
UDELAY(map, chip, adr, 1000000/HZ);
|
||||
ret = INVAL_CACHE_AND_WAIT(map, chip, adr,
|
||||
adr, len,
|
||||
&chip->erase_time);
|
||||
if (ret) {
|
||||
map_write(map, CMD(0x70), adr);
|
||||
chip->state = FL_STATUS;
|
||||
xip_enable(map, chip, adr);
|
||||
printk(KERN_ERR "%s: block erase error: (status timeout)\n", map->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* We've broken this before. It doesn't hurt to be safe */
|
||||
@ -1815,7 +1738,6 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip,
|
||||
ret = -EIO;
|
||||
} else if (chipstatus & 0x20 && retries--) {
|
||||
printk(KERN_DEBUG "block 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;
|
||||
@ -1921,15 +1843,11 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip
|
||||
{
|
||||
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 udelay;
|
||||
int ret;
|
||||
|
||||
adr += chip->start;
|
||||
|
||||
/* Let's determine this according to the interleave only once */
|
||||
status_OK = CMD(0x80);
|
||||
|
||||
spin_lock(chip->mutex);
|
||||
ret = get_chip(map, chip, adr, FL_LOCKING);
|
||||
if (ret) {
|
||||
@ -1954,41 +1872,21 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip
|
||||
* If Instant Individual Block Locking supported then no need
|
||||
* to delay.
|
||||
*/
|
||||
udelay = (!extp || !(extp->FeatureSupport & (1 << 5))) ? 1000000/HZ : 0;
|
||||
|
||||
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 */
|
||||
|
||||
timeo = jiffies + (HZ*20);
|
||||
for (;;) {
|
||||
|
||||
status = map_read(map, adr);
|
||||
if (map_word_andequal(map, status, status_OK, status_OK))
|
||||
break;
|
||||
|
||||
/* OK Still waiting */
|
||||
if (time_after(jiffies, timeo)) {
|
||||
map_write(map, CMD(0x70), adr);
|
||||
chip->state = FL_STATUS;
|
||||
xip_enable(map, chip, adr);
|
||||
printk(KERN_ERR "%s: block unlock error: (status timeout)\n", map->name);
|
||||
put_chip(map, chip, adr);
|
||||
spin_unlock(chip->mutex);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Latency issues. Drop the lock, wait a while and retry */
|
||||
UDELAY(map, chip, adr, 1);
|
||||
ret = WAIT_TIMEOUT(map, chip, adr, udelay);
|
||||
if (ret) {
|
||||
map_write(map, CMD(0x70), adr);
|
||||
chip->state = FL_STATUS;
|
||||
xip_enable(map, chip, adr);
|
||||
printk(KERN_ERR "%s: block unlock error: (status timeout)\n", map->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Done and happy. */
|
||||
chip->state = FL_STATUS;
|
||||
xip_enable(map, chip, adr);
|
||||
put_chip(map, chip, adr);
|
||||
out: put_chip(map, chip, adr);
|
||||
spin_unlock(chip->mutex);
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
|
||||
@ -2445,28 +2343,8 @@ static void cfi_intelext_destroy(struct mtd_info *mtd)
|
||||
kfree(mtd->eraseregions);
|
||||
}
|
||||
|
||||
static char im_name_0001[] = "cfi_cmdset_0001";
|
||||
static char im_name_0003[] = "cfi_cmdset_0003";
|
||||
static char im_name_0200[] = "cfi_cmdset_0200";
|
||||
|
||||
static int __init cfi_intelext_init(void)
|
||||
{
|
||||
inter_module_register(im_name_0001, THIS_MODULE, &cfi_cmdset_0001);
|
||||
inter_module_register(im_name_0003, THIS_MODULE, &cfi_cmdset_0001);
|
||||
inter_module_register(im_name_0200, THIS_MODULE, &cfi_cmdset_0001);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cfi_intelext_exit(void)
|
||||
{
|
||||
inter_module_unregister(im_name_0001);
|
||||
inter_module_unregister(im_name_0003);
|
||||
inter_module_unregister(im_name_0200);
|
||||
}
|
||||
|
||||
module_init(cfi_intelext_init);
|
||||
module_exit(cfi_intelext_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org> et al.");
|
||||
MODULE_DESCRIPTION("MTD chip driver for Intel/Sharp flash chips");
|
||||
MODULE_ALIAS("cfi_cmdset_0003");
|
||||
MODULE_ALIAS("cfi_cmdset_0200");
|
||||
|
@ -236,6 +236,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
|
||||
mtd->resume = cfi_amdstd_resume;
|
||||
mtd->flags = MTD_CAP_NORFLASH;
|
||||
mtd->name = map->name;
|
||||
mtd->writesize = 1;
|
||||
|
||||
if (cfi->cfi_mode==CFI_MODE_CFI){
|
||||
unsigned char bootloc;
|
||||
@ -326,7 +327,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
|
||||
|
||||
return cfi_amdstd_setup(mtd);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(cfi_cmdset_0002);
|
||||
|
||||
static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
|
||||
{
|
||||
@ -1758,25 +1759,6 @@ static void cfi_amdstd_destroy(struct mtd_info *mtd)
|
||||
kfree(mtd->eraseregions);
|
||||
}
|
||||
|
||||
static char im_name[]="cfi_cmdset_0002";
|
||||
|
||||
|
||||
static int __init cfi_amdstd_init(void)
|
||||
{
|
||||
inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0002);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void __exit cfi_amdstd_exit(void)
|
||||
{
|
||||
inter_module_unregister(im_name);
|
||||
}
|
||||
|
||||
|
||||
module_init(cfi_amdstd_init);
|
||||
module_exit(cfi_amdstd_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Crossnet Co. <info@crossnet.co.jp> et al.");
|
||||
MODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips");
|
||||
|
@ -162,6 +162,7 @@ struct mtd_info *cfi_cmdset_0020(struct map_info *map, int primary)
|
||||
|
||||
return cfi_staa_setup(map);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cfi_cmdset_0020);
|
||||
|
||||
static struct mtd_info *cfi_staa_setup(struct map_info *map)
|
||||
{
|
||||
@ -237,9 +238,8 @@ static struct mtd_info *cfi_staa_setup(struct map_info *map)
|
||||
mtd->unlock = cfi_staa_unlock;
|
||||
mtd->suspend = cfi_staa_suspend;
|
||||
mtd->resume = cfi_staa_resume;
|
||||
mtd->flags = MTD_CAP_NORFLASH;
|
||||
mtd->flags |= MTD_ECC; /* FIXME: Not all STMicro flashes have this */
|
||||
mtd->eccsize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */
|
||||
mtd->flags = MTD_CAP_NORFLASH & ~MTD_BIT_WRITEABLE;
|
||||
mtd->writesize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */
|
||||
map->fldrv = &cfi_staa_chipdrv;
|
||||
__module_get(THIS_MODULE);
|
||||
mtd->name = map->name;
|
||||
@ -1410,20 +1410,4 @@ static void cfi_staa_destroy(struct mtd_info *mtd)
|
||||
kfree(cfi);
|
||||
}
|
||||
|
||||
static char im_name[]="cfi_cmdset_0020";
|
||||
|
||||
static int __init cfi_staa_init(void)
|
||||
{
|
||||
inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0020);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cfi_staa_exit(void)
|
||||
{
|
||||
inter_module_unregister(im_name);
|
||||
}
|
||||
|
||||
module_init(cfi_staa_init);
|
||||
module_exit(cfi_staa_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -349,12 +349,12 @@ static void print_cfi_ident(struct cfi_ident *cfip)
|
||||
else
|
||||
printk("No Vpp line\n");
|
||||
|
||||
printk("Typical byte/word write timeout: %d µs\n", 1<<cfip->WordWriteTimeoutTyp);
|
||||
printk("Maximum byte/word write timeout: %d µs\n", (1<<cfip->WordWriteTimeoutMax) * (1<<cfip->WordWriteTimeoutTyp));
|
||||
printk("Typical byte/word write timeout: %d µs\n", 1<<cfip->WordWriteTimeoutTyp);
|
||||
printk("Maximum byte/word write timeout: %d µs\n", (1<<cfip->WordWriteTimeoutMax) * (1<<cfip->WordWriteTimeoutTyp));
|
||||
|
||||
if (cfip->BufWriteTimeoutTyp || cfip->BufWriteTimeoutMax) {
|
||||
printk("Typical full buffer write timeout: %d µs\n", 1<<cfip->BufWriteTimeoutTyp);
|
||||
printk("Maximum full buffer write timeout: %d µs\n", (1<<cfip->BufWriteTimeoutMax) * (1<<cfip->BufWriteTimeoutTyp));
|
||||
printk("Typical full buffer write timeout: %d µs\n", 1<<cfip->BufWriteTimeoutTyp);
|
||||
printk("Maximum full buffer write timeout: %d µs\n", (1<<cfip->BufWriteTimeoutMax) * (1<<cfip->BufWriteTimeoutTyp));
|
||||
}
|
||||
else
|
||||
printk("Full buffer write not supported\n");
|
||||
|
@ -37,8 +37,15 @@ struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp)
|
||||
if (!mtd)
|
||||
mtd = check_cmd_set(map, 0); /* Then the secondary */
|
||||
|
||||
if (mtd)
|
||||
if (mtd) {
|
||||
if (mtd->size > map->size) {
|
||||
printk(KERN_WARNING "Reducing visibility of %ldKiB chip to %ldKiB\n",
|
||||
(unsigned long)mtd->size >> 10,
|
||||
(unsigned long)map->size >> 10);
|
||||
mtd->size = map->size;
|
||||
}
|
||||
return mtd;
|
||||
}
|
||||
|
||||
printk(KERN_WARNING"gen_probe: No supported Vendor Command Set found\n");
|
||||
|
||||
@ -100,7 +107,12 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi
|
||||
* Align bitmap storage size to full byte.
|
||||
*/
|
||||
max_chips = map->size >> cfi.chipshift;
|
||||
mapsize = (max_chips / 8) + ((max_chips % 8) ? 1 : 0);
|
||||
if (!max_chips) {
|
||||
printk(KERN_WARNING "NOR chip too large to fit in mapping. Attempting to cope...\n");
|
||||
max_chips = 1;
|
||||
}
|
||||
|
||||
mapsize = (max_chips + BITS_PER_LONG-1) / BITS_PER_LONG;
|
||||
chip_map = kmalloc(mapsize, GFP_KERNEL);
|
||||
if (!chip_map) {
|
||||
printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name);
|
||||
@ -194,25 +206,28 @@ static inline struct mtd_info *cfi_cmdset_unknown(struct map_info *map,
|
||||
{
|
||||
struct cfi_private *cfi = map->fldrv_priv;
|
||||
__u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID;
|
||||
#if defined(CONFIG_MODULES) && defined(HAVE_INTER_MODULE)
|
||||
char probename[32];
|
||||
#ifdef CONFIG_MODULES
|
||||
char probename[16+sizeof(MODULE_SYMBOL_PREFIX)];
|
||||
cfi_cmdset_fn_t *probe_function;
|
||||
|
||||
sprintf(probename, "cfi_cmdset_%4.4X", type);
|
||||
sprintf(probename, MODULE_SYMBOL_PREFIX "cfi_cmdset_%4.4X", type);
|
||||
|
||||
probe_function = inter_module_get_request(probename, probename);
|
||||
probe_function = __symbol_get(probename);
|
||||
if (!probe_function) {
|
||||
request_module(probename + sizeof(MODULE_SYMBOL_PREFIX) - 1);
|
||||
probe_function = __symbol_get(probename);
|
||||
}
|
||||
|
||||
if (probe_function) {
|
||||
struct mtd_info *mtd;
|
||||
|
||||
mtd = (*probe_function)(map, primary);
|
||||
/* If it was happy, it'll have increased its own use count */
|
||||
inter_module_put(probename);
|
||||
symbol_put_addr(probe_function);
|
||||
return mtd;
|
||||
}
|
||||
#endif
|
||||
printk(KERN_NOTICE "Support for command set %04X not present\n",
|
||||
type);
|
||||
printk(KERN_NOTICE "Support for command set %04X not present\n", type);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -226,12 +241,8 @@ static struct mtd_info *check_cmd_set(struct map_info *map, int primary)
|
||||
return NULL;
|
||||
|
||||
switch(type){
|
||||
/* Urgh. Ifdefs. The version with weak symbols was
|
||||
* _much_ nicer. Shame it didn't seem to work on
|
||||
* anything but x86, really.
|
||||
* But we can't rely in inter_module_get() because
|
||||
* that'd mean we depend on link order.
|
||||
*/
|
||||
/* We need these for the !CONFIG_MODULES case,
|
||||
because symbol_get() doesn't work there */
|
||||
#ifdef CONFIG_MTD_CFI_INTELEXT
|
||||
case 0x0001:
|
||||
case 0x0003:
|
||||
@ -246,9 +257,9 @@ static struct mtd_info *check_cmd_set(struct map_info *map, int primary)
|
||||
case 0x0020:
|
||||
return cfi_cmdset_0020(map, primary);
|
||||
#endif
|
||||
default:
|
||||
return cfi_cmdset_unknown(map, primary);
|
||||
}
|
||||
|
||||
return cfi_cmdset_unknown(map, primary);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -70,7 +70,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map)
|
||||
mtd->read = mapram_read;
|
||||
mtd->write = mapram_write;
|
||||
mtd->sync = mapram_nop;
|
||||
mtd->flags = MTD_CAP_RAM | MTD_VOLATILE;
|
||||
mtd->flags = MTD_CAP_RAM;
|
||||
|
||||
mtd->erasesize = PAGE_SIZE;
|
||||
while(mtd->size & (mtd->erasesize - 1))
|
||||
|
@ -46,9 +46,7 @@ static struct mtd_info *map_rom_probe(struct map_info *map)
|
||||
mtd->write = maprom_write;
|
||||
mtd->sync = maprom_nop;
|
||||
mtd->flags = MTD_CAP_ROM;
|
||||
mtd->erasesize = 131072;
|
||||
while(mtd->size & (mtd->erasesize - 1))
|
||||
mtd->erasesize >>= 1;
|
||||
mtd->erasesize = map->size;
|
||||
|
||||
__module_get(THIS_MODULE);
|
||||
return mtd;
|
||||
|
@ -140,6 +140,7 @@ static struct mtd_info *sharp_probe(struct map_info *map)
|
||||
mtd->suspend = sharp_suspend;
|
||||
mtd->resume = sharp_resume;
|
||||
mtd->flags = MTD_CAP_NORFLASH;
|
||||
mtd->writesize = 1;
|
||||
mtd->name = map->name;
|
||||
|
||||
memset(sharp, 0, sizeof(*sharp));
|
||||
|
@ -47,6 +47,11 @@ config MTD_MS02NV
|
||||
accelerator. Say Y here if you have a DECstation 5000/2x0 or a
|
||||
DECsystem 5900 equipped with such a module.
|
||||
|
||||
If you want to compile this driver as a module ( = code which can be
|
||||
inserted in and removed from the running kernel whenever you want),
|
||||
say M here and read <file:Documentation/modules.txt>. The module will
|
||||
be called ms02-nv.o.
|
||||
|
||||
config MTD_DATAFLASH
|
||||
tristate "Support for AT45xxx DataFlash"
|
||||
depends on MTD && SPI_MASTER && EXPERIMENTAL
|
||||
@ -209,7 +214,6 @@ config MTD_DOC2001PLUS
|
||||
config MTD_DOCPROBE
|
||||
tristate
|
||||
select MTD_DOCECC
|
||||
select OBSOLETE_INTERMODULE
|
||||
|
||||
config MTD_DOCECC
|
||||
tristate
|
||||
|
@ -3,13 +3,6 @@
|
||||
#
|
||||
# $Id: Makefile.common,v 1.7 2004/12/22 17:51:15 joern Exp $
|
||||
|
||||
# *** BIG UGLY NOTE ***
|
||||
#
|
||||
# The removal of get_module_symbol() and replacement with
|
||||
# inter_module_register() et al has introduced a link order dependency
|
||||
# here where previously there was none. We now have to ensure that
|
||||
# doc200[01].o are linked before docprobe.o
|
||||
|
||||
obj-$(CONFIG_MTD_DOC2000) += doc2000.o
|
||||
obj-$(CONFIG_MTD_DOC2001) += doc2001.o
|
||||
obj-$(CONFIG_MTD_DOC2001PLUS) += doc2001plus.o
|
||||
|
@ -4,7 +4,7 @@
|
||||
* block2mtd.c - create an mtd from a block device
|
||||
*
|
||||
* Copyright (C) 2001,2002 Simon Evans <spse@secret.org.uk>
|
||||
* Copyright (C) 2004,2005 Jörn Engel <joern@wh.fh-wedel.de>
|
||||
* Copyright (C) 2004-2006 Jörn Engel <joern@wh.fh-wedel.de>
|
||||
*
|
||||
* Licence: GPL
|
||||
*/
|
||||
@ -331,7 +331,6 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
|
||||
dev->mtd.writev = default_mtd_writev;
|
||||
dev->mtd.sync = block2mtd_sync;
|
||||
dev->mtd.read = block2mtd_read;
|
||||
dev->mtd.readv = default_mtd_readv;
|
||||
dev->mtd.priv = dev;
|
||||
dev->mtd.owner = THIS_MODULE;
|
||||
|
||||
@ -351,6 +350,12 @@ devinit_err:
|
||||
}
|
||||
|
||||
|
||||
/* This function works similar to reguler strtoul. In addition, it
|
||||
* allows some suffixes for a more human-readable number format:
|
||||
* ki, Ki, kiB, KiB - multiply result with 1024
|
||||
* Mi, MiB - multiply result with 1024^2
|
||||
* Gi, GiB - multiply result with 1024^3
|
||||
*/
|
||||
static int ustrtoul(const char *cp, char **endp, unsigned int base)
|
||||
{
|
||||
unsigned long result = simple_strtoul(cp, endp, base);
|
||||
@ -359,11 +364,16 @@ static int ustrtoul(const char *cp, char **endp, unsigned int base)
|
||||
result *= 1024;
|
||||
case 'M':
|
||||
result *= 1024;
|
||||
case 'K':
|
||||
case 'k':
|
||||
result *= 1024;
|
||||
/* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */
|
||||
if ((*endp)[1] == 'i')
|
||||
(*endp) += 2;
|
||||
if ((*endp)[1] == 'i') {
|
||||
if ((*endp)[2] == 'B')
|
||||
(*endp) += 3;
|
||||
else
|
||||
(*endp) += 2;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -418,7 +428,8 @@ static inline void kill_final_newline(char *str)
|
||||
|
||||
static int block2mtd_setup(const char *val, struct kernel_param *kp)
|
||||
{
|
||||
char buf[80+12], *str=buf; /* 80 for device, 12 for erase size */
|
||||
char buf[80+12]; /* 80 for device, 12 for erase size */
|
||||
char *str = buf;
|
||||
char *token[2];
|
||||
char *name;
|
||||
size_t erase_size = PAGE_SIZE;
|
||||
@ -430,7 +441,7 @@ static int block2mtd_setup(const char *val, struct kernel_param *kp)
|
||||
strcpy(str, val);
|
||||
kill_final_newline(str);
|
||||
|
||||
for (i=0; i<2; i++)
|
||||
for (i = 0; i < 2; i++)
|
||||
token[i] = strsep(&str, ",");
|
||||
|
||||
if (str)
|
||||
@ -449,8 +460,10 @@ static int block2mtd_setup(const char *val, struct kernel_param *kp)
|
||||
|
||||
if (token[1]) {
|
||||
ret = parse_num(&erase_size, token[1]);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
kfree(name);
|
||||
parse_err("illegal erase size");
|
||||
}
|
||||
}
|
||||
|
||||
add_device(name, erase_size);
|
||||
|
@ -59,13 +59,10 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
|
||||
static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
|
||||
static int doc_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs,
|
||||
unsigned long count, loff_t to, size_t *retlen,
|
||||
u_char *eccbuf, struct nand_oobinfo *oobsel);
|
||||
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
size_t *retlen, u_char *buf);
|
||||
static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
size_t *retlen, const u_char *buf);
|
||||
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
struct mtd_oob_ops *ops);
|
||||
static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
struct mtd_oob_ops *ops);
|
||||
static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
size_t *retlen, const u_char *buf);
|
||||
static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
|
||||
@ -517,16 +514,9 @@ static int DoC2k_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static const char im_name[] = "DoC2k_init";
|
||||
|
||||
/* This routine is made available to other mtd code via
|
||||
* inter_module_register. It must only be accessed through
|
||||
* inter_module_get which will bump the use count of this module. The
|
||||
* addresses passed back in mtd are valid as long as the use count of
|
||||
* this module is non-zero, i.e. between inter_module_get and
|
||||
* inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000.
|
||||
*/
|
||||
static void DoC2k_init(struct mtd_info *mtd)
|
||||
/* This routine is found from the docprobe code by symbol_get(),
|
||||
* which will bump the use count of this module. */
|
||||
void DoC2k_init(struct mtd_info *mtd)
|
||||
{
|
||||
struct DiskOnChip *this = mtd->priv;
|
||||
struct DiskOnChip *old = NULL;
|
||||
@ -586,7 +576,7 @@ static void DoC2k_init(struct mtd_info *mtd)
|
||||
mtd->ecctype = MTD_ECC_RS_DiskOnChip;
|
||||
mtd->size = 0;
|
||||
mtd->erasesize = 0;
|
||||
mtd->oobblock = 512;
|
||||
mtd->writesize = 512;
|
||||
mtd->oobsize = 16;
|
||||
mtd->owner = THIS_MODULE;
|
||||
mtd->erase = doc_erase;
|
||||
@ -594,9 +584,6 @@ static void DoC2k_init(struct mtd_info *mtd)
|
||||
mtd->unpoint = NULL;
|
||||
mtd->read = doc_read;
|
||||
mtd->write = doc_write;
|
||||
mtd->read_ecc = doc_read_ecc;
|
||||
mtd->write_ecc = doc_write_ecc;
|
||||
mtd->writev_ecc = doc_writev_ecc;
|
||||
mtd->read_oob = doc_read_oob;
|
||||
mtd->write_oob = doc_write_oob;
|
||||
mtd->sync = NULL;
|
||||
@ -623,6 +610,7 @@ static void DoC2k_init(struct mtd_info *mtd)
|
||||
return;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(DoC2k_init);
|
||||
|
||||
static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t * retlen, u_char * buf)
|
||||
@ -971,72 +959,18 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int doc_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs,
|
||||
unsigned long count, loff_t to, size_t *retlen,
|
||||
u_char *eccbuf, struct nand_oobinfo *oobsel)
|
||||
{
|
||||
static char static_buf[512];
|
||||
static DEFINE_MUTEX(writev_buf_mutex);
|
||||
|
||||
size_t totretlen = 0;
|
||||
size_t thisvecofs = 0;
|
||||
int ret= 0;
|
||||
|
||||
mutex_lock(&writev_buf_mutex);
|
||||
|
||||
while(count) {
|
||||
size_t thislen, thisretlen;
|
||||
unsigned char *buf;
|
||||
|
||||
buf = vecs->iov_base + thisvecofs;
|
||||
thislen = vecs->iov_len - thisvecofs;
|
||||
|
||||
|
||||
if (thislen >= 512) {
|
||||
thislen = thislen & ~(512-1);
|
||||
thisvecofs += thislen;
|
||||
} else {
|
||||
/* Not enough to fill a page. Copy into buf */
|
||||
memcpy(static_buf, buf, thislen);
|
||||
buf = &static_buf[thislen];
|
||||
|
||||
while(count && thislen < 512) {
|
||||
vecs++;
|
||||
count--;
|
||||
thisvecofs = min((512-thislen), vecs->iov_len);
|
||||
memcpy(buf, vecs->iov_base, thisvecofs);
|
||||
thislen += thisvecofs;
|
||||
buf += thisvecofs;
|
||||
}
|
||||
buf = static_buf;
|
||||
}
|
||||
if (count && thisvecofs == vecs->iov_len) {
|
||||
thisvecofs = 0;
|
||||
vecs++;
|
||||
count--;
|
||||
}
|
||||
ret = doc_write_ecc(mtd, to, thislen, &thisretlen, buf, eccbuf, oobsel);
|
||||
|
||||
totretlen += thisretlen;
|
||||
|
||||
if (ret || thisretlen != thislen)
|
||||
break;
|
||||
|
||||
to += thislen;
|
||||
}
|
||||
|
||||
mutex_unlock(&writev_buf_mutex);
|
||||
*retlen = totretlen;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
size_t * retlen, u_char * buf)
|
||||
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
struct DiskOnChip *this = mtd->priv;
|
||||
int len256 = 0, ret;
|
||||
struct Nand *mychip;
|
||||
uint8_t *buf = ops->oobbuf;
|
||||
size_t len = ops->len;
|
||||
|
||||
BUG_ON(ops->mode != MTD_OOB_PLACE);
|
||||
|
||||
ofs += ops->ooboffs;
|
||||
|
||||
mutex_lock(&this->lock);
|
||||
|
||||
@ -1077,7 +1011,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
|
||||
DoC_ReadBuf(this, &buf[len256], len - len256);
|
||||
|
||||
*retlen = len;
|
||||
ops->retlen = len;
|
||||
/* Reading the full OOB data drops us off of the end of the page,
|
||||
* causing the flash device to go into busy mode, so we need
|
||||
* to wait until ready 11.4.1 and Toshiba TC58256FT docs */
|
||||
@ -1192,17 +1126,20 @@ static int doc_write_oob_nolock(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
|
||||
}
|
||||
|
||||
static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
size_t * retlen, const u_char * buf)
|
||||
static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
struct DiskOnChip *this = mtd->priv;
|
||||
int ret;
|
||||
struct DiskOnChip *this = mtd->priv;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&this->lock);
|
||||
ret = doc_write_oob_nolock(mtd, ofs, len, retlen, buf);
|
||||
BUG_ON(ops->mode != MTD_OOB_PLACE);
|
||||
|
||||
mutex_unlock(&this->lock);
|
||||
return ret;
|
||||
mutex_lock(&this->lock);
|
||||
ret = doc_write_oob_nolock(mtd, ofs + ops->ooboffs, ops->len,
|
||||
&ops->retlen, ops->oobbuf);
|
||||
|
||||
mutex_unlock(&this->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
@ -1277,12 +1214,6 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int __init init_doc2000(void)
|
||||
{
|
||||
inter_module_register(im_name, THIS_MODULE, &DoC2k_init);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cleanup_doc2000(void)
|
||||
{
|
||||
struct mtd_info *mtd;
|
||||
@ -1298,11 +1229,9 @@ static void __exit cleanup_doc2000(void)
|
||||
kfree(this->chips);
|
||||
kfree(mtd);
|
||||
}
|
||||
inter_module_unregister(im_name);
|
||||
}
|
||||
|
||||
module_exit(cleanup_doc2000);
|
||||
module_init(init_doc2000);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org> et al.");
|
||||
|
@ -43,10 +43,10 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf, u_char *eccbuf,
|
||||
struct nand_oobinfo *oobsel);
|
||||
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
size_t *retlen, u_char *buf);
|
||||
static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
size_t *retlen, const u_char *buf);
|
||||
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
struct mtd_oob_ops *ops);
|
||||
static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
struct mtd_oob_ops *ops);
|
||||
static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
|
||||
|
||||
static struct mtd_info *docmillist = NULL;
|
||||
@ -324,16 +324,9 @@ static int DoCMil_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static const char im_name[] = "DoCMil_init";
|
||||
|
||||
/* This routine is made available to other mtd code via
|
||||
* inter_module_register. It must only be accessed through
|
||||
* inter_module_get which will bump the use count of this module. The
|
||||
* addresses passed back in mtd are valid as long as the use count of
|
||||
* this module is non-zero, i.e. between inter_module_get and
|
||||
* inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000.
|
||||
*/
|
||||
static void DoCMil_init(struct mtd_info *mtd)
|
||||
/* This routine is found from the docprobe code by symbol_get(),
|
||||
* which will bump the use count of this module. */
|
||||
void DoCMil_init(struct mtd_info *mtd)
|
||||
{
|
||||
struct DiskOnChip *this = mtd->priv;
|
||||
struct DiskOnChip *old = NULL;
|
||||
@ -368,7 +361,7 @@ static void DoCMil_init(struct mtd_info *mtd)
|
||||
/* FIXME: erase size is not always 8KiB */
|
||||
mtd->erasesize = 0x2000;
|
||||
|
||||
mtd->oobblock = 512;
|
||||
mtd->writesize = 512;
|
||||
mtd->oobsize = 16;
|
||||
mtd->owner = THIS_MODULE;
|
||||
mtd->erase = doc_erase;
|
||||
@ -376,8 +369,6 @@ static void DoCMil_init(struct mtd_info *mtd)
|
||||
mtd->unpoint = NULL;
|
||||
mtd->read = doc_read;
|
||||
mtd->write = doc_write;
|
||||
mtd->read_ecc = doc_read_ecc;
|
||||
mtd->write_ecc = doc_write_ecc;
|
||||
mtd->read_oob = doc_read_oob;
|
||||
mtd->write_oob = doc_write_oob;
|
||||
mtd->sync = NULL;
|
||||
@ -401,6 +392,7 @@ static void DoCMil_init(struct mtd_info *mtd)
|
||||
return;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(DoCMil_init);
|
||||
|
||||
static int doc_read (struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
@ -670,8 +662,8 @@ static int doc_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
#ifndef USE_MEMCPY
|
||||
int i;
|
||||
@ -680,6 +672,12 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
struct DiskOnChip *this = mtd->priv;
|
||||
void __iomem *docptr = this->virtadr;
|
||||
struct Nand *mychip = &this->chips[ofs >> this->chipshift];
|
||||
uint8_t *buf = ops->oobbuf;
|
||||
size_t len = ops->len;
|
||||
|
||||
BUG_ON(ops->mode != MTD_OOB_PLACE);
|
||||
|
||||
ofs += ops->ooboffs;
|
||||
|
||||
/* Find the chip which is to be used and select it */
|
||||
if (this->curfloor != mychip->floor) {
|
||||
@ -716,13 +714,13 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
#endif
|
||||
buf[len - 1] = ReadDOC(docptr, LastDataRead);
|
||||
|
||||
*retlen = len;
|
||||
ops->retlen = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
#ifndef USE_MEMCPY
|
||||
int i;
|
||||
@ -732,6 +730,12 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
struct DiskOnChip *this = mtd->priv;
|
||||
void __iomem *docptr = this->virtadr;
|
||||
struct Nand *mychip = &this->chips[ofs >> this->chipshift];
|
||||
uint8_t *buf = ops->oobbuf;
|
||||
size_t len = ops->len;
|
||||
|
||||
BUG_ON(ops->mode != MTD_OOB_PLACE);
|
||||
|
||||
ofs += ops->ooboffs;
|
||||
|
||||
/* Find the chip which is to be used and select it */
|
||||
if (this->curfloor != mychip->floor) {
|
||||
@ -783,12 +787,12 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
if (ReadDOC(docptr, Mil_CDSN_IO) & 1) {
|
||||
printk("Error programming oob data\n");
|
||||
/* FIXME: implement Bad Block Replacement (in nftl.c ??) */
|
||||
*retlen = 0;
|
||||
ops->retlen = 0;
|
||||
ret = -EIO;
|
||||
}
|
||||
dummy = ReadDOC(docptr, LastDataRead);
|
||||
|
||||
*retlen = len;
|
||||
ops->retlen = len;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -856,12 +860,6 @@ int doc_erase (struct mtd_info *mtd, struct erase_info *instr)
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int __init init_doc2001(void)
|
||||
{
|
||||
inter_module_register(im_name, THIS_MODULE, &DoCMil_init);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cleanup_doc2001(void)
|
||||
{
|
||||
struct mtd_info *mtd;
|
||||
@ -877,11 +875,9 @@ static void __exit cleanup_doc2001(void)
|
||||
kfree(this->chips);
|
||||
kfree(mtd);
|
||||
}
|
||||
inter_module_unregister(im_name);
|
||||
}
|
||||
|
||||
module_exit(cleanup_doc2001);
|
||||
module_init(init_doc2001);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org> et al.");
|
||||
|
@ -47,10 +47,10 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf, u_char *eccbuf,
|
||||
struct nand_oobinfo *oobsel);
|
||||
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
size_t *retlen, u_char *buf);
|
||||
static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
size_t *retlen, const u_char *buf);
|
||||
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
struct mtd_oob_ops *ops);
|
||||
static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
struct mtd_oob_ops *ops);
|
||||
static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
|
||||
|
||||
static struct mtd_info *docmilpluslist = NULL;
|
||||
@ -447,16 +447,9 @@ static int DoCMilPlus_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2)
|
||||
return retval;
|
||||
}
|
||||
|
||||
static const char im_name[] = "DoCMilPlus_init";
|
||||
|
||||
/* This routine is made available to other mtd code via
|
||||
* inter_module_register. It must only be accessed through
|
||||
* inter_module_get which will bump the use count of this module. The
|
||||
* addresses passed back in mtd are valid as long as the use count of
|
||||
* this module is non-zero, i.e. between inter_module_get and
|
||||
* inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000.
|
||||
*/
|
||||
static void DoCMilPlus_init(struct mtd_info *mtd)
|
||||
/* This routine is found from the docprobe code by symbol_get(),
|
||||
* which will bump the use count of this module. */
|
||||
void DoCMilPlus_init(struct mtd_info *mtd)
|
||||
{
|
||||
struct DiskOnChip *this = mtd->priv;
|
||||
struct DiskOnChip *old = NULL;
|
||||
@ -490,7 +483,7 @@ static void DoCMilPlus_init(struct mtd_info *mtd)
|
||||
mtd->size = 0;
|
||||
|
||||
mtd->erasesize = 0;
|
||||
mtd->oobblock = 512;
|
||||
mtd->writesize = 512;
|
||||
mtd->oobsize = 16;
|
||||
mtd->owner = THIS_MODULE;
|
||||
mtd->erase = doc_erase;
|
||||
@ -498,8 +491,6 @@ static void DoCMilPlus_init(struct mtd_info *mtd)
|
||||
mtd->unpoint = NULL;
|
||||
mtd->read = doc_read;
|
||||
mtd->write = doc_write;
|
||||
mtd->read_ecc = doc_read_ecc;
|
||||
mtd->write_ecc = doc_write_ecc;
|
||||
mtd->read_oob = doc_read_oob;
|
||||
mtd->write_oob = doc_write_oob;
|
||||
mtd->sync = NULL;
|
||||
@ -524,6 +515,7 @@ static void DoCMilPlus_init(struct mtd_info *mtd)
|
||||
return;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(DoCMilPlus_init);
|
||||
|
||||
#if 0
|
||||
static int doc_dumpblk(struct mtd_info *mtd, loff_t from)
|
||||
@ -876,14 +868,20 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
static int doc_read_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
loff_t fofs, base;
|
||||
struct DiskOnChip *this = mtd->priv;
|
||||
void __iomem * docptr = this->virtadr;
|
||||
struct Nand *mychip = &this->chips[ofs >> this->chipshift];
|
||||
size_t i, size, got, want;
|
||||
uint8_t *buf = ops->oobbuf;
|
||||
size_t len = ops->len;
|
||||
|
||||
BUG_ON(ops->mode != MTD_OOB_PLACE);
|
||||
|
||||
ofs += ops->ooboffs;
|
||||
|
||||
DoC_CheckASIC(docptr);
|
||||
|
||||
@ -949,12 +947,12 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
/* Disable flash internally */
|
||||
WriteDOC(0, docptr, Mplus_FlashSelect);
|
||||
|
||||
*retlen = len;
|
||||
ops->retlen = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
static int doc_write_oob(struct mtd_info *mtd, loff_t ofs,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
volatile char dummy;
|
||||
loff_t fofs, base;
|
||||
@ -963,6 +961,12 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
struct Nand *mychip = &this->chips[ofs >> this->chipshift];
|
||||
size_t i, size, got, want;
|
||||
int ret = 0;
|
||||
uint8_t *buf = ops->oobbuf;
|
||||
size_t len = ops->len;
|
||||
|
||||
BUG_ON(ops->mode != MTD_OOB_PLACE);
|
||||
|
||||
ofs += ops->ooboffs;
|
||||
|
||||
DoC_CheckASIC(docptr);
|
||||
|
||||
@ -1038,7 +1042,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
printk("MTD: Error 0x%x programming oob at 0x%x\n",
|
||||
dummy, (int)ofs);
|
||||
/* FIXME: implement Bad Block Replacement */
|
||||
*retlen = 0;
|
||||
ops->retlen = 0;
|
||||
ret = -EIO;
|
||||
}
|
||||
dummy = ReadDOC(docptr, Mplus_LastDataRead);
|
||||
@ -1051,7 +1055,7 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
|
||||
/* Disable flash internally */
|
||||
WriteDOC(0, docptr, Mplus_FlashSelect);
|
||||
|
||||
*retlen = len;
|
||||
ops->retlen = len;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1122,12 +1126,6 @@ int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int __init init_doc2001plus(void)
|
||||
{
|
||||
inter_module_register(im_name, THIS_MODULE, &DoCMilPlus_init);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit cleanup_doc2001plus(void)
|
||||
{
|
||||
struct mtd_info *mtd;
|
||||
@ -1143,11 +1141,9 @@ static void __exit cleanup_doc2001plus(void)
|
||||
kfree(this->chips);
|
||||
kfree(mtd);
|
||||
}
|
||||
inter_module_unregister(im_name);
|
||||
}
|
||||
|
||||
module_exit(cleanup_doc2001plus);
|
||||
module_init(init_doc2001plus);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com> et al.");
|
||||
|
@ -231,6 +231,10 @@ static inline int __init doccheck(void __iomem *potential, unsigned long physadr
|
||||
|
||||
static int docfound;
|
||||
|
||||
extern void DoC2k_init(struct mtd_info *);
|
||||
extern void DoCMil_init(struct mtd_info *);
|
||||
extern void DoCMilPlus_init(struct mtd_info *);
|
||||
|
||||
static void __init DoC_Probe(unsigned long physadr)
|
||||
{
|
||||
void __iomem *docptr;
|
||||
@ -239,8 +243,6 @@ static void __init DoC_Probe(unsigned long physadr)
|
||||
int ChipID;
|
||||
char namebuf[15];
|
||||
char *name = namebuf;
|
||||
char *im_funcname = NULL;
|
||||
char *im_modname = NULL;
|
||||
void (*initroutine)(struct mtd_info *) = NULL;
|
||||
|
||||
docptr = ioremap(physadr, DOC_IOREMAP_LEN);
|
||||
@ -278,41 +280,33 @@ static void __init DoC_Probe(unsigned long physadr)
|
||||
switch(ChipID) {
|
||||
case DOC_ChipID_Doc2kTSOP:
|
||||
name="2000 TSOP";
|
||||
im_funcname = "DoC2k_init";
|
||||
im_modname = "doc2000";
|
||||
initroutine = symbol_request(DoC2k_init);
|
||||
break;
|
||||
|
||||
case DOC_ChipID_Doc2k:
|
||||
name="2000";
|
||||
im_funcname = "DoC2k_init";
|
||||
im_modname = "doc2000";
|
||||
initroutine = symbol_request(DoC2k_init);
|
||||
break;
|
||||
|
||||
case DOC_ChipID_DocMil:
|
||||
name="Millennium";
|
||||
#ifdef DOC_SINGLE_DRIVER
|
||||
im_funcname = "DoC2k_init";
|
||||
im_modname = "doc2000";
|
||||
initroutine = symbol_request(DoC2k_init);
|
||||
#else
|
||||
im_funcname = "DoCMil_init";
|
||||
im_modname = "doc2001";
|
||||
initroutine = symbol_request(DoCMil_init);
|
||||
#endif /* DOC_SINGLE_DRIVER */
|
||||
break;
|
||||
|
||||
case DOC_ChipID_DocMilPlus16:
|
||||
case DOC_ChipID_DocMilPlus32:
|
||||
name="MillenniumPlus";
|
||||
im_funcname = "DoCMilPlus_init";
|
||||
im_modname = "doc2001plus";
|
||||
initroutine = symbol_request(DoCMilPlus_init);
|
||||
break;
|
||||
}
|
||||
|
||||
if (im_funcname)
|
||||
initroutine = inter_module_get_request(im_funcname, im_modname);
|
||||
|
||||
if (initroutine) {
|
||||
(*initroutine)(mtd);
|
||||
inter_module_put(im_funcname);
|
||||
symbol_put_addr(initroutine);
|
||||
return;
|
||||
}
|
||||
printk(KERN_NOTICE "Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr);
|
||||
|
@ -635,6 +635,7 @@ int __init lart_flash_init (void)
|
||||
printk ("%s: This looks like a LART board to me.\n",module_name);
|
||||
mtd.name = module_name;
|
||||
mtd.type = MTD_NORFLASH;
|
||||
mtd.writesize = 1;
|
||||
mtd.flags = MTD_CAP_NORFLASH;
|
||||
mtd.size = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM + FLASH_BLOCKSIZE_MAIN * FLASH_NUMBLOCKS_16m_MAIN;
|
||||
mtd.erasesize = FLASH_BLOCKSIZE_MAIN;
|
||||
|
@ -465,6 +465,7 @@ static int __devinit m25p_probe(struct spi_device *spi)
|
||||
flash->mtd.name = spi->dev.bus_id;
|
||||
|
||||
flash->mtd.type = MTD_NORFLASH;
|
||||
flash->mtd.writesize = 1;
|
||||
flash->mtd.flags = MTD_CAP_NORFLASH;
|
||||
flash->mtd.size = info->sector_size * info->n_sectors;
|
||||
flash->mtd.erasesize = info->sector_size;
|
||||
|
@ -219,7 +219,7 @@ static int __init ms02nv_init_one(ulong addr)
|
||||
mp->uaddr = phys_to_virt(fixaddr);
|
||||
|
||||
mtd->type = MTD_RAM;
|
||||
mtd->flags = MTD_CAP_RAM | MTD_XIP;
|
||||
mtd->flags = MTD_CAP_RAM;
|
||||
mtd->size = fixsize;
|
||||
mtd->name = (char *)ms02nv_name;
|
||||
mtd->owner = THIS_MODULE;
|
||||
|
@ -106,6 +106,7 @@ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address,
|
||||
mtd->type = MTD_RAM;
|
||||
mtd->flags = MTD_CAP_RAM;
|
||||
mtd->size = size;
|
||||
mtd->writesize = 1;
|
||||
mtd->erasesize = MTDRAM_ERASE_SIZE;
|
||||
mtd->priv = mapped_address;
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
/**
|
||||
* $Id: phram.c,v 1.16 2005/11/07 11:14:25 gleixner Exp $
|
||||
*
|
||||
* Copyright (c) ???? Jochen Schäuble <psionic@psionic.de>
|
||||
* Copyright (c) 2003-2004 Jörn Engel <joern@wh.fh-wedel.de>
|
||||
* Copyright (c) ???? Jochen Schäuble <psionic@psionic.de>
|
||||
* Copyright (c) 2003-2004 Jörn Engel <joern@wh.fh-wedel.de>
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
@ -142,7 +142,7 @@ static int register_device(char *name, unsigned long start, unsigned long len)
|
||||
|
||||
new->mtd.name = name;
|
||||
new->mtd.size = len;
|
||||
new->mtd.flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE;
|
||||
new->mtd.flags = MTD_CAP_RAM;
|
||||
new->mtd.erase = phram_erase;
|
||||
new->mtd.point = phram_point;
|
||||
new->mtd.unpoint = phram_unpoint;
|
||||
@ -266,12 +266,16 @@ static int phram_setup(const char *val, struct kernel_param *kp)
|
||||
return 0;
|
||||
|
||||
ret = parse_num32(&start, token[1]);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
kfree(name);
|
||||
parse_err("illegal start address\n");
|
||||
}
|
||||
|
||||
ret = parse_num32(&len, token[2]);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
kfree(name);
|
||||
parse_err("illegal device length\n");
|
||||
}
|
||||
|
||||
register_device(name, start, len);
|
||||
|
||||
@ -296,5 +300,5 @@ module_init(init_phram);
|
||||
module_exit(cleanup_phram);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Jörn Engel <joern@wh.fh-wedel.de>");
|
||||
MODULE_AUTHOR("Jörn Engel <joern@wh.fh-wedel.de>");
|
||||
MODULE_DESCRIPTION("MTD driver for physical RAM");
|
||||
|
@ -200,8 +200,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_CAP_RAM;
|
||||
(*curmtd)->mtdinfo->flags = MTD_CAP_RAM;
|
||||
(*curmtd)->mtdinfo->erase = slram_erase;
|
||||
(*curmtd)->mtdinfo->point = slram_point;
|
||||
(*curmtd)->mtdinfo->unpoint = slram_unpoint;
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nftl.h>
|
||||
#include <linux/mtd/inftl.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/io.h>
|
||||
@ -79,14 +80,12 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
|
||||
inftl->mbd.devnum = -1;
|
||||
inftl->mbd.blksize = 512;
|
||||
inftl->mbd.tr = tr;
|
||||
memcpy(&inftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo));
|
||||
inftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY;
|
||||
|
||||
if (INFTL_mount(inftl) < 0) {
|
||||
if (INFTL_mount(inftl) < 0) {
|
||||
printk(KERN_WARNING "INFTL: could not mount device\n");
|
||||
kfree(inftl);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* OK, it's a new one. Set up all the data structures. */
|
||||
|
||||
@ -151,6 +150,69 @@ static void inftl_remove_dev(struct mtd_blktrans_dev *dev)
|
||||
* Actual INFTL access routines.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Read oob data from flash
|
||||
*/
|
||||
int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
size_t *retlen, uint8_t *buf)
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
int res;
|
||||
|
||||
ops.mode = MTD_OOB_PLACE;
|
||||
ops.ooboffs = offs & (mtd->writesize - 1);
|
||||
ops.ooblen = len;
|
||||
ops.oobbuf = buf;
|
||||
ops.datbuf = NULL;
|
||||
ops.len = len;
|
||||
|
||||
res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
|
||||
*retlen = ops.retlen;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write oob data to flash
|
||||
*/
|
||||
int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
size_t *retlen, uint8_t *buf)
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
int res;
|
||||
|
||||
ops.mode = MTD_OOB_PLACE;
|
||||
ops.ooboffs = offs & (mtd->writesize - 1);
|
||||
ops.ooblen = len;
|
||||
ops.oobbuf = buf;
|
||||
ops.datbuf = NULL;
|
||||
ops.len = len;
|
||||
|
||||
res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
|
||||
*retlen = ops.retlen;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write data and oob to flash
|
||||
*/
|
||||
static int inftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
size_t *retlen, uint8_t *buf, uint8_t *oob)
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
int res;
|
||||
|
||||
ops.mode = MTD_OOB_PLACE;
|
||||
ops.ooboffs = offs;
|
||||
ops.ooblen = mtd->oobsize;
|
||||
ops.oobbuf = oob;
|
||||
ops.datbuf = buf;
|
||||
ops.len = len;
|
||||
|
||||
res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
|
||||
*retlen = ops.retlen;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition.
|
||||
* This function is used when the give Virtual Unit Chain.
|
||||
@ -198,10 +260,11 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
|
||||
u16 BlockMap[MAX_SECTORS_PER_UNIT];
|
||||
unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
|
||||
unsigned int thisEUN, prevEUN, status;
|
||||
struct mtd_info *mtd = inftl->mbd.mtd;
|
||||
int block, silly;
|
||||
unsigned int targetEUN;
|
||||
struct inftl_oob oob;
|
||||
size_t retlen;
|
||||
size_t retlen;
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d,"
|
||||
"pending=%d)\n", inftl, thisVUC, pendingblock);
|
||||
@ -221,18 +284,18 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
|
||||
* Scan to find the Erase Unit which holds the actual data for each
|
||||
* 512-byte block within the Chain.
|
||||
*/
|
||||
silly = MAX_LOOPS;
|
||||
silly = MAX_LOOPS;
|
||||
while (thisEUN < inftl->nb_blocks) {
|
||||
for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) {
|
||||
if ((BlockMap[block] != 0xffff) || BlockDeleted[block])
|
||||
continue;
|
||||
|
||||
if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize)
|
||||
+ (block * SECTORSIZE), 16 , &retlen,
|
||||
(char *)&oob) < 0)
|
||||
if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
|
||||
+ (block * SECTORSIZE), 16, &retlen,
|
||||
(char *)&oob) < 0)
|
||||
status = SECTOR_IGNORE;
|
||||
else
|
||||
status = oob.b.Status | oob.b.Status1;
|
||||
status = oob.b.Status | oob.b.Status1;
|
||||
|
||||
switch(status) {
|
||||
case SECTOR_FREE:
|
||||
@ -282,29 +345,31 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* Copy only in non free block (free blocks can only
|
||||
* happen in case of media errors or deleted blocks).
|
||||
*/
|
||||
if (BlockMap[block] == BLOCK_NIL)
|
||||
continue;
|
||||
if (BlockMap[block] == BLOCK_NIL)
|
||||
continue;
|
||||
|
||||
ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize *
|
||||
BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE,
|
||||
&retlen, movebuf);
|
||||
if (ret < 0) {
|
||||
ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize *
|
||||
BlockMap[block]) + (block * SECTORSIZE),
|
||||
SECTORSIZE, &retlen, movebuf);
|
||||
ret = mtd->read(mtd, (inftl->EraseSize * BlockMap[block]) +
|
||||
(block * SECTORSIZE), SECTORSIZE, &retlen,
|
||||
movebuf);
|
||||
if (ret < 0 && ret != -EUCLEAN) {
|
||||
ret = mtd->read(mtd,
|
||||
(inftl->EraseSize * BlockMap[block]) +
|
||||
(block * SECTORSIZE), SECTORSIZE,
|
||||
&retlen, movebuf);
|
||||
if (ret != -EIO)
|
||||
DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "
|
||||
"away on retry?\n");
|
||||
}
|
||||
memset(&oob, 0xff, sizeof(struct inftl_oob));
|
||||
oob.b.Status = oob.b.Status1 = SECTOR_USED;
|
||||
MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
|
||||
(block * SECTORSIZE), SECTORSIZE, &retlen,
|
||||
movebuf, (char *)&oob, &inftl->oobinfo);
|
||||
DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "
|
||||
"away on retry?\n");
|
||||
}
|
||||
memset(&oob, 0xff, sizeof(struct inftl_oob));
|
||||
oob.b.Status = oob.b.Status1 = SECTOR_USED;
|
||||
|
||||
inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
|
||||
(block * SECTORSIZE), SECTORSIZE, &retlen,
|
||||
movebuf, (char *)&oob);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -329,17 +394,17 @@ static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned
|
||||
if (thisEUN == targetEUN)
|
||||
break;
|
||||
|
||||
if (INFTL_formatblock(inftl, thisEUN) < 0) {
|
||||
if (INFTL_formatblock(inftl, thisEUN) < 0) {
|
||||
/*
|
||||
* Could not erase : mark block as reserved.
|
||||
*/
|
||||
inftl->PUtable[thisEUN] = BLOCK_RESERVED;
|
||||
} else {
|
||||
} else {
|
||||
/* Correctly erased : mark it as free */
|
||||
inftl->PUtable[thisEUN] = BLOCK_FREE;
|
||||
inftl->PUtable[prevEUN] = BLOCK_NIL;
|
||||
inftl->numfreeEUNs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return targetEUN;
|
||||
@ -415,6 +480,7 @@ static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
|
||||
unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE);
|
||||
unsigned int thisEUN, writeEUN, prev_block, status;
|
||||
unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1);
|
||||
struct mtd_info *mtd = inftl->mbd.mtd;
|
||||
struct inftl_oob oob;
|
||||
struct inftl_bci bci;
|
||||
unsigned char anac, nacs, parity;
|
||||
@ -434,10 +500,10 @@ static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
|
||||
silly = MAX_LOOPS;
|
||||
|
||||
while (thisEUN <= inftl->lastEUN) {
|
||||
MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
|
||||
blockofs, 8, &retlen, (char *)&bci);
|
||||
inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
|
||||
blockofs, 8, &retlen, (char *)&bci);
|
||||
|
||||
status = bci.Status | bci.Status1;
|
||||
status = bci.Status | bci.Status1;
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in "
|
||||
"EUN %d is %x\n", block , writeEUN, status);
|
||||
|
||||
@ -522,8 +588,8 @@ hitused:
|
||||
nacs = 0;
|
||||
thisEUN = inftl->VUtable[thisVUC];
|
||||
if (thisEUN != BLOCK_NIL) {
|
||||
MTD_READOOB(inftl->mbd.mtd, thisEUN * inftl->EraseSize
|
||||
+ 8, 8, &retlen, (char *)&oob.u);
|
||||
inftl_read_oob(mtd, thisEUN * inftl->EraseSize
|
||||
+ 8, 8, &retlen, (char *)&oob.u);
|
||||
anac = oob.u.a.ANAC + 1;
|
||||
nacs = oob.u.a.NACs + 1;
|
||||
}
|
||||
@ -544,8 +610,8 @@ hitused:
|
||||
oob.u.a.parityPerField = parity;
|
||||
oob.u.a.discarded = 0xaa;
|
||||
|
||||
MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + 8, 8,
|
||||
&retlen, (char *)&oob.u);
|
||||
inftl_write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8,
|
||||
&retlen, (char *)&oob.u);
|
||||
|
||||
/* Also back up header... */
|
||||
oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC);
|
||||
@ -555,8 +621,8 @@ hitused:
|
||||
oob.u.b.parityPerField = parity;
|
||||
oob.u.b.discarded = 0xaa;
|
||||
|
||||
MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize +
|
||||
SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
|
||||
inftl_write_oob(mtd, writeEUN * inftl->EraseSize +
|
||||
SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
|
||||
|
||||
inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC];
|
||||
inftl->VUtable[thisVUC] = writeEUN;
|
||||
@ -576,6 +642,7 @@ hitused:
|
||||
*/
|
||||
static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
|
||||
{
|
||||
struct mtd_info *mtd = inftl->mbd.mtd;
|
||||
unsigned char BlockUsed[MAX_SECTORS_PER_UNIT];
|
||||
unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
|
||||
unsigned int thisEUN, status;
|
||||
@ -606,9 +673,9 @@ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
|
||||
if (BlockUsed[block] || BlockDeleted[block])
|
||||
continue;
|
||||
|
||||
if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize)
|
||||
+ (block * SECTORSIZE), 8 , &retlen,
|
||||
(char *)&bci) < 0)
|
||||
if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
|
||||
+ (block * SECTORSIZE), 8 , &retlen,
|
||||
(char *)&bci) < 0)
|
||||
status = SECTOR_IGNORE;
|
||||
else
|
||||
status = bci.Status | bci.Status1;
|
||||
@ -670,12 +737,12 @@ static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "Deleting EUN %d from VUC %d\n",
|
||||
thisEUN, thisVUC);
|
||||
|
||||
if (INFTL_formatblock(inftl, thisEUN) < 0) {
|
||||
if (INFTL_formatblock(inftl, thisEUN) < 0) {
|
||||
/*
|
||||
* Could not erase : mark block as reserved.
|
||||
*/
|
||||
inftl->PUtable[thisEUN] = BLOCK_RESERVED;
|
||||
} else {
|
||||
} else {
|
||||
/* Correctly erased : mark it as free */
|
||||
inftl->PUtable[thisEUN] = BLOCK_FREE;
|
||||
inftl->numfreeEUNs++;
|
||||
@ -697,6 +764,7 @@ static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block)
|
||||
{
|
||||
unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
|
||||
unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
|
||||
struct mtd_info *mtd = inftl->mbd.mtd;
|
||||
unsigned int status;
|
||||
int silly = MAX_LOOPS;
|
||||
size_t retlen;
|
||||
@ -706,8 +774,8 @@ static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block)
|
||||
"block=%d)\n", inftl, block);
|
||||
|
||||
while (thisEUN < inftl->nb_blocks) {
|
||||
if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
|
||||
blockofs, 8, &retlen, (char *)&bci) < 0)
|
||||
if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
|
||||
blockofs, 8, &retlen, (char *)&bci) < 0)
|
||||
status = SECTOR_IGNORE;
|
||||
else
|
||||
status = bci.Status | bci.Status1;
|
||||
@ -741,10 +809,10 @@ foundit:
|
||||
if (thisEUN != BLOCK_NIL) {
|
||||
loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
|
||||
|
||||
if (MTD_READOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0)
|
||||
if (inftl_read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
|
||||
return -EIO;
|
||||
bci.Status = bci.Status1 = SECTOR_DELETED;
|
||||
if (MTD_WRITEOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0)
|
||||
if (inftl_write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
|
||||
return -EIO;
|
||||
INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE));
|
||||
}
|
||||
@ -784,9 +852,10 @@ static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
|
||||
|
||||
memset(&oob, 0xff, sizeof(struct inftl_oob));
|
||||
oob.b.Status = oob.b.Status1 = SECTOR_USED;
|
||||
MTD_WRITEECC(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
|
||||
blockofs, SECTORSIZE, &retlen, (char *)buffer,
|
||||
(char *)&oob, &inftl->oobinfo);
|
||||
|
||||
inftl_write(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
|
||||
blockofs, SECTORSIZE, &retlen, (char *)buffer,
|
||||
(char *)&oob);
|
||||
/*
|
||||
* need to write SECTOR_USED flags since they are not written
|
||||
* in mtd_writeecc
|
||||
@ -804,17 +873,18 @@ static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
|
||||
struct INFTLrecord *inftl = (void *)mbd;
|
||||
unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
|
||||
unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
|
||||
unsigned int status;
|
||||
struct mtd_info *mtd = inftl->mbd.mtd;
|
||||
unsigned int status;
|
||||
int silly = MAX_LOOPS;
|
||||
struct inftl_bci bci;
|
||||
struct inftl_bci bci;
|
||||
size_t retlen;
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=%p,block=%ld,"
|
||||
"buffer=%p)\n", inftl, block, buffer);
|
||||
|
||||
while (thisEUN < inftl->nb_blocks) {
|
||||
if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
|
||||
blockofs, 8, &retlen, (char *)&bci) < 0)
|
||||
if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
|
||||
blockofs, 8, &retlen, (char *)&bci) < 0)
|
||||
status = SECTOR_IGNORE;
|
||||
else
|
||||
status = bci.Status | bci.Status1;
|
||||
@ -850,10 +920,12 @@ foundit:
|
||||
/* The requested block is not on the media, return all 0x00 */
|
||||
memset(buffer, 0, SECTORSIZE);
|
||||
} else {
|
||||
size_t retlen;
|
||||
size_t retlen;
|
||||
loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
|
||||
if (MTD_READ(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen,
|
||||
buffer))
|
||||
int ret = mtd->read(mtd, ptr, SECTORSIZE, &retlen, buffer);
|
||||
|
||||
/* Handle corrected bit flips gracefully */
|
||||
if (ret < 0 && ret != -EUCLEAN)
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
|
@ -43,6 +43,11 @@
|
||||
|
||||
char inftlmountrev[]="$Revision: 1.18 $";
|
||||
|
||||
extern int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
size_t *retlen, uint8_t *buf);
|
||||
extern int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
size_t *retlen, uint8_t *buf);
|
||||
|
||||
/*
|
||||
* find_boot_record: Find the INFTL Media Header and its Spare copy which
|
||||
* contains the various device information of the INFTL partition and
|
||||
@ -57,6 +62,7 @@ static int find_boot_record(struct INFTLrecord *inftl)
|
||||
unsigned int i, block;
|
||||
u8 buf[SECTORSIZE];
|
||||
struct INFTLMediaHeader *mh = &inftl->MediaHdr;
|
||||
struct mtd_info *mtd = inftl->mbd.mtd;
|
||||
struct INFTLPartition *ip;
|
||||
size_t retlen;
|
||||
|
||||
@ -80,8 +86,8 @@ static int find_boot_record(struct INFTLrecord *inftl)
|
||||
* Check for BNAND header first. Then whinge if it's found
|
||||
* but later checks fail.
|
||||
*/
|
||||
ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize,
|
||||
SECTORSIZE, &retlen, buf);
|
||||
ret = mtd->read(mtd, block * inftl->EraseSize,
|
||||
SECTORSIZE, &retlen, buf);
|
||||
/* We ignore ret in case the ECC of the MediaHeader is invalid
|
||||
(which is apparently acceptable) */
|
||||
if (retlen != SECTORSIZE) {
|
||||
@ -106,8 +112,9 @@ static int find_boot_record(struct INFTLrecord *inftl)
|
||||
}
|
||||
|
||||
/* To be safer with BIOS, also use erase mark as discriminant */
|
||||
if ((ret = MTD_READOOB(inftl->mbd.mtd, block * inftl->EraseSize +
|
||||
SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0)) {
|
||||
if ((ret = inftl_read_oob(mtd, block * inftl->EraseSize +
|
||||
SECTORSIZE + 8, 8, &retlen,
|
||||
(char *)&h1) < 0)) {
|
||||
printk(KERN_WARNING "INFTL: ANAND header found at "
|
||||
"0x%x in mtd%d, but OOB data read failed "
|
||||
"(err %d)\n", block * inftl->EraseSize,
|
||||
@ -123,8 +130,8 @@ static int find_boot_record(struct INFTLrecord *inftl)
|
||||
memcpy(mh, buf, sizeof(struct INFTLMediaHeader));
|
||||
|
||||
/* Read the spare media header at offset 4096 */
|
||||
MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize + 4096,
|
||||
SECTORSIZE, &retlen, buf);
|
||||
mtd->read(mtd, block * inftl->EraseSize + 4096,
|
||||
SECTORSIZE, &retlen, buf);
|
||||
if (retlen != SECTORSIZE) {
|
||||
printk(KERN_WARNING "INFTL: Unable to read spare "
|
||||
"Media Header\n");
|
||||
@ -233,7 +240,7 @@ static int find_boot_record(struct INFTLrecord *inftl)
|
||||
*/
|
||||
instr->addr = ip->Reserved0 * inftl->EraseSize;
|
||||
instr->len = inftl->EraseSize;
|
||||
MTD_ERASE(inftl->mbd.mtd, instr);
|
||||
mtd->erase(mtd, instr);
|
||||
}
|
||||
if ((ip->lastUnit - ip->firstUnit + 1) < ip->virtualUnits) {
|
||||
printk(KERN_WARNING "INFTL: Media Header "
|
||||
@ -350,21 +357,21 @@ static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address,
|
||||
int len, int check_oob)
|
||||
{
|
||||
u8 buf[SECTORSIZE + inftl->mbd.mtd->oobsize];
|
||||
struct mtd_info *mtd = inftl->mbd.mtd;
|
||||
size_t retlen;
|
||||
int i;
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=%p,"
|
||||
"address=0x%x,len=%d,check_oob=%d)\n", inftl,
|
||||
address, len, check_oob);
|
||||
|
||||
for (i = 0; i < len; i += SECTORSIZE) {
|
||||
if (MTD_READECC(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &inftl->oobinfo) < 0)
|
||||
if (mtd->read(mtd, address, SECTORSIZE, &retlen, buf))
|
||||
return -1;
|
||||
if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
|
||||
return -1;
|
||||
|
||||
if (check_oob) {
|
||||
if (memcmpb(buf + SECTORSIZE, 0xff, inftl->mbd.mtd->oobsize) != 0)
|
||||
if(inftl_read_oob(mtd, address, mtd->oobsize,
|
||||
&retlen, &buf[SECTORSIZE]) < 0)
|
||||
return -1;
|
||||
if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0)
|
||||
return -1;
|
||||
}
|
||||
address += SECTORSIZE;
|
||||
@ -387,6 +394,7 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block)
|
||||
size_t retlen;
|
||||
struct inftl_unittail uci;
|
||||
struct erase_info *instr = &inftl->instr;
|
||||
struct mtd_info *mtd = inftl->mbd.mtd;
|
||||
int physblock;
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=%p,"
|
||||
@ -404,8 +412,9 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block)
|
||||
/* Erase one physical eraseblock at a time, even though the NAND api
|
||||
allows us to group them. This way we if we have a failure, we can
|
||||
mark only the failed block in the bbt. */
|
||||
for (physblock = 0; physblock < inftl->EraseSize; physblock += instr->len, instr->addr += instr->len) {
|
||||
MTD_ERASE(inftl->mbd.mtd, instr);
|
||||
for (physblock = 0; physblock < inftl->EraseSize;
|
||||
physblock += instr->len, instr->addr += instr->len) {
|
||||
mtd->erase(inftl->mbd.mtd, instr);
|
||||
|
||||
if (instr->state == MTD_ERASE_FAILED) {
|
||||
printk(KERN_WARNING "INFTL: error while formatting block %d\n",
|
||||
@ -414,10 +423,10 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block)
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the "freeness" of Erase Unit before updating metadata.
|
||||
* FixMe: is this check really necessary? Since we have check the
|
||||
* return code after the erase operation.
|
||||
*/
|
||||
* Check the "freeness" of Erase Unit before updating metadata.
|
||||
* FixMe: is this check really necessary? Since we have check
|
||||
* the return code after the erase operation.
|
||||
*/
|
||||
if (check_free_sectors(inftl, instr->addr, instr->len, 1) != 0)
|
||||
goto fail;
|
||||
}
|
||||
@ -429,8 +438,7 @@ int INFTL_formatblock(struct INFTLrecord *inftl, int block)
|
||||
uci.Reserved[2] = 0;
|
||||
uci.Reserved[3] = 0;
|
||||
instr->addr = block * inftl->EraseSize + SECTORSIZE * 2;
|
||||
if (MTD_WRITEOOB(inftl->mbd.mtd, instr->addr +
|
||||
8, 8, &retlen, (char *)&uci) < 0)
|
||||
if (inftl_write_oob(mtd, instr->addr + 8, 8, &retlen, (char *)&uci) < 0)
|
||||
goto fail;
|
||||
return 0;
|
||||
fail:
|
||||
@ -549,6 +557,7 @@ void INFTL_dumpVUchains(struct INFTLrecord *s)
|
||||
|
||||
int INFTL_mount(struct INFTLrecord *s)
|
||||
{
|
||||
struct mtd_info *mtd = s->mbd.mtd;
|
||||
unsigned int block, first_block, prev_block, last_block;
|
||||
unsigned int first_logical_block, logical_block, erase_mark;
|
||||
int chain_length, do_format_chain;
|
||||
@ -607,10 +616,11 @@ int INFTL_mount(struct INFTLrecord *s)
|
||||
break;
|
||||
}
|
||||
|
||||
if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8,
|
||||
8, &retlen, (char *)&h0) < 0 ||
|
||||
MTD_READOOB(s->mbd.mtd, block * s->EraseSize +
|
||||
2 * SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) {
|
||||
if (inftl_read_oob(mtd, block * s->EraseSize + 8,
|
||||
8, &retlen, (char *)&h0) < 0 ||
|
||||
inftl_read_oob(mtd, block * s->EraseSize +
|
||||
2 * SECTORSIZE + 8, 8, &retlen,
|
||||
(char *)&h1) < 0) {
|
||||
/* Should never happen? */
|
||||
do_format_chain++;
|
||||
break;
|
||||
|
@ -37,7 +37,7 @@ config MTD_PHYSMAP_START
|
||||
config MTD_PHYSMAP_LEN
|
||||
hex "Physical length of flash mapping"
|
||||
depends on MTD_PHYSMAP
|
||||
default "0x4000000"
|
||||
default "0"
|
||||
help
|
||||
This is the total length of the mapping of the flash chips on
|
||||
your particular board. If there is space, or aliases, in the
|
||||
@ -78,7 +78,7 @@ config MTD_PNC2000
|
||||
|
||||
config MTD_SC520CDP
|
||||
tristate "CFI Flash device mapped on AMD SC520 CDP"
|
||||
depends on X86 && MTD_CFI
|
||||
depends on X86 && MTD_CFI && MTD_CONCAT
|
||||
help
|
||||
The SC520 CDP board has two banks of CFI-compliant chips and one
|
||||
Dual-in-line JEDEC chip. This 'mapping' driver supports that
|
||||
@ -109,7 +109,7 @@ config MTD_TS5500
|
||||
mtd1 allows you to reprogram your BIOS. BE VERY CAREFUL.
|
||||
|
||||
Note that jumper 3 ("Write Enable Drive A") must be set
|
||||
otherwise detection won't succeeed.
|
||||
otherwise detection won't succeed.
|
||||
|
||||
config MTD_SBC_GXX
|
||||
tristate "CFI Flash device mapped on Arcom SBC-GXx boards"
|
||||
@ -200,8 +200,8 @@ config MTD_TSUNAMI
|
||||
Support for the flash chip on Tsunami TIG bus.
|
||||
|
||||
config MTD_LASAT
|
||||
tristate "Flash chips on LASAT board"
|
||||
depends on LASAT
|
||||
tristate "LASAT flash device"
|
||||
depends on LASAT && MTD_CFI
|
||||
help
|
||||
Support for the flash chips on the Lasat 100 and 200 boards.
|
||||
|
||||
@ -561,7 +561,6 @@ config MTD_PCMCIA
|
||||
config MTD_PCMCIA_ANONYMOUS
|
||||
bool "Use PCMCIA MTD drivers for anonymous PCMCIA cards"
|
||||
depends on MTD_PCMCIA
|
||||
default N
|
||||
help
|
||||
If this option is enabled, PCMCIA cards which do not report
|
||||
anything about themselves are assumed to be MTD cards.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2001 Flaga hf. Medical Devices, Kári Davíðsson <kd@flaga.is>
|
||||
* Copyright © 2001 Flaga hf. Medical Devices, Kári Davíðsson <kd@flaga.is>
|
||||
*
|
||||
* $Id: cfi_flagadm.c,v 1.15 2005/11/07 11:14:26 gleixner Exp $
|
||||
*
|
||||
@ -135,5 +135,5 @@ module_exit(cleanup_flagadm);
|
||||
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>");
|
||||
MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>");
|
||||
MODULE_DESCRIPTION("MTD map driver for Flaga digital module");
|
||||
|
@ -122,5 +122,5 @@ module_exit(cleanup_dbox2_flash);
|
||||
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>, Bastian Blank <waldi@tuxbox.org>, Alexander Wild <wild@te-elektronik.com>");
|
||||
MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>, Bastian Blank <waldi@tuxbox.org>, Alexander Wild <wild@te-elektronik.com>");
|
||||
MODULE_DESCRIPTION("MTD map driver for D-Box 2 board");
|
||||
|
@ -4,7 +4,7 @@
|
||||
* $Id: mtx-1_flash.c,v 1.2 2005/11/07 11:14:27 gleixner Exp $
|
||||
*
|
||||
* (C) 2005 Bruno Randolf <bruno.randolf@4g-systems.biz>
|
||||
* (C) 2005 Jörn Engel <joern@wohnheim.fh-wedel.de>
|
||||
* (C) 2005 Jörn Engel <joern@wohnheim.fh-wedel.de>
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/cfi.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/root_dev.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/****************************************************************************/
|
||||
@ -188,7 +190,7 @@ int nettel_eraseconfig(void)
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
add_wait_queue(&wait_q, &wait);
|
||||
|
||||
ret = MTD_ERASE(mtd, &nettel_erase);
|
||||
ret = mtd->erase(mtd, &nettel_erase);
|
||||
if (ret) {
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&wait_q, &wait);
|
||||
|
@ -713,6 +713,7 @@ static void pcmciamtd_detach(struct pcmcia_device *link)
|
||||
|
||||
if(dev->mtd_info) {
|
||||
del_mtd_device(dev->mtd_info);
|
||||
map_destroy(dev->mtd_info);
|
||||
info("mtd%d: Removed", dev->mtd_info->index);
|
||||
}
|
||||
|
||||
|
@ -14,113 +14,230 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/map.h>
|
||||
#include <linux/config.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/physmap.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static struct mtd_info *mymtd;
|
||||
|
||||
struct map_info physmap_map = {
|
||||
.name = "phys_mapped_flash",
|
||||
.phys = CONFIG_MTD_PHYSMAP_START,
|
||||
.size = CONFIG_MTD_PHYSMAP_LEN,
|
||||
.bankwidth = CONFIG_MTD_PHYSMAP_BANKWIDTH,
|
||||
struct physmap_flash_info {
|
||||
struct mtd_info *mtd;
|
||||
struct map_info map;
|
||||
struct resource *res;
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
int nr_parts;
|
||||
struct mtd_partition *parts;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
static struct mtd_partition *mtd_parts;
|
||||
static int mtd_parts_nb;
|
||||
|
||||
static int num_physmap_partitions;
|
||||
static struct mtd_partition *physmap_partitions;
|
||||
|
||||
static const char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL};
|
||||
|
||||
void physmap_set_partitions(struct mtd_partition *parts, int num_parts)
|
||||
static int physmap_flash_remove(struct platform_device *dev)
|
||||
{
|
||||
physmap_partitions=parts;
|
||||
num_physmap_partitions=num_parts;
|
||||
}
|
||||
#endif /* CONFIG_MTD_PARTITIONS */
|
||||
struct physmap_flash_info *info;
|
||||
struct physmap_flash_data *physmap_data;
|
||||
|
||||
static int __init init_physmap(void)
|
||||
{
|
||||
static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL };
|
||||
const char **type;
|
||||
info = platform_get_drvdata(dev);
|
||||
if (info == NULL)
|
||||
return 0;
|
||||
platform_set_drvdata(dev, NULL);
|
||||
|
||||
printk(KERN_NOTICE "physmap flash device: %lx at %lx\n", physmap_map.size, physmap_map.phys);
|
||||
physmap_map.virt = ioremap(physmap_map.phys, physmap_map.size);
|
||||
|
||||
if (!physmap_map.virt) {
|
||||
printk("Failed to ioremap\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
simple_map_init(&physmap_map);
|
||||
|
||||
mymtd = NULL;
|
||||
type = rom_probe_types;
|
||||
for(; !mymtd && *type; type++) {
|
||||
mymtd = do_map_probe(*type, &physmap_map);
|
||||
}
|
||||
if (mymtd) {
|
||||
mymtd->owner = THIS_MODULE;
|
||||
physmap_data = dev->dev.platform_data;
|
||||
|
||||
if (info->mtd != NULL) {
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
mtd_parts_nb = parse_mtd_partitions(mymtd, part_probes,
|
||||
&mtd_parts, 0);
|
||||
|
||||
if (mtd_parts_nb > 0)
|
||||
{
|
||||
add_mtd_partitions (mymtd, mtd_parts, mtd_parts_nb);
|
||||
return 0;
|
||||
if (info->nr_parts) {
|
||||
del_mtd_partitions(info->mtd);
|
||||
kfree(info->parts);
|
||||
} else if (physmap_data->nr_parts) {
|
||||
del_mtd_partitions(info->mtd);
|
||||
} else {
|
||||
del_mtd_device(info->mtd);
|
||||
}
|
||||
|
||||
if (num_physmap_partitions != 0)
|
||||
{
|
||||
printk(KERN_NOTICE
|
||||
"Using physmap partition definition\n");
|
||||
add_mtd_partitions (mymtd, physmap_partitions, num_physmap_partitions);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
del_mtd_device(info->mtd);
|
||||
#endif
|
||||
add_mtd_device(mymtd);
|
||||
map_destroy(info->mtd);
|
||||
}
|
||||
|
||||
if (info->map.virt != NULL)
|
||||
iounmap((void *)info->map.virt);
|
||||
|
||||
if (info->res != NULL) {
|
||||
release_resource(info->res);
|
||||
kfree(info->res);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL };
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
static const char *part_probe_types[] = { "cmdlinepart", "RedBoot", NULL };
|
||||
#endif
|
||||
|
||||
static int physmap_flash_probe(struct platform_device *dev)
|
||||
{
|
||||
struct physmap_flash_data *physmap_data;
|
||||
struct physmap_flash_info *info;
|
||||
const char **probe_type;
|
||||
int err;
|
||||
|
||||
physmap_data = dev->dev.platform_data;
|
||||
if (physmap_data == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n",
|
||||
(unsigned long long)dev->resource->end - dev->resource->start + 1,
|
||||
(unsigned long long)dev->resource->start);
|
||||
|
||||
info = kmalloc(sizeof(struct physmap_flash_info), GFP_KERNEL);
|
||||
if (info == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
platform_set_drvdata(dev, info);
|
||||
|
||||
info->res = request_mem_region(dev->resource->start,
|
||||
dev->resource->end - dev->resource->start + 1,
|
||||
dev->dev.bus_id);
|
||||
if (info->res == NULL) {
|
||||
dev_err(&dev->dev, "Could not reserve memory region\n");
|
||||
err = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
info->map.name = dev->dev.bus_id;
|
||||
info->map.phys = dev->resource->start;
|
||||
info->map.size = dev->resource->end - dev->resource->start + 1;
|
||||
info->map.bankwidth = physmap_data->width;
|
||||
info->map.set_vpp = physmap_data->set_vpp;
|
||||
|
||||
info->map.virt = ioremap(info->map.phys, info->map.size);
|
||||
if (info->map.virt == NULL) {
|
||||
dev_err(&dev->dev, "Failed to ioremap flash region\n");
|
||||
err = EIO;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
simple_map_init(&info->map);
|
||||
|
||||
probe_type = rom_probe_types;
|
||||
for (; info->mtd == NULL && *probe_type != NULL; probe_type++)
|
||||
info->mtd = do_map_probe(*probe_type, &info->map);
|
||||
if (info->mtd == NULL) {
|
||||
dev_err(&dev->dev, "map_probe failed\n");
|
||||
err = -ENXIO;
|
||||
goto err_out;
|
||||
}
|
||||
info->mtd->owner = THIS_MODULE;
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
err = parse_mtd_partitions(info->mtd, part_probe_types, &info->parts, 0);
|
||||
if (err > 0) {
|
||||
add_mtd_partitions(info->mtd, info->parts, err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
iounmap(physmap_map.virt);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static void __exit cleanup_physmap(void)
|
||||
{
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
if (mtd_parts_nb) {
|
||||
del_mtd_partitions(mymtd);
|
||||
kfree(mtd_parts);
|
||||
} else if (num_physmap_partitions) {
|
||||
del_mtd_partitions(mymtd);
|
||||
} else {
|
||||
del_mtd_device(mymtd);
|
||||
if (physmap_data->nr_parts) {
|
||||
printk(KERN_NOTICE "Using physmap partition information\n");
|
||||
add_mtd_partitions(info->mtd, physmap_data->parts,
|
||||
physmap_data->nr_parts);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
del_mtd_device(mymtd);
|
||||
#endif
|
||||
map_destroy(mymtd);
|
||||
|
||||
iounmap(physmap_map.virt);
|
||||
physmap_map.virt = NULL;
|
||||
add_mtd_device(info->mtd);
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
physmap_flash_remove(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
module_init(init_physmap);
|
||||
module_exit(cleanup_physmap);
|
||||
static struct platform_driver physmap_flash_driver = {
|
||||
.probe = physmap_flash_probe,
|
||||
.remove = physmap_flash_remove,
|
||||
.driver = {
|
||||
.name = "physmap-flash",
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
#ifdef CONFIG_MTD_PHYSMAP_LEN
|
||||
#if CONFIG_MTD_PHYSMAP_LEN != 0
|
||||
#warning using PHYSMAP compat code
|
||||
#define PHYSMAP_COMPAT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef PHYSMAP_COMPAT
|
||||
static struct physmap_flash_data physmap_flash_data = {
|
||||
.width = CONFIG_MTD_PHYSMAP_BANKWIDTH,
|
||||
};
|
||||
|
||||
static struct resource physmap_flash_resource = {
|
||||
.start = CONFIG_MTD_PHYSMAP_START,
|
||||
.end = CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN,
|
||||
.flags = IORESOURCE_MEM,
|
||||
};
|
||||
|
||||
static struct platform_device physmap_flash = {
|
||||
.name = "physmap-flash",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.platform_data = &physmap_flash_data,
|
||||
},
|
||||
.num_resources = 1,
|
||||
.resource = &physmap_flash_resource,
|
||||
};
|
||||
|
||||
void physmap_configure(unsigned long addr, unsigned long size,
|
||||
int bankwidth, void (*set_vpp)(struct map_info *, int))
|
||||
{
|
||||
physmap_flash_resource.start = addr;
|
||||
physmap_flash_resource.end = addr + size - 1;
|
||||
physmap_flash_data.width = bankwidth;
|
||||
physmap_flash_data.set_vpp = set_vpp;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
void physmap_set_partitions(struct mtd_partition *parts, int num_parts)
|
||||
{
|
||||
physmap_flash_data.nr_parts = num_parts;
|
||||
physmap_flash_data.parts = parts;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static int __init physmap_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = platform_driver_register(&physmap_flash_driver);
|
||||
#ifdef PHYSMAP_COMPAT
|
||||
if (err == 0)
|
||||
platform_device_register(&physmap_flash);
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit physmap_exit(void)
|
||||
{
|
||||
#ifdef PHYSMAP_COMPAT
|
||||
platform_device_unregister(&physmap_flash);
|
||||
#endif
|
||||
platform_driver_unregister(&physmap_flash_driver);
|
||||
}
|
||||
|
||||
module_init(physmap_init);
|
||||
module_exit(physmap_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
|
||||
MODULE_DESCRIPTION("Generic configurable MTD map driver");
|
||||
|
@ -71,7 +71,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos,
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
add_wait_queue(&wait_q, &wait);
|
||||
|
||||
ret = MTD_ERASE(mtd, &erase);
|
||||
ret = mtd->erase(mtd, &erase);
|
||||
if (ret) {
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&wait_q, &wait);
|
||||
@ -88,7 +88,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos,
|
||||
* Next, writhe data to flash.
|
||||
*/
|
||||
|
||||
ret = MTD_WRITE (mtd, pos, len, &retlen, buf);
|
||||
ret = mtd->write(mtd, pos, len, &retlen, buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (retlen != len)
|
||||
@ -138,7 +138,7 @@ static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos,
|
||||
mtd->name, pos, len);
|
||||
|
||||
if (!sect_size)
|
||||
return MTD_WRITE (mtd, pos, len, &retlen, buf);
|
||||
return mtd->write(mtd, pos, len, &retlen, buf);
|
||||
|
||||
while (len > 0) {
|
||||
unsigned long sect_start = (pos/sect_size)*sect_size;
|
||||
@ -170,7 +170,8 @@ static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos,
|
||||
mtdblk->cache_offset != sect_start) {
|
||||
/* fill the cache with the current sector */
|
||||
mtdblk->cache_state = STATE_EMPTY;
|
||||
ret = MTD_READ(mtd, sect_start, sect_size, &retlen, mtdblk->cache_data);
|
||||
ret = mtd->read(mtd, sect_start, sect_size,
|
||||
&retlen, mtdblk->cache_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (retlen != sect_size)
|
||||
@ -207,7 +208,7 @@ static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos,
|
||||
mtd->name, pos, len);
|
||||
|
||||
if (!sect_size)
|
||||
return MTD_READ (mtd, pos, len, &retlen, buf);
|
||||
return mtd->read(mtd, pos, len, &retlen, buf);
|
||||
|
||||
while (len > 0) {
|
||||
unsigned long sect_start = (pos/sect_size)*sect_size;
|
||||
@ -226,7 +227,7 @@ static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos,
|
||||
mtdblk->cache_offset == sect_start) {
|
||||
memcpy (buf, mtdblk->cache_data + offset, size);
|
||||
} else {
|
||||
ret = MTD_READ (mtd, pos, size, &retlen, buf);
|
||||
ret = mtd->read(mtd, pos, size, &retlen, buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (retlen != size)
|
||||
@ -288,8 +289,7 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd)
|
||||
|
||||
mutex_init(&mtdblk->cache_mutex);
|
||||
mtdblk->cache_state = STATE_EMPTY;
|
||||
if ((mtdblk->mtd->flags & MTD_CAP_RAM) != MTD_CAP_RAM &&
|
||||
mtdblk->mtd->erasesize) {
|
||||
if ( !(mtdblk->mtd->flags & MTD_NO_ERASE) && mtdblk->mtd->erasesize) {
|
||||
mtdblk->cache_size = mtdblk->mtd->erasesize;
|
||||
mtdblk->cache_data = NULL;
|
||||
}
|
||||
|
@ -45,9 +45,7 @@ static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
|
||||
dev->blksize = 512;
|
||||
dev->size = mtd->size >> 9;
|
||||
dev->tr = tr;
|
||||
if ((mtd->flags & (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEABLE)) !=
|
||||
(MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEABLE))
|
||||
dev->readonly = 1;
|
||||
dev->readonly = 1;
|
||||
|
||||
add_mtd_blktrans_dev(dev);
|
||||
}
|
||||
|
@ -49,24 +49,18 @@ static struct mtd_notifier notifier = {
|
||||
};
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* Data structure to hold the pointer to the mtd device as well
|
||||
* as mode information ofr various use cases.
|
||||
*/
|
||||
|
||||
#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)
|
||||
struct mtd_file_info {
|
||||
struct mtd_info *mtd;
|
||||
enum mtd_file_modes mode;
|
||||
};
|
||||
|
||||
static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
|
||||
{
|
||||
struct mtd_info *mtd = TO_MTD(file);
|
||||
struct mtd_file_info *mfi = file->private_data;
|
||||
struct mtd_info *mtd = mfi->mtd;
|
||||
|
||||
switch (orig) {
|
||||
case 0:
|
||||
@ -97,6 +91,7 @@ static int mtd_open(struct inode *inode, struct file *file)
|
||||
int minor = iminor(inode);
|
||||
int devnum = minor >> 1;
|
||||
struct mtd_info *mtd;
|
||||
struct mtd_file_info *mfi;
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n");
|
||||
|
||||
@ -117,14 +112,20 @@ static int mtd_open(struct inode *inode, struct file *file)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
file->private_data = mtd;
|
||||
|
||||
/* You can't open it RW if it's not a writeable device */
|
||||
if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) {
|
||||
put_mtd_device(mtd);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
mfi = kzalloc(sizeof(*mfi), GFP_KERNEL);
|
||||
if (!mfi) {
|
||||
put_mtd_device(mtd);
|
||||
return -ENOMEM;
|
||||
}
|
||||
mfi->mtd = mtd;
|
||||
file->private_data = mfi;
|
||||
|
||||
return 0;
|
||||
} /* mtd_open */
|
||||
|
||||
@ -132,16 +133,17 @@ static int mtd_open(struct inode *inode, struct file *file)
|
||||
|
||||
static int mtd_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct mtd_info *mtd;
|
||||
struct mtd_file_info *mfi = file->private_data;
|
||||
struct mtd_info *mtd = mfi->mtd;
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
|
||||
|
||||
mtd = TO_MTD(file);
|
||||
|
||||
if (mtd->sync)
|
||||
mtd->sync(mtd);
|
||||
|
||||
put_mtd_device(mtd);
|
||||
file->private_data = NULL;
|
||||
kfree(mfi);
|
||||
|
||||
return 0;
|
||||
} /* mtd_close */
|
||||
@ -153,7 +155,8 @@ 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 = TO_MTD(file);
|
||||
struct mtd_file_info *mfi = file->private_data;
|
||||
struct mtd_info *mtd = mfi->mtd;
|
||||
size_t retlen=0;
|
||||
size_t total_retlen=0;
|
||||
int ret=0;
|
||||
@ -170,36 +173,58 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t
|
||||
|
||||
/* FIXME: Use kiovec in 2.5 to lock down the user's buffers
|
||||
and pass them directly to the MTD functions */
|
||||
|
||||
if (count > MAX_KMALLOC_SIZE)
|
||||
kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL);
|
||||
else
|
||||
kbuf=kmalloc(count, GFP_KERNEL);
|
||||
|
||||
if (!kbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
while (count) {
|
||||
|
||||
if (count > MAX_KMALLOC_SIZE)
|
||||
len = MAX_KMALLOC_SIZE;
|
||||
else
|
||||
len = count;
|
||||
|
||||
kbuf=kmalloc(len,GFP_KERNEL);
|
||||
if (!kbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
switch (MTD_MODE(file)) {
|
||||
case MTD_MODE_OTP_FACT:
|
||||
switch (mfi->mode) {
|
||||
case MTD_MODE_OTP_FACTORY:
|
||||
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;
|
||||
case MTD_MODE_RAW:
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
|
||||
ops.mode = MTD_OOB_RAW;
|
||||
ops.datbuf = kbuf;
|
||||
ops.oobbuf = NULL;
|
||||
ops.len = len;
|
||||
|
||||
ret = mtd->read_oob(mtd, *ppos, &ops);
|
||||
retlen = ops.retlen;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf);
|
||||
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 !
|
||||
* For kernel internal usage it also might return -EUCLEAN
|
||||
* to signal the caller that a bitflip has occured and has
|
||||
* been corrected by the ECC algorithm.
|
||||
* Userspace software which accesses NAND this way
|
||||
* must be aware of the fact that it deals with NAND
|
||||
*/
|
||||
if (!ret || (ret == -EBADMSG)) {
|
||||
if (!ret || (ret == -EUCLEAN) || (ret == -EBADMSG)) {
|
||||
*ppos += retlen;
|
||||
if (copy_to_user(buf, kbuf, retlen)) {
|
||||
kfree(kbuf);
|
||||
kfree(kbuf);
|
||||
return -EFAULT;
|
||||
}
|
||||
else
|
||||
@ -215,15 +240,16 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t
|
||||
return ret;
|
||||
}
|
||||
|
||||
kfree(kbuf);
|
||||
}
|
||||
|
||||
kfree(kbuf);
|
||||
return total_retlen;
|
||||
} /* mtd_read */
|
||||
|
||||
static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos)
|
||||
{
|
||||
struct mtd_info *mtd = TO_MTD(file);
|
||||
struct mtd_file_info *mfi = file->private_data;
|
||||
struct mtd_info *mtd = mfi->mtd;
|
||||
char *kbuf;
|
||||
size_t retlen;
|
||||
size_t total_retlen=0;
|
||||
@ -241,25 +267,28 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
if (count > MAX_KMALLOC_SIZE)
|
||||
kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL);
|
||||
else
|
||||
kbuf=kmalloc(count, GFP_KERNEL);
|
||||
|
||||
if (!kbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
while (count) {
|
||||
|
||||
if (count > MAX_KMALLOC_SIZE)
|
||||
len = MAX_KMALLOC_SIZE;
|
||||
else
|
||||
len = count;
|
||||
|
||||
kbuf=kmalloc(len,GFP_KERNEL);
|
||||
if (!kbuf) {
|
||||
printk("kmalloc is null\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (copy_from_user(kbuf, buf, len)) {
|
||||
kfree(kbuf);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
switch (MTD_MODE(file)) {
|
||||
case MTD_MODE_OTP_FACT:
|
||||
switch (mfi->mode) {
|
||||
case MTD_MODE_OTP_FACTORY:
|
||||
ret = -EROFS;
|
||||
break;
|
||||
case MTD_MODE_OTP_USER:
|
||||
@ -269,6 +298,21 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count
|
||||
}
|
||||
ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
|
||||
break;
|
||||
|
||||
case MTD_MODE_RAW:
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
|
||||
ops.mode = MTD_OOB_RAW;
|
||||
ops.datbuf = kbuf;
|
||||
ops.oobbuf = NULL;
|
||||
ops.len = len;
|
||||
|
||||
ret = mtd->write_oob(mtd, *ppos, &ops);
|
||||
retlen = ops.retlen;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);
|
||||
}
|
||||
@ -282,10 +326,9 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count
|
||||
kfree(kbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
kfree(kbuf);
|
||||
}
|
||||
|
||||
kfree(kbuf);
|
||||
return total_retlen;
|
||||
} /* mtd_write */
|
||||
|
||||
@ -299,13 +342,45 @@ static void mtdchar_erase_callback (struct erase_info *instr)
|
||||
wake_up((wait_queue_head_t *)instr->priv);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP)
|
||||
static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
|
||||
{
|
||||
struct mtd_info *mtd = mfi->mtd;
|
||||
int ret = 0;
|
||||
|
||||
switch (mode) {
|
||||
case MTD_OTP_FACTORY:
|
||||
if (!mtd->read_fact_prot_reg)
|
||||
ret = -EOPNOTSUPP;
|
||||
else
|
||||
mfi->mode = MTD_MODE_OTP_FACTORY;
|
||||
break;
|
||||
case MTD_OTP_USER:
|
||||
if (!mtd->read_fact_prot_reg)
|
||||
ret = -EOPNOTSUPP;
|
||||
else
|
||||
mfi->mode = MTD_MODE_OTP_USER;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
case MTD_OTP_OFF:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
# define otp_select_filemode(f,m) -EOPNOTSUPP
|
||||
#endif
|
||||
|
||||
static int mtd_ioctl(struct inode *inode, struct file *file,
|
||||
u_int cmd, u_long arg)
|
||||
{
|
||||
struct mtd_info *mtd = TO_MTD(file);
|
||||
struct mtd_file_info *mfi = file->private_data;
|
||||
struct mtd_info *mtd = mfi->mtd;
|
||||
void __user *argp = (void __user *)arg;
|
||||
int ret = 0;
|
||||
u_long size;
|
||||
struct mtd_info_user info;
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n");
|
||||
|
||||
@ -341,7 +416,15 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
|
||||
}
|
||||
|
||||
case MEMGETINFO:
|
||||
if (copy_to_user(argp, mtd, sizeof(struct mtd_info_user)))
|
||||
info.type = mtd->type;
|
||||
info.flags = mtd->flags;
|
||||
info.size = mtd->size;
|
||||
info.erasesize = mtd->erasesize;
|
||||
info.writesize = mtd->writesize;
|
||||
info.oobsize = mtd->oobsize;
|
||||
info.ecctype = mtd->ecctype;
|
||||
info.eccsize = mtd->eccsize;
|
||||
if (copy_to_user(argp, &info, sizeof(struct mtd_info_user)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
|
||||
@ -400,8 +483,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
|
||||
case MEMWRITEOOB:
|
||||
{
|
||||
struct mtd_oob_buf buf;
|
||||
void *databuf;
|
||||
ssize_t retlen;
|
||||
struct mtd_oob_ops ops;
|
||||
|
||||
if(!(file->f_mode & 2))
|
||||
return -EPERM;
|
||||
@ -409,7 +491,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
|
||||
if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
|
||||
return -EFAULT;
|
||||
|
||||
if (buf.length > 0x4096)
|
||||
if (buf.length > 4096)
|
||||
return -EINVAL;
|
||||
|
||||
if (!mtd->write_oob)
|
||||
@ -421,21 +503,32 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
databuf = kmalloc(buf.length, GFP_KERNEL);
|
||||
if (!databuf)
|
||||
ops.len = buf.length;
|
||||
ops.ooblen = buf.length;
|
||||
ops.ooboffs = buf.start & (mtd->oobsize - 1);
|
||||
ops.datbuf = NULL;
|
||||
ops.mode = MTD_OOB_PLACE;
|
||||
|
||||
if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs))
|
||||
return -EINVAL;
|
||||
|
||||
ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
|
||||
if (!ops.oobbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(databuf, buf.ptr, buf.length)) {
|
||||
kfree(databuf);
|
||||
if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) {
|
||||
kfree(ops.oobbuf);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf);
|
||||
buf.start &= ~(mtd->oobsize - 1);
|
||||
ret = mtd->write_oob(mtd, buf.start, &ops);
|
||||
|
||||
if (copy_to_user(argp + sizeof(uint32_t), &retlen, sizeof(uint32_t)))
|
||||
if (copy_to_user(argp + sizeof(uint32_t), &ops.retlen,
|
||||
sizeof(uint32_t)))
|
||||
ret = -EFAULT;
|
||||
|
||||
kfree(databuf);
|
||||
kfree(ops.oobbuf);
|
||||
break;
|
||||
|
||||
}
|
||||
@ -443,13 +536,12 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
|
||||
case MEMREADOOB:
|
||||
{
|
||||
struct mtd_oob_buf buf;
|
||||
void *databuf;
|
||||
ssize_t retlen;
|
||||
struct mtd_oob_ops ops;
|
||||
|
||||
if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
|
||||
return -EFAULT;
|
||||
|
||||
if (buf.length > 0x4096)
|
||||
if (buf.length > 4096)
|
||||
return -EINVAL;
|
||||
|
||||
if (!mtd->read_oob)
|
||||
@ -457,22 +549,32 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
|
||||
else
|
||||
ret = access_ok(VERIFY_WRITE, buf.ptr,
|
||||
buf.length) ? 0 : -EFAULT;
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
databuf = kmalloc(buf.length, GFP_KERNEL);
|
||||
if (!databuf)
|
||||
ops.len = buf.length;
|
||||
ops.ooblen = buf.length;
|
||||
ops.ooboffs = buf.start & (mtd->oobsize - 1);
|
||||
ops.datbuf = NULL;
|
||||
ops.mode = MTD_OOB_PLACE;
|
||||
|
||||
if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs))
|
||||
return -EINVAL;
|
||||
|
||||
ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
|
||||
if (!ops.oobbuf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf);
|
||||
buf.start &= ~(mtd->oobsize - 1);
|
||||
ret = mtd->read_oob(mtd, buf.start, &ops);
|
||||
|
||||
if (put_user(retlen, (uint32_t __user *)argp))
|
||||
if (put_user(ops.retlen, (uint32_t __user *)argp))
|
||||
ret = -EFAULT;
|
||||
else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
|
||||
else if (ops.retlen && copy_to_user(buf.ptr, ops.oobbuf,
|
||||
ops.retlen))
|
||||
ret = -EFAULT;
|
||||
|
||||
kfree(databuf);
|
||||
kfree(ops.oobbuf);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -504,16 +606,22 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
|
||||
break;
|
||||
}
|
||||
|
||||
case MEMSETOOBSEL:
|
||||
{
|
||||
if (copy_from_user(&mtd->oobinfo, argp, sizeof(struct nand_oobinfo)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Legacy interface */
|
||||
case MEMGETOOBSEL:
|
||||
{
|
||||
if (copy_to_user(argp, &(mtd->oobinfo), sizeof(struct nand_oobinfo)))
|
||||
struct nand_oobinfo oi;
|
||||
|
||||
if (!mtd->ecclayout)
|
||||
return -EOPNOTSUPP;
|
||||
if (mtd->ecclayout->eccbytes > ARRAY_SIZE(oi.eccpos))
|
||||
return -EINVAL;
|
||||
|
||||
oi.useecc = MTD_NANDECC_AUTOPLACE;
|
||||
memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos));
|
||||
memcpy(&oi.oobfree, mtd->ecclayout->oobfree,
|
||||
sizeof(oi.oobfree));
|
||||
|
||||
if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
}
|
||||
@ -544,31 +652,17 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_OTP
|
||||
#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_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;
|
||||
}
|
||||
|
||||
mfi->mode = MTD_MODE_NORMAL;
|
||||
|
||||
ret = otp_select_filemode(mfi, mode);
|
||||
|
||||
file->f_pos = 0;
|
||||
break;
|
||||
}
|
||||
@ -580,8 +674,8 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
ret = -EOPNOTSUPP;
|
||||
switch (MTD_MODE(file)) {
|
||||
case MTD_MODE_OTP_FACT:
|
||||
switch (mfi->mode) {
|
||||
case MTD_MODE_OTP_FACTORY:
|
||||
if (mtd->get_fact_prot_info)
|
||||
ret = mtd->get_fact_prot_info(mtd, buf, 4096);
|
||||
break;
|
||||
@ -589,6 +683,8 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
|
||||
if (mtd->get_user_prot_info)
|
||||
ret = mtd->get_user_prot_info(mtd, buf, 4096);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (ret >= 0) {
|
||||
if (cmd == OTPGETREGIONCOUNT) {
|
||||
@ -607,7 +703,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
|
||||
{
|
||||
struct otp_info info;
|
||||
|
||||
if (MTD_MODE(file) != MTD_MODE_OTP_USER)
|
||||
if (mfi->mode != MTD_MODE_OTP_USER)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&info, argp, sizeof(info)))
|
||||
return -EFAULT;
|
||||
@ -618,6 +714,49 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
|
||||
}
|
||||
#endif
|
||||
|
||||
case ECCGETLAYOUT:
|
||||
{
|
||||
if (!mtd->ecclayout)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (copy_to_user(argp, &mtd->ecclayout,
|
||||
sizeof(struct nand_ecclayout)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
case ECCGETSTATS:
|
||||
{
|
||||
if (copy_to_user(argp, &mtd->ecc_stats,
|
||||
sizeof(struct mtd_ecc_stats)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
case MTDFILEMODE:
|
||||
{
|
||||
mfi->mode = 0;
|
||||
|
||||
switch(arg) {
|
||||
case MTD_MODE_OTP_FACTORY:
|
||||
case MTD_MODE_OTP_USER:
|
||||
ret = otp_select_filemode(mfi, arg);
|
||||
break;
|
||||
|
||||
case MTD_MODE_RAW:
|
||||
if (!mtd->read_oob || !mtd->write_oob)
|
||||
return -EOPNOTSUPP;
|
||||
mfi->mode = arg;
|
||||
|
||||
case MTD_MODE_NORMAL:
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
file->f_pos = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ret = -ENOTTY;
|
||||
}
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/concat.h>
|
||||
|
||||
#include <asm/div64.h>
|
||||
|
||||
/*
|
||||
* Our storage structure:
|
||||
* Subdev points to an array of pointers to struct mtd_info objects
|
||||
@ -54,7 +56,7 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t * retlen, u_char * buf)
|
||||
{
|
||||
struct mtd_concat *concat = CONCAT(mtd);
|
||||
int err = -EINVAL;
|
||||
int ret = 0, err;
|
||||
int i;
|
||||
|
||||
*retlen = 0;
|
||||
@ -78,19 +80,29 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
|
||||
err = subdev->read(subdev, from, size, &retsize, buf);
|
||||
|
||||
if (err)
|
||||
break;
|
||||
/* Save information about bitflips! */
|
||||
if (unlikely(err)) {
|
||||
if (err == -EBADMSG) {
|
||||
mtd->ecc_stats.failed++;
|
||||
ret = err;
|
||||
} else if (err == -EUCLEAN) {
|
||||
mtd->ecc_stats.corrected++;
|
||||
/* Do not overwrite -EBADMSG !! */
|
||||
if (!ret)
|
||||
ret = err;
|
||||
} else
|
||||
return err;
|
||||
}
|
||||
|
||||
*retlen += retsize;
|
||||
len -= size;
|
||||
if (len == 0)
|
||||
break;
|
||||
return ret;
|
||||
|
||||
err = -EINVAL;
|
||||
buf += size;
|
||||
from = 0;
|
||||
}
|
||||
return err;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -141,211 +153,185 @@ concat_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
}
|
||||
|
||||
static int
|
||||
concat_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t * retlen, u_char * buf, u_char * eccbuf,
|
||||
struct nand_oobinfo *oobsel)
|
||||
concat_writev(struct mtd_info *mtd, const struct kvec *vecs,
|
||||
unsigned long count, loff_t to, size_t * retlen)
|
||||
{
|
||||
struct mtd_concat *concat = CONCAT(mtd);
|
||||
int err = -EINVAL;
|
||||
struct kvec *vecs_copy;
|
||||
unsigned long entry_low, entry_high;
|
||||
size_t total_len = 0;
|
||||
int i;
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
size_t size, retsize;
|
||||
|
||||
if (from >= subdev->size) {
|
||||
/* Not destined for this subdev */
|
||||
size = 0;
|
||||
from -= subdev->size;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (from + len > subdev->size)
|
||||
/* First part goes into this subdev */
|
||||
size = subdev->size - from;
|
||||
else
|
||||
/* Entire transaction goes into this subdev */
|
||||
size = len;
|
||||
|
||||
if (subdev->read_ecc)
|
||||
err = subdev->read_ecc(subdev, from, size,
|
||||
&retsize, buf, eccbuf, oobsel);
|
||||
else
|
||||
err = -EINVAL;
|
||||
|
||||
if (err)
|
||||
break;
|
||||
|
||||
*retlen += retsize;
|
||||
len -= size;
|
||||
if (len == 0)
|
||||
break;
|
||||
|
||||
err = -EINVAL;
|
||||
buf += size;
|
||||
if (eccbuf) {
|
||||
eccbuf += subdev->oobsize;
|
||||
/* in nand.c at least, eccbufs are
|
||||
tagged with 2 (int)eccstatus'; we
|
||||
must account for these */
|
||||
eccbuf += 2 * (sizeof (int));
|
||||
}
|
||||
from = 0;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
concat_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t * retlen, const u_char * buf, u_char * eccbuf,
|
||||
struct nand_oobinfo *oobsel)
|
||||
{
|
||||
struct mtd_concat *concat = CONCAT(mtd);
|
||||
int err = -EINVAL;
|
||||
int i;
|
||||
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
|
||||
*retlen = 0;
|
||||
|
||||
/* Calculate total length of data */
|
||||
for (i = 0; i < count; i++)
|
||||
total_len += vecs[i].iov_len;
|
||||
|
||||
/* Do not allow write past end of device */
|
||||
if ((to + total_len) > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check alignment */
|
||||
if (mtd->writesize > 1) {
|
||||
loff_t __to = to;
|
||||
if (do_div(__to, mtd->writesize) || (total_len % mtd->writesize))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* make a copy of vecs */
|
||||
vecs_copy = kmalloc(sizeof(struct kvec) * count, GFP_KERNEL);
|
||||
if (!vecs_copy)
|
||||
return -ENOMEM;
|
||||
memcpy(vecs_copy, vecs, sizeof(struct kvec) * count);
|
||||
|
||||
entry_low = 0;
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
size_t size, retsize;
|
||||
size_t size, wsize, retsize, old_iov_len;
|
||||
|
||||
if (to >= subdev->size) {
|
||||
size = 0;
|
||||
to -= subdev->size;
|
||||
continue;
|
||||
}
|
||||
if (to + len > subdev->size)
|
||||
size = subdev->size - to;
|
||||
else
|
||||
size = len;
|
||||
|
||||
size = min(total_len, (size_t)(subdev->size - to));
|
||||
wsize = size; /* store for future use */
|
||||
|
||||
entry_high = entry_low;
|
||||
while (entry_high < count) {
|
||||
if (size <= vecs_copy[entry_high].iov_len)
|
||||
break;
|
||||
size -= vecs_copy[entry_high++].iov_len;
|
||||
}
|
||||
|
||||
old_iov_len = vecs_copy[entry_high].iov_len;
|
||||
vecs_copy[entry_high].iov_len = size;
|
||||
|
||||
if (!(subdev->flags & MTD_WRITEABLE))
|
||||
err = -EROFS;
|
||||
else if (subdev->write_ecc)
|
||||
err = subdev->write_ecc(subdev, to, size,
|
||||
&retsize, buf, eccbuf, oobsel);
|
||||
else
|
||||
err = -EINVAL;
|
||||
err = subdev->writev(subdev, &vecs_copy[entry_low],
|
||||
entry_high - entry_low + 1, to, &retsize);
|
||||
|
||||
vecs_copy[entry_high].iov_len = old_iov_len - size;
|
||||
vecs_copy[entry_high].iov_base += size;
|
||||
|
||||
entry_low = entry_high;
|
||||
|
||||
if (err)
|
||||
break;
|
||||
|
||||
*retlen += retsize;
|
||||
len -= size;
|
||||
if (len == 0)
|
||||
total_len -= wsize;
|
||||
|
||||
if (total_len == 0)
|
||||
break;
|
||||
|
||||
err = -EINVAL;
|
||||
buf += size;
|
||||
if (eccbuf)
|
||||
eccbuf += subdev->oobsize;
|
||||
to = 0;
|
||||
}
|
||||
|
||||
kfree(vecs_copy);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t * retlen, u_char * buf)
|
||||
concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
|
||||
{
|
||||
struct mtd_concat *concat = CONCAT(mtd);
|
||||
int err = -EINVAL;
|
||||
int i;
|
||||
struct mtd_oob_ops devops = *ops;
|
||||
int i, err, ret = 0;
|
||||
|
||||
*retlen = 0;
|
||||
ops->retlen = 0;
|
||||
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
size_t size, retsize;
|
||||
|
||||
if (from >= subdev->size) {
|
||||
/* Not destined for this subdev */
|
||||
size = 0;
|
||||
from -= subdev->size;
|
||||
continue;
|
||||
}
|
||||
if (from + len > subdev->size)
|
||||
/* First part goes into this subdev */
|
||||
size = subdev->size - from;
|
||||
else
|
||||
/* Entire transaction goes into this subdev */
|
||||
size = len;
|
||||
|
||||
if (subdev->read_oob)
|
||||
err = subdev->read_oob(subdev, from, size,
|
||||
&retsize, buf);
|
||||
else
|
||||
err = -EINVAL;
|
||||
/* partial read ? */
|
||||
if (from + devops.len > subdev->size)
|
||||
devops.len = subdev->size - from;
|
||||
|
||||
if (err)
|
||||
break;
|
||||
err = subdev->read_oob(subdev, from, &devops);
|
||||
ops->retlen += devops.retlen;
|
||||
|
||||
*retlen += retsize;
|
||||
len -= size;
|
||||
if (len == 0)
|
||||
break;
|
||||
/* Save information about bitflips! */
|
||||
if (unlikely(err)) {
|
||||
if (err == -EBADMSG) {
|
||||
mtd->ecc_stats.failed++;
|
||||
ret = err;
|
||||
} else if (err == -EUCLEAN) {
|
||||
mtd->ecc_stats.corrected++;
|
||||
/* Do not overwrite -EBADMSG !! */
|
||||
if (!ret)
|
||||
ret = err;
|
||||
} else
|
||||
return err;
|
||||
}
|
||||
|
||||
devops.len = ops->len - ops->retlen;
|
||||
if (!devops.len)
|
||||
return ret;
|
||||
|
||||
if (devops.datbuf)
|
||||
devops.datbuf += devops.retlen;
|
||||
if (devops.oobbuf)
|
||||
devops.oobbuf += devops.ooblen;
|
||||
|
||||
err = -EINVAL;
|
||||
buf += size;
|
||||
from = 0;
|
||||
}
|
||||
return err;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t * retlen, const u_char * buf)
|
||||
concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
|
||||
{
|
||||
struct mtd_concat *concat = CONCAT(mtd);
|
||||
int err = -EINVAL;
|
||||
int i;
|
||||
struct mtd_oob_ops devops = *ops;
|
||||
int i, err;
|
||||
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
|
||||
*retlen = 0;
|
||||
ops->retlen = 0;
|
||||
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
size_t size, retsize;
|
||||
|
||||
if (to >= subdev->size) {
|
||||
size = 0;
|
||||
to -= subdev->size;
|
||||
continue;
|
||||
}
|
||||
if (to + len > subdev->size)
|
||||
size = subdev->size - to;
|
||||
else
|
||||
size = len;
|
||||
|
||||
if (!(subdev->flags & MTD_WRITEABLE))
|
||||
err = -EROFS;
|
||||
else if (subdev->write_oob)
|
||||
err = subdev->write_oob(subdev, to, size, &retsize,
|
||||
buf);
|
||||
else
|
||||
err = -EINVAL;
|
||||
/* partial write ? */
|
||||
if (to + devops.len > subdev->size)
|
||||
devops.len = subdev->size - to;
|
||||
|
||||
err = subdev->write_oob(subdev, to, &devops);
|
||||
ops->retlen += devops.retlen;
|
||||
if (err)
|
||||
break;
|
||||
return err;
|
||||
|
||||
*retlen += retsize;
|
||||
len -= size;
|
||||
if (len == 0)
|
||||
break;
|
||||
devops.len = ops->len - ops->retlen;
|
||||
if (!devops.len)
|
||||
return 0;
|
||||
|
||||
err = -EINVAL;
|
||||
buf += size;
|
||||
if (devops.datbuf)
|
||||
devops.datbuf += devops.retlen;
|
||||
if (devops.oobbuf)
|
||||
devops.oobbuf += devops.ooblen;
|
||||
to = 0;
|
||||
}
|
||||
return err;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void concat_erase_callback(struct erase_info *instr)
|
||||
@ -636,6 +622,60 @@ static void concat_resume(struct mtd_info *mtd)
|
||||
}
|
||||
}
|
||||
|
||||
static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs)
|
||||
{
|
||||
struct mtd_concat *concat = CONCAT(mtd);
|
||||
int i, res = 0;
|
||||
|
||||
if (!concat->subdev[0]->block_isbad)
|
||||
return res;
|
||||
|
||||
if (ofs > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
|
||||
if (ofs >= subdev->size) {
|
||||
ofs -= subdev->size;
|
||||
continue;
|
||||
}
|
||||
|
||||
res = subdev->block_isbad(subdev, ofs);
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
||||
{
|
||||
struct mtd_concat *concat = CONCAT(mtd);
|
||||
int i, err = -EINVAL;
|
||||
|
||||
if (!concat->subdev[0]->block_markbad)
|
||||
return 0;
|
||||
|
||||
if (ofs > mtd->size)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < concat->num_subdev; i++) {
|
||||
struct mtd_info *subdev = concat->subdev[i];
|
||||
|
||||
if (ofs >= subdev->size) {
|
||||
ofs -= subdev->size;
|
||||
continue;
|
||||
}
|
||||
|
||||
err = subdev->block_markbad(subdev, ofs);
|
||||
if (!err)
|
||||
mtd->ecc_stats.badblocks++;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function constructs a virtual MTD device by concatenating
|
||||
* num_devs MTD devices. A pointer to the new device object is
|
||||
@ -677,18 +717,22 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
|
||||
concat->mtd.flags = subdev[0]->flags;
|
||||
concat->mtd.size = subdev[0]->size;
|
||||
concat->mtd.erasesize = subdev[0]->erasesize;
|
||||
concat->mtd.oobblock = subdev[0]->oobblock;
|
||||
concat->mtd.writesize = subdev[0]->writesize;
|
||||
concat->mtd.oobsize = subdev[0]->oobsize;
|
||||
concat->mtd.ecctype = subdev[0]->ecctype;
|
||||
concat->mtd.eccsize = subdev[0]->eccsize;
|
||||
if (subdev[0]->read_ecc)
|
||||
concat->mtd.read_ecc = concat_read_ecc;
|
||||
if (subdev[0]->write_ecc)
|
||||
concat->mtd.write_ecc = concat_write_ecc;
|
||||
if (subdev[0]->writev)
|
||||
concat->mtd.writev = concat_writev;
|
||||
if (subdev[0]->read_oob)
|
||||
concat->mtd.read_oob = concat_read_oob;
|
||||
if (subdev[0]->write_oob)
|
||||
concat->mtd.write_oob = concat_write_oob;
|
||||
if (subdev[0]->block_isbad)
|
||||
concat->mtd.block_isbad = concat_block_isbad;
|
||||
if (subdev[0]->block_markbad)
|
||||
concat->mtd.block_markbad = concat_block_markbad;
|
||||
|
||||
concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;
|
||||
|
||||
concat->subdev[0] = subdev[0];
|
||||
|
||||
@ -717,12 +761,12 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
|
||||
subdev[i]->flags & MTD_WRITEABLE;
|
||||
}
|
||||
concat->mtd.size += subdev[i]->size;
|
||||
if (concat->mtd.oobblock != subdev[i]->oobblock ||
|
||||
concat->mtd.ecc_stats.badblocks +=
|
||||
subdev[i]->ecc_stats.badblocks;
|
||||
if (concat->mtd.writesize != subdev[i]->writesize ||
|
||||
concat->mtd.oobsize != subdev[i]->oobsize ||
|
||||
concat->mtd.ecctype != subdev[i]->ecctype ||
|
||||
concat->mtd.eccsize != subdev[i]->eccsize ||
|
||||
!concat->mtd.read_ecc != !subdev[i]->read_ecc ||
|
||||
!concat->mtd.write_ecc != !subdev[i]->write_ecc ||
|
||||
!concat->mtd.read_oob != !subdev[i]->read_oob ||
|
||||
!concat->mtd.write_oob != !subdev[i]->write_oob) {
|
||||
kfree(concat);
|
||||
@ -734,14 +778,11 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
|
||||
|
||||
}
|
||||
|
||||
concat->mtd.ecclayout = subdev[0]->ecclayout;
|
||||
|
||||
concat->num_subdev = num_devs;
|
||||
concat->mtd.name = name;
|
||||
|
||||
/*
|
||||
* NOTE: for now, we do not provide any readv()/writev() methods
|
||||
* because they are messy to implement and they are not
|
||||
* used to a great extent anyway.
|
||||
*/
|
||||
concat->mtd.erase = concat_erase;
|
||||
concat->mtd.read = concat_read;
|
||||
concat->mtd.write = concat_write;
|
||||
|
@ -47,6 +47,7 @@ int add_mtd_device(struct mtd_info *mtd)
|
||||
{
|
||||
int i;
|
||||
|
||||
BUG_ON(mtd->writesize == 0);
|
||||
mutex_lock(&mtd_table_mutex);
|
||||
|
||||
for (i=0; i < MAX_MTD_DEVICES; i++)
|
||||
@ -254,37 +255,6 @@ int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* default_mtd_readv - default mtd readv method for MTD devices that dont
|
||||
* implement their own
|
||||
*/
|
||||
|
||||
int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs,
|
||||
unsigned long count, loff_t from, size_t *retlen)
|
||||
{
|
||||
unsigned long i;
|
||||
size_t totlen = 0, thislen;
|
||||
int ret = 0;
|
||||
|
||||
if(!mtd->read) {
|
||||
ret = -EIO;
|
||||
} else {
|
||||
for (i=0; i<count; i++) {
|
||||
if (!vecs[i].iov_len)
|
||||
continue;
|
||||
ret = mtd->read(mtd, from, vecs[i].iov_len, &thislen, vecs[i].iov_base);
|
||||
totlen += thislen;
|
||||
if (ret || thislen != vecs[i].iov_len)
|
||||
break;
|
||||
from += vecs[i].iov_len;
|
||||
}
|
||||
}
|
||||
if (retlen)
|
||||
*retlen = totlen;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
EXPORT_SYMBOL(add_mtd_device);
|
||||
EXPORT_SYMBOL(del_mtd_device);
|
||||
EXPORT_SYMBOL(get_mtd_device);
|
||||
@ -292,7 +262,6 @@ EXPORT_SYMBOL(put_mtd_device);
|
||||
EXPORT_SYMBOL(register_mtd_user);
|
||||
EXPORT_SYMBOL(unregister_mtd_user);
|
||||
EXPORT_SYMBOL(default_mtd_writev);
|
||||
EXPORT_SYMBOL(default_mtd_readv);
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
|
@ -51,16 +51,21 @@ static int part_read (struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
int res;
|
||||
|
||||
if (from >= mtd->size)
|
||||
len = 0;
|
||||
else if (from + len > mtd->size)
|
||||
len = mtd->size - from;
|
||||
if (part->master->read_ecc == NULL)
|
||||
return part->master->read (part->master, from + part->offset,
|
||||
len, retlen, buf);
|
||||
else
|
||||
return part->master->read_ecc (part->master, from + part->offset,
|
||||
len, retlen, buf, NULL, &mtd->oobinfo);
|
||||
res = part->master->read (part->master, from + part->offset,
|
||||
len, retlen, buf);
|
||||
if (unlikely(res)) {
|
||||
if (res == -EUCLEAN)
|
||||
mtd->ecc_stats.corrected++;
|
||||
if (res == -EBADMSG)
|
||||
mtd->ecc_stats.failed++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int part_point (struct mtd_info *mtd, loff_t from, size_t len,
|
||||
@ -74,6 +79,7 @@ static int part_point (struct mtd_info *mtd, loff_t from, size_t len,
|
||||
return part->master->point (part->master, from + part->offset,
|
||||
len, retlen, buf);
|
||||
}
|
||||
|
||||
static void part_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
@ -81,31 +87,25 @@ static void part_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_
|
||||
part->master->unpoint (part->master, addr, from + part->offset, len);
|
||||
}
|
||||
|
||||
|
||||
static int part_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel)
|
||||
static int part_read_oob(struct mtd_info *mtd, loff_t from,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
if (oobsel == NULL)
|
||||
oobsel = &mtd->oobinfo;
|
||||
if (from >= mtd->size)
|
||||
len = 0;
|
||||
else if (from + len > mtd->size)
|
||||
len = mtd->size - from;
|
||||
return part->master->read_ecc (part->master, from + part->offset,
|
||||
len, retlen, buf, eccbuf, oobsel);
|
||||
}
|
||||
int res;
|
||||
|
||||
static int part_read_oob (struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
if (from >= mtd->size)
|
||||
len = 0;
|
||||
else if (from + len > mtd->size)
|
||||
len = mtd->size - from;
|
||||
return part->master->read_oob (part->master, from + part->offset,
|
||||
len, retlen, buf);
|
||||
return -EINVAL;
|
||||
if (from + ops->len > mtd->size)
|
||||
return -EINVAL;
|
||||
res = part->master->read_oob(part->master, from + part->offset, ops);
|
||||
|
||||
if (unlikely(res)) {
|
||||
if (res == -EUCLEAN)
|
||||
mtd->ecc_stats.corrected++;
|
||||
if (res == -EBADMSG)
|
||||
mtd->ecc_stats.failed++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
|
||||
@ -148,44 +148,23 @@ static int part_write (struct mtd_info *mtd, loff_t to, size_t len,
|
||||
len = 0;
|
||||
else if (to + len > mtd->size)
|
||||
len = mtd->size - to;
|
||||
if (part->master->write_ecc == NULL)
|
||||
return part->master->write (part->master, to + part->offset,
|
||||
len, retlen, buf);
|
||||
else
|
||||
return part->master->write_ecc (part->master, to + part->offset,
|
||||
len, retlen, buf, NULL, &mtd->oobinfo);
|
||||
|
||||
return part->master->write (part->master, to + part->offset,
|
||||
len, retlen, buf);
|
||||
}
|
||||
|
||||
static int part_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf,
|
||||
u_char *eccbuf, struct nand_oobinfo *oobsel)
|
||||
static int part_write_oob(struct mtd_info *mtd, loff_t to,
|
||||
struct mtd_oob_ops *ops)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
if (oobsel == NULL)
|
||||
oobsel = &mtd->oobinfo;
|
||||
if (to >= mtd->size)
|
||||
len = 0;
|
||||
else if (to + len > mtd->size)
|
||||
len = mtd->size - to;
|
||||
return part->master->write_ecc (part->master, to + part->offset,
|
||||
len, retlen, buf, eccbuf, oobsel);
|
||||
}
|
||||
|
||||
static int part_write_oob (struct mtd_info *mtd, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
|
||||
if (to >= mtd->size)
|
||||
len = 0;
|
||||
else if (to + len > mtd->size)
|
||||
len = mtd->size - to;
|
||||
return part->master->write_oob (part->master, to + part->offset,
|
||||
len, retlen, buf);
|
||||
return -EINVAL;
|
||||
if (to + ops->len > mtd->size)
|
||||
return -EINVAL;
|
||||
return part->master->write_oob(part->master, to + part->offset, ops);
|
||||
}
|
||||
|
||||
static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len,
|
||||
@ -208,52 +187,8 @@ static int part_writev (struct mtd_info *mtd, const struct kvec *vecs,
|
||||
struct mtd_part *part = PART(mtd);
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
if (part->master->writev_ecc == NULL)
|
||||
return part->master->writev (part->master, vecs, count,
|
||||
return part->master->writev (part->master, vecs, count,
|
||||
to + part->offset, retlen);
|
||||
else
|
||||
return part->master->writev_ecc (part->master, vecs, count,
|
||||
to + part->offset, retlen,
|
||||
NULL, &mtd->oobinfo);
|
||||
}
|
||||
|
||||
static int part_readv (struct mtd_info *mtd, struct kvec *vecs,
|
||||
unsigned long count, loff_t from, size_t *retlen)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
if (part->master->readv_ecc == NULL)
|
||||
return part->master->readv (part->master, vecs, count,
|
||||
from + part->offset, retlen);
|
||||
else
|
||||
return part->master->readv_ecc (part->master, vecs, count,
|
||||
from + part->offset, retlen,
|
||||
NULL, &mtd->oobinfo);
|
||||
}
|
||||
|
||||
static int part_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs,
|
||||
unsigned long count, loff_t to, size_t *retlen,
|
||||
u_char *eccbuf, struct nand_oobinfo *oobsel)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
if (oobsel == NULL)
|
||||
oobsel = &mtd->oobinfo;
|
||||
return part->master->writev_ecc (part->master, vecs, count,
|
||||
to + part->offset, retlen,
|
||||
eccbuf, oobsel);
|
||||
}
|
||||
|
||||
static int part_readv_ecc (struct mtd_info *mtd, struct kvec *vecs,
|
||||
unsigned long count, loff_t from, size_t *retlen,
|
||||
u_char *eccbuf, struct nand_oobinfo *oobsel)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
if (oobsel == NULL)
|
||||
oobsel = &mtd->oobinfo;
|
||||
return part->master->readv_ecc (part->master, vecs, count,
|
||||
from + part->offset, retlen,
|
||||
eccbuf, oobsel);
|
||||
}
|
||||
|
||||
static int part_erase (struct mtd_info *mtd, struct erase_info *instr)
|
||||
@ -329,12 +264,17 @@ static int part_block_isbad (struct mtd_info *mtd, loff_t ofs)
|
||||
static int part_block_markbad (struct mtd_info *mtd, loff_t ofs)
|
||||
{
|
||||
struct mtd_part *part = PART(mtd);
|
||||
int res;
|
||||
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
return -EROFS;
|
||||
if (ofs >= mtd->size)
|
||||
return -EINVAL;
|
||||
ofs += part->offset;
|
||||
return part->master->block_markbad(part->master, ofs);
|
||||
res = part->master->block_markbad(part->master, ofs);
|
||||
if (!res)
|
||||
mtd->ecc_stats.badblocks++;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -398,7 +338,7 @@ int add_mtd_partitions(struct mtd_info *master,
|
||||
slave->mtd.type = master->type;
|
||||
slave->mtd.flags = master->flags & ~parts[i].mask_flags;
|
||||
slave->mtd.size = parts[i].size;
|
||||
slave->mtd.oobblock = master->oobblock;
|
||||
slave->mtd.writesize = master->writesize;
|
||||
slave->mtd.oobsize = master->oobsize;
|
||||
slave->mtd.ecctype = master->ecctype;
|
||||
slave->mtd.eccsize = master->eccsize;
|
||||
@ -415,10 +355,6 @@ int add_mtd_partitions(struct mtd_info *master,
|
||||
slave->mtd.unpoint = part_unpoint;
|
||||
}
|
||||
|
||||
if (master->read_ecc)
|
||||
slave->mtd.read_ecc = part_read_ecc;
|
||||
if (master->write_ecc)
|
||||
slave->mtd.write_ecc = part_write_ecc;
|
||||
if (master->read_oob)
|
||||
slave->mtd.read_oob = part_read_oob;
|
||||
if (master->write_oob)
|
||||
@ -443,12 +379,6 @@ int add_mtd_partitions(struct mtd_info *master,
|
||||
}
|
||||
if (master->writev)
|
||||
slave->mtd.writev = part_writev;
|
||||
if (master->readv)
|
||||
slave->mtd.readv = part_readv;
|
||||
if (master->writev_ecc)
|
||||
slave->mtd.writev_ecc = part_writev_ecc;
|
||||
if (master->readv_ecc)
|
||||
slave->mtd.readv_ecc = part_readv_ecc;
|
||||
if (master->lock)
|
||||
slave->mtd.lock = part_lock;
|
||||
if (master->unlock)
|
||||
@ -528,8 +458,17 @@ int add_mtd_partitions(struct mtd_info *master,
|
||||
parts[i].name);
|
||||
}
|
||||
|
||||
/* copy oobinfo from master */
|
||||
memcpy(&slave->mtd.oobinfo, &master->oobinfo, sizeof(slave->mtd.oobinfo));
|
||||
slave->mtd.ecclayout = master->ecclayout;
|
||||
if (master->block_isbad) {
|
||||
uint32_t offs = 0;
|
||||
|
||||
while(offs < slave->mtd.size) {
|
||||
if (master->block_isbad(master,
|
||||
offs + slave->offset))
|
||||
slave->mtd.ecc_stats.badblocks++;
|
||||
offs += slave->mtd.erasesize;
|
||||
}
|
||||
}
|
||||
|
||||
if(parts[i].mtdp)
|
||||
{ /* store the object pointer (caller may or may not register it */
|
||||
|
@ -23,6 +23,14 @@ config MTD_NAND_VERIFY_WRITE
|
||||
device thinks the write was successful, a bit could have been
|
||||
flipped accidentaly due to device wear or something else.
|
||||
|
||||
config MTD_NAND_ECC_SMC
|
||||
bool "NAND ECC Smart Media byte order"
|
||||
depends on MTD_NAND
|
||||
default n
|
||||
help
|
||||
Software ECC according to the Smart Media Specification.
|
||||
The original Linux implementation had byte 0 and 1 swapped.
|
||||
|
||||
config MTD_NAND_AUTCPU12
|
||||
tristate "SmartMediaCard on autronix autcpu12 board"
|
||||
depends on MTD_NAND && ARCH_AUTCPU12
|
||||
@ -49,12 +57,24 @@ config MTD_NAND_SPIA
|
||||
help
|
||||
If you had to ask, you don't have one. Say 'N'.
|
||||
|
||||
config MTD_NAND_AMS_DELTA
|
||||
tristate "NAND Flash device on Amstrad E3"
|
||||
depends on MACH_AMS_DELTA && MTD_NAND
|
||||
help
|
||||
Support for NAND flash on Amstrad E3 (Delta).
|
||||
|
||||
config MTD_NAND_TOTO
|
||||
tristate "NAND Flash device on TOTO board"
|
||||
depends on ARCH_OMAP && MTD_NAND
|
||||
depends on ARCH_OMAP && MTD_NAND && BROKEN
|
||||
help
|
||||
Support for NAND flash on Texas Instruments Toto platform.
|
||||
|
||||
config MTD_NAND_TS7250
|
||||
tristate "NAND Flash device on TS-7250 board"
|
||||
depends on MACH_TS72XX && MTD_NAND
|
||||
help
|
||||
Support for NAND flash on Technologic Systems TS-7250 platform.
|
||||
|
||||
config MTD_NAND_IDS
|
||||
tristate
|
||||
|
||||
@ -76,7 +96,7 @@ config MTD_NAND_RTC_FROM4
|
||||
|
||||
config MTD_NAND_PPCHAMELEONEVB
|
||||
tristate "NAND Flash device on PPChameleonEVB board"
|
||||
depends on PPCHAMELEONEVB && MTD_NAND
|
||||
depends on PPCHAMELEONEVB && MTD_NAND && BROKEN
|
||||
help
|
||||
This enables the NAND flash driver on the PPChameleon EVB Board.
|
||||
|
||||
@ -87,7 +107,7 @@ config MTD_NAND_S3C2410
|
||||
This enables the NAND flash controller on the S3C2410 and S3C2440
|
||||
SoCs
|
||||
|
||||
No board specfic support is done by this driver, each board
|
||||
No board specific support is done by this driver, each board
|
||||
must advertise a platform_device for the driver to attach.
|
||||
|
||||
config MTD_NAND_S3C2410_DEBUG
|
||||
@ -109,6 +129,22 @@ config MTD_NAND_S3C2410_HWECC
|
||||
currently not be able to switch to software, as there is no
|
||||
implementation for ECC method used by the S3C2410
|
||||
|
||||
config MTD_NAND_NDFC
|
||||
tristate "NDFC NanD Flash Controller"
|
||||
depends on MTD_NAND && 44x
|
||||
help
|
||||
NDFC Nand Flash Controllers are integrated in EP44x SoCs
|
||||
|
||||
config MTD_NAND_S3C2410_CLKSTOP
|
||||
bool "S3C2410 NAND IDLE clock stop"
|
||||
depends on MTD_NAND_S3C2410
|
||||
default n
|
||||
help
|
||||
Stop the clock to the NAND controller when there is no chip
|
||||
selected to save power. This will mean there is a small delay
|
||||
when the is NAND chip selected or released, but will save
|
||||
approximately 5mA of power when there is nothing happening.
|
||||
|
||||
config MTD_NAND_DISKONCHIP
|
||||
tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)"
|
||||
depends on MTD_NAND && EXPERIMENTAL
|
||||
@ -183,11 +219,24 @@ config MTD_NAND_SHARPSL
|
||||
tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
|
||||
depends on MTD_NAND && ARCH_PXA
|
||||
|
||||
config MTD_NAND_CS553X
|
||||
tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)"
|
||||
depends on MTD_NAND && X86_32 && (X86_PC || X86_GENERICARCH)
|
||||
help
|
||||
The CS553x companion chips for the AMD Geode processor
|
||||
include NAND flash controllers with built-in hardware ECC
|
||||
capabilities; enabling this option will allow you to use
|
||||
these. The driver will check the MSRs to verify that the
|
||||
controller is enabled for NAND, and currently requires that
|
||||
the controller be in MMIO mode.
|
||||
|
||||
If you say "m", the module will be called "cs553x_nand.ko".
|
||||
|
||||
config MTD_NAND_NANDSIM
|
||||
tristate "Support for NAND Flash Simulator"
|
||||
depends on MTD_NAND && MTD_PARTITIONS
|
||||
help
|
||||
The simulator may simulate verious NAND flash chips for the
|
||||
The simulator may simulate various NAND flash chips for the
|
||||
MTD nand layer.
|
||||
|
||||
endmenu
|
||||
|
@ -7,6 +7,7 @@ obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o
|
||||
obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o
|
||||
|
||||
obj-$(CONFIG_MTD_NAND_SPIA) += spia.o
|
||||
obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o
|
||||
obj-$(CONFIG_MTD_NAND_TOTO) += toto.o
|
||||
obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o
|
||||
obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o
|
||||
@ -17,6 +18,9 @@ obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
|
||||
obj-$(CONFIG_MTD_NAND_H1900) += h1910.o
|
||||
obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o
|
||||
obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o
|
||||
obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o
|
||||
obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
|
||||
obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
|
||||
obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o
|
||||
|
||||
nand-objs = nand_base.o nand_bbt.o
|
||||
|
237
drivers/mtd/nand/ams-delta.c
Normal file
237
drivers/mtd/nand/ams-delta.c
Normal file
@ -0,0 +1,237 @@
|
||||
/*
|
||||
* drivers/mtd/nand/ams-delta.c
|
||||
*
|
||||
* Copyright (C) 2006 Jonathan McDowell <noodles@earth.li>
|
||||
*
|
||||
* Derived from drivers/mtd/toto.c
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Overview:
|
||||
* This is a device driver for the NAND flash device found on the
|
||||
* Amstrad E3 (Delta).
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/sizes.h>
|
||||
#include <asm/arch/gpio.h>
|
||||
#include <asm/arch/board-ams-delta.h>
|
||||
|
||||
/*
|
||||
* MTD structure for E3 (Delta)
|
||||
*/
|
||||
static struct mtd_info *ams_delta_mtd = NULL;
|
||||
|
||||
#define NAND_MASK (AMS_DELTA_LATCH2_NAND_NRE | AMS_DELTA_LATCH2_NAND_NWE | AMS_DELTA_LATCH2_NAND_CLE | AMS_DELTA_LATCH2_NAND_ALE | AMS_DELTA_LATCH2_NAND_NCE | AMS_DELTA_LATCH2_NAND_NWP)
|
||||
|
||||
/*
|
||||
* Define partitions for flash devices
|
||||
*/
|
||||
|
||||
static struct mtd_partition partition_info[] = {
|
||||
{ .name = "Kernel",
|
||||
.offset = 0,
|
||||
.size = 3 * SZ_1M + SZ_512K },
|
||||
{ .name = "u-boot",
|
||||
.offset = 3 * SZ_1M + SZ_512K,
|
||||
.size = SZ_256K },
|
||||
{ .name = "u-boot params",
|
||||
.offset = 3 * SZ_1M + SZ_512K + SZ_256K,
|
||||
.size = SZ_256K },
|
||||
{ .name = "Amstrad LDR",
|
||||
.offset = 4 * SZ_1M,
|
||||
.size = SZ_256K },
|
||||
{ .name = "File system",
|
||||
.offset = 4 * SZ_1M + 1 * SZ_256K,
|
||||
.size = 27 * SZ_1M },
|
||||
{ .name = "PBL reserved",
|
||||
.offset = 32 * SZ_1M - 3 * SZ_256K,
|
||||
.size = 3 * SZ_256K },
|
||||
};
|
||||
|
||||
static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
omap_writew(0, (OMAP_MPUIO_BASE + OMAP_MPUIO_IO_CNTL));
|
||||
omap_writew(byte, this->IO_ADDR_W);
|
||||
ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NWE, 0);
|
||||
ndelay(40);
|
||||
ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NWE,
|
||||
AMS_DELTA_LATCH2_NAND_NWE);
|
||||
}
|
||||
|
||||
static u_char ams_delta_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
u_char res;
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NRE, 0);
|
||||
ndelay(40);
|
||||
omap_writew(~0, (OMAP_MPUIO_BASE + OMAP_MPUIO_IO_CNTL));
|
||||
res = omap_readw(this->IO_ADDR_R);
|
||||
ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NRE,
|
||||
AMS_DELTA_LATCH2_NAND_NRE);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void ams_delta_write_buf(struct mtd_info *mtd, const u_char *buf,
|
||||
int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<len; i++)
|
||||
ams_delta_write_byte(mtd, buf[i]);
|
||||
}
|
||||
|
||||
static void ams_delta_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<len; i++)
|
||||
buf[i] = ams_delta_read_byte(mtd);
|
||||
}
|
||||
|
||||
static int ams_delta_verify_buf(struct mtd_info *mtd, const u_char *buf,
|
||||
int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i<len; i++)
|
||||
if (buf[i] != ams_delta_read_byte(mtd))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Command control function
|
||||
*
|
||||
* ctrl:
|
||||
* NAND_NCE: bit 0 -> bit 2
|
||||
* NAND_CLE: bit 1 -> bit 7
|
||||
* NAND_ALE: bit 2 -> bit 6
|
||||
*/
|
||||
static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
unsigned long bits;
|
||||
|
||||
bits = (~ctrl & NAND_NCE) << 2;
|
||||
bits |= (ctrl & NAND_CLE) << 7;
|
||||
bits |= (ctrl & NAND_ALE) << 6;
|
||||
|
||||
ams_delta_latch2_write(0xC2, bits);
|
||||
}
|
||||
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
ams_delta_write_byte(mtd, cmd);
|
||||
}
|
||||
|
||||
static int ams_delta_nand_ready(struct mtd_info *mtd)
|
||||
{
|
||||
return omap_get_gpio_datain(AMS_DELTA_GPIO_PIN_NAND_RB);
|
||||
}
|
||||
|
||||
/*
|
||||
* Main initialization routine
|
||||
*/
|
||||
static int __init ams_delta_init(void)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
int err = 0;
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
ams_delta_mtd = kmalloc(sizeof(struct mtd_info) +
|
||||
sizeof(struct nand_chip), GFP_KERNEL);
|
||||
if (!ams_delta_mtd) {
|
||||
printk (KERN_WARNING "Unable to allocate E3 NAND MTD device structure.\n");
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ams_delta_mtd->owner = THIS_MODULE;
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *) (&ams_delta_mtd[1]);
|
||||
|
||||
/* Initialize structures */
|
||||
memset(ams_delta_mtd, 0, sizeof(struct mtd_info));
|
||||
memset(this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
ams_delta_mtd->priv = this;
|
||||
|
||||
/* Set address of NAND IO lines */
|
||||
this->IO_ADDR_R = (OMAP_MPUIO_BASE + OMAP_MPUIO_INPUT_LATCH);
|
||||
this->IO_ADDR_W = (OMAP_MPUIO_BASE + OMAP_MPUIO_OUTPUT);
|
||||
this->read_byte = ams_delta_read_byte;
|
||||
this->write_buf = ams_delta_write_buf;
|
||||
this->read_buf = ams_delta_read_buf;
|
||||
this->verify_buf = ams_delta_verify_buf;
|
||||
this->cmd_ctrl = ams_delta_hwcontrol;
|
||||
if (!omap_request_gpio(AMS_DELTA_GPIO_PIN_NAND_RB)) {
|
||||
this->dev_ready = ams_delta_nand_ready;
|
||||
} else {
|
||||
this->dev_ready = NULL;
|
||||
printk(KERN_NOTICE "Couldn't request gpio for Delta NAND ready.\n");
|
||||
}
|
||||
/* 25 us command delay time */
|
||||
this->chip_delay = 30;
|
||||
this->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
/* Set chip enabled, but */
|
||||
ams_delta_latch2_write(NAND_MASK, AMS_DELTA_LATCH2_NAND_NRE |
|
||||
AMS_DELTA_LATCH2_NAND_NWE |
|
||||
AMS_DELTA_LATCH2_NAND_NCE |
|
||||
AMS_DELTA_LATCH2_NAND_NWP);
|
||||
|
||||
/* Scan to find existance of the device */
|
||||
if (nand_scan(ams_delta_mtd, 1)) {
|
||||
err = -ENXIO;
|
||||
goto out_mtd;
|
||||
}
|
||||
|
||||
/* Register the partitions */
|
||||
add_mtd_partitions(ams_delta_mtd, partition_info,
|
||||
ARRAY_SIZE(partition_info));
|
||||
|
||||
goto out;
|
||||
|
||||
out_mtd:
|
||||
kfree(ams_delta_mtd);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
module_init(ams_delta_init);
|
||||
|
||||
/*
|
||||
* Clean up routine
|
||||
*/
|
||||
static void __exit ams_delta_cleanup(void)
|
||||
{
|
||||
/* Release resources, unregister device */
|
||||
nand_release(ams_delta_mtd);
|
||||
|
||||
/* Free the MTD device structure */
|
||||
kfree(ams_delta_mtd);
|
||||
}
|
||||
module_exit(ams_delta_cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>");
|
||||
MODULE_DESCRIPTION("Glue layer for NAND flash on Amstrad E3 (Delta)");
|
@ -14,6 +14,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
@ -38,22 +39,21 @@
|
||||
*/
|
||||
static struct mtd_info *au1550_mtd = NULL;
|
||||
static void __iomem *p_nand;
|
||||
static int nand_width = 1; /* default x8*/
|
||||
static int nand_width = 1; /* default x8 */
|
||||
static void (*au1550_write_byte)(struct mtd_info *, u_char);
|
||||
|
||||
/*
|
||||
* Define partitions for flash device
|
||||
*/
|
||||
static const struct mtd_partition partition_info[] = {
|
||||
{
|
||||
.name = "NAND FS 0",
|
||||
.offset = 0,
|
||||
.size = 8*1024*1024
|
||||
},
|
||||
.name = "NAND FS 0",
|
||||
.offset = 0,
|
||||
.size = 8 * 1024 * 1024},
|
||||
{
|
||||
.name = "NAND FS 1",
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
.size = MTDPART_SIZ_FULL
|
||||
}
|
||||
.name = "NAND FS 1",
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
.size = MTDPART_SIZ_FULL}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -129,21 +129,6 @@ static u16 au_read_word(struct mtd_info *mtd)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* au_write_word - write one word to the chip
|
||||
* @mtd: MTD device structure
|
||||
* @word: data word to write
|
||||
*
|
||||
* write function for 16bit buswith without
|
||||
* endianess conversion
|
||||
*/
|
||||
static void au_write_word(struct mtd_info *mtd, u16 word)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
writew(word, this->IO_ADDR_W);
|
||||
au_sync();
|
||||
}
|
||||
|
||||
/**
|
||||
* au_write_buf - write buffer to chip
|
||||
* @mtd: MTD device structure
|
||||
@ -157,7 +142,7 @@ static void au_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++) {
|
||||
for (i = 0; i < len; i++) {
|
||||
writeb(buf[i], this->IO_ADDR_W);
|
||||
au_sync();
|
||||
}
|
||||
@ -176,7 +161,7 @@ static void au_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++) {
|
||||
for (i = 0; i < len; i++) {
|
||||
buf[i] = readb(this->IO_ADDR_R);
|
||||
au_sync();
|
||||
}
|
||||
@ -195,7 +180,7 @@ static int au_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++) {
|
||||
for (i = 0; i < len; i++) {
|
||||
if (buf[i] != readb(this->IO_ADDR_R))
|
||||
return -EFAULT;
|
||||
au_sync();
|
||||
@ -219,7 +204,7 @@ static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
u16 *p = (u16 *) buf;
|
||||
len >>= 1;
|
||||
|
||||
for (i=0; i<len; i++) {
|
||||
for (i = 0; i < len; i++) {
|
||||
writew(p[i], this->IO_ADDR_W);
|
||||
au_sync();
|
||||
}
|
||||
@ -241,7 +226,7 @@ static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
|
||||
u16 *p = (u16 *) buf;
|
||||
len >>= 1;
|
||||
|
||||
for (i=0; i<len; i++) {
|
||||
for (i = 0; i < len; i++) {
|
||||
p[i] = readw(this->IO_ADDR_R);
|
||||
au_sync();
|
||||
}
|
||||
@ -262,7 +247,7 @@ static int au_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
u16 *p = (u16 *) buf;
|
||||
len >>= 1;
|
||||
|
||||
for (i=0; i<len; i++) {
|
||||
for (i = 0; i < len; i++) {
|
||||
if (p[i] != readw(this->IO_ADDR_R))
|
||||
return -EFAULT;
|
||||
au_sync();
|
||||
@ -270,32 +255,52 @@ static int au_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Select the chip by setting nCE to low */
|
||||
#define NAND_CTL_SETNCE 1
|
||||
/* Deselect the chip by setting nCE to high */
|
||||
#define NAND_CTL_CLRNCE 2
|
||||
/* Select the command latch by setting CLE to high */
|
||||
#define NAND_CTL_SETCLE 3
|
||||
/* Deselect the command latch by setting CLE to low */
|
||||
#define NAND_CTL_CLRCLE 4
|
||||
/* Select the address latch by setting ALE to high */
|
||||
#define NAND_CTL_SETALE 5
|
||||
/* Deselect the address latch by setting ALE to low */
|
||||
#define NAND_CTL_CLRALE 6
|
||||
|
||||
static void au1550_hwcontrol(struct mtd_info *mtd, int cmd)
|
||||
{
|
||||
register struct nand_chip *this = mtd->priv;
|
||||
|
||||
switch(cmd){
|
||||
switch (cmd) {
|
||||
|
||||
case NAND_CTL_SETCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_CMD; break;
|
||||
case NAND_CTL_CLRCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_DATA; break;
|
||||
case NAND_CTL_SETCLE:
|
||||
this->IO_ADDR_W = p_nand + MEM_STNAND_CMD;
|
||||
break;
|
||||
|
||||
case NAND_CTL_CLRCLE:
|
||||
this->IO_ADDR_W = p_nand + MEM_STNAND_DATA;
|
||||
break;
|
||||
|
||||
case NAND_CTL_SETALE:
|
||||
this->IO_ADDR_W = p_nand + MEM_STNAND_ADDR;
|
||||
break;
|
||||
|
||||
case NAND_CTL_SETALE: this->IO_ADDR_W = p_nand + MEM_STNAND_ADDR; break;
|
||||
case NAND_CTL_CLRALE:
|
||||
this->IO_ADDR_W = p_nand + MEM_STNAND_DATA;
|
||||
/* FIXME: Nobody knows why this is neccecary,
|
||||
/* FIXME: Nobody knows why this is necessary,
|
||||
* but it works only that way */
|
||||
udelay(1);
|
||||
break;
|
||||
|
||||
case NAND_CTL_SETNCE:
|
||||
/* assert (force assert) chip enable */
|
||||
au_writel((1<<(4+NAND_CS)) , MEM_STNDCTL); break;
|
||||
au_writel((1 << (4 + NAND_CS)), MEM_STNDCTL);
|
||||
break;
|
||||
|
||||
case NAND_CTL_CLRNCE:
|
||||
/* deassert chip enable */
|
||||
au_writel(0, MEM_STNDCTL); break;
|
||||
/* deassert chip enable */
|
||||
au_writel(0, MEM_STNDCTL);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -312,69 +317,200 @@ int au1550_device_ready(struct mtd_info *mtd)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* au1550_select_chip - control -CE line
|
||||
* Forbid driving -CE manually permitting the NAND controller to do this.
|
||||
* Keeping -CE asserted during the whole sector reads interferes with the
|
||||
* NOR flash and PCMCIA drivers as it causes contention on the static bus.
|
||||
* We only have to hold -CE low for the NAND read commands since the flash
|
||||
* chip needs it to be asserted during chip not ready time but the NAND
|
||||
* controller keeps it released.
|
||||
*
|
||||
* @mtd: MTD device structure
|
||||
* @chip: chipnumber to select, -1 for deselect
|
||||
*/
|
||||
static void au1550_select_chip(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* au1550_command - Send command to NAND device
|
||||
* @mtd: MTD device structure
|
||||
* @command: the command to be sent
|
||||
* @column: the column address for this command, -1 if none
|
||||
* @page_addr: the page address for this command, -1 if none
|
||||
*/
|
||||
static void au1550_command(struct mtd_info *mtd, unsigned command, int column, int page_addr)
|
||||
{
|
||||
register struct nand_chip *this = mtd->priv;
|
||||
int ce_override = 0, i;
|
||||
ulong flags;
|
||||
|
||||
/* Begin command latch cycle */
|
||||
au1550_hwcontrol(mtd, NAND_CTL_SETCLE);
|
||||
/*
|
||||
* Write out the command to the device.
|
||||
*/
|
||||
if (command == NAND_CMD_SEQIN) {
|
||||
int readcmd;
|
||||
|
||||
if (column >= mtd->writesize) {
|
||||
/* OOB area */
|
||||
column -= mtd->writesize;
|
||||
readcmd = NAND_CMD_READOOB;
|
||||
} else if (column < 256) {
|
||||
/* First 256 bytes --> READ0 */
|
||||
readcmd = NAND_CMD_READ0;
|
||||
} else {
|
||||
column -= 256;
|
||||
readcmd = NAND_CMD_READ1;
|
||||
}
|
||||
au1550_write_byte(mtd, readcmd);
|
||||
}
|
||||
au1550_write_byte(mtd, command);
|
||||
|
||||
/* Set ALE and clear CLE to start address cycle */
|
||||
au1550_hwcontrol(mtd, NAND_CTL_CLRCLE);
|
||||
|
||||
if (column != -1 || page_addr != -1) {
|
||||
au1550_hwcontrol(mtd, NAND_CTL_SETALE);
|
||||
|
||||
/* Serially input address */
|
||||
if (column != -1) {
|
||||
/* Adjust columns for 16 bit buswidth */
|
||||
if (this->options & NAND_BUSWIDTH_16)
|
||||
column >>= 1;
|
||||
au1550_write_byte(mtd, column);
|
||||
}
|
||||
if (page_addr != -1) {
|
||||
au1550_write_byte(mtd, (u8)(page_addr & 0xff));
|
||||
|
||||
if (command == NAND_CMD_READ0 ||
|
||||
command == NAND_CMD_READ1 ||
|
||||
command == NAND_CMD_READOOB) {
|
||||
/*
|
||||
* NAND controller will release -CE after
|
||||
* the last address byte is written, so we'll
|
||||
* have to forcibly assert it. No interrupts
|
||||
* are allowed while we do this as we don't
|
||||
* want the NOR flash or PCMCIA drivers to
|
||||
* steal our precious bytes of data...
|
||||
*/
|
||||
ce_override = 1;
|
||||
local_irq_save(flags);
|
||||
au1550_hwcontrol(mtd, NAND_CTL_SETNCE);
|
||||
}
|
||||
|
||||
au1550_write_byte(mtd, (u8)(page_addr >> 8));
|
||||
|
||||
/* One more address cycle for devices > 32MiB */
|
||||
if (this->chipsize > (32 << 20))
|
||||
au1550_write_byte(mtd, (u8)((page_addr >> 16) & 0x0f));
|
||||
}
|
||||
/* Latch in address */
|
||||
au1550_hwcontrol(mtd, NAND_CTL_CLRALE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Program and erase have their own busy handlers.
|
||||
* Status and sequential in need 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:
|
||||
break;
|
||||
|
||||
case NAND_CMD_READ0:
|
||||
case NAND_CMD_READ1:
|
||||
case NAND_CMD_READOOB:
|
||||
/* Check if we're really driving -CE low (just in case) */
|
||||
if (unlikely(!ce_override))
|
||||
break;
|
||||
|
||||
/* Apply a short delay always to ensure that we do wait tWB. */
|
||||
ndelay(100);
|
||||
/* Wait for a chip to become ready... */
|
||||
for (i = this->chip_delay; !this->dev_ready(mtd) && i > 0; --i)
|
||||
udelay(1);
|
||||
|
||||
/* Release -CE and re-enable interrupts. */
|
||||
au1550_hwcontrol(mtd, NAND_CTL_CLRNCE);
|
||||
local_irq_restore(flags);
|
||||
return;
|
||||
}
|
||||
/* Apply this short delay always to ensure that we do wait tWB. */
|
||||
ndelay(100);
|
||||
|
||||
while(!this->dev_ready(mtd));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Main initialization routine
|
||||
*/
|
||||
int __init au1xxx_nand_init (void)
|
||||
static int __init au1xxx_nand_init(void)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
u16 boot_swapboot = 0; /* default value */
|
||||
u16 boot_swapboot = 0; /* default value */
|
||||
int retval;
|
||||
u32 mem_staddr;
|
||||
u32 nand_phys;
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
au1550_mtd = kmalloc (sizeof(struct mtd_info) +
|
||||
sizeof (struct nand_chip), GFP_KERNEL);
|
||||
au1550_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
|
||||
if (!au1550_mtd) {
|
||||
printk ("Unable to allocate NAND MTD dev structure.\n");
|
||||
printk("Unable to allocate NAND MTD dev structure.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *) (&au1550_mtd[1]);
|
||||
this = (struct nand_chip *)(&au1550_mtd[1]);
|
||||
|
||||
/* Initialize structures */
|
||||
memset((char *) au1550_mtd, 0, sizeof(struct mtd_info));
|
||||
memset((char *) this, 0, sizeof(struct nand_chip));
|
||||
memset(au1550_mtd, 0, sizeof(struct mtd_info));
|
||||
memset(this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
au1550_mtd->priv = this;
|
||||
au1550_mtd->owner = THIS_MODULE;
|
||||
|
||||
|
||||
/* disable interrupts */
|
||||
au_writel(au_readl(MEM_STNDCTL) & ~(1<<8), MEM_STNDCTL);
|
||||
|
||||
/* disable NAND boot */
|
||||
au_writel(au_readl(MEM_STNDCTL) & ~(1<<0), MEM_STNDCTL);
|
||||
/* MEM_STNDCTL: disable ints, disable nand boot */
|
||||
au_writel(0, MEM_STNDCTL);
|
||||
|
||||
#ifdef CONFIG_MIPS_PB1550
|
||||
/* set gpio206 high */
|
||||
au_writel(au_readl(GPIO2_DIR) & ~(1<<6), GPIO2_DIR);
|
||||
au_writel(au_readl(GPIO2_DIR) & ~(1 << 6), GPIO2_DIR);
|
||||
|
||||
boot_swapboot = (au_readl(MEM_STSTAT) & (0x7<<1)) |
|
||||
((bcsr->status >> 6) & 0x1);
|
||||
boot_swapboot = (au_readl(MEM_STSTAT) & (0x7 << 1)) | ((bcsr->status >> 6) & 0x1);
|
||||
switch (boot_swapboot) {
|
||||
case 0:
|
||||
case 2:
|
||||
case 8:
|
||||
case 0xC:
|
||||
case 0xD:
|
||||
/* x16 NAND Flash */
|
||||
nand_width = 0;
|
||||
break;
|
||||
case 1:
|
||||
case 9:
|
||||
case 3:
|
||||
case 0xE:
|
||||
case 0xF:
|
||||
/* x8 NAND Flash */
|
||||
nand_width = 1;
|
||||
break;
|
||||
default:
|
||||
printk("Pb1550 NAND: bad boot:swap\n");
|
||||
retval = -EINVAL;
|
||||
goto outmem;
|
||||
case 0:
|
||||
case 2:
|
||||
case 8:
|
||||
case 0xC:
|
||||
case 0xD:
|
||||
/* x16 NAND Flash */
|
||||
nand_width = 0;
|
||||
break;
|
||||
case 1:
|
||||
case 9:
|
||||
case 3:
|
||||
case 0xE:
|
||||
case 0xF:
|
||||
/* x8 NAND Flash */
|
||||
nand_width = 1;
|
||||
break;
|
||||
default:
|
||||
printk("Pb1550 NAND: bad boot:swap\n");
|
||||
retval = -EINVAL;
|
||||
goto outmem;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -424,21 +560,22 @@ int __init au1xxx_nand_init (void)
|
||||
|
||||
/* make controller and MTD agree */
|
||||
if (NAND_CS == 0)
|
||||
nand_width = au_readl(MEM_STCFG0) & (1<<22);
|
||||
nand_width = au_readl(MEM_STCFG0) & (1 << 22);
|
||||
if (NAND_CS == 1)
|
||||
nand_width = au_readl(MEM_STCFG1) & (1<<22);
|
||||
nand_width = au_readl(MEM_STCFG1) & (1 << 22);
|
||||
if (NAND_CS == 2)
|
||||
nand_width = au_readl(MEM_STCFG2) & (1<<22);
|
||||
nand_width = au_readl(MEM_STCFG2) & (1 << 22);
|
||||
if (NAND_CS == 3)
|
||||
nand_width = au_readl(MEM_STCFG3) & (1<<22);
|
||||
|
||||
nand_width = au_readl(MEM_STCFG3) & (1 << 22);
|
||||
|
||||
/* Set address of hardware control function */
|
||||
this->hwcontrol = au1550_hwcontrol;
|
||||
this->dev_ready = au1550_device_ready;
|
||||
this->select_chip = au1550_select_chip;
|
||||
this->cmdfunc = au1550_command;
|
||||
|
||||
/* 30 us command delay time */
|
||||
this->chip_delay = 30;
|
||||
this->eccmode = NAND_ECC_SOFT;
|
||||
this->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
this->options = NAND_NO_AUTOINCR;
|
||||
|
||||
@ -446,15 +583,14 @@ int __init au1xxx_nand_init (void)
|
||||
this->options |= NAND_BUSWIDTH_16;
|
||||
|
||||
this->read_byte = (!nand_width) ? au_read_byte16 : au_read_byte;
|
||||
this->write_byte = (!nand_width) ? au_write_byte16 : au_write_byte;
|
||||
this->write_word = au_write_word;
|
||||
au1550_write_byte = (!nand_width) ? au_write_byte16 : au_write_byte;
|
||||
this->read_word = au_read_word;
|
||||
this->write_buf = (!nand_width) ? au_write_buf16 : au_write_buf;
|
||||
this->read_buf = (!nand_width) ? au_read_buf16 : au_read_buf;
|
||||
this->verify_buf = (!nand_width) ? au_verify_buf16 : au_verify_buf;
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
if (nand_scan (au1550_mtd, 1)) {
|
||||
if (nand_scan(au1550_mtd, 1)) {
|
||||
retval = -ENXIO;
|
||||
goto outio;
|
||||
}
|
||||
@ -465,10 +601,10 @@ int __init au1xxx_nand_init (void)
|
||||
return 0;
|
||||
|
||||
outio:
|
||||
iounmap ((void *)p_nand);
|
||||
iounmap((void *)p_nand);
|
||||
|
||||
outmem:
|
||||
kfree (au1550_mtd);
|
||||
kfree(au1550_mtd);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -477,22 +613,21 @@ module_init(au1xxx_nand_init);
|
||||
/*
|
||||
* Clean up routine
|
||||
*/
|
||||
#ifdef MODULE
|
||||
static void __exit au1550_cleanup (void)
|
||||
static void __exit au1550_cleanup(void)
|
||||
{
|
||||
struct nand_chip *this = (struct nand_chip *) &au1550_mtd[1];
|
||||
struct nand_chip *this = (struct nand_chip *)&au1550_mtd[1];
|
||||
|
||||
/* Release resources, unregister device */
|
||||
nand_release (au1550_mtd);
|
||||
nand_release(au1550_mtd);
|
||||
|
||||
/* Free the MTD device structure */
|
||||
kfree (au1550_mtd);
|
||||
kfree(au1550_mtd);
|
||||
|
||||
/* Unmap */
|
||||
iounmap ((void *)p_nand);
|
||||
iounmap((void *)p_nand);
|
||||
}
|
||||
|
||||
module_exit(au1550_cleanup);
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Embedded Edge, LLC");
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de>
|
||||
*
|
||||
* Derived from drivers/mtd/spia.c
|
||||
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
|
||||
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
|
||||
*
|
||||
* $Id: autcpu12.c,v 1.23 2005/11/07 11:14:30 gleixner Exp $
|
||||
*
|
||||
@ -42,12 +42,7 @@
|
||||
* MTD structure for AUTCPU12 board
|
||||
*/
|
||||
static struct mtd_info *autcpu12_mtd = NULL;
|
||||
|
||||
static int autcpu12_io_base = CS89712_VIRT_BASE;
|
||||
static int autcpu12_fio_pbase = AUTCPU12_PHYS_SMC;
|
||||
static int autcpu12_fio_ctrl = AUTCPU12_SMC_SELECT_OFFSET;
|
||||
static int autcpu12_pedr = AUTCPU12_SMC_PORT_OFFSET;
|
||||
static void __iomem * autcpu12_fio_base;
|
||||
static void __iomem *autcpu12_fio_base;
|
||||
|
||||
/*
|
||||
* Define partitions for flash devices
|
||||
@ -94,108 +89,131 @@ static struct mtd_partition partition_info128k[] = {
|
||||
#define NUM_PARTITIONS128K 2
|
||||
/*
|
||||
* hardware specific access to control-lines
|
||||
*/
|
||||
static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd)
|
||||
*
|
||||
* ALE bit 4 autcpu12_pedr
|
||||
* CLE bit 5 autcpu12_pedr
|
||||
* NCE bit 0 fio_ctrl
|
||||
*
|
||||
*/
|
||||
static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
switch(cmd){
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
void __iomem *addr
|
||||
unsigned char bits;
|
||||
|
||||
case NAND_CTL_SETCLE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) |= AUTCPU12_SMC_CLE; break;
|
||||
case NAND_CTL_CLRCLE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) &= ~AUTCPU12_SMC_CLE; break;
|
||||
addr = CS89712_VIRT_BASE + AUTCPU12_SMC_PORT_OFFSET;
|
||||
bits = (ctrl & NAND_CLE) << 4;
|
||||
bits |= (ctrl & NAND_ALE) << 2;
|
||||
writeb((readb(addr) & ~0x30) | bits, addr);
|
||||
|
||||
case NAND_CTL_SETALE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) |= AUTCPU12_SMC_ALE; break;
|
||||
case NAND_CTL_CLRALE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) &= ~AUTCPU12_SMC_ALE; break;
|
||||
|
||||
case NAND_CTL_SETNCE: (*(volatile unsigned char *) (autcpu12_fio_base + autcpu12_fio_ctrl)) = 0x01; break;
|
||||
case NAND_CTL_CLRNCE: (*(volatile unsigned char *) (autcpu12_fio_base + autcpu12_fio_ctrl)) = 0x00; break;
|
||||
addr = autcpu12_fio_base + AUTCPU12_SMC_SELECT_OFFSET;
|
||||
writeb((readb(addr) & ~0x1) | (ctrl & NAND_NCE), addr);
|
||||
}
|
||||
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
writeb(cmd, chip->IO_ADDR_W);
|
||||
}
|
||||
|
||||
/*
|
||||
* read device ready pin
|
||||
*/
|
||||
* read device ready pin
|
||||
*/
|
||||
int autcpu12_device_ready(struct mtd_info *mtd)
|
||||
{
|
||||
void __iomem *addr = CS89712_VIRT_BASE + AUTCPU12_SMC_PORT_OFFSET;
|
||||
|
||||
return ( (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) & AUTCPU12_SMC_RDY) ? 1 : 0;
|
||||
|
||||
return readb(addr) & AUTCPU12_SMC_RDY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main initialization routine
|
||||
*/
|
||||
int __init autcpu12_init (void)
|
||||
static int __init autcpu12_init(void)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
int err = 0;
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
autcpu12_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
|
||||
GFP_KERNEL);
|
||||
autcpu12_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip),
|
||||
GFP_KERNEL);
|
||||
if (!autcpu12_mtd) {
|
||||
printk ("Unable to allocate AUTCPU12 NAND MTD device structure.\n");
|
||||
printk("Unable to allocate AUTCPU12 NAND MTD device structure.\n");
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* map physical adress */
|
||||
autcpu12_fio_base = ioremap(autcpu12_fio_pbase,SZ_1K);
|
||||
if(!autcpu12_fio_base){
|
||||
autcpu12_fio_base = ioremap(AUTCPU12_PHYS_SMC, SZ_1K);
|
||||
if (!autcpu12_fio_base) {
|
||||
printk("Ioremap autcpu12 SmartMedia Card failed\n");
|
||||
err = -EIO;
|
||||
goto out_mtd;
|
||||
}
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *) (&autcpu12_mtd[1]);
|
||||
this = (struct nand_chip *)(&autcpu12_mtd[1]);
|
||||
|
||||
/* Initialize structures */
|
||||
memset((char *) autcpu12_mtd, 0, sizeof(struct mtd_info));
|
||||
memset((char *) this, 0, sizeof(struct nand_chip));
|
||||
memset(autcpu12_mtd, 0, sizeof(struct mtd_info));
|
||||
memset(this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
autcpu12_mtd->priv = this;
|
||||
autcpu12_mtd->owner = THIS_MODULE;
|
||||
|
||||
/* Set address of NAND IO lines */
|
||||
this->IO_ADDR_R = autcpu12_fio_base;
|
||||
this->IO_ADDR_W = autcpu12_fio_base;
|
||||
this->hwcontrol = autcpu12_hwcontrol;
|
||||
this->cmd_ctrl = autcpu12_hwcontrol;
|
||||
this->dev_ready = autcpu12_device_ready;
|
||||
/* 20 us command delay time */
|
||||
this->chip_delay = 20;
|
||||
this->eccmode = NAND_ECC_SOFT;
|
||||
this->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
/* Enable the following for a flash based bad block table */
|
||||
/*
|
||||
this->options = NAND_USE_FLASH_BBT;
|
||||
*/
|
||||
this->options = NAND_USE_FLASH_BBT;
|
||||
*/
|
||||
this->options = NAND_USE_FLASH_BBT;
|
||||
|
||||
/* Scan to find existance of the device */
|
||||
if (nand_scan (autcpu12_mtd, 1)) {
|
||||
if (nand_scan(autcpu12_mtd, 1)) {
|
||||
err = -ENXIO;
|
||||
goto out_ior;
|
||||
}
|
||||
|
||||
/* Register the partitions */
|
||||
switch(autcpu12_mtd->size){
|
||||
case SZ_16M: add_mtd_partitions(autcpu12_mtd, partition_info16k, NUM_PARTITIONS16K); break;
|
||||
case SZ_32M: add_mtd_partitions(autcpu12_mtd, partition_info32k, NUM_PARTITIONS32K); break;
|
||||
case SZ_64M: add_mtd_partitions(autcpu12_mtd, partition_info64k, NUM_PARTITIONS64K); break;
|
||||
case SZ_128M: add_mtd_partitions(autcpu12_mtd, partition_info128k, NUM_PARTITIONS128K); break;
|
||||
default: {
|
||||
printk ("Unsupported SmartMedia device\n");
|
||||
switch (autcpu12_mtd->size) {
|
||||
case SZ_16M:
|
||||
add_mtd_partitions(autcpu12_mtd, partition_info16k,
|
||||
NUM_PARTITIONS16K);
|
||||
break;
|
||||
case SZ_32M:
|
||||
add_mtd_partitions(autcpu12_mtd, partition_info32k,
|
||||
NUM_PARTITIONS32K);
|
||||
break;
|
||||
case SZ_64M:
|
||||
add_mtd_partitions(autcpu12_mtd, partition_info64k,
|
||||
NUM_PARTITIONS64K);
|
||||
break;
|
||||
case SZ_128M:
|
||||
add_mtd_partitions(autcpu12_mtd, partition_info128k,
|
||||
NUM_PARTITIONS128K);
|
||||
break;
|
||||
default:
|
||||
printk("Unsupported SmartMedia device\n");
|
||||
err = -ENXIO;
|
||||
goto out_ior;
|
||||
}
|
||||
}
|
||||
goto out;
|
||||
|
||||
out_ior:
|
||||
iounmap((void *)autcpu12_fio_base);
|
||||
out_mtd:
|
||||
kfree (autcpu12_mtd);
|
||||
out:
|
||||
out_ior:
|
||||
iounmap(autcpu12_fio_base);
|
||||
out_mtd:
|
||||
kfree(autcpu12_mtd);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -204,20 +222,19 @@ module_init(autcpu12_init);
|
||||
/*
|
||||
* Clean up routine
|
||||
*/
|
||||
#ifdef MODULE
|
||||
static void __exit autcpu12_cleanup (void)
|
||||
static void __exit autcpu12_cleanup(void)
|
||||
{
|
||||
/* Release resources, unregister device */
|
||||
nand_release (autcpu12_mtd);
|
||||
nand_release(autcpu12_mtd);
|
||||
|
||||
/* unmap physical adress */
|
||||
iounmap((void *)autcpu12_fio_base);
|
||||
iounmap(autcpu12_fio_base);
|
||||
|
||||
/* Free the MTD device structure */
|
||||
kfree (autcpu12_mtd);
|
||||
kfree(autcpu12_mtd);
|
||||
}
|
||||
|
||||
module_exit(autcpu12_cleanup);
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
|
||||
|
353
drivers/mtd/nand/cs553x_nand.c
Normal file
353
drivers/mtd/nand/cs553x_nand.c
Normal file
@ -0,0 +1,353 @@
|
||||
/*
|
||||
* drivers/mtd/nand/cs553x_nand.c
|
||||
*
|
||||
* (C) 2005, 2006 Red Hat Inc.
|
||||
*
|
||||
* Author: David Woodhouse <dwmw2@infradead.org>
|
||||
* Tom Sylla <tom.sylla@amd.com>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Overview:
|
||||
* This is a device driver for the NAND flash controller found on
|
||||
* the AMD CS5535/CS5536 companion chipsets for the Geode processor.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
|
||||
#include <asm/msr.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define NR_CS553X_CONTROLLERS 4
|
||||
|
||||
#define MSR_DIVIL_GLD_CAP 0x51400000 /* DIVIL capabilitiies */
|
||||
#define CAP_CS5535 0x2df000ULL
|
||||
#define CAP_CS5536 0x5df500ULL
|
||||
|
||||
/* NAND Timing MSRs */
|
||||
#define MSR_NANDF_DATA 0x5140001b /* NAND Flash Data Timing MSR */
|
||||
#define MSR_NANDF_CTL 0x5140001c /* NAND Flash Control Timing */
|
||||
#define MSR_NANDF_RSVD 0x5140001d /* Reserved */
|
||||
|
||||
/* NAND BAR MSRs */
|
||||
#define MSR_DIVIL_LBAR_FLSH0 0x51400010 /* Flash Chip Select 0 */
|
||||
#define MSR_DIVIL_LBAR_FLSH1 0x51400011 /* Flash Chip Select 1 */
|
||||
#define MSR_DIVIL_LBAR_FLSH2 0x51400012 /* Flash Chip Select 2 */
|
||||
#define MSR_DIVIL_LBAR_FLSH3 0x51400013 /* Flash Chip Select 3 */
|
||||
/* Each made up of... */
|
||||
#define FLSH_LBAR_EN (1ULL<<32)
|
||||
#define FLSH_NOR_NAND (1ULL<<33) /* 1 for NAND */
|
||||
#define FLSH_MEM_IO (1ULL<<34) /* 1 for MMIO */
|
||||
/* I/O BARs have BASE_ADDR in bits 15:4, IO_MASK in 47:36 */
|
||||
/* MMIO BARs have BASE_ADDR in bits 31:12, MEM_MASK in 63:44 */
|
||||
|
||||
/* Pin function selection MSR (IDE vs. flash on the IDE pins) */
|
||||
#define MSR_DIVIL_BALL_OPTS 0x51400015
|
||||
#define PIN_OPT_IDE (1<<0) /* 0 for flash, 1 for IDE */
|
||||
|
||||
/* Registers within the NAND flash controller BAR -- memory mapped */
|
||||
#define MM_NAND_DATA 0x00 /* 0 to 0x7ff, in fact */
|
||||
#define MM_NAND_CTL 0x800 /* Any even address 0x800-0x80e */
|
||||
#define MM_NAND_IO 0x801 /* Any odd address 0x801-0x80f */
|
||||
#define MM_NAND_STS 0x810
|
||||
#define MM_NAND_ECC_LSB 0x811
|
||||
#define MM_NAND_ECC_MSB 0x812
|
||||
#define MM_NAND_ECC_COL 0x813
|
||||
#define MM_NAND_LAC 0x814
|
||||
#define MM_NAND_ECC_CTL 0x815
|
||||
|
||||
/* Registers within the NAND flash controller BAR -- I/O mapped */
|
||||
#define IO_NAND_DATA 0x00 /* 0 to 3, in fact */
|
||||
#define IO_NAND_CTL 0x04
|
||||
#define IO_NAND_IO 0x05
|
||||
#define IO_NAND_STS 0x06
|
||||
#define IO_NAND_ECC_CTL 0x08
|
||||
#define IO_NAND_ECC_LSB 0x09
|
||||
#define IO_NAND_ECC_MSB 0x0a
|
||||
#define IO_NAND_ECC_COL 0x0b
|
||||
#define IO_NAND_LAC 0x0c
|
||||
|
||||
#define CS_NAND_CTL_DIST_EN (1<<4) /* Enable NAND Distract interrupt */
|
||||
#define CS_NAND_CTL_RDY_INT_MASK (1<<3) /* Enable RDY/BUSY# interrupt */
|
||||
#define CS_NAND_CTL_ALE (1<<2)
|
||||
#define CS_NAND_CTL_CLE (1<<1)
|
||||
#define CS_NAND_CTL_CE (1<<0) /* Keep low; 1 to reset */
|
||||
|
||||
#define CS_NAND_STS_FLASH_RDY (1<<3)
|
||||
#define CS_NAND_CTLR_BUSY (1<<2)
|
||||
#define CS_NAND_CMD_COMP (1<<1)
|
||||
#define CS_NAND_DIST_ST (1<<0)
|
||||
|
||||
#define CS_NAND_ECC_PARITY (1<<2)
|
||||
#define CS_NAND_ECC_CLRECC (1<<1)
|
||||
#define CS_NAND_ECC_ENECC (1<<0)
|
||||
|
||||
static void cs553x_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
while (unlikely(len > 0x800)) {
|
||||
memcpy_fromio(buf, this->IO_ADDR_R, 0x800);
|
||||
buf += 0x800;
|
||||
len -= 0x800;
|
||||
}
|
||||
memcpy_fromio(buf, this->IO_ADDR_R, len);
|
||||
}
|
||||
|
||||
static void cs553x_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
while (unlikely(len > 0x800)) {
|
||||
memcpy_toio(this->IO_ADDR_R, buf, 0x800);
|
||||
buf += 0x800;
|
||||
len -= 0x800;
|
||||
}
|
||||
memcpy_toio(this->IO_ADDR_R, buf, len);
|
||||
}
|
||||
|
||||
static unsigned char cs553x_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
return readb(this->IO_ADDR_R);
|
||||
}
|
||||
|
||||
static void cs553x_write_byte(struct mtd_info *mtd, u_char byte)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
int i = 100000;
|
||||
|
||||
while (i && readb(this->IO_ADDR_R + MM_NAND_STS) & CS_NAND_CTLR_BUSY) {
|
||||
udelay(1);
|
||||
i--;
|
||||
}
|
||||
writeb(byte, this->IO_ADDR_W + 0x801);
|
||||
}
|
||||
|
||||
static void cs553x_hwcontrol(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
void __iomem *mmio_base = this->IO_ADDR_R;
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
unsigned char ctl = (ctrl & ~NAND_CTRL_CHANGE ) ^ 0x01;
|
||||
writeb(ctl, mmio_base + MM_NAND_CTL);
|
||||
}
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
cs553x_write_byte(mtd, cmd);
|
||||
}
|
||||
|
||||
static int cs553x_device_ready(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
void __iomem *mmio_base = this->IO_ADDR_R;
|
||||
unsigned char foo = readb(mmio_base + MM_NAND_STS);
|
||||
|
||||
return (foo & CS_NAND_STS_FLASH_RDY) && !(foo & CS_NAND_CTLR_BUSY);
|
||||
}
|
||||
|
||||
static void cs_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
void __iomem *mmio_base = this->IO_ADDR_R;
|
||||
|
||||
writeb(0x07, mmio_base + MM_NAND_ECC_CTL);
|
||||
}
|
||||
|
||||
static int cs_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
|
||||
{
|
||||
uint32_t ecc;
|
||||
struct nand_chip *this = mtd->priv;
|
||||
void __iomem *mmio_base = this->IO_ADDR_R;
|
||||
|
||||
ecc = readl(mmio_base + MM_NAND_STS);
|
||||
|
||||
ecc_code[1] = ecc >> 8;
|
||||
ecc_code[0] = ecc >> 16;
|
||||
ecc_code[2] = ecc >> 24;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mtd_info *cs553x_mtd[4];
|
||||
|
||||
static int __init cs553x_init_one(int cs, int mmio, unsigned long adr)
|
||||
{
|
||||
int err = 0;
|
||||
struct nand_chip *this;
|
||||
struct mtd_info *new_mtd;
|
||||
|
||||
printk(KERN_NOTICE "Probing CS553x NAND controller CS#%d at %sIO 0x%08lx\n", cs, mmio?"MM":"P", adr);
|
||||
|
||||
if (!mmio) {
|
||||
printk(KERN_NOTICE "PIO mode not yet implemented for CS553X NAND controller\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
new_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
|
||||
if (!new_mtd) {
|
||||
printk(KERN_WARNING "Unable to allocate CS553X NAND MTD device structure.\n");
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *)(&new_mtd[1]);
|
||||
|
||||
/* Initialize structures */
|
||||
memset(new_mtd, 0, sizeof(struct mtd_info));
|
||||
memset(this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
new_mtd->priv = this;
|
||||
new_mtd->owner = THIS_MODULE;
|
||||
|
||||
/* map physical address */
|
||||
this->IO_ADDR_R = this->IO_ADDR_W = ioremap(adr, 4096);
|
||||
if (!this->IO_ADDR_R) {
|
||||
printk(KERN_WARNING "ioremap cs553x NAND @0x%08lx failed\n", adr);
|
||||
err = -EIO;
|
||||
goto out_mtd;
|
||||
}
|
||||
|
||||
this->cmd_ctrl = cs553x_hwcontrol;
|
||||
this->dev_ready = cs553x_device_ready;
|
||||
this->read_byte = cs553x_read_byte;
|
||||
this->read_buf = cs553x_read_buf;
|
||||
this->write_buf = cs553x_write_buf;
|
||||
|
||||
this->chip_delay = 0;
|
||||
|
||||
this->ecc.mode = NAND_ECC_HW;
|
||||
this->ecc.size = 256;
|
||||
this->ecc.bytes = 3;
|
||||
this->ecc.hwctl = cs_enable_hwecc;
|
||||
this->ecc.calculate = cs_calculate_ecc;
|
||||
this->ecc.correct = nand_correct_data;
|
||||
|
||||
/* Enable the following for a flash based bad block table */
|
||||
this->options = NAND_USE_FLASH_BBT | NAND_NO_AUTOINCR;
|
||||
|
||||
/* Scan to find existance of the device */
|
||||
if (nand_scan(new_mtd, 1)) {
|
||||
err = -ENXIO;
|
||||
goto out_ior;
|
||||
}
|
||||
|
||||
cs553x_mtd[cs] = new_mtd;
|
||||
goto out;
|
||||
|
||||
out_ior:
|
||||
iounmap((void *)this->IO_ADDR_R);
|
||||
out_mtd:
|
||||
kfree(new_mtd);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int is_geode(void)
|
||||
{
|
||||
/* These are the CPUs which will have a CS553[56] companion chip */
|
||||
if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
|
||||
boot_cpu_data.x86 == 5 &&
|
||||
boot_cpu_data.x86_model == 10)
|
||||
return 1; /* Geode LX */
|
||||
|
||||
if ((boot_cpu_data.x86_vendor == X86_VENDOR_NSC ||
|
||||
boot_cpu_data.x86_vendor == X86_VENDOR_CYRIX) &&
|
||||
boot_cpu_data.x86 == 5 &&
|
||||
boot_cpu_data.x86_model == 5)
|
||||
return 1; /* Geode GX (née GX2) */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init cs553x_init(void)
|
||||
{
|
||||
int err = -ENXIO;
|
||||
int i;
|
||||
uint64_t val;
|
||||
|
||||
/* If the CPU isn't a Geode GX or LX, abort */
|
||||
if (!is_geode())
|
||||
return -ENXIO;
|
||||
|
||||
/* If it doesn't have the CS553[56], abort */
|
||||
rdmsrl(MSR_DIVIL_GLD_CAP, val);
|
||||
val &= ~0xFFULL;
|
||||
if (val != CAP_CS5535 && val != CAP_CS5536)
|
||||
return -ENXIO;
|
||||
|
||||
/* If it doesn't have the NAND controller enabled, abort */
|
||||
rdmsrl(MSR_DIVIL_BALL_OPTS, val);
|
||||
if (val & 1) {
|
||||
printk(KERN_INFO "CS553x NAND controller: Flash I/O not enabled in MSR_DIVIL_BALL_OPTS.\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
|
||||
rdmsrl(MSR_DIVIL_LBAR_FLSH0 + i, val);
|
||||
|
||||
if ((val & (FLSH_LBAR_EN|FLSH_NOR_NAND)) == (FLSH_LBAR_EN|FLSH_NOR_NAND))
|
||||
err = cs553x_init_one(i, !!(val & FLSH_MEM_IO), val & 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
/* Register all devices together here. This means we can easily hack it to
|
||||
do mtdconcat etc. if we want to. */
|
||||
for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
|
||||
if (cs553x_mtd[i]) {
|
||||
add_mtd_device(cs553x_mtd[i]);
|
||||
|
||||
/* If any devices registered, return success. Else the last error. */
|
||||
err = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
module_init(cs553x_init);
|
||||
|
||||
static void __exit cs553x_cleanup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NR_CS553X_CONTROLLERS; i++) {
|
||||
struct mtd_info *mtd = cs553x_mtd[i];
|
||||
struct nand_chip *this;
|
||||
void __iomem *mmio_base;
|
||||
|
||||
if (!mtd)
|
||||
break;
|
||||
|
||||
this = cs553x_mtd[i]->priv;
|
||||
mmio_base = this->IO_ADDR_R;
|
||||
|
||||
/* Release resources, unregister device */
|
||||
nand_release(cs553x_mtd[i]);
|
||||
cs553x_mtd[i] = NULL;
|
||||
|
||||
/* unmap physical adress */
|
||||
iounmap(mmio_base);
|
||||
|
||||
/* Free the MTD device structure */
|
||||
kfree(mtd);
|
||||
}
|
||||
}
|
||||
|
||||
module_exit(cs553x_cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
|
||||
MODULE_DESCRIPTION("NAND controller driver for AMD CS5535/CS5536 companion chip");
|
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* drivers/mtd/nand/edb7312.c
|
||||
*
|
||||
* Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
|
||||
* Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
|
||||
*
|
||||
* Derived from drivers/mtd/nand/autcpu12.c
|
||||
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
|
||||
@ -25,7 +25,7 @@
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/hardware.h> /* for CLPS7111_VIRT_BASE */
|
||||
#include <asm/arch/hardware.h> /* for CLPS7111_VIRT_BASE */
|
||||
#include <asm/sizes.h>
|
||||
#include <asm/hardware/clps7111.h>
|
||||
|
||||
@ -54,51 +54,45 @@ static struct mtd_info *ep7312_mtd = NULL;
|
||||
*/
|
||||
|
||||
static unsigned long ep7312_fio_pbase = EP7312_FIO_PBASE;
|
||||
static void __iomem * ep7312_pxdr = (void __iomem *) EP7312_PXDR;
|
||||
static void __iomem * ep7312_pxddr = (void __iomem *) EP7312_PXDDR;
|
||||
static void __iomem *ep7312_pxdr = (void __iomem *)EP7312_PXDR;
|
||||
static void __iomem *ep7312_pxddr = (void __iomem *)EP7312_PXDDR;
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
/*
|
||||
* Define static partitions for flash device
|
||||
*/
|
||||
static struct mtd_partition partition_info[] = {
|
||||
{ .name = "EP7312 Nand Flash",
|
||||
.offset = 0,
|
||||
.size = 8*1024*1024 }
|
||||
{.name = "EP7312 Nand Flash",
|
||||
.offset = 0,
|
||||
.size = 8 * 1024 * 1024}
|
||||
};
|
||||
|
||||
#define NUM_PARTITIONS 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* hardware specific access to control-lines
|
||||
*
|
||||
* NAND_NCE: bit 0 -> bit 7
|
||||
* NAND_CLE: bit 1 -> bit 4
|
||||
* NAND_ALE: bit 2 -> bit 5
|
||||
*/
|
||||
static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd)
|
||||
static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
||||
{
|
||||
switch(cmd) {
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
case NAND_CTL_SETCLE:
|
||||
clps_writeb(clps_readb(ep7312_pxdr) | 0x10, ep7312_pxdr);
|
||||
break;
|
||||
case NAND_CTL_CLRCLE:
|
||||
clps_writeb(clps_readb(ep7312_pxdr) & ~0x10, ep7312_pxdr);
|
||||
break;
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
unsigned char bits;
|
||||
|
||||
case NAND_CTL_SETALE:
|
||||
clps_writeb(clps_readb(ep7312_pxdr) | 0x20, ep7312_pxdr);
|
||||
break;
|
||||
case NAND_CTL_CLRALE:
|
||||
clps_writeb(clps_readb(ep7312_pxdr) & ~0x20, ep7312_pxdr);
|
||||
break;
|
||||
bits = (ctrl & (NAND_CLE | NAND_ALE)) << 3;
|
||||
bits = (ctrl & NAND_NCE) << 7;
|
||||
|
||||
case NAND_CTL_SETNCE:
|
||||
clps_writeb((clps_readb(ep7312_pxdr) | 0x80) & ~0x40, ep7312_pxdr);
|
||||
break;
|
||||
case NAND_CTL_CLRNCE:
|
||||
clps_writeb((clps_readb(ep7312_pxdr) | 0x80) | 0x40, ep7312_pxdr);
|
||||
break;
|
||||
clps_writeb((clps_readb(ep7312_pxdr) & 0xB0) | 0x10,
|
||||
ep7312_pxdr);
|
||||
}
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
writeb(cmd, chip->IO_ADDR_W);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -108,6 +102,7 @@ static int ep7312_device_ready(struct mtd_info *mtd)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
const char *part_probes[] = { "cmdlinepart", NULL };
|
||||
#endif
|
||||
@ -115,18 +110,16 @@ const char *part_probes[] = { "cmdlinepart", NULL };
|
||||
/*
|
||||
* Main initialization routine
|
||||
*/
|
||||
static int __init ep7312_init (void)
|
||||
static int __init ep7312_init(void)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
const char *part_type = 0;
|
||||
int mtd_parts_nb = 0;
|
||||
struct mtd_partition *mtd_parts = 0;
|
||||
void __iomem * ep7312_fio_base;
|
||||
void __iomem *ep7312_fio_base;
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
ep7312_mtd = kmalloc(sizeof(struct mtd_info) +
|
||||
sizeof(struct nand_chip),
|
||||
GFP_KERNEL);
|
||||
ep7312_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
|
||||
if (!ep7312_mtd) {
|
||||
printk("Unable to allocate EDB7312 NAND MTD device structure.\n");
|
||||
return -ENOMEM;
|
||||
@ -134,21 +127,22 @@ static int __init ep7312_init (void)
|
||||
|
||||
/* map physical adress */
|
||||
ep7312_fio_base = ioremap(ep7312_fio_pbase, SZ_1K);
|
||||
if(!ep7312_fio_base) {
|
||||
if (!ep7312_fio_base) {
|
||||
printk("ioremap EDB7312 NAND flash failed\n");
|
||||
kfree(ep7312_mtd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *) (&ep7312_mtd[1]);
|
||||
this = (struct nand_chip *)(&ep7312_mtd[1]);
|
||||
|
||||
/* Initialize structures */
|
||||
memset((char *) ep7312_mtd, 0, sizeof(struct mtd_info));
|
||||
memset((char *) this, 0, sizeof(struct nand_chip));
|
||||
memset(ep7312_mtd, 0, sizeof(struct mtd_info));
|
||||
memset(this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
ep7312_mtd->priv = this;
|
||||
ep7312_mtd->owner = THIS_MODULE;
|
||||
|
||||
/*
|
||||
* Set GPIO Port B control register so that the pins are configured
|
||||
@ -159,22 +153,20 @@ static int __init ep7312_init (void)
|
||||
/* insert callbacks */
|
||||
this->IO_ADDR_R = ep7312_fio_base;
|
||||
this->IO_ADDR_W = ep7312_fio_base;
|
||||
this->hwcontrol = ep7312_hwcontrol;
|
||||
this->cmd_ctrl = ep7312_hwcontrol;
|
||||
this->dev_ready = ep7312_device_ready;
|
||||
/* 15 us command delay time */
|
||||
this->chip_delay = 15;
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
if (nand_scan (ep7312_mtd, 1)) {
|
||||
if (nand_scan(ep7312_mtd, 1)) {
|
||||
iounmap((void *)ep7312_fio_base);
|
||||
kfree (ep7312_mtd);
|
||||
kfree(ep7312_mtd);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
ep7312_mtd->name = "edb7312-nand";
|
||||
mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes,
|
||||
&mtd_parts, 0);
|
||||
mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes, &mtd_parts, 0);
|
||||
if (mtd_parts_nb > 0)
|
||||
part_type = "command line";
|
||||
else
|
||||
@ -193,24 +185,23 @@ static int __init ep7312_init (void)
|
||||
/* Return happy */
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(ep7312_init);
|
||||
|
||||
/*
|
||||
* Clean up routine
|
||||
*/
|
||||
static void __exit ep7312_cleanup (void)
|
||||
static void __exit ep7312_cleanup(void)
|
||||
{
|
||||
struct nand_chip *this = (struct nand_chip *) &ep7312_mtd[1];
|
||||
struct nand_chip *this = (struct nand_chip *)&ep7312_mtd[1];
|
||||
|
||||
/* Release resources, unregister device */
|
||||
nand_release (ap7312_mtd);
|
||||
|
||||
/* Free internal data buffer */
|
||||
kfree (this->data_buf);
|
||||
nand_release(ap7312_mtd);
|
||||
|
||||
/* Free the MTD device structure */
|
||||
kfree (ep7312_mtd);
|
||||
kfree(ep7312_mtd);
|
||||
}
|
||||
|
||||
module_exit(ep7312_cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Copyright (C) 2003 Joshua Wise (joshua@joshuawise.com)
|
||||
*
|
||||
* Derived from drivers/mtd/nand/edb7312.c
|
||||
* Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
|
||||
* Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
|
||||
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
|
||||
*
|
||||
* $Id: h1910.c,v 1.6 2005/11/07 11:14:30 gleixner Exp $
|
||||
@ -26,7 +26,7 @@
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/hardware.h> /* for CLPS7111_VIRT_BASE */
|
||||
#include <asm/arch/hardware.h> /* for CLPS7111_VIRT_BASE */
|
||||
#include <asm/sizes.h>
|
||||
#include <asm/arch/h1900-gpio.h>
|
||||
#include <asm/arch/ipaq.h>
|
||||
@ -45,47 +45,29 @@ static struct mtd_info *h1910_nand_mtd = NULL;
|
||||
* Define static partitions for flash device
|
||||
*/
|
||||
static struct mtd_partition partition_info[] = {
|
||||
{ name: "h1910 NAND Flash",
|
||||
offset: 0,
|
||||
size: 16*1024*1024 }
|
||||
{name:"h1910 NAND Flash",
|
||||
offset:0,
|
||||
size:16 * 1024 * 1024}
|
||||
};
|
||||
|
||||
#define NUM_PARTITIONS 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* hardware specific access to control-lines
|
||||
*
|
||||
* NAND_NCE: bit 0 - don't care
|
||||
* NAND_CLE: bit 1 - address bit 2
|
||||
* NAND_ALE: bit 2 - address bit 3
|
||||
*/
|
||||
static void h1910_hwcontrol(struct mtd_info *mtd, int cmd)
|
||||
static void h1910_hwcontrol(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip* this = (struct nand_chip *) (mtd->priv);
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
switch(cmd) {
|
||||
|
||||
case NAND_CTL_SETCLE:
|
||||
this->IO_ADDR_R |= (1 << 2);
|
||||
this->IO_ADDR_W |= (1 << 2);
|
||||
break;
|
||||
case NAND_CTL_CLRCLE:
|
||||
this->IO_ADDR_R &= ~(1 << 2);
|
||||
this->IO_ADDR_W &= ~(1 << 2);
|
||||
break;
|
||||
|
||||
case NAND_CTL_SETALE:
|
||||
this->IO_ADDR_R |= (1 << 3);
|
||||
this->IO_ADDR_W |= (1 << 3);
|
||||
break;
|
||||
case NAND_CTL_CLRALE:
|
||||
this->IO_ADDR_R &= ~(1 << 3);
|
||||
this->IO_ADDR_W &= ~(1 << 3);
|
||||
break;
|
||||
|
||||
case NAND_CTL_SETNCE:
|
||||
break;
|
||||
case NAND_CTL_CLRNCE:
|
||||
break;
|
||||
}
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
writeb(cmd, chip->IO_ADDR_W | ((ctrl & 0x6) << 1));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -101,7 +83,7 @@ static int h1910_device_ready(struct mtd_info *mtd)
|
||||
/*
|
||||
* Main initialization routine
|
||||
*/
|
||||
static int __init h1910_init (void)
|
||||
static int __init h1910_init(void)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
const char *part_type = 0;
|
||||
@ -119,24 +101,23 @@ static int __init h1910_init (void)
|
||||
}
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
h1910_nand_mtd = kmalloc(sizeof(struct mtd_info) +
|
||||
sizeof(struct nand_chip),
|
||||
GFP_KERNEL);
|
||||
h1910_nand_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
|
||||
if (!h1910_nand_mtd) {
|
||||
printk("Unable to allocate h1910 NAND MTD device structure.\n");
|
||||
iounmap ((void *) nandaddr);
|
||||
iounmap((void *)nandaddr);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *) (&h1910_nand_mtd[1]);
|
||||
this = (struct nand_chip *)(&h1910_nand_mtd[1]);
|
||||
|
||||
/* Initialize structures */
|
||||
memset((char *) h1910_nand_mtd, 0, sizeof(struct mtd_info));
|
||||
memset((char *) this, 0, sizeof(struct nand_chip));
|
||||
memset(h1910_nand_mtd, 0, sizeof(struct mtd_info));
|
||||
memset(this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
h1910_nand_mtd->priv = this;
|
||||
h1910_nand_mtd->owner = THIS_MODULE;
|
||||
|
||||
/*
|
||||
* Enable VPEN
|
||||
@ -146,31 +127,28 @@ static int __init h1910_init (void)
|
||||
/* insert callbacks */
|
||||
this->IO_ADDR_R = nandaddr;
|
||||
this->IO_ADDR_W = nandaddr;
|
||||
this->hwcontrol = h1910_hwcontrol;
|
||||
this->cmd_ctrl = h1910_hwcontrol;
|
||||
this->dev_ready = NULL; /* unknown whether that was correct or not so we will just do it like this */
|
||||
/* 15 us command delay time */
|
||||
this->chip_delay = 50;
|
||||
this->eccmode = NAND_ECC_SOFT;
|
||||
this->ecc.mode = NAND_ECC_SOFT;
|
||||
this->options = NAND_NO_AUTOINCR;
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
if (nand_scan (h1910_nand_mtd, 1)) {
|
||||
if (nand_scan(h1910_nand_mtd, 1)) {
|
||||
printk(KERN_NOTICE "No NAND device - returning -ENXIO\n");
|
||||
kfree (h1910_nand_mtd);
|
||||
iounmap ((void *) nandaddr);
|
||||
kfree(h1910_nand_mtd);
|
||||
iounmap((void *)nandaddr);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_CMDLINE_PARTS
|
||||
mtd_parts_nb = parse_cmdline_partitions(h1910_nand_mtd, &mtd_parts,
|
||||
"h1910-nand");
|
||||
mtd_parts_nb = parse_cmdline_partitions(h1910_nand_mtd, &mtd_parts, "h1910-nand");
|
||||
if (mtd_parts_nb > 0)
|
||||
part_type = "command line";
|
||||
part_type = "command line";
|
||||
else
|
||||
mtd_parts_nb = 0;
|
||||
mtd_parts_nb = 0;
|
||||
#endif
|
||||
if (mtd_parts_nb == 0)
|
||||
{
|
||||
if (mtd_parts_nb == 0) {
|
||||
mtd_parts = partition_info;
|
||||
mtd_parts_nb = NUM_PARTITIONS;
|
||||
part_type = "static";
|
||||
@ -183,24 +161,26 @@ static int __init h1910_init (void)
|
||||
/* Return happy */
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(h1910_init);
|
||||
|
||||
/*
|
||||
* Clean up routine
|
||||
*/
|
||||
static void __exit h1910_cleanup (void)
|
||||
static void __exit h1910_cleanup(void)
|
||||
{
|
||||
struct nand_chip *this = (struct nand_chip *) &h1910_nand_mtd[1];
|
||||
struct nand_chip *this = (struct nand_chip *)&h1910_nand_mtd[1];
|
||||
|
||||
/* Release resources, unregister device */
|
||||
nand_release (h1910_nand_mtd);
|
||||
nand_release(h1910_nand_mtd);
|
||||
|
||||
/* Release io resource */
|
||||
iounmap ((void *) this->IO_ADDR_W);
|
||||
iounmap((void *)this->IO_ADDR_W);
|
||||
|
||||
/* Free the MTD device structure */
|
||||
kfree (h1910_nand_mtd);
|
||||
kfree(h1910_nand_mtd);
|
||||
}
|
||||
|
||||
module_exit(h1910_cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -48,7 +48,7 @@
|
||||
*
|
||||
* Following assumptions are made:
|
||||
* - bbts start at a page boundary, if autolocated on a block boundary
|
||||
* - the space neccecary for a bbt in FLASH does not exceed a block boundary
|
||||
* - the space necessary for a bbt in FLASH does not exceed a block boundary
|
||||
*
|
||||
*/
|
||||
|
||||
@ -60,7 +60,7 @@
|
||||
#include <linux/mtd/compatmac.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
/**
|
||||
* check_pattern - [GENERIC] check if a pattern is in the buffer
|
||||
@ -75,7 +75,7 @@
|
||||
* pattern area contain 0xff
|
||||
*
|
||||
*/
|
||||
static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
|
||||
static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
|
||||
{
|
||||
int i, end = 0;
|
||||
uint8_t *p = buf;
|
||||
@ -116,7 +116,7 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des
|
||||
* no optional empty check
|
||||
*
|
||||
*/
|
||||
static int check_short_pattern (uint8_t *buf, struct nand_bbt_descr *td)
|
||||
static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
|
||||
{
|
||||
int i;
|
||||
uint8_t *p = buf;
|
||||
@ -142,8 +142,8 @@ static int check_short_pattern (uint8_t *buf, struct nand_bbt_descr *td)
|
||||
* Read the bad block table starting from page.
|
||||
*
|
||||
*/
|
||||
static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,
|
||||
int bits, int offs, int reserved_block_code)
|
||||
static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
|
||||
int bits, int offs, int reserved_block_code)
|
||||
{
|
||||
int res, i, j, act = 0;
|
||||
struct nand_chip *this = mtd->priv;
|
||||
@ -152,17 +152,17 @@ static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,
|
||||
uint8_t msk = (uint8_t) ((1 << bits) - 1);
|
||||
|
||||
totlen = (num * bits) >> 3;
|
||||
from = ((loff_t)page) << this->page_shift;
|
||||
from = ((loff_t) page) << this->page_shift;
|
||||
|
||||
while (totlen) {
|
||||
len = min (totlen, (size_t) (1 << this->bbt_erase_shift));
|
||||
res = mtd->read_ecc (mtd, from, len, &retlen, buf, NULL, this->autooob);
|
||||
len = min(totlen, (size_t) (1 << this->bbt_erase_shift));
|
||||
res = mtd->read(mtd, from, len, &retlen, buf);
|
||||
if (res < 0) {
|
||||
if (retlen != len) {
|
||||
printk (KERN_INFO "nand_bbt: Error reading bad block table\n");
|
||||
printk(KERN_INFO "nand_bbt: Error reading bad block table\n");
|
||||
return res;
|
||||
}
|
||||
printk (KERN_WARNING "nand_bbt: ECC error while reading bad block table\n");
|
||||
printk(KERN_WARNING "nand_bbt: ECC error while reading bad block table\n");
|
||||
}
|
||||
|
||||
/* Analyse data */
|
||||
@ -172,22 +172,23 @@ static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,
|
||||
uint8_t tmp = (dat >> j) & msk;
|
||||
if (tmp == msk)
|
||||
continue;
|
||||
if (reserved_block_code &&
|
||||
(tmp == reserved_block_code)) {
|
||||
printk (KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n",
|
||||
((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
|
||||
if (reserved_block_code && (tmp == reserved_block_code)) {
|
||||
printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n",
|
||||
((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
|
||||
this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
|
||||
mtd->ecc_stats.bbtblocks++;
|
||||
continue;
|
||||
}
|
||||
/* Leave it for now, if its matured we can move this
|
||||
* message to MTD_DEBUG_LEVEL0 */
|
||||
printk (KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n",
|
||||
((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
|
||||
printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n",
|
||||
((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
|
||||
/* Factory marked bad or worn out ? */
|
||||
if (tmp == 0)
|
||||
this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
|
||||
else
|
||||
this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06);
|
||||
mtd->ecc_stats.badblocks++;
|
||||
}
|
||||
}
|
||||
totlen -= len;
|
||||
@ -207,7 +208,7 @@ static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,
|
||||
* Read the bad block table for all chips starting at a given page
|
||||
* We assume that the bbt bits are in consecutive order.
|
||||
*/
|
||||
static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
|
||||
static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
int res = 0, i;
|
||||
@ -231,6 +232,42 @@ static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan read raw data from flash
|
||||
*/
|
||||
static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
|
||||
size_t len)
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
|
||||
ops.mode = MTD_OOB_RAW;
|
||||
ops.ooboffs = 0;
|
||||
ops.ooblen = mtd->oobsize;
|
||||
ops.oobbuf = buf;
|
||||
ops.datbuf = buf;
|
||||
ops.len = len;
|
||||
|
||||
return mtd->read_oob(mtd, offs, &ops);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan write data with oob to flash
|
||||
*/
|
||||
static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
uint8_t *buf, uint8_t *oob)
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
|
||||
ops.mode = MTD_OOB_PLACE;
|
||||
ops.ooboffs = 0;
|
||||
ops.ooblen = mtd->oobsize;
|
||||
ops.datbuf = buf;
|
||||
ops.oobbuf = oob;
|
||||
ops.len = len;
|
||||
|
||||
return mtd->write_oob(mtd, offs, &ops);
|
||||
}
|
||||
|
||||
/**
|
||||
* read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
|
||||
* @mtd: MTD device structure
|
||||
@ -242,28 +279,85 @@ static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des
|
||||
* We assume that the bbt bits are in consecutive order.
|
||||
*
|
||||
*/
|
||||
static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td,
|
||||
struct nand_bbt_descr *md)
|
||||
static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
|
||||
struct nand_bbt_descr *td, struct nand_bbt_descr *md)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
/* Read the primary version, if available */
|
||||
if (td->options & NAND_BBT_VERSION) {
|
||||
nand_read_raw (mtd, buf, td->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize);
|
||||
td->version[0] = buf[mtd->oobblock + td->veroffs];
|
||||
printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]);
|
||||
scan_read_raw(mtd, buf, td->pages[0] << this->page_shift,
|
||||
mtd->writesize);
|
||||
td->version[0] = buf[mtd->writesize + td->veroffs];
|
||||
printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
|
||||
td->pages[0], td->version[0]);
|
||||
}
|
||||
|
||||
/* Read the mirror version, if available */
|
||||
if (md && (md->options & NAND_BBT_VERSION)) {
|
||||
nand_read_raw (mtd, buf, md->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize);
|
||||
md->version[0] = buf[mtd->oobblock + md->veroffs];
|
||||
printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]);
|
||||
scan_read_raw(mtd, buf, md->pages[0] << this->page_shift,
|
||||
mtd->writesize);
|
||||
md->version[0] = buf[mtd->writesize + md->veroffs];
|
||||
printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
|
||||
md->pages[0], md->version[0]);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan a given block full
|
||||
*/
|
||||
static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,
|
||||
loff_t offs, uint8_t *buf, size_t readlen,
|
||||
int scanlen, int len)
|
||||
{
|
||||
int ret, j;
|
||||
|
||||
ret = scan_read_raw(mtd, buf, offs, readlen);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (j = 0; j < len; j++, buf += scanlen) {
|
||||
if (check_pattern(buf, scanlen, mtd->writesize, bd))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan a given block partially
|
||||
*/
|
||||
static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
|
||||
loff_t offs, uint8_t *buf, int len)
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
int j, ret;
|
||||
|
||||
ops.len = mtd->oobsize;
|
||||
ops.ooblen = mtd->oobsize;
|
||||
ops.oobbuf = buf;
|
||||
ops.ooboffs = 0;
|
||||
ops.datbuf = NULL;
|
||||
ops.mode = MTD_OOB_PLACE;
|
||||
|
||||
for (j = 0; j < len; j++) {
|
||||
/*
|
||||
* Read the full oob until read_oob is fixed to
|
||||
* handle single byte reads for 16 bit
|
||||
* buswidth
|
||||
*/
|
||||
ret = mtd->read_oob(mtd, offs, &ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (check_short_pattern(buf, bd))
|
||||
return 1;
|
||||
|
||||
offs += mtd->writesize;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* create_bbt - [GENERIC] Create a bad block table by scanning the device
|
||||
* @mtd: MTD device structure
|
||||
@ -275,15 +369,16 @@ 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 int 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;
|
||||
int i, numblocks, len, scanlen;
|
||||
int startblock;
|
||||
loff_t from;
|
||||
size_t readlen, ooblen;
|
||||
size_t readlen;
|
||||
|
||||
printk (KERN_INFO "Scanning device for bad blocks\n");
|
||||
printk(KERN_INFO "Scanning device for bad blocks\n");
|
||||
|
||||
if (bd->options & NAND_BBT_SCANALLPAGES)
|
||||
len = 1 << (this->bbt_erase_shift - this->page_shift);
|
||||
@ -296,25 +391,24 @@ static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
||||
|
||||
if (!(bd->options & NAND_BBT_SCANEMPTY)) {
|
||||
/* We need only read few bytes from the OOB area */
|
||||
scanlen = ooblen = 0;
|
||||
scanlen = 0;
|
||||
readlen = bd->len;
|
||||
} else {
|
||||
/* Full page content should be read */
|
||||
scanlen = mtd->oobblock + mtd->oobsize;
|
||||
readlen = len * mtd->oobblock;
|
||||
ooblen = len * mtd->oobsize;
|
||||
scanlen = mtd->writesize + mtd->oobsize;
|
||||
readlen = len * mtd->writesize;
|
||||
}
|
||||
|
||||
if (chip == -1) {
|
||||
/* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it
|
||||
* makes shifting and masking less painful */
|
||||
/* Note that numblocks is 2 * (real numblocks) here, see i+=2
|
||||
* below as it makes shifting and masking less painful */
|
||||
numblocks = mtd->size >> (this->bbt_erase_shift - 1);
|
||||
startblock = 0;
|
||||
from = 0;
|
||||
} else {
|
||||
if (chip >= this->numchips) {
|
||||
printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n",
|
||||
chip + 1, this->numchips);
|
||||
printk(KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n",
|
||||
chip + 1, this->numchips);
|
||||
return -EINVAL;
|
||||
}
|
||||
numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
|
||||
@ -326,36 +420,22 @@ static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
||||
for (i = startblock; i < numblocks;) {
|
||||
int ret;
|
||||
|
||||
if (bd->options & NAND_BBT_SCANEMPTY)
|
||||
if ((ret = nand_read_raw (mtd, buf, from, readlen, ooblen)))
|
||||
return ret;
|
||||
if (bd->options & NAND_BBT_SCANALLPAGES)
|
||||
ret = scan_block_full(mtd, bd, from, buf, readlen,
|
||||
scanlen, len);
|
||||
else
|
||||
ret = scan_block_fast(mtd, bd, from, buf, len);
|
||||
|
||||
for (j = 0; j < len; j++) {
|
||||
if (!(bd->options & NAND_BBT_SCANEMPTY)) {
|
||||
size_t retlen;
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Read the full oob until read_oob is fixed to
|
||||
* handle single byte reads for 16 bit buswidth */
|
||||
ret = mtd->read_oob(mtd, from + j * mtd->oobblock,
|
||||
mtd->oobsize, &retlen, buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (check_short_pattern (buf, 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;
|
||||
}
|
||||
}
|
||||
if (ret) {
|
||||
this->bbt[i >> 3] |= 0x03 << (i & 0x6);
|
||||
printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
|
||||
i >> 1, (unsigned int)from);
|
||||
mtd->ecc_stats.badblocks++;
|
||||
}
|
||||
|
||||
i += 2;
|
||||
from += (1 << this->bbt_erase_shift);
|
||||
}
|
||||
@ -374,22 +454,23 @@ static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
||||
* block.
|
||||
* If the option NAND_BBT_PERCHIP is given, each chip is searched
|
||||
* for a bbt, which contains the bad block information of this chip.
|
||||
* This is neccecary to provide support for certain DOC devices.
|
||||
* This is necessary to provide support for certain DOC devices.
|
||||
*
|
||||
* The bbt ident pattern resides in the oob area of the first page
|
||||
* in a block.
|
||||
*/
|
||||
static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
|
||||
static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
int i, chips;
|
||||
int bits, startblock, block, dir;
|
||||
int scanlen = mtd->oobblock + mtd->oobsize;
|
||||
int scanlen = mtd->writesize + mtd->oobsize;
|
||||
int bbtblocks;
|
||||
int blocktopage = this->bbt_erase_shift - this->page_shift;
|
||||
|
||||
/* Search direction top -> down ? */
|
||||
if (td->options & NAND_BBT_LASTBLOCK) {
|
||||
startblock = (mtd->size >> this->bbt_erase_shift) -1;
|
||||
startblock = (mtd->size >> this->bbt_erase_shift) - 1;
|
||||
dir = -1;
|
||||
} else {
|
||||
startblock = 0;
|
||||
@ -415,13 +496,16 @@ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
||||
td->pages[i] = -1;
|
||||
/* Scan the maximum number of blocks */
|
||||
for (block = 0; block < td->maxblocks; block++) {
|
||||
|
||||
int actblock = startblock + dir * block;
|
||||
loff_t offs = actblock << this->bbt_erase_shift;
|
||||
|
||||
/* Read first page */
|
||||
nand_read_raw (mtd, buf, actblock << this->bbt_erase_shift, mtd->oobblock, mtd->oobsize);
|
||||
if (!check_pattern(buf, scanlen, mtd->oobblock, td)) {
|
||||
td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift);
|
||||
scan_read_raw(mtd, buf, offs, mtd->writesize);
|
||||
if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
|
||||
td->pages[i] = actblock << blocktopage;
|
||||
if (td->options & NAND_BBT_VERSION) {
|
||||
td->version[i] = buf[mtd->oobblock + td->veroffs];
|
||||
td->version[i] = buf[mtd->writesize + td->veroffs];
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -431,9 +515,10 @@ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
||||
/* Check, if we found a bbt for each requested chip */
|
||||
for (i = 0; i < chips; i++) {
|
||||
if (td->pages[i] == -1)
|
||||
printk (KERN_WARNING "Bad block table not found for chip %d\n", i);
|
||||
printk(KERN_WARNING "Bad block table not found for chip %d\n", i);
|
||||
else
|
||||
printk (KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i], td->version[i]);
|
||||
printk(KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i],
|
||||
td->version[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -447,21 +532,19 @@ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
||||
*
|
||||
* Search and read the bad block table(s)
|
||||
*/
|
||||
static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf,
|
||||
struct nand_bbt_descr *td, struct nand_bbt_descr *md)
|
||||
static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md)
|
||||
{
|
||||
/* Search the primary table */
|
||||
search_bbt (mtd, buf, td);
|
||||
search_bbt(mtd, buf, td);
|
||||
|
||||
/* Search the mirror table */
|
||||
if (md)
|
||||
search_bbt (mtd, buf, md);
|
||||
search_bbt(mtd, buf, md);
|
||||
|
||||
/* Force result check */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* write_bbt - [GENERIC] (Re)write the bad block table
|
||||
*
|
||||
@ -474,25 +557,31 @@ static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf,
|
||||
* (Re)write the bad block table
|
||||
*
|
||||
*/
|
||||
static int write_bbt (struct mtd_info *mtd, uint8_t *buf,
|
||||
struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel)
|
||||
static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
|
||||
struct nand_bbt_descr *td, struct nand_bbt_descr *md,
|
||||
int chipsel)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
struct nand_oobinfo oobinfo;
|
||||
struct erase_info einfo;
|
||||
int i, j, res, chip = 0;
|
||||
int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
|
||||
int nrchips, bbtoffs, pageoffs;
|
||||
int nrchips, bbtoffs, pageoffs, ooboffs;
|
||||
uint8_t msk[4];
|
||||
uint8_t rcode = td->reserved_block_code;
|
||||
size_t retlen, len = 0;
|
||||
loff_t to;
|
||||
struct mtd_oob_ops ops;
|
||||
|
||||
ops.ooblen = mtd->oobsize;
|
||||
ops.ooboffs = 0;
|
||||
ops.datbuf = NULL;
|
||||
ops.mode = MTD_OOB_PLACE;
|
||||
|
||||
if (!rcode)
|
||||
rcode = 0xff;
|
||||
/* Write bad block table per chip rather than per device ? */
|
||||
if (td->options & NAND_BBT_PERCHIP) {
|
||||
numblocks = (int) (this->chipsize >> this->bbt_erase_shift);
|
||||
numblocks = (int)(this->chipsize >> this->bbt_erase_shift);
|
||||
/* Full device write or specific chip ? */
|
||||
if (chipsel == -1) {
|
||||
nrchips = this->numchips;
|
||||
@ -501,7 +590,7 @@ static int write_bbt (struct mtd_info *mtd, uint8_t *buf,
|
||||
chip = chipsel;
|
||||
}
|
||||
} else {
|
||||
numblocks = (int) (mtd->size >> this->bbt_erase_shift);
|
||||
numblocks = (int)(mtd->size >> this->bbt_erase_shift);
|
||||
nrchips = 1;
|
||||
}
|
||||
|
||||
@ -530,27 +619,38 @@ static int write_bbt (struct mtd_info *mtd, uint8_t *buf,
|
||||
for (i = 0; i < td->maxblocks; i++) {
|
||||
int block = startblock + dir * i;
|
||||
/* Check, if the block is bad */
|
||||
switch ((this->bbt[block >> 2] >> (2 * (block & 0x03))) & 0x03) {
|
||||
switch ((this->bbt[block >> 2] >>
|
||||
(2 * (block & 0x03))) & 0x03) {
|
||||
case 0x01:
|
||||
case 0x03:
|
||||
continue;
|
||||
}
|
||||
page = block << (this->bbt_erase_shift - this->page_shift);
|
||||
page = block <<
|
||||
(this->bbt_erase_shift - this->page_shift);
|
||||
/* Check, if the block is used by the mirror table */
|
||||
if (!md || md->pages[chip] != page)
|
||||
goto write;
|
||||
}
|
||||
printk (KERN_ERR "No space left to write bad block table\n");
|
||||
printk(KERN_ERR "No space left to write bad block table\n");
|
||||
return -ENOSPC;
|
||||
write:
|
||||
write:
|
||||
|
||||
/* Set up shift count and masks for the flash table */
|
||||
bits = td->options & NAND_BBT_NRBITS_MSK;
|
||||
msk[2] = ~rcode;
|
||||
switch (bits) {
|
||||
case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x01; break;
|
||||
case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x03; break;
|
||||
case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; msk[2] = ~rcode; msk[3] = 0x0f; break;
|
||||
case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; msk[2] = ~rcode; msk[3] = 0xff; break;
|
||||
case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01;
|
||||
msk[3] = 0x01;
|
||||
break;
|
||||
case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01;
|
||||
msk[3] = 0x03;
|
||||
break;
|
||||
case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C;
|
||||
msk[3] = 0x0f;
|
||||
break;
|
||||
case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F;
|
||||
msk[3] = 0xff;
|
||||
break;
|
||||
default: return -EINVAL;
|
||||
}
|
||||
|
||||
@ -558,82 +658,92 @@ write:
|
||||
|
||||
to = ((loff_t) page) << this->page_shift;
|
||||
|
||||
memcpy (&oobinfo, this->autooob, sizeof(oobinfo));
|
||||
oobinfo.useecc = MTD_NANDECC_PLACEONLY;
|
||||
|
||||
/* Must we save the block contents ? */
|
||||
if (td->options & NAND_BBT_SAVECONTENT) {
|
||||
/* Make it block aligned */
|
||||
to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1));
|
||||
len = 1 << this->bbt_erase_shift;
|
||||
res = mtd->read_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo);
|
||||
res = mtd->read(mtd, to, len, &retlen, buf);
|
||||
if (res < 0) {
|
||||
if (retlen != len) {
|
||||
printk (KERN_INFO "nand_bbt: Error reading block for writing the bad block table\n");
|
||||
printk(KERN_INFO "nand_bbt: Error "
|
||||
"reading block for writing "
|
||||
"the bad block table\n");
|
||||
return res;
|
||||
}
|
||||
printk (KERN_WARNING "nand_bbt: ECC error while reading block for writing bad block table\n");
|
||||
printk(KERN_WARNING "nand_bbt: ECC error "
|
||||
"while reading block for writing "
|
||||
"bad block table\n");
|
||||
}
|
||||
/* Read oob data */
|
||||
ops.len = (len >> this->page_shift) * mtd->oobsize;
|
||||
ops.oobbuf = &buf[len];
|
||||
res = mtd->read_oob(mtd, to + mtd->writesize, &ops);
|
||||
if (res < 0 || ops.retlen != ops.len)
|
||||
goto outerr;
|
||||
|
||||
/* Calc the byte offset in the buffer */
|
||||
pageoffs = page - (int)(to >> this->page_shift);
|
||||
offs = pageoffs << this->page_shift;
|
||||
/* Preset the bbt area with 0xff */
|
||||
memset (&buf[offs], 0xff, (size_t)(numblocks >> sft));
|
||||
/* Preset the bbt's oob area with 0xff */
|
||||
memset (&buf[len + pageoffs * mtd->oobsize], 0xff,
|
||||
((len >> this->page_shift) - pageoffs) * mtd->oobsize);
|
||||
if (td->options & NAND_BBT_VERSION) {
|
||||
buf[len + (pageoffs * mtd->oobsize) + td->veroffs] = td->version[chip];
|
||||
}
|
||||
memset(&buf[offs], 0xff, (size_t) (numblocks >> sft));
|
||||
ooboffs = len + (pageoffs * mtd->oobsize);
|
||||
|
||||
} else {
|
||||
/* Calc length */
|
||||
len = (size_t) (numblocks >> sft);
|
||||
/* Make it page aligned ! */
|
||||
len = (len + (mtd->oobblock-1)) & ~(mtd->oobblock-1);
|
||||
len = (len + (mtd->writesize - 1)) &
|
||||
~(mtd->writesize - 1);
|
||||
/* Preset the buffer with 0xff */
|
||||
memset (buf, 0xff, len + (len >> this->page_shift) * mtd->oobsize);
|
||||
memset(buf, 0xff, len +
|
||||
(len >> this->page_shift)* mtd->oobsize);
|
||||
offs = 0;
|
||||
ooboffs = len;
|
||||
/* Pattern is located in oob area of first page */
|
||||
memcpy (&buf[len + td->offs], td->pattern, td->len);
|
||||
if (td->options & NAND_BBT_VERSION) {
|
||||
buf[len + td->veroffs] = td->version[chip];
|
||||
}
|
||||
memcpy(&buf[ooboffs + td->offs], td->pattern, td->len);
|
||||
}
|
||||
|
||||
if (td->options & NAND_BBT_VERSION)
|
||||
buf[ooboffs + td->veroffs] = td->version[chip];
|
||||
|
||||
/* walk through the memory table */
|
||||
for (i = 0; i < numblocks; ) {
|
||||
for (i = 0; i < numblocks;) {
|
||||
uint8_t dat;
|
||||
dat = this->bbt[bbtoffs + (i >> 2)];
|
||||
for (j = 0; j < 4; j++ , i++) {
|
||||
for (j = 0; j < 4; j++, i++) {
|
||||
int sftcnt = (i << (3 - sft)) & sftmsk;
|
||||
/* Do not store the reserved bbt blocks ! */
|
||||
buf[offs + (i >> sft)] &= ~(msk[dat & 0x03] << sftcnt);
|
||||
buf[offs + (i >> sft)] &=
|
||||
~(msk[dat & 0x03] << sftcnt);
|
||||
dat >>= 2;
|
||||
}
|
||||
}
|
||||
|
||||
memset (&einfo, 0, sizeof (einfo));
|
||||
memset(&einfo, 0, sizeof(einfo));
|
||||
einfo.mtd = mtd;
|
||||
einfo.addr = (unsigned long) to;
|
||||
einfo.addr = (unsigned long)to;
|
||||
einfo.len = 1 << this->bbt_erase_shift;
|
||||
res = nand_erase_nand (mtd, &einfo, 1);
|
||||
if (res < 0) {
|
||||
printk (KERN_WARNING "nand_bbt: Error during block erase: %d\n", res);
|
||||
return res;
|
||||
}
|
||||
res = nand_erase_nand(mtd, &einfo, 1);
|
||||
if (res < 0)
|
||||
goto outerr;
|
||||
|
||||
res = mtd->write_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo);
|
||||
if (res < 0) {
|
||||
printk (KERN_WARNING "nand_bbt: Error while writing bad block table %d\n", res);
|
||||
return res;
|
||||
}
|
||||
printk (KERN_DEBUG "Bad block table written to 0x%08x, version 0x%02X\n",
|
||||
(unsigned int) to, td->version[chip]);
|
||||
res = scan_write_bbt(mtd, to, len, buf, &buf[len]);
|
||||
if (res < 0)
|
||||
goto outerr;
|
||||
|
||||
printk(KERN_DEBUG "Bad block table written to 0x%08x, version "
|
||||
"0x%02X\n", (unsigned int)to, td->version[chip]);
|
||||
|
||||
/* Mark it as used */
|
||||
td->pages[chip] = page;
|
||||
}
|
||||
return 0;
|
||||
|
||||
outerr:
|
||||
printk(KERN_WARNING
|
||||
"nand_bbt: Error while writing bad block table %d\n", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -644,27 +754,27 @@ write:
|
||||
* The function creates a memory based bbt by scanning the device
|
||||
* for manufacturer / software marked good / bad blocks
|
||||
*/
|
||||
static inline 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;
|
||||
|
||||
bd->options &= ~NAND_BBT_SCANEMPTY;
|
||||
return create_bbt (mtd, this->data_buf, bd, -1);
|
||||
return create_bbt(mtd, this->buffers.databuf, bd, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* check_create - [GENERIC] create and write bbt(s) if neccecary
|
||||
* check_create - [GENERIC] create and write bbt(s) if necessary
|
||||
* @mtd: MTD device structure
|
||||
* @buf: temporary buffer
|
||||
* @bd: descriptor for the good/bad block search pattern
|
||||
*
|
||||
* The function checks the results of the previous call to read_bbt
|
||||
* and creates / updates the bbt(s) if neccecary
|
||||
* Creation is neccecary if no bbt was found for the chip/device
|
||||
* Update is neccecary if one of the tables is missing or the
|
||||
* and creates / updates the bbt(s) if necessary
|
||||
* Creation is necessary if no bbt was found for the chip/device
|
||||
* Update is necessary if one of the tables is missing or the
|
||||
* version nr. of one table is less than the other
|
||||
*/
|
||||
static int check_create (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
|
||||
static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
|
||||
{
|
||||
int i, chips, writeops, chipsel, res;
|
||||
struct nand_chip *this = mtd->priv;
|
||||
@ -732,35 +842,35 @@ static int check_create (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des
|
||||
rd = td;
|
||||
goto writecheck;
|
||||
}
|
||||
create:
|
||||
create:
|
||||
/* Create the bad block table by scanning the device ? */
|
||||
if (!(td->options & NAND_BBT_CREATE))
|
||||
continue;
|
||||
|
||||
/* Create the table in memory by scanning the chip(s) */
|
||||
create_bbt (mtd, buf, bd, chipsel);
|
||||
create_bbt(mtd, buf, bd, chipsel);
|
||||
|
||||
td->version[i] = 1;
|
||||
if (md)
|
||||
md->version[i] = 1;
|
||||
writecheck:
|
||||
writecheck:
|
||||
/* read back first ? */
|
||||
if (rd)
|
||||
read_abs_bbt (mtd, buf, rd, chipsel);
|
||||
read_abs_bbt(mtd, buf, rd, chipsel);
|
||||
/* If they weren't versioned, read both. */
|
||||
if (rd2)
|
||||
read_abs_bbt (mtd, buf, rd2, chipsel);
|
||||
read_abs_bbt(mtd, buf, rd2, chipsel);
|
||||
|
||||
/* Write the bad block table to the device ? */
|
||||
if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
|
||||
res = write_bbt (mtd, buf, td, md, chipsel);
|
||||
res = write_bbt(mtd, buf, td, md, chipsel);
|
||||
if (res < 0)
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Write the mirror bad block table to the device ? */
|
||||
if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
|
||||
res = write_bbt (mtd, buf, md, td, chipsel);
|
||||
res = write_bbt(mtd, buf, md, td, chipsel);
|
||||
if (res < 0)
|
||||
return res;
|
||||
}
|
||||
@ -777,7 +887,7 @@ writecheck:
|
||||
* accidental erasures / writes. The regions are identified by
|
||||
* the mark 0x02.
|
||||
*/
|
||||
static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)
|
||||
static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
int i, j, chips, block, nrblocks, update;
|
||||
@ -795,7 +905,8 @@ static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)
|
||||
for (i = 0; i < chips; i++) {
|
||||
if ((td->options & NAND_BBT_ABSPAGE) ||
|
||||
!(td->options & NAND_BBT_WRITE)) {
|
||||
if (td->pages[i] == -1) continue;
|
||||
if (td->pages[i] == -1)
|
||||
continue;
|
||||
block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
|
||||
block <<= 1;
|
||||
oldval = this->bbt[(block >> 3)];
|
||||
@ -815,7 +926,8 @@ static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)
|
||||
oldval = this->bbt[(block >> 3)];
|
||||
newval = oldval | (0x2 << (block & 0x06));
|
||||
this->bbt[(block >> 3)] = newval;
|
||||
if (oldval != newval) update = 1;
|
||||
if (oldval != newval)
|
||||
update = 1;
|
||||
block += 2;
|
||||
}
|
||||
/* If we want reserved blocks to be recorded to flash, and some
|
||||
@ -840,7 +952,7 @@ static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)
|
||||
* by calling the nand_free_bbt function.
|
||||
*
|
||||
*/
|
||||
int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||
int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
int len, res = 0;
|
||||
@ -850,21 +962,21 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||
|
||||
len = mtd->size >> (this->bbt_erase_shift + 2);
|
||||
/* Allocate memory (2bit per block) */
|
||||
this->bbt = kmalloc (len, GFP_KERNEL);
|
||||
this->bbt = kmalloc(len, GFP_KERNEL);
|
||||
if (!this->bbt) {
|
||||
printk (KERN_ERR "nand_scan_bbt: Out of memory\n");
|
||||
printk(KERN_ERR "nand_scan_bbt: Out of memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* Clear the memory bad block table */
|
||||
memset (this->bbt, 0x00, len);
|
||||
memset(this->bbt, 0x00, len);
|
||||
|
||||
/* If no primary table decriptor is given, scan the device
|
||||
* to build a memory based bad block table
|
||||
*/
|
||||
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);
|
||||
printk(KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n");
|
||||
kfree(this->bbt);
|
||||
this->bbt = NULL;
|
||||
}
|
||||
return res;
|
||||
@ -873,35 +985,34 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||
/* Allocate a temporary buffer for one eraseblock incl. oob */
|
||||
len = (1 << this->bbt_erase_shift);
|
||||
len += (len >> this->page_shift) * mtd->oobsize;
|
||||
buf = kmalloc (len, GFP_KERNEL);
|
||||
buf = vmalloc(len);
|
||||
if (!buf) {
|
||||
printk (KERN_ERR "nand_bbt: Out of memory\n");
|
||||
kfree (this->bbt);
|
||||
printk(KERN_ERR "nand_bbt: Out of memory\n");
|
||||
kfree(this->bbt);
|
||||
this->bbt = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Is the bbt at a given page ? */
|
||||
if (td->options & NAND_BBT_ABSPAGE) {
|
||||
res = read_abs_bbts (mtd, buf, td, md);
|
||||
res = read_abs_bbts(mtd, buf, td, md);
|
||||
} else {
|
||||
/* Search the bad block table using a pattern in oob */
|
||||
res = search_read_bbts (mtd, buf, td, md);
|
||||
res = search_read_bbts(mtd, buf, td, md);
|
||||
}
|
||||
|
||||
if (res)
|
||||
res = check_create (mtd, buf, bd);
|
||||
res = check_create(mtd, buf, bd);
|
||||
|
||||
/* Prevent the bbt regions from erasing / writing */
|
||||
mark_bbt_region (mtd, td);
|
||||
mark_bbt_region(mtd, td);
|
||||
if (md)
|
||||
mark_bbt_region (mtd, md);
|
||||
mark_bbt_region(mtd, md);
|
||||
|
||||
kfree (buf);
|
||||
vfree(buf);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* nand_update_bbt - [NAND Interface] update bad block table(s)
|
||||
* @mtd: MTD device structure
|
||||
@ -909,7 +1020,7 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
|
||||
*
|
||||
* The function updates the bad block table(s)
|
||||
*/
|
||||
int nand_update_bbt (struct mtd_info *mtd, loff_t offs)
|
||||
int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
int len, res = 0, writeops = 0;
|
||||
@ -925,9 +1036,9 @@ int nand_update_bbt (struct mtd_info *mtd, loff_t offs)
|
||||
/* Allocate a temporary buffer for one eraseblock incl. oob */
|
||||
len = (1 << this->bbt_erase_shift);
|
||||
len += (len >> this->page_shift) * mtd->oobsize;
|
||||
buf = kmalloc (len, GFP_KERNEL);
|
||||
buf = kmalloc(len, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
printk (KERN_ERR "nand_update_bbt: Out of memory\n");
|
||||
printk(KERN_ERR "nand_update_bbt: Out of memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -935,7 +1046,7 @@ int nand_update_bbt (struct mtd_info *mtd, loff_t offs)
|
||||
|
||||
/* Do we have a bbt per chip ? */
|
||||
if (td->options & NAND_BBT_PERCHIP) {
|
||||
chip = (int) (offs >> this->chip_shift);
|
||||
chip = (int)(offs >> this->chip_shift);
|
||||
chipsel = chip;
|
||||
} else {
|
||||
chip = 0;
|
||||
@ -948,17 +1059,17 @@ int nand_update_bbt (struct mtd_info *mtd, loff_t offs)
|
||||
|
||||
/* Write the bad block table to the device ? */
|
||||
if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
|
||||
res = write_bbt (mtd, buf, td, md, chipsel);
|
||||
res = write_bbt(mtd, buf, td, md, chipsel);
|
||||
if (res < 0)
|
||||
goto out;
|
||||
}
|
||||
/* Write the mirror bad block table to the device ? */
|
||||
if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
|
||||
res = write_bbt (mtd, buf, md, td, chipsel);
|
||||
res = write_bbt(mtd, buf, md, td, chipsel);
|
||||
}
|
||||
|
||||
out:
|
||||
kfree (buf);
|
||||
out:
|
||||
kfree(buf);
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -981,14 +1092,14 @@ static struct nand_bbt_descr largepage_memorybased = {
|
||||
};
|
||||
|
||||
static struct nand_bbt_descr smallpage_flashbased = {
|
||||
.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
|
||||
.options = NAND_BBT_SCAN2NDPAGE,
|
||||
.offs = 5,
|
||||
.len = 1,
|
||||
.pattern = scan_ff_pattern
|
||||
};
|
||||
|
||||
static struct nand_bbt_descr largepage_flashbased = {
|
||||
.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
|
||||
.options = NAND_BBT_SCAN2NDPAGE,
|
||||
.offs = 0,
|
||||
.len = 2,
|
||||
.pattern = scan_ff_pattern
|
||||
@ -1036,7 +1147,7 @@ static struct nand_bbt_descr bbt_mirror_descr = {
|
||||
* support for the device and calls the nand_scan_bbt function
|
||||
*
|
||||
*/
|
||||
int nand_default_bbt (struct mtd_info *mtd)
|
||||
int nand_default_bbt(struct mtd_info *mtd)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
@ -1046,7 +1157,7 @@ int nand_default_bbt (struct mtd_info *mtd)
|
||||
* of the good / bad information, so we _must_ store
|
||||
* this information in a good / bad table during
|
||||
* startup
|
||||
*/
|
||||
*/
|
||||
if (this->options & NAND_IS_AND) {
|
||||
/* Use the default pattern descriptors */
|
||||
if (!this->bbt_td) {
|
||||
@ -1054,10 +1165,9 @@ int nand_default_bbt (struct mtd_info *mtd)
|
||||
this->bbt_md = &bbt_mirror_descr;
|
||||
}
|
||||
this->options |= NAND_USE_FLASH_BBT;
|
||||
return nand_scan_bbt (mtd, &agand_flashbased);
|
||||
return nand_scan_bbt(mtd, &agand_flashbased);
|
||||
}
|
||||
|
||||
|
||||
/* Is a flash based bad block table requested ? */
|
||||
if (this->options & NAND_USE_FLASH_BBT) {
|
||||
/* Use the default pattern descriptors */
|
||||
@ -1066,18 +1176,17 @@ int nand_default_bbt (struct mtd_info *mtd)
|
||||
this->bbt_md = &bbt_mirror_descr;
|
||||
}
|
||||
if (!this->badblock_pattern) {
|
||||
this->badblock_pattern = (mtd->oobblock > 512) ?
|
||||
&largepage_flashbased : &smallpage_flashbased;
|
||||
this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased;
|
||||
}
|
||||
} else {
|
||||
this->bbt_td = NULL;
|
||||
this->bbt_md = NULL;
|
||||
if (!this->badblock_pattern) {
|
||||
this->badblock_pattern = (mtd->oobblock > 512) ?
|
||||
&largepage_memorybased : &smallpage_memorybased;
|
||||
this->badblock_pattern = (mtd->writesize > 512) ?
|
||||
&largepage_memorybased : &smallpage_memorybased;
|
||||
}
|
||||
}
|
||||
return nand_scan_bbt (mtd, this->badblock_pattern);
|
||||
return nand_scan_bbt(mtd, this->badblock_pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1087,26 +1196,29 @@ int nand_default_bbt (struct mtd_info *mtd)
|
||||
* @allowbbt: allow access to bad block table region
|
||||
*
|
||||
*/
|
||||
int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt)
|
||||
int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
int block;
|
||||
uint8_t res;
|
||||
uint8_t res;
|
||||
|
||||
/* Get block number * 2 */
|
||||
block = (int) (offs >> (this->bbt_erase_shift - 1));
|
||||
block = (int)(offs >> (this->bbt_erase_shift - 1));
|
||||
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, block >> 1, res);
|
||||
DEBUG(MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
|
||||
(unsigned int)offs, block >> 1, res);
|
||||
|
||||
switch ((int)res) {
|
||||
case 0x00: return 0;
|
||||
case 0x01: return 1;
|
||||
case 0x02: return allowbbt ? 0 : 1;
|
||||
case 0x00:
|
||||
return 0;
|
||||
case 0x01:
|
||||
return 1;
|
||||
case 0x02:
|
||||
return allowbbt ? 0 : 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL (nand_scan_bbt);
|
||||
EXPORT_SYMBOL (nand_default_bbt);
|
||||
EXPORT_SYMBOL(nand_scan_bbt);
|
||||
EXPORT_SYMBOL(nand_default_bbt);
|
||||
|
@ -7,6 +7,8 @@
|
||||
* Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
|
||||
* Toshiba America Electronics Components, Inc.
|
||||
*
|
||||
* Copyright (C) 2006 Thomas Gleixner <tglx@linutronix.de>
|
||||
*
|
||||
* $Id: nand_ecc.c,v 1.15 2005/11/07 11:14:30 gleixner Exp $
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify it
|
||||
@ -62,90 +64,76 @@ static const u_char nand_ecc_precalc_table[] = {
|
||||
0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* nand_trans_result - [GENERIC] create non-inverted ECC
|
||||
* @reg2: line parity reg 2
|
||||
* @reg3: line parity reg 3
|
||||
* @ecc_code: ecc
|
||||
*
|
||||
* Creates non-inverted ECC code from line parity
|
||||
*/
|
||||
static void nand_trans_result(u_char reg2, u_char reg3,
|
||||
u_char *ecc_code)
|
||||
{
|
||||
u_char a, b, i, tmp1, tmp2;
|
||||
|
||||
/* Initialize variables */
|
||||
a = b = 0x80;
|
||||
tmp1 = tmp2 = 0;
|
||||
|
||||
/* Calculate first ECC byte */
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (reg3 & a) /* LP15,13,11,9 --> ecc_code[0] */
|
||||
tmp1 |= b;
|
||||
b >>= 1;
|
||||
if (reg2 & a) /* LP14,12,10,8 --> ecc_code[0] */
|
||||
tmp1 |= b;
|
||||
b >>= 1;
|
||||
a >>= 1;
|
||||
}
|
||||
|
||||
/* Calculate second ECC byte */
|
||||
b = 0x80;
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (reg3 & a) /* LP7,5,3,1 --> ecc_code[1] */
|
||||
tmp2 |= b;
|
||||
b >>= 1;
|
||||
if (reg2 & a) /* LP6,4,2,0 --> ecc_code[1] */
|
||||
tmp2 |= b;
|
||||
b >>= 1;
|
||||
a >>= 1;
|
||||
}
|
||||
|
||||
/* Store two of the ECC bytes */
|
||||
ecc_code[0] = tmp1;
|
||||
ecc_code[1] = tmp2;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for 256 byte block
|
||||
* nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code
|
||||
* for 256 byte block
|
||||
* @mtd: MTD block structure
|
||||
* @dat: raw data
|
||||
* @ecc_code: buffer for ECC
|
||||
*/
|
||||
int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
|
||||
int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
|
||||
u_char *ecc_code)
|
||||
{
|
||||
u_char idx, reg1, reg2, reg3;
|
||||
int j;
|
||||
uint8_t idx, reg1, reg2, reg3, tmp1, tmp2;
|
||||
int i;
|
||||
|
||||
/* Initialize variables */
|
||||
reg1 = reg2 = reg3 = 0;
|
||||
ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;
|
||||
|
||||
/* Build up column parity */
|
||||
for(j = 0; j < 256; j++) {
|
||||
|
||||
for(i = 0; i < 256; i++) {
|
||||
/* Get CP0 - CP5 from table */
|
||||
idx = nand_ecc_precalc_table[dat[j]];
|
||||
idx = nand_ecc_precalc_table[*dat++];
|
||||
reg1 ^= (idx & 0x3f);
|
||||
|
||||
/* All bit XOR = 1 ? */
|
||||
if (idx & 0x40) {
|
||||
reg3 ^= (u_char) j;
|
||||
reg2 ^= ~((u_char) j);
|
||||
reg3 ^= (uint8_t) i;
|
||||
reg2 ^= ~((uint8_t) i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create non-inverted ECC code from line parity */
|
||||
nand_trans_result(reg2, reg3, ecc_code);
|
||||
tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */
|
||||
tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */
|
||||
tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */
|
||||
tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */
|
||||
tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */
|
||||
tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */
|
||||
tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */
|
||||
tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */
|
||||
|
||||
tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */
|
||||
tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */
|
||||
tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */
|
||||
tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */
|
||||
tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */
|
||||
tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */
|
||||
tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */
|
||||
tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */
|
||||
|
||||
/* Calculate final ECC code */
|
||||
ecc_code[0] = ~ecc_code[0];
|
||||
ecc_code[1] = ~ecc_code[1];
|
||||
#ifdef CONFIG_NAND_ECC_SMC
|
||||
ecc_code[0] = ~tmp2;
|
||||
ecc_code[1] = ~tmp1;
|
||||
#else
|
||||
ecc_code[0] = ~tmp1;
|
||||
ecc_code[1] = ~tmp2;
|
||||
#endif
|
||||
ecc_code[2] = ((~reg1) << 2) | 0x03;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(nand_calculate_ecc);
|
||||
|
||||
static inline int countbits(uint32_t byte)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
for (;byte; byte >>= 1)
|
||||
res += byte & 0x01;
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* nand_correct_data - [NAND Interface] Detect and correct bit error(s)
|
||||
@ -156,93 +144,54 @@ int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code
|
||||
*
|
||||
* Detect and correct a 1 bit error for 256 byte block
|
||||
*/
|
||||
int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
|
||||
int nand_correct_data(struct mtd_info *mtd, u_char *dat,
|
||||
u_char *read_ecc, u_char *calc_ecc)
|
||||
{
|
||||
u_char a, b, c, d1, d2, d3, add, bit, i;
|
||||
uint8_t s0, s1, s2;
|
||||
|
||||
/* Do error detection */
|
||||
d1 = calc_ecc[0] ^ read_ecc[0];
|
||||
d2 = calc_ecc[1] ^ read_ecc[1];
|
||||
d3 = calc_ecc[2] ^ read_ecc[2];
|
||||
|
||||
if ((d1 | d2 | d3) == 0) {
|
||||
/* No errors */
|
||||
#ifdef CONFIG_NAND_ECC_SMC
|
||||
s0 = calc_ecc[0] ^ read_ecc[0];
|
||||
s1 = calc_ecc[1] ^ read_ecc[1];
|
||||
s2 = calc_ecc[2] ^ read_ecc[2];
|
||||
#else
|
||||
s1 = calc_ecc[0] ^ read_ecc[0];
|
||||
s0 = calc_ecc[1] ^ read_ecc[1];
|
||||
s2 = calc_ecc[2] ^ read_ecc[2];
|
||||
#endif
|
||||
if ((s0 | s1 | s2) == 0)
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
a = (d1 ^ (d1 >> 1)) & 0x55;
|
||||
b = (d2 ^ (d2 >> 1)) & 0x55;
|
||||
c = (d3 ^ (d3 >> 1)) & 0x54;
|
||||
|
||||
/* Found and will correct single bit error in the data */
|
||||
if ((a == 0x55) && (b == 0x55) && (c == 0x54)) {
|
||||
c = 0x80;
|
||||
add = 0;
|
||||
a = 0x80;
|
||||
for (i=0; i<4; i++) {
|
||||
if (d1 & c)
|
||||
add |= a;
|
||||
c >>= 2;
|
||||
a >>= 1;
|
||||
}
|
||||
c = 0x80;
|
||||
for (i=0; i<4; i++) {
|
||||
if (d2 & c)
|
||||
add |= a;
|
||||
c >>= 2;
|
||||
a >>= 1;
|
||||
}
|
||||
bit = 0;
|
||||
b = 0x04;
|
||||
c = 0x80;
|
||||
for (i=0; i<3; i++) {
|
||||
if (d3 & c)
|
||||
bit |= b;
|
||||
c >>= 2;
|
||||
b >>= 1;
|
||||
}
|
||||
b = 0x01;
|
||||
a = dat[add];
|
||||
a ^= (b << bit);
|
||||
dat[add] = a;
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
i = 0;
|
||||
while (d1) {
|
||||
if (d1 & 0x01)
|
||||
++i;
|
||||
d1 >>= 1;
|
||||
}
|
||||
while (d2) {
|
||||
if (d2 & 0x01)
|
||||
++i;
|
||||
d2 >>= 1;
|
||||
}
|
||||
while (d3) {
|
||||
if (d3 & 0x01)
|
||||
++i;
|
||||
d3 >>= 1;
|
||||
}
|
||||
if (i == 1) {
|
||||
/* ECC Code Error Correction */
|
||||
read_ecc[0] = calc_ecc[0];
|
||||
read_ecc[1] = calc_ecc[1];
|
||||
read_ecc[2] = calc_ecc[2];
|
||||
return 2;
|
||||
}
|
||||
else {
|
||||
/* Uncorrectable Error */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/* Check for a single bit error */
|
||||
if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 &&
|
||||
((s1 ^ (s1 >> 1)) & 0x55) == 0x55 &&
|
||||
((s2 ^ (s2 >> 1)) & 0x54) == 0x54) {
|
||||
|
||||
uint32_t byteoffs, bitnum;
|
||||
|
||||
byteoffs = (s1 << 0) & 0x80;
|
||||
byteoffs |= (s1 << 1) & 0x40;
|
||||
byteoffs |= (s1 << 2) & 0x20;
|
||||
byteoffs |= (s1 << 3) & 0x10;
|
||||
|
||||
byteoffs |= (s0 >> 4) & 0x08;
|
||||
byteoffs |= (s0 >> 3) & 0x04;
|
||||
byteoffs |= (s0 >> 2) & 0x02;
|
||||
byteoffs |= (s0 >> 1) & 0x01;
|
||||
|
||||
bitnum = (s2 >> 5) & 0x04;
|
||||
bitnum |= (s2 >> 4) & 0x02;
|
||||
bitnum |= (s2 >> 3) & 0x01;
|
||||
|
||||
dat[byteoffs] ^= (1 << bitnum);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Should never happen */
|
||||
if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1)
|
||||
return 1;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(nand_calculate_ecc);
|
||||
EXPORT_SYMBOL(nand_correct_data);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -18,99 +18,110 @@
|
||||
* Name. ID code, pagesize, chipsize in MegaByte, eraseblock size,
|
||||
* options
|
||||
*
|
||||
* Pagesize; 0, 256, 512
|
||||
* 0 get this information from the extended chip ID
|
||||
* Pagesize; 0, 256, 512
|
||||
* 0 get this information from the extended chip ID
|
||||
+ 256 256 Byte page size
|
||||
* 512 512 Byte page size
|
||||
*/
|
||||
struct nand_flash_dev nand_flash_ids[] = {
|
||||
{"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0},
|
||||
{"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0},
|
||||
{"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0},
|
||||
{"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0},
|
||||
{"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0},
|
||||
{"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0},
|
||||
{"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0},
|
||||
{"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0},
|
||||
{"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0},
|
||||
{"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0},
|
||||
{"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0},
|
||||
{"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0},
|
||||
{"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0},
|
||||
{"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0},
|
||||
{"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0},
|
||||
{"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0},
|
||||
{"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0},
|
||||
{"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0},
|
||||
{"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0},
|
||||
{"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0},
|
||||
|
||||
{"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0},
|
||||
{"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0},
|
||||
{"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},
|
||||
{"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16},
|
||||
{"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0},
|
||||
{"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0},
|
||||
{"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},
|
||||
{"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16},
|
||||
|
||||
{"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0},
|
||||
{"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0},
|
||||
{"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16},
|
||||
{"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16},
|
||||
{"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0},
|
||||
{"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0},
|
||||
{"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16},
|
||||
{"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16},
|
||||
|
||||
{"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0},
|
||||
{"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0},
|
||||
{"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16},
|
||||
{"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16},
|
||||
{"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0},
|
||||
{"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0},
|
||||
{"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16},
|
||||
{"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16},
|
||||
|
||||
{"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0},
|
||||
{"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0},
|
||||
{"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16},
|
||||
{"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16},
|
||||
{"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0},
|
||||
{"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0},
|
||||
{"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16},
|
||||
{"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 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 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0},
|
||||
|
||||
/*
|
||||
* These are the new chips with large page size. The pagesize and the
|
||||
* erasesize is determined from the extended id bytes
|
||||
*/
|
||||
#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR)
|
||||
#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16)
|
||||
|
||||
/* 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},
|
||||
{"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS},
|
||||
{"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS},
|
||||
{"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16},
|
||||
{"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16},
|
||||
|
||||
/* 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},
|
||||
{"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||
{"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||
{"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS},
|
||||
{"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS},
|
||||
{"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16},
|
||||
{"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16},
|
||||
|
||||
/* 2 Gigabit */
|
||||
{"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||
{"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||
{"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||
{"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||
{"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS},
|
||||
{"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS},
|
||||
{"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, LP_OPTIONS16},
|
||||
{"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, LP_OPTIONS16},
|
||||
|
||||
/* 4 Gigabit */
|
||||
{"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||
{"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||
{"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||
{"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||
{"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, LP_OPTIONS},
|
||||
{"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, LP_OPTIONS},
|
||||
{"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, LP_OPTIONS16},
|
||||
{"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, LP_OPTIONS16},
|
||||
|
||||
/* 8 Gigabit */
|
||||
{"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||
{"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||
{"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||
{"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||
{"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, LP_OPTIONS},
|
||||
{"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, LP_OPTIONS},
|
||||
{"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, LP_OPTIONS16},
|
||||
{"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, LP_OPTIONS16},
|
||||
|
||||
/* 16 Gigabit */
|
||||
{"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||
{"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
|
||||
{"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||
{"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
|
||||
{"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, LP_OPTIONS},
|
||||
{"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS},
|
||||
{"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16},
|
||||
{"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16},
|
||||
|
||||
/* Renesas AND 1 Gigabit. Those chips do not support extended id and have a strange page/block layout !
|
||||
* The chosen minimum erasesize is 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page planes
|
||||
* 1 block = 2 pages, but due to plane arrangement the blocks 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7
|
||||
* 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
|
||||
/*
|
||||
* Renesas AND 1 Gigabit. Those chips do not support extended id and
|
||||
* have a strange page/block layout ! The chosen minimum erasesize is
|
||||
* 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page
|
||||
* planes 1 block = 2 pages, but due to plane arrangement the blocks
|
||||
* 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 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 | BBT_AUTO_REFRESH},
|
||||
{"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000,
|
||||
NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY |
|
||||
BBT_AUTO_REFRESH
|
||||
},
|
||||
|
||||
{NULL,}
|
||||
};
|
||||
@ -125,13 +136,13 @@ struct nand_manufacturers nand_manuf_ids[] = {
|
||||
{NAND_MFR_NATIONAL, "National"},
|
||||
{NAND_MFR_RENESAS, "Renesas"},
|
||||
{NAND_MFR_STMICRO, "ST Micro"},
|
||||
{NAND_MFR_HYNIX, "Hynix"},
|
||||
{NAND_MFR_HYNIX, "Hynix"},
|
||||
{0x0, "Unknown"}
|
||||
};
|
||||
|
||||
EXPORT_SYMBOL (nand_manuf_ids);
|
||||
EXPORT_SYMBOL (nand_flash_ids);
|
||||
EXPORT_SYMBOL(nand_manuf_ids);
|
||||
EXPORT_SYMBOL(nand_flash_ids);
|
||||
|
||||
MODULE_LICENSE ("GPL");
|
||||
MODULE_AUTHOR ("Thomas Gleixner <tglx@linutronix.de>");
|
||||
MODULE_DESCRIPTION ("Nand device & manufacturer ID's");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
|
||||
MODULE_DESCRIPTION("Nand device & manufacturer IDs");
|
||||
|
@ -369,7 +369,7 @@ init_nandsim(struct mtd_info *mtd)
|
||||
/* Initialize the NAND flash parameters */
|
||||
ns->busw = chip->options & NAND_BUSWIDTH_16 ? 16 : 8;
|
||||
ns->geom.totsz = mtd->size;
|
||||
ns->geom.pgsz = mtd->oobblock;
|
||||
ns->geom.pgsz = mtd->writesize;
|
||||
ns->geom.oobsz = mtd->oobsize;
|
||||
ns->geom.secsz = mtd->erasesize;
|
||||
ns->geom.pgszoob = ns->geom.pgsz + ns->geom.oobsz;
|
||||
@ -1071,68 +1071,6 @@ switch_state(struct nandsim *ns)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ns_hwcontrol(struct mtd_info *mtd, int cmd)
|
||||
{
|
||||
struct nandsim *ns = (struct nandsim *)((struct nand_chip *)mtd->priv)->priv;
|
||||
|
||||
switch (cmd) {
|
||||
|
||||
/* set CLE line high */
|
||||
case NAND_CTL_SETCLE:
|
||||
NS_DBG("ns_hwcontrol: start command latch cycles\n");
|
||||
ns->lines.cle = 1;
|
||||
break;
|
||||
|
||||
/* set CLE line low */
|
||||
case NAND_CTL_CLRCLE:
|
||||
NS_DBG("ns_hwcontrol: stop command latch cycles\n");
|
||||
ns->lines.cle = 0;
|
||||
break;
|
||||
|
||||
/* set ALE line high */
|
||||
case NAND_CTL_SETALE:
|
||||
NS_DBG("ns_hwcontrol: start address latch cycles\n");
|
||||
ns->lines.ale = 1;
|
||||
break;
|
||||
|
||||
/* set ALE line low */
|
||||
case NAND_CTL_CLRALE:
|
||||
NS_DBG("ns_hwcontrol: stop address latch cycles\n");
|
||||
ns->lines.ale = 0;
|
||||
break;
|
||||
|
||||
/* set WP line high */
|
||||
case NAND_CTL_SETWP:
|
||||
NS_DBG("ns_hwcontrol: enable write protection\n");
|
||||
ns->lines.wp = 1;
|
||||
break;
|
||||
|
||||
/* set WP line low */
|
||||
case NAND_CTL_CLRWP:
|
||||
NS_DBG("ns_hwcontrol: disable write protection\n");
|
||||
ns->lines.wp = 0;
|
||||
break;
|
||||
|
||||
/* set CE line low */
|
||||
case NAND_CTL_SETNCE:
|
||||
NS_DBG("ns_hwcontrol: enable chip\n");
|
||||
ns->lines.ce = 1;
|
||||
break;
|
||||
|
||||
/* set CE line high */
|
||||
case NAND_CTL_CLRNCE:
|
||||
NS_DBG("ns_hwcontrol: disable chip\n");
|
||||
ns->lines.ce = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
NS_ERR("hwcontrol: unknown command\n");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static u_char
|
||||
ns_nand_read_byte(struct mtd_info *mtd)
|
||||
{
|
||||
@ -1359,6 +1297,18 @@ ns_nand_write_byte(struct mtd_info *mtd, u_char byte)
|
||||
return;
|
||||
}
|
||||
|
||||
static void ns_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int bitmask)
|
||||
{
|
||||
struct nandsim *ns = ((struct nand_chip *)mtd->priv)->priv;
|
||||
|
||||
ns->lines.cle = bitmask & NAND_CLE ? 1 : 0;
|
||||
ns->lines.ale = bitmask & NAND_ALE ? 1 : 0;
|
||||
ns->lines.ce = bitmask & NAND_NCE ? 1 : 0;
|
||||
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
ns_nand_write_byte(mtd, cmd);
|
||||
}
|
||||
|
||||
static int
|
||||
ns_device_ready(struct mtd_info *mtd)
|
||||
{
|
||||
@ -1376,17 +1326,6 @@ ns_nand_read_word(struct mtd_info *mtd)
|
||||
return chip->read_byte(mtd) | (chip->read_byte(mtd) << 8);
|
||||
}
|
||||
|
||||
static void
|
||||
ns_nand_write_word(struct mtd_info *mtd, uint16_t word)
|
||||
{
|
||||
struct nand_chip *chip = (struct nand_chip *)mtd->priv;
|
||||
|
||||
NS_DBG("write_word\n");
|
||||
|
||||
chip->write_byte(mtd, word & 0xFF);
|
||||
chip->write_byte(mtd, word >> 8);
|
||||
}
|
||||
|
||||
static void
|
||||
ns_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
{
|
||||
@ -1514,16 +1453,14 @@ static int __init ns_init_module(void)
|
||||
/*
|
||||
* Register simulator's callbacks.
|
||||
*/
|
||||
chip->hwcontrol = ns_hwcontrol;
|
||||
chip->cmd_ctrl = ns_hwcontrol;
|
||||
chip->read_byte = ns_nand_read_byte;
|
||||
chip->dev_ready = ns_device_ready;
|
||||
chip->write_byte = ns_nand_write_byte;
|
||||
chip->write_buf = ns_nand_write_buf;
|
||||
chip->read_buf = ns_nand_read_buf;
|
||||
chip->verify_buf = ns_nand_verify_buf;
|
||||
chip->write_word = ns_nand_write_word;
|
||||
chip->read_word = ns_nand_read_word;
|
||||
chip->eccmode = NAND_ECC_SOFT;
|
||||
chip->ecc.mode = NAND_ECC_SOFT;
|
||||
chip->options |= NAND_SKIP_BBTSCAN;
|
||||
|
||||
/*
|
||||
@ -1546,6 +1483,8 @@ static int __init ns_init_module(void)
|
||||
chip->options |= NAND_BUSWIDTH_16;
|
||||
}
|
||||
|
||||
nsmtd->owner = THIS_MODULE;
|
||||
|
||||
if ((retval = nand_scan(nsmtd, 1)) != 0) {
|
||||
NS_ERR("can't register NAND Simulator\n");
|
||||
if (retval > 0)
|
||||
|
311
drivers/mtd/nand/ndfc.c
Normal file
311
drivers/mtd/nand/ndfc.c
Normal file
@ -0,0 +1,311 @@
|
||||
/*
|
||||
* drivers/mtd/ndfc.c
|
||||
*
|
||||
* Overview:
|
||||
* Platform independend driver for NDFC (NanD Flash Controller)
|
||||
* integrated into EP440 cores
|
||||
*
|
||||
* Author: Thomas Gleixner
|
||||
*
|
||||
* Copyright 2006 IBM
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/nand_ecc.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <linux/mtd/ndfc.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/ibm44x.h>
|
||||
|
||||
struct ndfc_nand_mtd {
|
||||
struct mtd_info mtd;
|
||||
struct nand_chip chip;
|
||||
struct platform_nand_chip *pl_chip;
|
||||
};
|
||||
|
||||
static struct ndfc_nand_mtd ndfc_mtd[NDFC_MAX_BANKS];
|
||||
|
||||
struct ndfc_controller {
|
||||
void __iomem *ndfcbase;
|
||||
struct nand_hw_control ndfc_control;
|
||||
atomic_t childs_active;
|
||||
};
|
||||
|
||||
static struct ndfc_controller ndfc_ctrl;
|
||||
|
||||
static void ndfc_select_chip(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
uint32_t ccr;
|
||||
struct ndfc_controller *ndfc = &ndfc_ctrl;
|
||||
struct nand_chip *nandchip = mtd->priv;
|
||||
struct ndfc_nand_mtd *nandmtd = nandchip->priv;
|
||||
struct platform_nand_chip *pchip = nandmtd->pl_chip;
|
||||
|
||||
ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR);
|
||||
if (chip >= 0) {
|
||||
ccr &= ~NDFC_CCR_BS_MASK;
|
||||
ccr |= NDFC_CCR_BS(chip + pchip->chip_offset);
|
||||
} else
|
||||
ccr |= NDFC_CCR_RESET_CE;
|
||||
writel(ccr, ndfc->ndfcbase + NDFC_CCR);
|
||||
}
|
||||
|
||||
static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
if (cmd == NAND_CMD_NONE)
|
||||
return;
|
||||
|
||||
if (ctrl & NAND_CLE)
|
||||
writel(cmd & 0xFF, chip->IO_ADDR_W + NDFC_CMD);
|
||||
else
|
||||
writel(cmd & 0xFF, chip->IO_ADDR_W + NDFC_ALE);
|
||||
}
|
||||
|
||||
static int ndfc_ready(struct mtd_info *mtd)
|
||||
{
|
||||
struct ndfc_controller *ndfc = &ndfc_ctrl;
|
||||
|
||||
return __raw_readl(ndfc->ndfcbase + NDFC_STAT) & NDFC_STAT_IS_READY;
|
||||
}
|
||||
|
||||
static void ndfc_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
uint32_t ccr;
|
||||
struct ndfc_controller *ndfc = &ndfc_ctrl;
|
||||
|
||||
ccr = __raw_readl(ndfc->ndfcbase + NDFC_CCR);
|
||||
ccr |= NDFC_CCR_RESET_ECC;
|
||||
__raw_writel(ccr, ndfc->ndfcbase + NDFC_CCR);
|
||||
wmb();
|
||||
}
|
||||
|
||||
static int ndfc_calculate_ecc(struct mtd_info *mtd,
|
||||
const u_char *dat, u_char *ecc_code)
|
||||
{
|
||||
struct ndfc_controller *ndfc = &ndfc_ctrl;
|
||||
uint32_t ecc;
|
||||
uint8_t *p = (uint8_t *)&ecc;
|
||||
|
||||
wmb();
|
||||
ecc = __raw_readl(ndfc->ndfcbase + NDFC_ECC);
|
||||
ecc_code[0] = p[1];
|
||||
ecc_code[1] = p[2];
|
||||
ecc_code[2] = p[3];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Speedups for buffer read/write/verify
|
||||
*
|
||||
* NDFC allows 32bit read/write of data. So we can speed up the buffer
|
||||
* functions. No further checking, as nand_base will always read/write
|
||||
* page aligned.
|
||||
*/
|
||||
static void ndfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
|
||||
{
|
||||
struct ndfc_controller *ndfc = &ndfc_ctrl;
|
||||
uint32_t *p = (uint32_t *) buf;
|
||||
|
||||
for(;len > 0; len -= 4)
|
||||
*p++ = __raw_readl(ndfc->ndfcbase + NDFC_DATA);
|
||||
}
|
||||
|
||||
static void ndfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
||||
{
|
||||
struct ndfc_controller *ndfc = &ndfc_ctrl;
|
||||
uint32_t *p = (uint32_t *) buf;
|
||||
|
||||
for(;len > 0; len -= 4)
|
||||
__raw_writel(*p++, ndfc->ndfcbase + NDFC_DATA);
|
||||
}
|
||||
|
||||
static int ndfc_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
|
||||
{
|
||||
struct ndfc_controller *ndfc = &ndfc_ctrl;
|
||||
uint32_t *p = (uint32_t *) buf;
|
||||
|
||||
for(;len > 0; len -= 4)
|
||||
if (*p++ != __raw_readl(ndfc->ndfcbase + NDFC_DATA))
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize chip structure
|
||||
*/
|
||||
static void ndfc_chip_init(struct ndfc_nand_mtd *mtd)
|
||||
{
|
||||
struct ndfc_controller *ndfc = &ndfc_ctrl;
|
||||
struct nand_chip *chip = &mtd->chip;
|
||||
|
||||
chip->IO_ADDR_R = ndfc->ndfcbase + NDFC_DATA;
|
||||
chip->IO_ADDR_W = ndfc->ndfcbase + NDFC_DATA;
|
||||
chip->cmd_ctrl = ndfc_hwcontrol;
|
||||
chip->dev_ready = ndfc_ready;
|
||||
chip->select_chip = ndfc_select_chip;
|
||||
chip->chip_delay = 50;
|
||||
chip->priv = mtd;
|
||||
chip->options = mtd->pl_chip->options;
|
||||
chip->controller = &ndfc->ndfc_control;
|
||||
chip->read_buf = ndfc_read_buf;
|
||||
chip->write_buf = ndfc_write_buf;
|
||||
chip->verify_buf = ndfc_verify_buf;
|
||||
chip->ecc.correct = nand_correct_data;
|
||||
chip->ecc.hwctl = ndfc_enable_hwecc;
|
||||
chip->ecc.calculate = ndfc_calculate_ecc;
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->ecc.size = 256;
|
||||
chip->ecc.bytes = 3;
|
||||
chip->ecclayout = mtd->pl_chip->ecclayout;
|
||||
mtd->mtd.priv = chip;
|
||||
mtd->mtd.owner = THIS_MODULE;
|
||||
}
|
||||
|
||||
static int ndfc_chip_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct platform_nand_chip *nc = pdev->dev.platform_data;
|
||||
struct ndfc_chip_settings *settings = nc->priv;
|
||||
struct ndfc_controller *ndfc = &ndfc_ctrl;
|
||||
struct ndfc_nand_mtd *nandmtd;
|
||||
|
||||
if (nc->chip_offset >= NDFC_MAX_BANKS || nc->nr_chips > NDFC_MAX_BANKS)
|
||||
return -EINVAL;
|
||||
|
||||
/* Set the bank settings */
|
||||
__raw_writel(settings->bank_settings,
|
||||
ndfc->ndfcbase + NDFC_BCFG0 + (nc->chip_offset << 2));
|
||||
|
||||
nandmtd = &ndfc_mtd[pdev->id];
|
||||
if (nandmtd->pl_chip)
|
||||
return -EBUSY;
|
||||
|
||||
nandmtd->pl_chip = nc;
|
||||
ndfc_chip_init(nandmtd);
|
||||
|
||||
/* Scan for chips */
|
||||
if (nand_scan(&nandmtd->mtd, nc->nr_chips)) {
|
||||
nandmtd->pl_chip = NULL;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
printk("Number of partitions %d\n", nc->nr_partitions);
|
||||
if (nc->nr_partitions) {
|
||||
/* Add the full device, so complete dumps can be made */
|
||||
add_mtd_device(&nandmtd->mtd);
|
||||
add_mtd_partitions(&nandmtd->mtd, nc->partitions,
|
||||
nc->nr_partitions);
|
||||
|
||||
} else
|
||||
#else
|
||||
add_mtd_device(&nandmtd->mtd);
|
||||
#endif
|
||||
|
||||
atomic_inc(&ndfc->childs_active);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ndfc_chip_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ndfc_nand_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct platform_nand_ctrl *nc = pdev->dev.platform_data;
|
||||
struct ndfc_controller_settings *settings = nc->priv;
|
||||
struct resource *res = pdev->resource;
|
||||
struct ndfc_controller *ndfc = &ndfc_ctrl;
|
||||
unsigned long long phys = settings->ndfc_erpn | res->start;
|
||||
|
||||
ndfc->ndfcbase = ioremap64(phys, res->end - res->start + 1);
|
||||
if (!ndfc->ndfcbase) {
|
||||
printk(KERN_ERR "NDFC: ioremap failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
__raw_writel(settings->ccr_settings, ndfc->ndfcbase + NDFC_CCR);
|
||||
|
||||
spin_lock_init(&ndfc->ndfc_control.lock);
|
||||
init_waitqueue_head(&ndfc->ndfc_control.wq);
|
||||
|
||||
platform_set_drvdata(pdev, ndfc);
|
||||
|
||||
printk("NDFC NAND Driver initialized. Chip-Rev: 0x%08x\n",
|
||||
__raw_readl(ndfc->ndfcbase + NDFC_REVID));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ndfc_nand_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ndfc_controller *ndfc = platform_get_drvdata(pdev);
|
||||
|
||||
if (atomic_read(&ndfc->childs_active))
|
||||
return -EBUSY;
|
||||
|
||||
if (ndfc) {
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
iounmap(ndfc_ctrl.ndfcbase);
|
||||
ndfc_ctrl.ndfcbase = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* driver device registration */
|
||||
|
||||
static struct platform_driver ndfc_chip_driver = {
|
||||
.probe = ndfc_chip_probe,
|
||||
.remove = ndfc_chip_remove,
|
||||
.driver = {
|
||||
.name = "ndfc-chip",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_driver ndfc_nand_driver = {
|
||||
.probe = ndfc_nand_probe,
|
||||
.remove = ndfc_nand_remove,
|
||||
.driver = {
|
||||
.name = "ndfc-nand",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init ndfc_nand_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
spin_lock_init(&ndfc_ctrl.ndfc_control.lock);
|
||||
init_waitqueue_head(&ndfc_ctrl.ndfc_control.wq);
|
||||
|
||||
ret = platform_driver_register(&ndfc_nand_driver);
|
||||
if (!ret)
|
||||
ret = platform_driver_register(&ndfc_chip_driver);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit ndfc_nand_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ndfc_chip_driver);
|
||||
platform_driver_unregister(&ndfc_nand_driver);
|
||||
}
|
||||
|
||||
module_init(ndfc_nand_init);
|
||||
module_exit(ndfc_nand_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
|
||||
MODULE_DESCRIPTION("Platform driver for NDFC");
|
@ -58,21 +58,21 @@
|
||||
/*
|
||||
* MTD structure for PPChameleonEVB board
|
||||
*/
|
||||
static struct mtd_info *ppchameleon_mtd = NULL;
|
||||
static struct mtd_info *ppchameleon_mtd = NULL;
|
||||
static struct mtd_info *ppchameleonevb_mtd = NULL;
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
*/
|
||||
static unsigned long ppchameleon_fio_pbase = CFG_NAND0_PADDR;
|
||||
static unsigned long ppchameleon_fio_pbase = CFG_NAND0_PADDR;
|
||||
static unsigned long ppchameleonevb_fio_pbase = CFG_NAND1_PADDR;
|
||||
|
||||
#ifdef MODULE
|
||||
module_param(ppchameleon_fio_pbase, ulong, 0);
|
||||
module_param(ppchameleonevb_fio_pbase, ulong, 0);
|
||||
#else
|
||||
__setup("ppchameleon_fio_pbase=",ppchameleon_fio_pbase);
|
||||
__setup("ppchameleonevb_fio_pbase=",ppchameleonevb_fio_pbase);
|
||||
__setup("ppchameleon_fio_pbase=", ppchameleon_fio_pbase);
|
||||
__setup("ppchameleonevb_fio_pbase=", ppchameleonevb_fio_pbase);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
@ -80,82 +80,96 @@ __setup("ppchameleonevb_fio_pbase=",ppchameleonevb_fio_pbase);
|
||||
* Define static partitions for flash devices
|
||||
*/
|
||||
static struct mtd_partition partition_info_hi[] = {
|
||||
{ name: "PPChameleon HI Nand Flash",
|
||||
offset: 0,
|
||||
size: 128*1024*1024 }
|
||||
{ .name = "PPChameleon HI Nand Flash",
|
||||
offset = 0,
|
||||
.size = 128 * 1024 * 1024
|
||||
}
|
||||
};
|
||||
|
||||
static struct mtd_partition partition_info_me[] = {
|
||||
{ name: "PPChameleon ME Nand Flash",
|
||||
offset: 0,
|
||||
size: 32*1024*1024 }
|
||||
{ .name = "PPChameleon ME Nand Flash",
|
||||
.offset = 0,
|
||||
.size = 32 * 1024 * 1024
|
||||
}
|
||||
};
|
||||
|
||||
static struct mtd_partition partition_info_evb[] = {
|
||||
{ name: "PPChameleonEVB Nand Flash",
|
||||
offset: 0,
|
||||
size: 32*1024*1024 }
|
||||
{ .name = "PPChameleonEVB Nand Flash",
|
||||
.offset = 0,
|
||||
.size = 32 * 1024 * 1024
|
||||
}
|
||||
};
|
||||
|
||||
#define NUM_PARTITIONS 1
|
||||
|
||||
extern int parse_cmdline_partitions(struct mtd_info *master,
|
||||
struct mtd_partition **pparts,
|
||||
const char *mtd_id);
|
||||
extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, const char *mtd_id);
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* hardware specific access to control-lines
|
||||
*/
|
||||
static void ppchameleon_hwcontrol(struct mtd_info *mtdinfo, int cmd)
|
||||
static void ppchameleon_hwcontrol(struct mtd_info *mtdinfo, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
switch(cmd) {
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
case NAND_CTL_SETCLE:
|
||||
MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND0_PADDR);
|
||||
break;
|
||||
case NAND_CTL_CLRCLE:
|
||||
MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND0_PADDR);
|
||||
break;
|
||||
case NAND_CTL_SETALE:
|
||||
MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND0_PADDR);
|
||||
break;
|
||||
case NAND_CTL_CLRALE:
|
||||
MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND0_PADDR);
|
||||
break;
|
||||
case NAND_CTL_SETNCE:
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
#error Missing headerfiles. No way to fix this. -tglx
|
||||
switch (cmd) {
|
||||
case NAND_CTL_SETCLE:
|
||||
MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND0_PADDR);
|
||||
break;
|
||||
case NAND_CTL_CLRCLE:
|
||||
MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND0_PADDR);
|
||||
break;
|
||||
case NAND_CTL_SETALE:
|
||||
MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND0_PADDR);
|
||||
break;
|
||||
case NAND_CTL_CLRALE:
|
||||
MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND0_PADDR);
|
||||
break;
|
||||
case NAND_CTL_SETNCE:
|
||||
MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND0_PADDR);
|
||||
break;
|
||||
case NAND_CTL_CLRNCE:
|
||||
break;
|
||||
case NAND_CTL_CLRNCE:
|
||||
MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND0_PADDR);
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
writeb(cmd, chip->IO_ADDR_W);
|
||||
}
|
||||
|
||||
static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd)
|
||||
static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
switch(cmd) {
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
case NAND_CTL_SETCLE:
|
||||
MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND1_PADDR);
|
||||
break;
|
||||
case NAND_CTL_CLRCLE:
|
||||
MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND1_PADDR);
|
||||
break;
|
||||
case NAND_CTL_SETALE:
|
||||
MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND1_PADDR);
|
||||
break;
|
||||
case NAND_CTL_CLRALE:
|
||||
MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND1_PADDR);
|
||||
break;
|
||||
case NAND_CTL_SETNCE:
|
||||
MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND1_PADDR);
|
||||
break;
|
||||
case NAND_CTL_CLRNCE:
|
||||
MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND1_PADDR);
|
||||
break;
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
#error Missing headerfiles. No way to fix this. -tglx
|
||||
switch (cmd) {
|
||||
case NAND_CTL_SETCLE:
|
||||
MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND1_PADDR);
|
||||
break;
|
||||
case NAND_CTL_CLRCLE:
|
||||
MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND1_PADDR);
|
||||
break;
|
||||
case NAND_CTL_SETALE:
|
||||
MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND1_PADDR);
|
||||
break;
|
||||
case NAND_CTL_CLRALE:
|
||||
MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND1_PADDR);
|
||||
break;
|
||||
case NAND_CTL_SETNCE:
|
||||
MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND1_PADDR);
|
||||
break;
|
||||
case NAND_CTL_CLRNCE:
|
||||
MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND1_PADDR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
writeb(cmd, chip->IO_ADDR_W);
|
||||
}
|
||||
|
||||
#ifdef USE_READY_BUSY_PIN
|
||||
@ -164,15 +178,15 @@ static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd)
|
||||
*/
|
||||
static int ppchameleon_device_ready(struct mtd_info *minfo)
|
||||
{
|
||||
if (in_be32((volatile unsigned*)GPIO0_IR) & NAND_RB_GPIO_PIN)
|
||||
if (in_be32((volatile unsigned *)GPIO0_IR) & NAND_RB_GPIO_PIN)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ppchameleonevb_device_ready(struct mtd_info *minfo)
|
||||
{
|
||||
if (in_be32((volatile unsigned*)GPIO0_IR) & NAND_EVB_RB_GPIO_PIN)
|
||||
return 1;
|
||||
if (in_be32((volatile unsigned *)GPIO0_IR) & NAND_EVB_RB_GPIO_PIN)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@ -185,7 +199,7 @@ const char *part_probes_evb[] = { "cmdlinepart", NULL };
|
||||
/*
|
||||
* Main initialization routine
|
||||
*/
|
||||
static int __init ppchameleonevb_init (void)
|
||||
static int __init ppchameleonevb_init(void)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
const char *part_type = 0;
|
||||
@ -194,13 +208,11 @@ static int __init ppchameleonevb_init (void)
|
||||
void __iomem *ppchameleon_fio_base;
|
||||
void __iomem *ppchameleonevb_fio_base;
|
||||
|
||||
|
||||
/*********************************
|
||||
* Processor module NAND (if any) *
|
||||
*********************************/
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
ppchameleon_mtd = kmalloc(sizeof(struct mtd_info) +
|
||||
sizeof(struct nand_chip), GFP_KERNEL);
|
||||
ppchameleon_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
|
||||
if (!ppchameleon_mtd) {
|
||||
printk("Unable to allocate PPChameleon NAND MTD device structure.\n");
|
||||
return -ENOMEM;
|
||||
@ -208,63 +220,65 @@ static int __init ppchameleonevb_init (void)
|
||||
|
||||
/* map physical address */
|
||||
ppchameleon_fio_base = ioremap(ppchameleon_fio_pbase, SZ_4M);
|
||||
if(!ppchameleon_fio_base) {
|
||||
if (!ppchameleon_fio_base) {
|
||||
printk("ioremap PPChameleon NAND flash failed\n");
|
||||
kfree(ppchameleon_mtd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *) (&ppchameleon_mtd[1]);
|
||||
this = (struct nand_chip *)(&ppchameleon_mtd[1]);
|
||||
|
||||
/* Initialize structures */
|
||||
memset((char *) ppchameleon_mtd, 0, sizeof(struct mtd_info));
|
||||
memset((char *) this, 0, sizeof(struct nand_chip));
|
||||
memset(ppchameleon_mtd, 0, sizeof(struct mtd_info));
|
||||
memset(this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
ppchameleon_mtd->priv = this;
|
||||
ppchameleon_mtd->owner = THIS_MODULE;
|
||||
|
||||
/* Initialize GPIOs */
|
||||
/* Initialize GPIOs */
|
||||
/* Pin mapping for NAND chip */
|
||||
/*
|
||||
CE GPIO_01
|
||||
CLE GPIO_02
|
||||
ALE GPIO_03
|
||||
R/B GPIO_04
|
||||
*/
|
||||
CE GPIO_01
|
||||
CLE GPIO_02
|
||||
ALE GPIO_03
|
||||
R/B GPIO_04
|
||||
*/
|
||||
/* output select */
|
||||
out_be32((volatile unsigned*)GPIO0_OSRH, in_be32((volatile unsigned*)GPIO0_OSRH) & 0xC0FFFFFF);
|
||||
out_be32((volatile unsigned *)GPIO0_OSRH, in_be32((volatile unsigned *)GPIO0_OSRH) & 0xC0FFFFFF);
|
||||
/* three-state select */
|
||||
out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xC0FFFFFF);
|
||||
out_be32((volatile unsigned *)GPIO0_TSRH, in_be32((volatile unsigned *)GPIO0_TSRH) & 0xC0FFFFFF);
|
||||
/* enable output driver */
|
||||
out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_nCE_GPIO_PIN | NAND_CLE_GPIO_PIN | NAND_ALE_GPIO_PIN);
|
||||
out_be32((volatile unsigned *)GPIO0_TCR,
|
||||
in_be32((volatile unsigned *)GPIO0_TCR) | NAND_nCE_GPIO_PIN | NAND_CLE_GPIO_PIN | NAND_ALE_GPIO_PIN);
|
||||
#ifdef USE_READY_BUSY_PIN
|
||||
/* three-state select */
|
||||
out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xFF3FFFFF);
|
||||
out_be32((volatile unsigned *)GPIO0_TSRH, in_be32((volatile unsigned *)GPIO0_TSRH) & 0xFF3FFFFF);
|
||||
/* high-impedecence */
|
||||
out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) & (~NAND_RB_GPIO_PIN));
|
||||
out_be32((volatile unsigned *)GPIO0_TCR, in_be32((volatile unsigned *)GPIO0_TCR) & (~NAND_RB_GPIO_PIN));
|
||||
/* input select */
|
||||
out_be32((volatile unsigned*)GPIO0_ISR1H, (in_be32((volatile unsigned*)GPIO0_ISR1H) & 0xFF3FFFFF) | 0x00400000);
|
||||
out_be32((volatile unsigned *)GPIO0_ISR1H,
|
||||
(in_be32((volatile unsigned *)GPIO0_ISR1H) & 0xFF3FFFFF) | 0x00400000);
|
||||
#endif
|
||||
|
||||
/* insert callbacks */
|
||||
this->IO_ADDR_R = ppchameleon_fio_base;
|
||||
this->IO_ADDR_W = ppchameleon_fio_base;
|
||||
this->hwcontrol = ppchameleon_hwcontrol;
|
||||
this->cmd_ctrl = ppchameleon_hwcontrol;
|
||||
#ifdef USE_READY_BUSY_PIN
|
||||
this->dev_ready = ppchameleon_device_ready;
|
||||
#endif
|
||||
this->chip_delay = NAND_BIG_DELAY_US;
|
||||
/* ECC mode */
|
||||
this->eccmode = NAND_ECC_SOFT;
|
||||
this->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
/* Scan to find existence of the device (it could not be mounted) */
|
||||
if (nand_scan (ppchameleon_mtd, 1)) {
|
||||
if (nand_scan(ppchameleon_mtd, 1)) {
|
||||
iounmap((void *)ppchameleon_fio_base);
|
||||
kfree (ppchameleon_mtd);
|
||||
kfree(ppchameleon_mtd);
|
||||
goto nand_evb_init;
|
||||
}
|
||||
|
||||
#ifndef USE_READY_BUSY_PIN
|
||||
/* Adjust delay if necessary */
|
||||
if (ppchameleon_mtd->size == NAND_SMALL_SIZE)
|
||||
@ -275,12 +289,11 @@ static int __init ppchameleonevb_init (void)
|
||||
ppchameleon_mtd->name = "ppchameleon-nand";
|
||||
mtd_parts_nb = parse_mtd_partitions(ppchameleon_mtd, part_probes, &mtd_parts, 0);
|
||||
if (mtd_parts_nb > 0)
|
||||
part_type = "command line";
|
||||
part_type = "command line";
|
||||
else
|
||||
mtd_parts_nb = 0;
|
||||
mtd_parts_nb = 0;
|
||||
#endif
|
||||
if (mtd_parts_nb == 0)
|
||||
{
|
||||
if (mtd_parts_nb == 0) {
|
||||
if (ppchameleon_mtd->size == NAND_SMALL_SIZE)
|
||||
mtd_parts = partition_info_me;
|
||||
else
|
||||
@ -293,13 +306,12 @@ static int __init ppchameleonevb_init (void)
|
||||
printk(KERN_NOTICE "Using %s partition definition\n", part_type);
|
||||
add_mtd_partitions(ppchameleon_mtd, mtd_parts, mtd_parts_nb);
|
||||
|
||||
nand_evb_init:
|
||||
nand_evb_init:
|
||||
/****************************
|
||||
* EVB NAND (always present) *
|
||||
****************************/
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
ppchameleonevb_mtd = kmalloc(sizeof(struct mtd_info) +
|
||||
sizeof(struct nand_chip), GFP_KERNEL);
|
||||
ppchameleonevb_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
|
||||
if (!ppchameleonevb_mtd) {
|
||||
printk("Unable to allocate PPChameleonEVB NAND MTD device structure.\n");
|
||||
return -ENOMEM;
|
||||
@ -307,77 +319,76 @@ nand_evb_init:
|
||||
|
||||
/* map physical address */
|
||||
ppchameleonevb_fio_base = ioremap(ppchameleonevb_fio_pbase, SZ_4M);
|
||||
if(!ppchameleonevb_fio_base) {
|
||||
if (!ppchameleonevb_fio_base) {
|
||||
printk("ioremap PPChameleonEVB NAND flash failed\n");
|
||||
kfree(ppchameleonevb_mtd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *) (&ppchameleonevb_mtd[1]);
|
||||
this = (struct nand_chip *)(&ppchameleonevb_mtd[1]);
|
||||
|
||||
/* Initialize structures */
|
||||
memset((char *) ppchameleonevb_mtd, 0, sizeof(struct mtd_info));
|
||||
memset((char *) this, 0, sizeof(struct nand_chip));
|
||||
memset(ppchameleonevb_mtd, 0, sizeof(struct mtd_info));
|
||||
memset(this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
ppchameleonevb_mtd->priv = this;
|
||||
|
||||
/* Initialize GPIOs */
|
||||
/* Initialize GPIOs */
|
||||
/* Pin mapping for NAND chip */
|
||||
/*
|
||||
CE GPIO_14
|
||||
CLE GPIO_15
|
||||
ALE GPIO_16
|
||||
R/B GPIO_31
|
||||
*/
|
||||
CE GPIO_14
|
||||
CLE GPIO_15
|
||||
ALE GPIO_16
|
||||
R/B GPIO_31
|
||||
*/
|
||||
/* output select */
|
||||
out_be32((volatile unsigned*)GPIO0_OSRH, in_be32((volatile unsigned*)GPIO0_OSRH) & 0xFFFFFFF0);
|
||||
out_be32((volatile unsigned*)GPIO0_OSRL, in_be32((volatile unsigned*)GPIO0_OSRL) & 0x3FFFFFFF);
|
||||
out_be32((volatile unsigned *)GPIO0_OSRH, in_be32((volatile unsigned *)GPIO0_OSRH) & 0xFFFFFFF0);
|
||||
out_be32((volatile unsigned *)GPIO0_OSRL, in_be32((volatile unsigned *)GPIO0_OSRL) & 0x3FFFFFFF);
|
||||
/* three-state select */
|
||||
out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xFFFFFFF0);
|
||||
out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0x3FFFFFFF);
|
||||
out_be32((volatile unsigned *)GPIO0_TSRH, in_be32((volatile unsigned *)GPIO0_TSRH) & 0xFFFFFFF0);
|
||||
out_be32((volatile unsigned *)GPIO0_TSRL, in_be32((volatile unsigned *)GPIO0_TSRL) & 0x3FFFFFFF);
|
||||
/* enable output driver */
|
||||
out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_EVB_nCE_GPIO_PIN |
|
||||
out_be32((volatile unsigned *)GPIO0_TCR, in_be32((volatile unsigned *)GPIO0_TCR) | NAND_EVB_nCE_GPIO_PIN |
|
||||
NAND_EVB_CLE_GPIO_PIN | NAND_EVB_ALE_GPIO_PIN);
|
||||
#ifdef USE_READY_BUSY_PIN
|
||||
/* three-state select */
|
||||
out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0xFFFFFFFC);
|
||||
out_be32((volatile unsigned *)GPIO0_TSRL, in_be32((volatile unsigned *)GPIO0_TSRL) & 0xFFFFFFFC);
|
||||
/* high-impedecence */
|
||||
out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) & (~NAND_EVB_RB_GPIO_PIN));
|
||||
out_be32((volatile unsigned *)GPIO0_TCR, in_be32((volatile unsigned *)GPIO0_TCR) & (~NAND_EVB_RB_GPIO_PIN));
|
||||
/* input select */
|
||||
out_be32((volatile unsigned*)GPIO0_ISR1L, (in_be32((volatile unsigned*)GPIO0_ISR1L) & 0xFFFFFFFC) | 0x00000001);
|
||||
out_be32((volatile unsigned *)GPIO0_ISR1L,
|
||||
(in_be32((volatile unsigned *)GPIO0_ISR1L) & 0xFFFFFFFC) | 0x00000001);
|
||||
#endif
|
||||
|
||||
/* insert callbacks */
|
||||
this->IO_ADDR_R = ppchameleonevb_fio_base;
|
||||
this->IO_ADDR_W = ppchameleonevb_fio_base;
|
||||
this->hwcontrol = ppchameleonevb_hwcontrol;
|
||||
this->cmd_ctrl = ppchameleonevb_hwcontrol;
|
||||
#ifdef USE_READY_BUSY_PIN
|
||||
this->dev_ready = ppchameleonevb_device_ready;
|
||||
#endif
|
||||
this->chip_delay = NAND_SMALL_DELAY_US;
|
||||
|
||||
/* ECC mode */
|
||||
this->eccmode = NAND_ECC_SOFT;
|
||||
this->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
if (nand_scan (ppchameleonevb_mtd, 1)) {
|
||||
if (nand_scan(ppchameleonevb_mtd, 1)) {
|
||||
iounmap((void *)ppchameleonevb_fio_base);
|
||||
kfree (ppchameleonevb_mtd);
|
||||
kfree(ppchameleonevb_mtd);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
ppchameleonevb_mtd->name = NAND_EVB_MTD_NAME;
|
||||
mtd_parts_nb = parse_mtd_partitions(ppchameleonevb_mtd, part_probes_evb, &mtd_parts, 0);
|
||||
if (mtd_parts_nb > 0)
|
||||
part_type = "command line";
|
||||
part_type = "command line";
|
||||
else
|
||||
mtd_parts_nb = 0;
|
||||
mtd_parts_nb = 0;
|
||||
#endif
|
||||
if (mtd_parts_nb == 0)
|
||||
{
|
||||
if (mtd_parts_nb == 0) {
|
||||
mtd_parts = partition_info_evb;
|
||||
mtd_parts_nb = NUM_PARTITIONS;
|
||||
part_type = "static";
|
||||
@ -390,18 +401,19 @@ nand_evb_init:
|
||||
/* Return happy */
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(ppchameleonevb_init);
|
||||
|
||||
/*
|
||||
* Clean up routine
|
||||
*/
|
||||
static void __exit ppchameleonevb_cleanup (void)
|
||||
static void __exit ppchameleonevb_cleanup(void)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
|
||||
/* Release resources, unregister device(s) */
|
||||
nand_release (ppchameleon_mtd);
|
||||
nand_release (ppchameleonevb_mtd);
|
||||
nand_release(ppchameleon_mtd);
|
||||
nand_release(ppchameleonevb_mtd);
|
||||
|
||||
/* Release iomaps */
|
||||
this = (struct nand_chip *) &ppchameleon_mtd[1];
|
||||
|
@ -97,12 +97,12 @@ static struct mtd_info *rtc_from4_mtd = NULL;
|
||||
static void __iomem *rtc_from4_fio_base = (void *)P2SEGADDR(RTC_FROM4_FIO_BASE);
|
||||
|
||||
static const struct mtd_partition partition_info[] = {
|
||||
{
|
||||
.name = "Renesas flash partition 1",
|
||||
.offset = 0,
|
||||
.size = MTDPART_SIZ_FULL
|
||||
},
|
||||
{
|
||||
.name = "Renesas flash partition 1",
|
||||
.offset = 0,
|
||||
.size = MTDPART_SIZ_FULL},
|
||||
};
|
||||
|
||||
#define NUM_PARTITIONS 1
|
||||
|
||||
/*
|
||||
@ -111,8 +111,8 @@ static const struct mtd_partition partition_info[] = {
|
||||
* NAND_BBT_CREATE and/or NAND_BBT_WRITE
|
||||
*
|
||||
*/
|
||||
static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
|
||||
static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
|
||||
static uint8_t bbt_pattern[] = { 'B', 'b', 't', '0' };
|
||||
static uint8_t mirror_pattern[] = { '1', 't', 'b', 'B' };
|
||||
|
||||
static struct nand_bbt_descr rtc_from4_bbt_main_descr = {
|
||||
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
|
||||
@ -134,8 +134,6 @@ static struct nand_bbt_descr rtc_from4_bbt_mirror_descr = {
|
||||
.pattern = mirror_pattern
|
||||
};
|
||||
|
||||
|
||||
|
||||
#ifdef RTC_FROM4_HWECC
|
||||
|
||||
/* the Reed Solomon control structure */
|
||||
@ -144,15 +142,14 @@ static struct rs_control *rs_decoder;
|
||||
/*
|
||||
* hardware specific Out Of Band information
|
||||
*/
|
||||
static struct nand_oobinfo rtc_from4_nand_oobinfo = {
|
||||
.useecc = MTD_NANDECC_AUTOPLACE,
|
||||
static struct nand_ecclayout rtc_from4_nand_oobinfo = {
|
||||
.eccbytes = 32,
|
||||
.eccpos = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23,
|
||||
24, 25, 26, 27, 28, 29, 30, 31},
|
||||
.oobfree = { {32, 32} }
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23,
|
||||
24, 25, 26, 27, 28, 29, 30, 31},
|
||||
.oobfree = {{32, 32}}
|
||||
};
|
||||
|
||||
/* Aargh. I missed the reversed bit order, when I
|
||||
@ -162,44 +159,42 @@ static struct nand_oobinfo rtc_from4_nand_oobinfo = {
|
||||
* of the ecc byte which we get from the FPGA
|
||||
*/
|
||||
static uint8_t revbits[256] = {
|
||||
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
|
||||
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
|
||||
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
|
||||
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
|
||||
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
|
||||
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
|
||||
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
|
||||
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
|
||||
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
|
||||
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
|
||||
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
|
||||
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
|
||||
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
|
||||
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
|
||||
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
|
||||
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
|
||||
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
|
||||
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
|
||||
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
|
||||
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
|
||||
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
|
||||
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
|
||||
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
|
||||
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
|
||||
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
|
||||
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
|
||||
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
|
||||
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
|
||||
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
|
||||
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
|
||||
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
|
||||
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
|
||||
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
|
||||
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
|
||||
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
|
||||
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
|
||||
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
|
||||
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
|
||||
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
|
||||
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
|
||||
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
|
||||
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
|
||||
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
|
||||
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
|
||||
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
|
||||
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
|
||||
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
|
||||
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
|
||||
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
|
||||
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
|
||||
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
|
||||
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
|
||||
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
|
||||
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
|
||||
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
|
||||
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
|
||||
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
|
||||
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
|
||||
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
|
||||
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
|
||||
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
|
||||
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
|
||||
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
|
||||
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* rtc_from4_hwcontrol - hardware specific access to control-lines
|
||||
* @mtd: MTD device structure
|
||||
@ -212,35 +207,20 @@ static uint8_t revbits[256] = {
|
||||
* Address lines (A24-A22), so no action is required here.
|
||||
*
|
||||
*/
|
||||
static void rtc_from4_hwcontrol(struct mtd_info *mtd, int cmd)
|
||||
static void rtc_from4_hwcontrol(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip* this = (struct nand_chip *) (mtd->priv);
|
||||
struct nand_chip *chip = (mtd->priv);
|
||||
|
||||
switch(cmd) {
|
||||
if (cmd == NAND_CMD_NONE)
|
||||
return;
|
||||
|
||||
case NAND_CTL_SETCLE:
|
||||
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_CLE);
|
||||
break;
|
||||
case NAND_CTL_CLRCLE:
|
||||
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_CLE);
|
||||
break;
|
||||
|
||||
case NAND_CTL_SETALE:
|
||||
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_ALE);
|
||||
break;
|
||||
case NAND_CTL_CLRALE:
|
||||
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_ALE);
|
||||
break;
|
||||
|
||||
case NAND_CTL_SETNCE:
|
||||
break;
|
||||
case NAND_CTL_CLRNCE:
|
||||
break;
|
||||
|
||||
}
|
||||
if (ctrl & NAND_CLE)
|
||||
writeb(cmd, chip->IO_ADDR_W | RTC_FROM4_CLE);
|
||||
else
|
||||
writeb(cmd, chip->IO_ADDR_W | RTC_FROM4_ALE);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* rtc_from4_nand_select_chip - hardware specific chip select
|
||||
* @mtd: MTD device structure
|
||||
@ -252,26 +232,25 @@ static void rtc_from4_hwcontrol(struct mtd_info *mtd, int cmd)
|
||||
*/
|
||||
static void rtc_from4_nand_select_chip(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R & ~RTC_FROM4_NAND_ADDR_MASK);
|
||||
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_NAND_ADDR_MASK);
|
||||
|
||||
switch(chip) {
|
||||
switch (chip) {
|
||||
|
||||
case 0: /* select slot 3 chip */
|
||||
case 0: /* select slot 3 chip */
|
||||
this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT3);
|
||||
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT3);
|
||||
break;
|
||||
case 1: /* select slot 4 chip */
|
||||
break;
|
||||
case 1: /* select slot 4 chip */
|
||||
this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT4);
|
||||
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT4);
|
||||
break;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* rtc_from4_nand_device_ready - hardware specific ready/busy check
|
||||
* @mtd: MTD device structure
|
||||
@ -290,7 +269,6 @@ 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
|
||||
@ -306,24 +284,23 @@ static int rtc_from4_nand_device_ready(struct mtd_info *mtd)
|
||||
*/
|
||||
static void deplete(struct mtd_info *mtd, int chip)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
struct nand_chip *this = mtd->priv;
|
||||
|
||||
/* wait until device is ready */
|
||||
while (!this->dev_ready(mtd));
|
||||
/* 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);
|
||||
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);
|
||||
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
|
||||
@ -335,39 +312,35 @@ static void deplete(struct mtd_info *mtd, int chip)
|
||||
*/
|
||||
static void rtc_from4_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
volatile unsigned short * rs_ecc_ctl = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CTL);
|
||||
volatile unsigned short *rs_ecc_ctl = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CTL);
|
||||
unsigned short status;
|
||||
|
||||
switch (mode) {
|
||||
case NAND_ECC_READ :
|
||||
status = RTC_FROM4_RS_ECC_CTL_CLR
|
||||
| RTC_FROM4_RS_ECC_CTL_FD_E;
|
||||
case NAND_ECC_READ:
|
||||
status = RTC_FROM4_RS_ECC_CTL_CLR | RTC_FROM4_RS_ECC_CTL_FD_E;
|
||||
|
||||
*rs_ecc_ctl = status;
|
||||
break;
|
||||
|
||||
case NAND_ECC_READSYN :
|
||||
status = 0x00;
|
||||
case NAND_ECC_READSYN:
|
||||
status = 0x00;
|
||||
|
||||
*rs_ecc_ctl = status;
|
||||
break;
|
||||
|
||||
case NAND_ECC_WRITE :
|
||||
status = RTC_FROM4_RS_ECC_CTL_CLR
|
||||
| RTC_FROM4_RS_ECC_CTL_GEN
|
||||
| RTC_FROM4_RS_ECC_CTL_FD_E;
|
||||
case NAND_ECC_WRITE:
|
||||
status = RTC_FROM4_RS_ECC_CTL_CLR | RTC_FROM4_RS_ECC_CTL_GEN | RTC_FROM4_RS_ECC_CTL_FD_E;
|
||||
|
||||
*rs_ecc_ctl = status;
|
||||
break;
|
||||
|
||||
default:
|
||||
default:
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* rtc_from4_calculate_ecc - hardware specific code to read ECC code
|
||||
* @mtd: MTD device structure
|
||||
@ -383,7 +356,7 @@ static void rtc_from4_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
*/
|
||||
static void rtc_from4_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
|
||||
{
|
||||
volatile unsigned short * rs_eccn = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECCN);
|
||||
volatile unsigned short *rs_eccn = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECCN);
|
||||
unsigned short value;
|
||||
int i;
|
||||
|
||||
@ -395,7 +368,6 @@ 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
|
||||
@ -414,7 +386,7 @@ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_cha
|
||||
unsigned short status;
|
||||
uint16_t par[6], syn[6];
|
||||
uint8_t ecc[8];
|
||||
volatile unsigned short *rs_ecc;
|
||||
volatile unsigned short *rs_ecc;
|
||||
|
||||
status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CHK));
|
||||
|
||||
@ -424,23 +396,18 @@ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_cha
|
||||
|
||||
/* Read the syndrom pattern from the FPGA and correct the bitorder */
|
||||
rs_ecc = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC);
|
||||
for (i = 0; i < 8; i++) {
|
||||
ecc[i] = revbits[(*rs_ecc) & 0xFF];
|
||||
rs_ecc++;
|
||||
}
|
||||
for (i = 0; i < 8; i++) {
|
||||
ecc[i] = revbits[(*rs_ecc) & 0xFF];
|
||||
rs_ecc++;
|
||||
}
|
||||
|
||||
/* convert into 6 10bit syndrome fields */
|
||||
par[5] = rs_decoder->index_of[(((uint16_t)ecc[0] >> 0) & 0x0ff) |
|
||||
(((uint16_t)ecc[1] << 8) & 0x300)];
|
||||
par[4] = rs_decoder->index_of[(((uint16_t)ecc[1] >> 2) & 0x03f) |
|
||||
(((uint16_t)ecc[2] << 6) & 0x3c0)];
|
||||
par[3] = rs_decoder->index_of[(((uint16_t)ecc[2] >> 4) & 0x00f) |
|
||||
(((uint16_t)ecc[3] << 4) & 0x3f0)];
|
||||
par[2] = rs_decoder->index_of[(((uint16_t)ecc[3] >> 6) & 0x003) |
|
||||
(((uint16_t)ecc[4] << 2) & 0x3fc)];
|
||||
par[1] = rs_decoder->index_of[(((uint16_t)ecc[5] >> 0) & 0x0ff) |
|
||||
(((uint16_t)ecc[6] << 8) & 0x300)];
|
||||
par[0] = (((uint16_t)ecc[6] >> 2) & 0x03f) | (((uint16_t)ecc[7] << 6) & 0x3c0);
|
||||
par[5] = rs_decoder->index_of[(((uint16_t) ecc[0] >> 0) & 0x0ff) | (((uint16_t) ecc[1] << 8) & 0x300)];
|
||||
par[4] = rs_decoder->index_of[(((uint16_t) ecc[1] >> 2) & 0x03f) | (((uint16_t) ecc[2] << 6) & 0x3c0)];
|
||||
par[3] = rs_decoder->index_of[(((uint16_t) ecc[2] >> 4) & 0x00f) | (((uint16_t) ecc[3] << 4) & 0x3f0)];
|
||||
par[2] = rs_decoder->index_of[(((uint16_t) ecc[3] >> 6) & 0x003) | (((uint16_t) ecc[4] << 2) & 0x3fc)];
|
||||
par[1] = rs_decoder->index_of[(((uint16_t) ecc[5] >> 0) & 0x0ff) | (((uint16_t) ecc[6] << 8) & 0x300)];
|
||||
par[0] = (((uint16_t) ecc[6] >> 2) & 0x03f) | (((uint16_t) ecc[7] << 6) & 0x3c0);
|
||||
|
||||
/* Convert to computable syndrome */
|
||||
for (i = 0; i < 6; i++) {
|
||||
@ -453,16 +420,14 @@ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_cha
|
||||
syn[i] = rs_decoder->index_of[syn[i]];
|
||||
}
|
||||
|
||||
/* Let the library code do its magic.*/
|
||||
res = decode_rs8(rs_decoder, (uint8_t *)buf, par, 512, syn, 0, NULL, 0xff, NULL);
|
||||
/* Let the library code do its magic. */
|
||||
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);
|
||||
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
|
||||
@ -478,54 +443,66 @@ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_cha
|
||||
* 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)
|
||||
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;
|
||||
int er_stat = 0;
|
||||
int rtn, retlen;
|
||||
size_t len;
|
||||
uint8_t *buf;
|
||||
int i;
|
||||
int i;
|
||||
|
||||
this->cmdfunc (mtd, NAND_CMD_STATUS_CLEAR, -1, -1);
|
||||
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 */
|
||||
}
|
||||
}
|
||||
if (state == FL_ERASING) {
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (!(status & 1 << (i + 1)))
|
||||
continue;
|
||||
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);
|
||||
|
||||
/* err_ecc_not_avail */
|
||||
if (!(rtn & ERR_STAT_ECC_AVAILABLE))
|
||||
er_stat |= 1 << (i + 1);
|
||||
}
|
||||
|
||||
} else if (state == FL_WRITING) {
|
||||
|
||||
unsigned long corrected = mtd->ecc_stats.corrected;
|
||||
|
||||
/* single bank write logic */
|
||||
this->cmdfunc (mtd, NAND_CMD_STATUS_ERROR, -1, -1);
|
||||
this->cmdfunc(mtd, NAND_CMD_STATUS_ERROR, -1, -1);
|
||||
rtn = this->read_byte(mtd);
|
||||
this->cmdfunc (mtd, NAND_CMD_STATUS_RESET, -1, -1);
|
||||
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);
|
||||
}
|
||||
/* err_ecc_not_avail */
|
||||
er_stat |= 1 << 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
len = mtd->writesize;
|
||||
buf = kmalloc(len, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
printk(KERN_ERR "rtc_from4_errstat: Out of memory!\n");
|
||||
er_stat = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* recovery read */
|
||||
rtn = nand_do_read(mtd, page, len, &retlen, buf);
|
||||
|
||||
/* if read failed or > 1-bit error corrected */
|
||||
if (rtn || (mtd->ecc_stats.corrected - corrected) > 1) {
|
||||
er_stat |= 1 << 1;
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
rtn = status;
|
||||
if (er_stat == 0) { /* if ECC is available */
|
||||
if (er_stat == 0) { /* if ECC is available */
|
||||
rtn = (status & ~NAND_STATUS_FAIL); /* clear the error bit */
|
||||
}
|
||||
|
||||
@ -533,33 +510,32 @@ static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, int s
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Main initialization routine
|
||||
*/
|
||||
int __init rtc_from4_init (void)
|
||||
static 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),
|
||||
GFP_KERNEL);
|
||||
rtc_from4_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
|
||||
if (!rtc_from4_mtd) {
|
||||
printk ("Unable to allocate Renesas NAND MTD device structure.\n");
|
||||
printk("Unable to allocate Renesas NAND MTD device structure.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *) (&rtc_from4_mtd[1]);
|
||||
this = (struct nand_chip *)(&rtc_from4_mtd[1]);
|
||||
|
||||
/* Initialize structures */
|
||||
memset((char *) rtc_from4_mtd, 0, sizeof(struct mtd_info));
|
||||
memset((char *) this, 0, sizeof(struct nand_chip));
|
||||
memset(rtc_from4_mtd, 0, sizeof(struct mtd_info));
|
||||
memset(this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
rtc_from4_mtd->priv = this;
|
||||
rtc_from4_mtd->owner = THIS_MODULE;
|
||||
|
||||
/* set area 5 as PCMCIA mode to clear the spec of tDH(Data hold time;9ns min) */
|
||||
bcr1 = *SH77X9_BCR1 & ~0x0002;
|
||||
@ -580,9 +556,9 @@ int __init rtc_from4_init (void)
|
||||
this->IO_ADDR_R = rtc_from4_fio_base;
|
||||
this->IO_ADDR_W = rtc_from4_fio_base;
|
||||
/* Set address of hardware control function */
|
||||
this->hwcontrol = rtc_from4_hwcontrol;
|
||||
this->cmd_ctrl = rtc_from4_hwcontrol;
|
||||
/* Set address of chip select function */
|
||||
this->select_chip = rtc_from4_nand_select_chip;
|
||||
this->select_chip = rtc_from4_nand_select_chip;
|
||||
/* command delay time (in us) */
|
||||
this->chip_delay = 100;
|
||||
/* return the status of the Ready/Busy line */
|
||||
@ -591,19 +567,20 @@ int __init rtc_from4_init (void)
|
||||
#ifdef RTC_FROM4_HWECC
|
||||
printk(KERN_INFO "rtc_from4_init: using hardware ECC detection.\n");
|
||||
|
||||
this->eccmode = NAND_ECC_HW8_512;
|
||||
this->options |= NAND_HWECC_SYNDROME;
|
||||
this->ecc.mode = NAND_ECC_HW_SYNDROME;
|
||||
this->ecc.size = 512;
|
||||
this->ecc.bytes = 8;
|
||||
/* 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;
|
||||
this->calculate_ecc = rtc_from4_calculate_ecc;
|
||||
this->correct_data = rtc_from4_correct_data;
|
||||
this->ecc.layout = &rtc_from4_nand_oobinfo;
|
||||
this->ecc.hwctl = rtc_from4_enable_hwecc;
|
||||
this->ecc.calculate = rtc_from4_calculate_ecc;
|
||||
this->ecc.correct = rtc_from4_correct_data;
|
||||
#else
|
||||
printk(KERN_INFO "rtc_from4_init: using software ECC detection.\n");
|
||||
|
||||
this->eccmode = NAND_ECC_SOFT;
|
||||
this->ecc.mode = NAND_ECC_SOFT;
|
||||
#endif
|
||||
|
||||
/* set the bad block tables to support debugging */
|
||||
@ -617,7 +594,7 @@ int __init rtc_from4_init (void)
|
||||
}
|
||||
|
||||
/* Perform 'device recovery' for each chip in case there was a power loss. */
|
||||
for (i=0; i < this->numchips; i++) {
|
||||
for (i = 0; i < this->numchips; i++) {
|
||||
deplete(rtc_from4_mtd, i);
|
||||
}
|
||||
|
||||
@ -643,7 +620,7 @@ int __init rtc_from4_init (void)
|
||||
*/
|
||||
rs_decoder = init_rs(10, 0x409, 0, 1, 6);
|
||||
if (!rs_decoder) {
|
||||
printk (KERN_ERR "Could not create a RS decoder\n");
|
||||
printk(KERN_ERR "Could not create a RS decoder\n");
|
||||
nand_release(rtc_from4_mtd);
|
||||
kfree(rtc_from4_mtd);
|
||||
return -ENOMEM;
|
||||
@ -652,20 +629,19 @@ int __init rtc_from4_init (void)
|
||||
/* Return happy */
|
||||
return 0;
|
||||
}
|
||||
module_init(rtc_from4_init);
|
||||
|
||||
module_init(rtc_from4_init);
|
||||
|
||||
/*
|
||||
* Clean up routine
|
||||
*/
|
||||
#ifdef MODULE
|
||||
static void __exit rtc_from4_cleanup (void)
|
||||
static void __exit rtc_from4_cleanup(void)
|
||||
{
|
||||
/* Release resource, unregister partitions */
|
||||
nand_release(rtc_from4_mtd);
|
||||
|
||||
/* Free the MTD device structure */
|
||||
kfree (rtc_from4_mtd);
|
||||
kfree(rtc_from4_mtd);
|
||||
|
||||
#ifdef RTC_FROM4_HWECC
|
||||
/* Free the reed solomon resources */
|
||||
@ -674,10 +650,9 @@ static void __exit rtc_from4_cleanup (void)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
module_exit(rtc_from4_cleanup);
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("d.marlin <dmarlin@redhat.com");
|
||||
MODULE_DESCRIPTION("Board-specific glue layer for AG-AND flash on Renesas FROM_BOARD4");
|
||||
|
||||
|
@ -18,8 +18,9 @@
|
||||
* 20-Jun-2005 BJD Updated s3c2440 support, fixed timing bug
|
||||
* 08-Jul-2005 BJD Fix OOPS when no platform data supplied
|
||||
* 20-Oct-2005 BJD Fix timing calculation bug
|
||||
* 14-Jan-2006 BJD Allow clock to be stopped when idle
|
||||
*
|
||||
* $Id: s3c2410.c,v 1.20 2005/11/07 11:14:31 gleixner Exp $
|
||||
* $Id: s3c2410.c,v 1.23 2006/04/01 18:06:29 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
|
||||
@ -36,9 +37,6 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <config/mtd/nand/s3c2410/hwecc.h>
|
||||
#include <config/mtd/nand/s3c2410/debug.h>
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_S3C2410_DEBUG
|
||||
#define DEBUG
|
||||
#endif
|
||||
@ -73,14 +71,20 @@ static int hardware_ecc = 1;
|
||||
static int hardware_ecc = 0;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MTD_NAND_S3C2410_CLKSTOP
|
||||
static int clock_stop = 1;
|
||||
#else
|
||||
static const int clock_stop = 0;
|
||||
#endif
|
||||
|
||||
|
||||
/* new oob placement block for use with hardware ecc generation
|
||||
*/
|
||||
|
||||
static struct nand_oobinfo nand_hw_eccoob = {
|
||||
.useecc = MTD_NANDECC_AUTOPLACE,
|
||||
.eccbytes = 3,
|
||||
.eccpos = {0, 1, 2 },
|
||||
.oobfree = { {8, 8} }
|
||||
static struct nand_ecclayout nand_hw_eccoob = {
|
||||
.eccbytes = 3,
|
||||
.eccpos = {0, 1, 2},
|
||||
.oobfree = {{8, 8}}
|
||||
};
|
||||
|
||||
/* controller and mtd information */
|
||||
@ -135,6 +139,11 @@ static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev)
|
||||
return dev->dev.platform_data;
|
||||
}
|
||||
|
||||
static inline int allow_clk_stop(struct s3c2410_nand_info *info)
|
||||
{
|
||||
return clock_stop;
|
||||
}
|
||||
|
||||
/* timing calculations */
|
||||
|
||||
#define NS_IN_KHZ 1000000
|
||||
@ -149,8 +158,7 @@ static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max)
|
||||
pr_debug("result %d from %ld, %d\n", result, clk, wanted);
|
||||
|
||||
if (result > max) {
|
||||
printk("%d ns is too big for current clock rate %ld\n",
|
||||
wanted, clk);
|
||||
printk("%d ns is too big for current clock rate %ld\n", wanted, clk);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -164,8 +172,7 @@ static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max)
|
||||
|
||||
/* controller setup */
|
||||
|
||||
static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
|
||||
struct platform_device *pdev)
|
||||
static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, struct platform_device *pdev)
|
||||
{
|
||||
struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
|
||||
unsigned long clkrate = clk_get_rate(info->clk);
|
||||
@ -177,7 +184,7 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
|
||||
clkrate /= 1000; /* turn clock into kHz for ease of use */
|
||||
|
||||
if (plat != NULL) {
|
||||
tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 4);
|
||||
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 {
|
||||
@ -193,19 +200,22 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
|
||||
}
|
||||
|
||||
printk(KERN_INFO PFX "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n",
|
||||
tacls, to_ns(tacls, clkrate),
|
||||
twrph0, to_ns(twrph0, clkrate),
|
||||
twrph1, to_ns(twrph1, clkrate));
|
||||
tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate));
|
||||
|
||||
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);
|
||||
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);
|
||||
cfg = S3C2440_NFCONF_TACLS(tacls - 1);
|
||||
cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
|
||||
cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
|
||||
|
||||
/* enable the controller and de-assert nFCE */
|
||||
|
||||
writel(S3C2440_NFCONT_ENABLE | S3C2440_NFCONT_ENABLE,
|
||||
info->regs + S3C2440_NFCONT);
|
||||
}
|
||||
|
||||
pr_debug(PFX "NF_CONF is 0x%lx\n", cfg);
|
||||
@ -229,7 +239,10 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
|
||||
info = nmtd->info;
|
||||
|
||||
bit = (info->is_s3c2440) ? S3C2440_NFCONT_nFCE : S3C2410_NFCONF_nFCE;
|
||||
reg = info->regs+((info->is_s3c2440) ? S3C2440_NFCONT:S3C2410_NFCONF);
|
||||
reg = info->regs + ((info->is_s3c2440) ? S3C2440_NFCONT : S3C2410_NFCONF);
|
||||
|
||||
if (chip != -1 && allow_clk_stop(info))
|
||||
clk_enable(info->clk);
|
||||
|
||||
cur = readl(reg);
|
||||
|
||||
@ -243,77 +256,51 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
|
||||
|
||||
if (info->platform != NULL) {
|
||||
if (info->platform->select_chip != NULL)
|
||||
(info->platform->select_chip)(nmtd->set, chip);
|
||||
(info->platform->select_chip) (nmtd->set, chip);
|
||||
}
|
||||
|
||||
cur &= ~bit;
|
||||
}
|
||||
|
||||
writel(cur, reg);
|
||||
|
||||
if (chip == -1 && allow_clk_stop(info))
|
||||
clk_disable(info->clk);
|
||||
}
|
||||
|
||||
/* 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.
|
||||
/* s3c2410_nand_hwcontrol
|
||||
*
|
||||
* Issue command and address cycles to the chip
|
||||
*/
|
||||
|
||||
static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd)
|
||||
static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
if (cmd == NAND_CMD_NONE)
|
||||
return;
|
||||
|
||||
switch (cmd) {
|
||||
case NAND_CTL_SETNCE:
|
||||
case NAND_CTL_CLRNCE:
|
||||
printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__);
|
||||
break;
|
||||
|
||||
case NAND_CTL_SETCLE:
|
||||
chip->IO_ADDR_W = info->regs + S3C2410_NFCMD;
|
||||
break;
|
||||
|
||||
case NAND_CTL_SETALE:
|
||||
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;
|
||||
}
|
||||
if (ctrl & NAND_CLE)
|
||||
writeb(cmd, info->regs + S3C2410_NFCMD);
|
||||
else
|
||||
writeb(cmd, info->regs + S3C2410_NFADDR);
|
||||
}
|
||||
|
||||
/* command and control functions */
|
||||
|
||||
static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd)
|
||||
static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
switch (cmd) {
|
||||
case NAND_CTL_SETNCE:
|
||||
case NAND_CTL_CLRNCE:
|
||||
printk(KERN_ERR "%s: called for NCE\n", __FUNCTION__);
|
||||
break;
|
||||
if (cmd == NAND_CMD_NONE)
|
||||
return;
|
||||
|
||||
case NAND_CTL_SETCLE:
|
||||
chip->IO_ADDR_W = info->regs + S3C2440_NFCMD;
|
||||
break;
|
||||
|
||||
case NAND_CTL_SETALE:
|
||||
chip->IO_ADDR_W = info->regs + S3C2440_NFADDR;
|
||||
break;
|
||||
|
||||
/* NAND_CTL_CLRCLE: */
|
||||
/* NAND_CTL_CLRALE: */
|
||||
default:
|
||||
chip->IO_ADDR_W = info->regs + S3C2440_NFDATA;
|
||||
break;
|
||||
}
|
||||
if (ctrl & NAND_CLE)
|
||||
writeb(cmd, info->regs + S3C2440_NFCMD);
|
||||
else
|
||||
writeb(cmd, info->regs + S3C2440_NFADDR);
|
||||
}
|
||||
|
||||
/* s3c2410_nand_devready()
|
||||
@ -330,22 +317,16 @@ static int s3c2410_nand_devready(struct mtd_info *mtd)
|
||||
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,
|
||||
u_char *read_ecc, u_char *calc_ecc)
|
||||
static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
|
||||
{
|
||||
pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n",
|
||||
mtd, dat, read_ecc, calc_ecc);
|
||||
pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", mtd, dat, read_ecc, calc_ecc);
|
||||
|
||||
pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n",
|
||||
read_ecc[0], read_ecc[1], read_ecc[2],
|
||||
calc_ecc[0], calc_ecc[1], calc_ecc[2]);
|
||||
read_ecc[0], read_ecc[1], read_ecc[2], calc_ecc[0], calc_ecc[1], calc_ecc[2]);
|
||||
|
||||
if (read_ecc[0] == calc_ecc[0] &&
|
||||
read_ecc[1] == calc_ecc[1] &&
|
||||
read_ecc[2] == calc_ecc[2])
|
||||
if (read_ecc[0] == calc_ecc[0] && read_ecc[1] == calc_ecc[1] && read_ecc[2] == calc_ecc[2])
|
||||
return 0;
|
||||
|
||||
/* we curently have no method for correcting the error */
|
||||
@ -378,8 +359,7 @@ static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
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)
|
||||
static int s3c2410_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);
|
||||
|
||||
@ -387,15 +367,12 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd,
|
||||
ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1);
|
||||
ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2);
|
||||
|
||||
pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n",
|
||||
ecc_code[0], ecc_code[1], ecc_code[2]);
|
||||
pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd,
|
||||
const u_char *dat, u_char *ecc_code)
|
||||
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);
|
||||
@ -404,13 +381,11 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd,
|
||||
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]);
|
||||
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
|
||||
*/
|
||||
@ -421,8 +396,7 @@ static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
|
||||
readsb(this->IO_ADDR_R, buf, len);
|
||||
}
|
||||
|
||||
static void s3c2410_nand_write_buf(struct mtd_info *mtd,
|
||||
const u_char *buf, int len)
|
||||
static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
|
||||
{
|
||||
struct nand_chip *this = mtd->priv;
|
||||
writesb(this->IO_ADDR_W, buf, len);
|
||||
@ -459,7 +433,8 @@ static int s3c2410_nand_remove(struct platform_device *pdev)
|
||||
/* free the common resources */
|
||||
|
||||
if (info->clk != NULL && !IS_ERR(info->clk)) {
|
||||
clk_disable(info->clk);
|
||||
if (!allow_clk_stop(info))
|
||||
clk_disable(info->clk);
|
||||
clk_put(info->clk);
|
||||
}
|
||||
|
||||
@ -488,9 +463,7 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
|
||||
return add_mtd_device(&mtd->mtd);
|
||||
|
||||
if (set->nr_partitions > 0 && set->partitions != NULL) {
|
||||
return add_mtd_partitions(&mtd->mtd,
|
||||
set->partitions,
|
||||
set->nr_partitions);
|
||||
return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions);
|
||||
}
|
||||
|
||||
return add_mtd_device(&mtd->mtd);
|
||||
@ -517,7 +490,7 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
|
||||
|
||||
chip->IO_ADDR_R = info->regs + S3C2410_NFDATA;
|
||||
chip->IO_ADDR_W = info->regs + S3C2410_NFDATA;
|
||||
chip->hwcontrol = s3c2410_nand_hwcontrol;
|
||||
chip->cmd_ctrl = s3c2410_nand_hwcontrol;
|
||||
chip->dev_ready = s3c2410_nand_devready;
|
||||
chip->write_buf = s3c2410_nand_write_buf;
|
||||
chip->read_buf = s3c2410_nand_read_buf;
|
||||
@ -530,26 +503,29 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
|
||||
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;
|
||||
chip->cmd_ctrl = s3c2440_nand_hwcontrol;
|
||||
}
|
||||
|
||||
nmtd->info = info;
|
||||
nmtd->mtd.priv = chip;
|
||||
nmtd->mtd.owner = THIS_MODULE;
|
||||
nmtd->set = set;
|
||||
|
||||
if (hardware_ecc) {
|
||||
chip->correct_data = s3c2410_nand_correct_data;
|
||||
chip->enable_hwecc = s3c2410_nand_enable_hwecc;
|
||||
chip->calculate_ecc = s3c2410_nand_calculate_ecc;
|
||||
chip->eccmode = NAND_ECC_HW3_512;
|
||||
chip->autooob = &nand_hw_eccoob;
|
||||
chip->ecc.correct = s3c2410_nand_correct_data;
|
||||
chip->ecc.hwctl = s3c2410_nand_enable_hwecc;
|
||||
chip->ecc.calculate = s3c2410_nand_calculate_ecc;
|
||||
chip->ecc.mode = NAND_ECC_HW;
|
||||
chip->ecc.size = 512;
|
||||
chip->ecc.bytes = 3;
|
||||
chip->ecc.layout = &nand_hw_eccoob;
|
||||
|
||||
if (info->is_s3c2440) {
|
||||
chip->enable_hwecc = s3c2440_nand_enable_hwecc;
|
||||
chip->calculate_ecc = s3c2440_nand_calculate_ecc;
|
||||
chip->ecc.hwctl = s3c2440_nand_enable_hwecc;
|
||||
chip->ecc.calculate = s3c2440_nand_calculate_ecc;
|
||||
}
|
||||
} else {
|
||||
chip->eccmode = NAND_ECC_SOFT;
|
||||
chip->ecc.mode = NAND_ECC_SOFT;
|
||||
}
|
||||
}
|
||||
|
||||
@ -654,13 +630,11 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, int is_s3c2440)
|
||||
nmtd = info->mtds;
|
||||
|
||||
for (setno = 0; setno < nr_sets; setno++, nmtd++) {
|
||||
pr_debug("initialising set %d (%p, info %p)\n",
|
||||
setno, nmtd, info);
|
||||
pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info);
|
||||
|
||||
s3c2410_nand_init_chip(info, nmtd, sets);
|
||||
|
||||
nmtd->scan_res = nand_scan(&nmtd->mtd,
|
||||
(sets) ? sets->nr_chips : 1);
|
||||
nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1);
|
||||
|
||||
if (nmtd->scan_res == 0) {
|
||||
s3c2410_nand_add_partition(info, nmtd, sets);
|
||||
@ -670,6 +644,11 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, int is_s3c2440)
|
||||
sets++;
|
||||
}
|
||||
|
||||
if (allow_clk_stop(info)) {
|
||||
dev_info(&pdev->dev, "clock idle support enabled\n");
|
||||
clk_disable(info->clk);
|
||||
}
|
||||
|
||||
pr_debug("initialised ok\n");
|
||||
return 0;
|
||||
|
||||
@ -681,6 +660,41 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, int is_s3c2440)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* PM Support */
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int s3c24xx_nand_suspend(struct platform_device *dev, pm_message_t pm)
|
||||
{
|
||||
struct s3c2410_nand_info *info = platform_get_drvdata(dev);
|
||||
|
||||
if (info) {
|
||||
if (!allow_clk_stop(info))
|
||||
clk_disable(info->clk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c24xx_nand_resume(struct platform_device *dev)
|
||||
{
|
||||
struct s3c2410_nand_info *info = platform_get_drvdata(dev);
|
||||
|
||||
if (info) {
|
||||
clk_enable(info->clk);
|
||||
s3c2410_nand_inithw(info, dev);
|
||||
|
||||
if (allow_clk_stop(info))
|
||||
clk_disable(info->clk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
#define s3c24xx_nand_suspend NULL
|
||||
#define s3c24xx_nand_resume NULL
|
||||
#endif
|
||||
|
||||
/* driver device registration */
|
||||
|
||||
static int s3c2410_nand_probe(struct platform_device *dev)
|
||||
@ -696,6 +710,8 @@ static int s3c2440_nand_probe(struct platform_device *dev)
|
||||
static struct platform_driver s3c2410_nand_driver = {
|
||||
.probe = s3c2410_nand_probe,
|
||||
.remove = s3c2410_nand_remove,
|
||||
.suspend = s3c24xx_nand_suspend,
|
||||
.resume = s3c24xx_nand_resume,
|
||||
.driver = {
|
||||
.name = "s3c2410-nand",
|
||||
.owner = THIS_MODULE,
|
||||
@ -705,6 +721,8 @@ static struct platform_driver s3c2410_nand_driver = {
|
||||
static struct platform_driver s3c2440_nand_driver = {
|
||||
.probe = s3c2440_nand_probe,
|
||||
.remove = s3c2410_nand_remove,
|
||||
.suspend = s3c24xx_nand_suspend,
|
||||
.resume = s3c24xx_nand_resume,
|
||||
.driver = {
|
||||
.name = "s3c2440-nand",
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -46,7 +46,6 @@ static int sharpsl_phys_base = 0x0C000000;
|
||||
#define FLCLE (1 << 1)
|
||||
#define FLCE0 (1 << 0)
|
||||
|
||||
|
||||
/*
|
||||
* MTD structure for SharpSL
|
||||
*/
|
||||
@ -60,50 +59,44 @@ static struct mtd_info *sharpsl_mtd = NULL;
|
||||
static int nr_partitions;
|
||||
static struct mtd_partition sharpsl_nand_default_partition_info[] = {
|
||||
{
|
||||
.name = "System Area",
|
||||
.offset = 0,
|
||||
.size = 7 * 1024 * 1024,
|
||||
},
|
||||
.name = "System Area",
|
||||
.offset = 0,
|
||||
.size = 7 * 1024 * 1024,
|
||||
},
|
||||
{
|
||||
.name = "Root Filesystem",
|
||||
.offset = 7 * 1024 * 1024,
|
||||
.size = 30 * 1024 * 1024,
|
||||
},
|
||||
.name = "Root Filesystem",
|
||||
.offset = 7 * 1024 * 1024,
|
||||
.size = 30 * 1024 * 1024,
|
||||
},
|
||||
{
|
||||
.name = "Home Filesystem",
|
||||
.offset = MTDPART_OFS_APPEND ,
|
||||
.size = MTDPART_SIZ_FULL ,
|
||||
},
|
||||
.name = "Home Filesystem",
|
||||
.offset = MTDPART_OFS_APPEND,
|
||||
.size = MTDPART_SIZ_FULL,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* hardware specific access to control-lines
|
||||
* ctrl:
|
||||
* NAND_CNE: bit 0 -> bit 0 & 4
|
||||
* NAND_CLE: bit 1 -> bit 1
|
||||
* NAND_ALE: bit 2 -> bit 2
|
||||
*
|
||||
*/
|
||||
static void
|
||||
sharpsl_nand_hwcontrol(struct mtd_info* mtd, int cmd)
|
||||
static void sharpsl_nand_hwcontrol(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
switch (cmd) {
|
||||
case NAND_CTL_SETCLE:
|
||||
writeb(readb(FLASHCTL) | FLCLE, FLASHCTL);
|
||||
break;
|
||||
case NAND_CTL_CLRCLE:
|
||||
writeb(readb(FLASHCTL) & ~FLCLE, FLASHCTL);
|
||||
break;
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
case NAND_CTL_SETALE:
|
||||
writeb(readb(FLASHCTL) | FLALE, FLASHCTL);
|
||||
break;
|
||||
case NAND_CTL_CLRALE:
|
||||
writeb(readb(FLASHCTL) & ~FLALE, FLASHCTL);
|
||||
break;
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
unsigned char bits = ctrl & 0x07;
|
||||
|
||||
case NAND_CTL_SETNCE:
|
||||
writeb(readb(FLASHCTL) & ~(FLCE0|FLCE1), FLASHCTL);
|
||||
break;
|
||||
case NAND_CTL_CLRNCE:
|
||||
writeb(readb(FLASHCTL) | (FLCE0|FLCE1), FLASHCTL);
|
||||
break;
|
||||
bits |= (ctrl & 0x01) << 4;
|
||||
writeb((readb(FLASHCTL) & 0x17) | bits, FLASHCTL);
|
||||
}
|
||||
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
writeb(cmd, chip->IO_ADDR_W);
|
||||
}
|
||||
|
||||
static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
|
||||
@ -122,31 +115,26 @@ static struct nand_bbt_descr sharpsl_akita_bbt = {
|
||||
.pattern = scan_ff_pattern
|
||||
};
|
||||
|
||||
static struct nand_oobinfo akita_oobinfo = {
|
||||
.useecc = MTD_NANDECC_AUTOPLACE,
|
||||
static struct nand_ecclayout akita_oobinfo = {
|
||||
.eccbytes = 24,
|
||||
.eccpos = {
|
||||
0x5, 0x1, 0x2, 0x3, 0x6, 0x7, 0x15, 0x11,
|
||||
0x12, 0x13, 0x16, 0x17, 0x25, 0x21, 0x22, 0x23,
|
||||
0x26, 0x27, 0x35, 0x31, 0x32, 0x33, 0x36, 0x37},
|
||||
.oobfree = { {0x08, 0x09} }
|
||||
0x5, 0x1, 0x2, 0x3, 0x6, 0x7, 0x15, 0x11,
|
||||
0x12, 0x13, 0x16, 0x17, 0x25, 0x21, 0x22, 0x23,
|
||||
0x26, 0x27, 0x35, 0x31, 0x32, 0x33, 0x36, 0x37},
|
||||
.oobfree = {{0x08, 0x09}}
|
||||
};
|
||||
|
||||
static int
|
||||
sharpsl_nand_dev_ready(struct mtd_info* mtd)
|
||||
static int sharpsl_nand_dev_ready(struct mtd_info *mtd)
|
||||
{
|
||||
return !((readb(FLASHCTL) & FLRYBY) == 0);
|
||||
}
|
||||
|
||||
static void
|
||||
sharpsl_nand_enable_hwecc(struct mtd_info* mtd, int mode)
|
||||
static void sharpsl_nand_enable_hwecc(struct mtd_info *mtd, int mode)
|
||||
{
|
||||
writeb(0 ,ECCCLRR);
|
||||
writeb(0, ECCCLRR);
|
||||
}
|
||||
|
||||
static int
|
||||
sharpsl_nand_calculate_ecc(struct mtd_info* mtd, const u_char* dat,
|
||||
u_char* ecc_code)
|
||||
static int sharpsl_nand_calculate_ecc(struct mtd_info *mtd, const u_char * dat, u_char * ecc_code)
|
||||
{
|
||||
ecc_code[0] = ~readb(ECCLPUB);
|
||||
ecc_code[1] = ~readb(ECCLPLB);
|
||||
@ -154,47 +142,44 @@ sharpsl_nand_calculate_ecc(struct mtd_info* mtd, const u_char* dat,
|
||||
return readb(ECCCNTR) != 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
const char *part_probes[] = { "cmdlinepart", NULL };
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Main initialization routine
|
||||
*/
|
||||
int __init
|
||||
sharpsl_nand_init(void)
|
||||
static int __init sharpsl_nand_init(void)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
struct mtd_partition* sharpsl_partition_info;
|
||||
struct mtd_partition *sharpsl_partition_info;
|
||||
int err = 0;
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
sharpsl_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip),
|
||||
GFP_KERNEL);
|
||||
sharpsl_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
|
||||
if (!sharpsl_mtd) {
|
||||
printk ("Unable to allocate SharpSL NAND MTD device structure.\n");
|
||||
printk("Unable to allocate SharpSL NAND MTD device structure.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* map physical adress */
|
||||
sharpsl_io_base = ioremap(sharpsl_phys_base, 0x1000);
|
||||
if(!sharpsl_io_base){
|
||||
if (!sharpsl_io_base) {
|
||||
printk("ioremap to access Sharp SL NAND chip failed\n");
|
||||
kfree(sharpsl_mtd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *) (&sharpsl_mtd[1]);
|
||||
this = (struct nand_chip *)(&sharpsl_mtd[1]);
|
||||
|
||||
/* Initialize structures */
|
||||
memset((char *) sharpsl_mtd, 0, sizeof(struct mtd_info));
|
||||
memset((char *) this, 0, sizeof(struct nand_chip));
|
||||
memset(sharpsl_mtd, 0, sizeof(struct mtd_info));
|
||||
memset(this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
sharpsl_mtd->priv = this;
|
||||
sharpsl_mtd->owner = THIS_MODULE;
|
||||
|
||||
/*
|
||||
* PXA initialize
|
||||
@ -205,23 +190,25 @@ sharpsl_nand_init(void)
|
||||
this->IO_ADDR_R = FLASHIO;
|
||||
this->IO_ADDR_W = FLASHIO;
|
||||
/* Set address of hardware control function */
|
||||
this->hwcontrol = sharpsl_nand_hwcontrol;
|
||||
this->cmd_ctrl = sharpsl_nand_hwcontrol;
|
||||
this->dev_ready = sharpsl_nand_dev_ready;
|
||||
/* 15 us command delay time */
|
||||
this->chip_delay = 15;
|
||||
/* set eccmode using hardware ECC */
|
||||
this->eccmode = NAND_ECC_HW3_256;
|
||||
this->ecc.mode = NAND_ECC_HW;
|
||||
this->ecc.size = 256;
|
||||
this->ecc.bytes = 3;
|
||||
this->badblock_pattern = &sharpsl_bbt;
|
||||
if (machine_is_akita() || machine_is_borzoi()) {
|
||||
this->badblock_pattern = &sharpsl_akita_bbt;
|
||||
this->autooob = &akita_oobinfo;
|
||||
this->ecc.layout = &akita_oobinfo;
|
||||
}
|
||||
this->enable_hwecc = sharpsl_nand_enable_hwecc;
|
||||
this->calculate_ecc = sharpsl_nand_calculate_ecc;
|
||||
this->correct_data = nand_correct_data;
|
||||
this->ecc.hwctl = sharpsl_nand_enable_hwecc;
|
||||
this->ecc.calculate = sharpsl_nand_calculate_ecc;
|
||||
this->ecc.correct = nand_correct_data;
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
err=nand_scan(sharpsl_mtd,1);
|
||||
err = nand_scan(sharpsl_mtd, 1);
|
||||
if (err) {
|
||||
iounmap(sharpsl_io_base);
|
||||
kfree(sharpsl_mtd);
|
||||
@ -230,24 +217,23 @@ sharpsl_nand_init(void)
|
||||
|
||||
/* Register the partitions */
|
||||
sharpsl_mtd->name = "sharpsl-nand";
|
||||
nr_partitions = parse_mtd_partitions(sharpsl_mtd, part_probes,
|
||||
&sharpsl_partition_info, 0);
|
||||
nr_partitions = parse_mtd_partitions(sharpsl_mtd, part_probes, &sharpsl_partition_info, 0);
|
||||
|
||||
if (nr_partitions <= 0) {
|
||||
nr_partitions = DEFAULT_NUM_PARTITIONS;
|
||||
sharpsl_partition_info = sharpsl_nand_default_partition_info;
|
||||
if (machine_is_poodle()) {
|
||||
sharpsl_partition_info[1].size=30 * 1024 * 1024;
|
||||
sharpsl_partition_info[1].size = 22 * 1024 * 1024;
|
||||
} else if (machine_is_corgi() || machine_is_shepherd()) {
|
||||
sharpsl_partition_info[1].size=25 * 1024 * 1024;
|
||||
sharpsl_partition_info[1].size = 25 * 1024 * 1024;
|
||||
} else if (machine_is_husky()) {
|
||||
sharpsl_partition_info[1].size=53 * 1024 * 1024;
|
||||
sharpsl_partition_info[1].size = 53 * 1024 * 1024;
|
||||
} else if (machine_is_spitz()) {
|
||||
sharpsl_partition_info[1].size=5 * 1024 * 1024;
|
||||
sharpsl_partition_info[1].size = 5 * 1024 * 1024;
|
||||
} else if (machine_is_akita()) {
|
||||
sharpsl_partition_info[1].size=58 * 1024 * 1024;
|
||||
sharpsl_partition_info[1].size = 58 * 1024 * 1024;
|
||||
} else if (machine_is_borzoi()) {
|
||||
sharpsl_partition_info[1].size=32 * 1024 * 1024;
|
||||
sharpsl_partition_info[1].size = 32 * 1024 * 1024;
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,15 +247,15 @@ sharpsl_nand_init(void)
|
||||
/* Return happy */
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(sharpsl_nand_init);
|
||||
|
||||
/*
|
||||
* Clean up routine
|
||||
*/
|
||||
#ifdef MODULE
|
||||
static void __exit sharpsl_nand_cleanup(void)
|
||||
{
|
||||
struct nand_chip *this = (struct nand_chip *) &sharpsl_mtd[1];
|
||||
struct nand_chip *this = (struct nand_chip *)&sharpsl_mtd[1];
|
||||
|
||||
/* Release resources, unregister device */
|
||||
nand_release(sharpsl_mtd);
|
||||
@ -279,8 +265,8 @@ static void __exit sharpsl_nand_cleanup(void)
|
||||
/* Free the MTD device structure */
|
||||
kfree(sharpsl_mtd);
|
||||
}
|
||||
|
||||
module_exit(sharpsl_nand_cleanup);
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
|
||||
|
@ -39,16 +39,16 @@ static struct mtd_info *spia_mtd = NULL;
|
||||
*/
|
||||
#define SPIA_IO_BASE 0xd0000000 /* Start of EP7212 IO address space */
|
||||
#define SPIA_FIO_BASE 0xf0000000 /* Address where flash is mapped */
|
||||
#define SPIA_PEDR 0x0080 /*
|
||||
* IO offset to Port E data register
|
||||
* where the CLE, ALE and NCE pins
|
||||
* are wired to.
|
||||
*/
|
||||
#define SPIA_PEDDR 0x00c0 /*
|
||||
* IO offset to Port E data direction
|
||||
* register so we can control the IO
|
||||
* lines.
|
||||
*/
|
||||
#define SPIA_PEDR 0x0080 /*
|
||||
* IO offset to Port E data register
|
||||
* where the CLE, ALE and NCE pins
|
||||
* are wired to.
|
||||
*/
|
||||
#define SPIA_PEDDR 0x00c0 /*
|
||||
* IO offset to Port E data direction
|
||||
* register so we can control the IO
|
||||
* lines.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Module stuff
|
||||
@ -69,79 +69,84 @@ module_param(spia_peddr, int, 0);
|
||||
*/
|
||||
static const struct mtd_partition partition_info[] = {
|
||||
{
|
||||
.name = "SPIA flash partition 1",
|
||||
.offset = 0,
|
||||
.size = 2*1024*1024
|
||||
},
|
||||
.name = "SPIA flash partition 1",
|
||||
.offset = 0,
|
||||
.size = 2 * 1024 * 1024},
|
||||
{
|
||||
.name = "SPIA flash partition 2",
|
||||
.offset = 2*1024*1024,
|
||||
.size = 6*1024*1024
|
||||
}
|
||||
.name = "SPIA flash partition 2",
|
||||
.offset = 2 * 1024 * 1024,
|
||||
.size = 6 * 1024 * 1024}
|
||||
};
|
||||
#define NUM_PARTITIONS 2
|
||||
|
||||
#define NUM_PARTITIONS 2
|
||||
|
||||
/*
|
||||
* hardware specific access to control-lines
|
||||
*/
|
||||
static void spia_hwcontrol(struct mtd_info *mtd, int cmd){
|
||||
*
|
||||
* ctrl:
|
||||
* NAND_CNE: bit 0 -> bit 2
|
||||
* NAND_CLE: bit 1 -> bit 0
|
||||
* NAND_ALE: bit 2 -> bit 1
|
||||
*/
|
||||
static void spia_hwcontrol(struct mtd_info *mtd, int cmd)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
switch(cmd){
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
void __iomem *addr = spia_io_base + spia_pedr;
|
||||
unsigned char bits;
|
||||
|
||||
case NAND_CTL_SETCLE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) |= 0x01; break;
|
||||
case NAND_CTL_CLRCLE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) &= ~0x01; break;
|
||||
bits = (ctrl & NAND_CNE) << 2;
|
||||
bits |= (ctrl & NAND_CLE | NAND_ALE) >> 1;
|
||||
writeb((readb(addr) & ~0x7) | bits, addr);
|
||||
}
|
||||
|
||||
case NAND_CTL_SETALE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) |= 0x02; break;
|
||||
case NAND_CTL_CLRALE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) &= ~0x02; break;
|
||||
|
||||
case NAND_CTL_SETNCE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) &= ~0x04; break;
|
||||
case NAND_CTL_CLRNCE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) |= 0x04; break;
|
||||
}
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
writeb(cmd, chip->IO_ADDR_W);
|
||||
}
|
||||
|
||||
/*
|
||||
* Main initialization routine
|
||||
*/
|
||||
int __init spia_init (void)
|
||||
static int __init spia_init(void)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
spia_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
|
||||
GFP_KERNEL);
|
||||
spia_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
|
||||
if (!spia_mtd) {
|
||||
printk ("Unable to allocate SPIA NAND MTD device structure.\n");
|
||||
printk("Unable to allocate SPIA NAND MTD device structure.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *) (&spia_mtd[1]);
|
||||
this = (struct nand_chip *)(&spia_mtd[1]);
|
||||
|
||||
/* Initialize structures */
|
||||
memset((char *) spia_mtd, 0, sizeof(struct mtd_info));
|
||||
memset((char *) this, 0, sizeof(struct nand_chip));
|
||||
memset(spia_mtd, 0, sizeof(struct mtd_info));
|
||||
memset(this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
spia_mtd->priv = this;
|
||||
spia_mtd->owner = THIS_MODULE;
|
||||
|
||||
/*
|
||||
* Set GPIO Port E control register so that the pins are configured
|
||||
* to be outputs for controlling the NAND flash.
|
||||
*/
|
||||
(*(volatile unsigned char *) (spia_io_base + spia_peddr)) = 0x07;
|
||||
(*(volatile unsigned char *)(spia_io_base + spia_peddr)) = 0x07;
|
||||
|
||||
/* Set address of NAND IO lines */
|
||||
this->IO_ADDR_R = (void __iomem *) spia_fio_base;
|
||||
this->IO_ADDR_W = (void __iomem *) spia_fio_base;
|
||||
this->IO_ADDR_R = (void __iomem *)spia_fio_base;
|
||||
this->IO_ADDR_W = (void __iomem *)spia_fio_base;
|
||||
/* Set address of hardware control function */
|
||||
this->hwcontrol = spia_hwcontrol;
|
||||
this->cmd_ctrl = spia_hwcontrol;
|
||||
/* 15 us command delay time */
|
||||
this->chip_delay = 15;
|
||||
|
||||
/* Scan to find existence of the device */
|
||||
if (nand_scan (spia_mtd, 1)) {
|
||||
kfree (spia_mtd);
|
||||
if (nand_scan(spia_mtd, 1)) {
|
||||
kfree(spia_mtd);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
@ -151,22 +156,22 @@ int __init spia_init (void)
|
||||
/* Return happy */
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(spia_init);
|
||||
|
||||
/*
|
||||
* Clean up routine
|
||||
*/
|
||||
#ifdef MODULE
|
||||
static void __exit spia_cleanup (void)
|
||||
static void __exit spia_cleanup(void)
|
||||
{
|
||||
/* Release resources, unregister device */
|
||||
nand_release (spia_mtd);
|
||||
nand_release(spia_mtd);
|
||||
|
||||
/* Free the MTD device structure */
|
||||
kfree (spia_mtd);
|
||||
kfree(spia_mtd);
|
||||
}
|
||||
|
||||
module_exit(spia_cleanup);
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com");
|
||||
|
@ -32,6 +32,8 @@
|
||||
#include <asm/arch-omap1510/hardware.h>
|
||||
#include <asm/arch/gpio.h>
|
||||
|
||||
#define CONFIG_NAND_WORKAROUND 1
|
||||
|
||||
/*
|
||||
* MTD structure for TOTO board
|
||||
*/
|
||||
@ -39,25 +41,6 @@ static struct mtd_info *toto_mtd = NULL;
|
||||
|
||||
static unsigned long toto_io_base = OMAP_FLASH_1_BASE;
|
||||
|
||||
#define CONFIG_NAND_WORKAROUND 1
|
||||
|
||||
#define NAND_NCE 0x4000
|
||||
#define NAND_CLE 0x1000
|
||||
#define NAND_ALE 0x0002
|
||||
#define NAND_MASK (NAND_CLE | NAND_ALE | NAND_NCE)
|
||||
|
||||
#define T_NAND_CTL_CLRALE(iob) gpiosetout(NAND_ALE, 0)
|
||||
#define T_NAND_CTL_SETALE(iob) gpiosetout(NAND_ALE, NAND_ALE)
|
||||
#ifdef CONFIG_NAND_WORKAROUND /* "some" dev boards busted, blue wired to rts2 :( */
|
||||
#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0); rts2setout(2, 2)
|
||||
#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE); rts2setout(2, 0)
|
||||
#else
|
||||
#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0)
|
||||
#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE)
|
||||
#endif
|
||||
#define T_NAND_CTL_SETNCE(iob) gpiosetout(NAND_NCE, 0)
|
||||
#define T_NAND_CTL_CLRNCE(iob) gpiosetout(NAND_NCE, NAND_NCE)
|
||||
|
||||
/*
|
||||
* Define partitions for flash devices
|
||||
*/
|
||||
@ -91,91 +74,110 @@ static struct mtd_partition partition_info32M[] = {
|
||||
|
||||
#define NUM_PARTITIONS32M 3
|
||||
#define NUM_PARTITIONS64M 4
|
||||
|
||||
/*
|
||||
* hardware specific access to control-lines
|
||||
*/
|
||||
|
||||
static void toto_hwcontrol(struct mtd_info *mtd, int cmd)
|
||||
*
|
||||
* ctrl:
|
||||
* NAND_NCE: bit 0 -> bit 14 (0x4000)
|
||||
* NAND_CLE: bit 1 -> bit 12 (0x1000)
|
||||
* NAND_ALE: bit 2 -> bit 1 (0x0002)
|
||||
*/
|
||||
static void toto_hwcontrol(struct mtd_info *mtd, int cmd,
|
||||
unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
udelay(1); /* hopefully enough time for tc make proceding write to clear */
|
||||
switch(cmd){
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
unsigned long bits;
|
||||
|
||||
case NAND_CTL_SETCLE: T_NAND_CTL_SETCLE(cmd); break;
|
||||
case NAND_CTL_CLRCLE: T_NAND_CTL_CLRCLE(cmd); break;
|
||||
/* hopefully enough time for tc make proceding write to clear */
|
||||
udelay(1);
|
||||
|
||||
case NAND_CTL_SETALE: T_NAND_CTL_SETALE(cmd); break;
|
||||
case NAND_CTL_CLRALE: T_NAND_CTL_CLRALE(cmd); break;
|
||||
bits = (~ctrl & NAND_NCE) << 14;
|
||||
bits |= (ctrl & NAND_CLE) << 12;
|
||||
bits |= (ctrl & NAND_ALE) >> 1;
|
||||
|
||||
case NAND_CTL_SETNCE: T_NAND_CTL_SETNCE(cmd); break;
|
||||
case NAND_CTL_CLRNCE: T_NAND_CTL_CLRNCE(cmd); break;
|
||||
#warning Wild guess as gpiosetout() is nowhere defined in the kernel source - tglx
|
||||
gpiosetout(0x5002, bits);
|
||||
|
||||
#ifdef CONFIG_NAND_WORKAROUND
|
||||
/* "some" dev boards busted, blue wired to rts2 :( */
|
||||
rts2setout(2, (ctrl & NAND_CLE) << 1);
|
||||
#endif
|
||||
/* allow time to ensure gpio state to over take memory write */
|
||||
udelay(1);
|
||||
}
|
||||
udelay(1); /* allow time to ensure gpio state to over take memory write */
|
||||
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
writeb(cmd, chip->IO_ADDR_W);
|
||||
}
|
||||
|
||||
/*
|
||||
* Main initialization routine
|
||||
*/
|
||||
int __init toto_init (void)
|
||||
static int __init toto_init(void)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
int err = 0;
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
toto_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
|
||||
GFP_KERNEL);
|
||||
toto_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
|
||||
if (!toto_mtd) {
|
||||
printk (KERN_WARNING "Unable to allocate toto NAND MTD device structure.\n");
|
||||
printk(KERN_WARNING "Unable to allocate toto NAND MTD device structure.\n");
|
||||
err = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *) (&toto_mtd[1]);
|
||||
this = (struct nand_chip *)(&toto_mtd[1]);
|
||||
|
||||
/* Initialize structures */
|
||||
memset((char *) toto_mtd, 0, sizeof(struct mtd_info));
|
||||
memset((char *) this, 0, sizeof(struct nand_chip));
|
||||
memset(toto_mtd, 0, sizeof(struct mtd_info));
|
||||
memset(this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
toto_mtd->priv = this;
|
||||
toto_mtd->owner = THIS_MODULE;
|
||||
|
||||
/* Set address of NAND IO lines */
|
||||
this->IO_ADDR_R = toto_io_base;
|
||||
this->IO_ADDR_W = toto_io_base;
|
||||
this->hwcontrol = toto_hwcontrol;
|
||||
this->cmd_ctrl = toto_hwcontrol;
|
||||
this->dev_ready = NULL;
|
||||
/* 25 us command delay time */
|
||||
this->chip_delay = 30;
|
||||
this->eccmode = NAND_ECC_SOFT;
|
||||
this->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
/* Scan to find existance of the device */
|
||||
if (nand_scan (toto_mtd, 1)) {
|
||||
/* Scan to find existance of the device */
|
||||
if (nand_scan(toto_mtd, 1)) {
|
||||
err = -ENXIO;
|
||||
goto out_mtd;
|
||||
}
|
||||
|
||||
/* Register the partitions */
|
||||
switch(toto_mtd->size){
|
||||
case SZ_64M: add_mtd_partitions(toto_mtd, partition_info64M, NUM_PARTITIONS64M); break;
|
||||
case SZ_32M: add_mtd_partitions(toto_mtd, partition_info32M, NUM_PARTITIONS32M); break;
|
||||
default: {
|
||||
printk (KERN_WARNING "Unsupported Nand device\n");
|
||||
switch (toto_mtd->size) {
|
||||
case SZ_64M:
|
||||
add_mtd_partitions(toto_mtd, partition_info64M, NUM_PARTITIONS64M);
|
||||
break;
|
||||
case SZ_32M:
|
||||
add_mtd_partitions(toto_mtd, partition_info32M, NUM_PARTITIONS32M);
|
||||
break;
|
||||
default:{
|
||||
printk(KERN_WARNING "Unsupported Nand device\n");
|
||||
err = -ENXIO;
|
||||
goto out_buf;
|
||||
}
|
||||
}
|
||||
|
||||
gpioreserve(NAND_MASK); /* claim our gpios */
|
||||
archflashwp(0,0); /* open up flash for writing */
|
||||
gpioreserve(NAND_MASK); /* claim our gpios */
|
||||
archflashwp(0, 0); /* open up flash for writing */
|
||||
|
||||
goto out;
|
||||
|
||||
out_buf:
|
||||
kfree (this->data_buf);
|
||||
out_mtd:
|
||||
kfree (toto_mtd);
|
||||
out:
|
||||
out_mtd:
|
||||
kfree(toto_mtd);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -184,20 +186,21 @@ module_init(toto_init);
|
||||
/*
|
||||
* Clean up routine
|
||||
*/
|
||||
static void __exit toto_cleanup (void)
|
||||
static void __exit toto_cleanup(void)
|
||||
{
|
||||
/* Release resources, unregister device */
|
||||
nand_release (toto_mtd);
|
||||
nand_release(toto_mtd);
|
||||
|
||||
/* Free the MTD device structure */
|
||||
kfree (toto_mtd);
|
||||
kfree(toto_mtd);
|
||||
|
||||
/* stop flash writes */
|
||||
archflashwp(0,1);
|
||||
archflashwp(0, 1);
|
||||
|
||||
/* release gpios to system */
|
||||
gpiorelease(NAND_MASK);
|
||||
gpiorelease(NAND_MASK);
|
||||
}
|
||||
|
||||
module_exit(toto_cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
206
drivers/mtd/nand/ts7250.c
Normal file
206
drivers/mtd/nand/ts7250.c
Normal file
@ -0,0 +1,206 @@
|
||||
/*
|
||||
* drivers/mtd/nand/ts7250.c
|
||||
*
|
||||
* Copyright (C) 2004 Technologic Systems (support@embeddedARM.com)
|
||||
*
|
||||
* Derived from drivers/mtd/nand/edb7312.c
|
||||
* Copyright (C) 2004 Marius Gröger (mag@sysgo.de)
|
||||
*
|
||||
* Derived from drivers/mtd/nand/autcpu12.c
|
||||
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
|
||||
*
|
||||
* $Id: ts7250.c,v 1.4 2004/12/30 22:02:07 joff 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.
|
||||
*
|
||||
* Overview:
|
||||
* This is a device driver for the NAND flash device found on the
|
||||
* TS-7250 board which utilizes a Samsung 32 Mbyte part.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/nand.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/sizes.h>
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
/*
|
||||
* MTD structure for TS7250 board
|
||||
*/
|
||||
static struct mtd_info *ts7250_mtd = NULL;
|
||||
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
static const char *part_probes[] = { "cmdlinepart", NULL };
|
||||
|
||||
#define NUM_PARTITIONS 3
|
||||
|
||||
/*
|
||||
* Define static partitions for flash device
|
||||
*/
|
||||
static struct mtd_partition partition_info32[] = {
|
||||
{
|
||||
.name = "TS-BOOTROM",
|
||||
.offset = 0x00000000,
|
||||
.size = 0x00004000,
|
||||
}, {
|
||||
.name = "Linux",
|
||||
.offset = 0x00004000,
|
||||
.size = 0x01d00000,
|
||||
}, {
|
||||
.name = "RedBoot",
|
||||
.offset = 0x01d04000,
|
||||
.size = 0x002fc000,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Define static partitions for flash device
|
||||
*/
|
||||
static struct mtd_partition partition_info128[] = {
|
||||
{
|
||||
.name = "TS-BOOTROM",
|
||||
.offset = 0x00000000,
|
||||
.size = 0x00004000,
|
||||
}, {
|
||||
.name = "Linux",
|
||||
.offset = 0x00004000,
|
||||
.size = 0x07d00000,
|
||||
}, {
|
||||
.name = "RedBoot",
|
||||
.offset = 0x07d04000,
|
||||
.size = 0x002fc000,
|
||||
},
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* hardware specific access to control-lines
|
||||
*
|
||||
* ctrl:
|
||||
* NAND_NCE: bit 0 -> bit 2
|
||||
* NAND_CLE: bit 1 -> bit 1
|
||||
* NAND_ALE: bit 2 -> bit 0
|
||||
*/
|
||||
static void ts7250_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
|
||||
{
|
||||
struct nand_chip *chip = mtd->priv;
|
||||
|
||||
if (ctrl & NAND_CTRL_CHANGE) {
|
||||
unsigned long addr = TS72XX_NAND_CONTROL_VIRT_BASE;
|
||||
unsigned char bits;
|
||||
|
||||
bits = (ctrl & NAND_CNE) << 2;
|
||||
bits |= ctrl & NAND_CLE;
|
||||
bits |= (ctrl & NAND_ALE) >> 2;
|
||||
|
||||
__raw_writeb((__raw_readb(addr) & ~0x7) | bits, addr);
|
||||
}
|
||||
|
||||
if (cmd != NAND_CMD_NONE)
|
||||
writeb(cmd, chip->IO_ADDR_W);
|
||||
}
|
||||
|
||||
/*
|
||||
* read device ready pin
|
||||
*/
|
||||
static int ts7250_device_ready(struct mtd_info *mtd)
|
||||
{
|
||||
return __raw_readb(TS72XX_NAND_BUSY_VIRT_BASE) & 0x20;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main initialization routine
|
||||
*/
|
||||
static int __init ts7250_init(void)
|
||||
{
|
||||
struct nand_chip *this;
|
||||
const char *part_type = 0;
|
||||
int mtd_parts_nb = 0;
|
||||
struct mtd_partition *mtd_parts = 0;
|
||||
|
||||
if (!machine_is_ts72xx() || board_is_ts7200())
|
||||
return -ENXIO;
|
||||
|
||||
/* Allocate memory for MTD device structure and private data */
|
||||
ts7250_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL);
|
||||
if (!ts7250_mtd) {
|
||||
printk("Unable to allocate TS7250 NAND MTD device structure.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Get pointer to private data */
|
||||
this = (struct nand_chip *)(&ts7250_mtd[1]);
|
||||
|
||||
/* Initialize structures */
|
||||
memset(ts7250_mtd, 0, sizeof(struct mtd_info));
|
||||
memset(this, 0, sizeof(struct nand_chip));
|
||||
|
||||
/* Link the private data with the MTD structure */
|
||||
ts7250_mtd->priv = this;
|
||||
ts7250_mtd->owner = THIS_MODULE;
|
||||
|
||||
/* insert callbacks */
|
||||
this->IO_ADDR_R = (void *)TS72XX_NAND_DATA_VIRT_BASE;
|
||||
this->IO_ADDR_W = (void *)TS72XX_NAND_DATA_VIRT_BASE;
|
||||
this->cmd_ctrl = ts7250_hwcontrol;
|
||||
this->dev_ready = ts7250_device_ready;
|
||||
this->chip_delay = 15;
|
||||
this->ecc.mode = NAND_ECC_SOFT;
|
||||
|
||||
printk("Searching for NAND flash...\n");
|
||||
/* Scan to find existence of the device */
|
||||
if (nand_scan(ts7250_mtd, 1)) {
|
||||
kfree(ts7250_mtd);
|
||||
return -ENXIO;
|
||||
}
|
||||
#ifdef CONFIG_MTD_PARTITIONS
|
||||
ts7250_mtd->name = "ts7250-nand";
|
||||
mtd_parts_nb = parse_mtd_partitions(ts7250_mtd, part_probes, &mtd_parts, 0);
|
||||
if (mtd_parts_nb > 0)
|
||||
part_type = "command line";
|
||||
else
|
||||
mtd_parts_nb = 0;
|
||||
#endif
|
||||
if (mtd_parts_nb == 0) {
|
||||
mtd_parts = partition_info32;
|
||||
if (ts7250_mtd->size >= (128 * 0x100000))
|
||||
mtd_parts = partition_info128;
|
||||
mtd_parts_nb = NUM_PARTITIONS;
|
||||
part_type = "static";
|
||||
}
|
||||
|
||||
/* Register the partitions */
|
||||
printk(KERN_NOTICE "Using %s partition definition\n", part_type);
|
||||
add_mtd_partitions(ts7250_mtd, mtd_parts, mtd_parts_nb);
|
||||
|
||||
/* Return happy */
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(ts7250_init);
|
||||
|
||||
/*
|
||||
* Clean up routine
|
||||
*/
|
||||
static void __exit ts7250_cleanup(void)
|
||||
{
|
||||
/* Unregister the device */
|
||||
del_mtd_device(ts7250_mtd);
|
||||
|
||||
/* Free the MTD device structure */
|
||||
kfree(ts7250_mtd);
|
||||
}
|
||||
|
||||
module_exit(ts7250_cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Jesse Off <joff@embeddedARM.com>");
|
||||
MODULE_DESCRIPTION("MTD map driver for Technologic Systems TS-7250 board");
|
@ -70,8 +70,6 @@ static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
|
||||
nftl->mbd.devnum = -1;
|
||||
nftl->mbd.blksize = 512;
|
||||
nftl->mbd.tr = tr;
|
||||
memcpy(&nftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo));
|
||||
nftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY;
|
||||
|
||||
if (NFTL_mount(nftl) < 0) {
|
||||
printk(KERN_WARNING "NFTL: could not mount device\n");
|
||||
@ -136,6 +134,69 @@ static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
|
||||
kfree(nftl);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read oob data from flash
|
||||
*/
|
||||
int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
size_t *retlen, uint8_t *buf)
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
int res;
|
||||
|
||||
ops.mode = MTD_OOB_PLACE;
|
||||
ops.ooboffs = offs & (mtd->writesize - 1);
|
||||
ops.ooblen = len;
|
||||
ops.oobbuf = buf;
|
||||
ops.datbuf = NULL;
|
||||
ops.len = len;
|
||||
|
||||
res = mtd->read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
|
||||
*retlen = ops.retlen;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write oob data to flash
|
||||
*/
|
||||
int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
size_t *retlen, uint8_t *buf)
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
int res;
|
||||
|
||||
ops.mode = MTD_OOB_PLACE;
|
||||
ops.ooboffs = offs & (mtd->writesize - 1);
|
||||
ops.ooblen = len;
|
||||
ops.oobbuf = buf;
|
||||
ops.datbuf = NULL;
|
||||
ops.len = len;
|
||||
|
||||
res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
|
||||
*retlen = ops.retlen;
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write data and oob to flash
|
||||
*/
|
||||
static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
size_t *retlen, uint8_t *buf, uint8_t *oob)
|
||||
{
|
||||
struct mtd_oob_ops ops;
|
||||
int res;
|
||||
|
||||
ops.mode = MTD_OOB_PLACE;
|
||||
ops.ooboffs = offs;
|
||||
ops.ooblen = mtd->oobsize;
|
||||
ops.oobbuf = oob;
|
||||
ops.datbuf = buf;
|
||||
ops.len = len;
|
||||
|
||||
res = mtd->write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
|
||||
*retlen = ops.retlen;
|
||||
return res;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NFTL_RW
|
||||
|
||||
/* Actual NFTL access routines */
|
||||
@ -185,6 +246,7 @@ static u16 NFTL_findfreeblock(struct NFTLrecord *nftl, int desperate )
|
||||
|
||||
static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned pendingblock )
|
||||
{
|
||||
struct mtd_info *mtd = nftl->mbd.mtd;
|
||||
u16 BlockMap[MAX_SECTORS_PER_UNIT];
|
||||
unsigned char BlockLastState[MAX_SECTORS_PER_UNIT];
|
||||
unsigned char BlockFreeFound[MAX_SECTORS_PER_UNIT];
|
||||
@ -194,7 +256,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
|
||||
unsigned int targetEUN;
|
||||
struct nftl_oob oob;
|
||||
int inplace = 1;
|
||||
size_t retlen;
|
||||
size_t retlen;
|
||||
|
||||
memset(BlockMap, 0xff, sizeof(BlockMap));
|
||||
memset(BlockFreeFound, 0, sizeof(BlockFreeFound));
|
||||
@ -210,21 +272,21 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
|
||||
/* Scan to find the Erase Unit which holds the actual data for each
|
||||
512-byte block within the Chain.
|
||||
*/
|
||||
silly = MAX_LOOPS;
|
||||
silly = MAX_LOOPS;
|
||||
targetEUN = BLOCK_NIL;
|
||||
while (thisEUN <= nftl->lastEUN ) {
|
||||
unsigned int status, foldmark;
|
||||
unsigned int status, foldmark;
|
||||
|
||||
targetEUN = thisEUN;
|
||||
for (block = 0; block < nftl->EraseSize / 512; block ++) {
|
||||
MTD_READOOB(nftl->mbd.mtd,
|
||||
(thisEUN * nftl->EraseSize) + (block * 512),
|
||||
16 , &retlen, (char *)&oob);
|
||||
nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
|
||||
(block * 512), 16 , &retlen,
|
||||
(char *)&oob);
|
||||
if (block == 2) {
|
||||
foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
|
||||
if (foldmark == FOLD_MARK_IN_PROGRESS) {
|
||||
DEBUG(MTD_DEBUG_LEVEL1,
|
||||
"Write Inhibited on EUN %d\n", thisEUN);
|
||||
foldmark = oob.u.c.FoldMark | oob.u.c.FoldMark1;
|
||||
if (foldmark == FOLD_MARK_IN_PROGRESS) {
|
||||
DEBUG(MTD_DEBUG_LEVEL1,
|
||||
"Write Inhibited on EUN %d\n", thisEUN);
|
||||
inplace = 0;
|
||||
} else {
|
||||
/* There's no other reason not to do inplace,
|
||||
@ -233,7 +295,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
|
||||
inplace = 1;
|
||||
}
|
||||
}
|
||||
status = oob.b.Status | oob.b.Status1;
|
||||
status = oob.b.Status | oob.b.Status1;
|
||||
BlockLastState[block] = status;
|
||||
|
||||
switch(status) {
|
||||
@ -328,15 +390,15 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
|
||||
return BLOCK_NIL;
|
||||
}
|
||||
} else {
|
||||
/* We put a fold mark in the chain we are folding only if
|
||||
we fold in place to help the mount check code. If we do
|
||||
not fold in place, it is possible to find the valid
|
||||
chain by selecting the longer one */
|
||||
oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
|
||||
oob.u.c.unused = 0xffffffff;
|
||||
MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
|
||||
8, &retlen, (char *)&oob.u);
|
||||
}
|
||||
/* We put a fold mark in the chain we are folding only if we
|
||||
fold in place to help the mount check code. If we do not fold in
|
||||
place, it is possible to find the valid chain by selecting the
|
||||
longer one */
|
||||
oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
|
||||
oob.u.c.unused = 0xffffffff;
|
||||
nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8,
|
||||
8, &retlen, (char *)&oob.u);
|
||||
}
|
||||
|
||||
/* OK. We now know the location of every block in the Virtual Unit Chain,
|
||||
and the Erase Unit into which we are supposed to be copying.
|
||||
@ -353,33 +415,33 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
|
||||
continue;
|
||||
}
|
||||
|
||||
/* copy only in non free block (free blocks can only
|
||||
/* copy only in non free block (free blocks can only
|
||||
happen in case of media errors or deleted blocks) */
|
||||
if (BlockMap[block] == BLOCK_NIL)
|
||||
continue;
|
||||
if (BlockMap[block] == BLOCK_NIL)
|
||||
continue;
|
||||
|
||||
ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
|
||||
512, &retlen, movebuf);
|
||||
if (ret < 0) {
|
||||
ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block])
|
||||
+ (block * 512), 512, &retlen,
|
||||
movebuf);
|
||||
if (ret != -EIO)
|
||||
printk("Error went away on retry.\n");
|
||||
}
|
||||
ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
|
||||
512, &retlen, movebuf);
|
||||
if (ret < 0 && ret != -EUCLEAN) {
|
||||
ret = mtd->read(mtd, (nftl->EraseSize * BlockMap[block])
|
||||
+ (block * 512), 512, &retlen,
|
||||
movebuf);
|
||||
if (ret != -EIO)
|
||||
printk("Error went away on retry.\n");
|
||||
}
|
||||
memset(&oob, 0xff, sizeof(struct nftl_oob));
|
||||
oob.b.Status = oob.b.Status1 = SECTOR_USED;
|
||||
MTD_WRITEECC(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + (block * 512),
|
||||
512, &retlen, movebuf, (char *)&oob, &nftl->oobinfo);
|
||||
|
||||
nftl_write(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) +
|
||||
(block * 512), 512, &retlen, movebuf, (char *)&oob);
|
||||
}
|
||||
|
||||
/* add the header so that it is now a valid chain */
|
||||
oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum
|
||||
= cpu_to_le16(thisVUC);
|
||||
oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
|
||||
/* add the header so that it is now a valid chain */
|
||||
oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
|
||||
oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
|
||||
|
||||
MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 8,
|
||||
8, &retlen, (char *)&oob.u);
|
||||
nftl_write_oob(mtd, (nftl->EraseSize * targetEUN) + 8,
|
||||
8, &retlen, (char *)&oob.u);
|
||||
|
||||
/* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
|
||||
|
||||
@ -396,18 +458,18 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p
|
||||
while (thisEUN <= nftl->lastEUN && thisEUN != targetEUN) {
|
||||
unsigned int EUNtmp;
|
||||
|
||||
EUNtmp = nftl->ReplUnitTable[thisEUN];
|
||||
EUNtmp = nftl->ReplUnitTable[thisEUN];
|
||||
|
||||
if (NFTL_formatblock(nftl, thisEUN) < 0) {
|
||||
if (NFTL_formatblock(nftl, thisEUN) < 0) {
|
||||
/* could not erase : mark block as reserved
|
||||
*/
|
||||
nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
|
||||
} else {
|
||||
} else {
|
||||
/* correctly erased : mark it as free */
|
||||
nftl->ReplUnitTable[thisEUN] = BLOCK_FREE;
|
||||
nftl->numfreeEUNs++;
|
||||
}
|
||||
thisEUN = EUNtmp;
|
||||
}
|
||||
thisEUN = EUNtmp;
|
||||
}
|
||||
|
||||
/* Make this the new start of chain for thisVUC */
|
||||
@ -473,6 +535,7 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
|
||||
{
|
||||
u16 lastEUN;
|
||||
u16 thisVUC = block / (nftl->EraseSize / 512);
|
||||
struct mtd_info *mtd = nftl->mbd.mtd;
|
||||
unsigned int writeEUN;
|
||||
unsigned long blockofs = (block * 512) & (nftl->EraseSize -1);
|
||||
size_t retlen;
|
||||
@ -489,21 +552,22 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
|
||||
*/
|
||||
lastEUN = BLOCK_NIL;
|
||||
writeEUN = nftl->EUNtable[thisVUC];
|
||||
silly = MAX_LOOPS;
|
||||
silly = MAX_LOOPS;
|
||||
while (writeEUN <= nftl->lastEUN) {
|
||||
struct nftl_bci bci;
|
||||
size_t retlen;
|
||||
unsigned int status;
|
||||
unsigned int status;
|
||||
|
||||
lastEUN = writeEUN;
|
||||
|
||||
MTD_READOOB(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
|
||||
8, &retlen, (char *)&bci);
|
||||
nftl_read_oob(mtd,
|
||||
(writeEUN * nftl->EraseSize) + blockofs,
|
||||
8, &retlen, (char *)&bci);
|
||||
|
||||
DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
|
||||
block , writeEUN, le16_to_cpu(bci.Status));
|
||||
|
||||
status = bci.Status | bci.Status1;
|
||||
status = bci.Status | bci.Status1;
|
||||
switch(status) {
|
||||
case SECTOR_FREE:
|
||||
return writeEUN;
|
||||
@ -574,10 +638,10 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
|
||||
/* We've found a free block. Insert it into the chain. */
|
||||
|
||||
if (lastEUN != BLOCK_NIL) {
|
||||
thisVUC |= 0x8000; /* It's a replacement block */
|
||||
thisVUC |= 0x8000; /* It's a replacement block */
|
||||
} else {
|
||||
/* The first block in a new chain */
|
||||
nftl->EUNtable[thisVUC] = writeEUN;
|
||||
/* The first block in a new chain */
|
||||
nftl->EUNtable[thisVUC] = writeEUN;
|
||||
}
|
||||
|
||||
/* set up the actual EUN we're writing into */
|
||||
@ -585,29 +649,29 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block)
|
||||
nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
|
||||
|
||||
/* ... and on the flash itself */
|
||||
MTD_READOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8,
|
||||
&retlen, (char *)&oob.u);
|
||||
nftl_read_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
|
||||
&retlen, (char *)&oob.u);
|
||||
|
||||
oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
|
||||
|
||||
MTD_WRITEOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8,
|
||||
&retlen, (char *)&oob.u);
|
||||
nftl_write_oob(mtd, writeEUN * nftl->EraseSize + 8, 8,
|
||||
&retlen, (char *)&oob.u);
|
||||
|
||||
/* we link the new block to the chain only after the
|
||||
/* we link the new block to the chain only after the
|
||||
block is ready. It avoids the case where the chain
|
||||
could point to a free block */
|
||||
if (lastEUN != BLOCK_NIL) {
|
||||
if (lastEUN != BLOCK_NIL) {
|
||||
/* Both in our cache... */
|
||||
nftl->ReplUnitTable[lastEUN] = writeEUN;
|
||||
/* ... and on the flash itself */
|
||||
MTD_READOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8,
|
||||
8, &retlen, (char *)&oob.u);
|
||||
nftl_read_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
|
||||
8, &retlen, (char *)&oob.u);
|
||||
|
||||
oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
|
||||
= cpu_to_le16(writeEUN);
|
||||
|
||||
MTD_WRITEOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8,
|
||||
8, &retlen, (char *)&oob.u);
|
||||
nftl_write_oob(mtd, (lastEUN * nftl->EraseSize) + 8,
|
||||
8, &retlen, (char *)&oob.u);
|
||||
}
|
||||
|
||||
return writeEUN;
|
||||
@ -639,10 +703,9 @@ static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
|
||||
|
||||
memset(&oob, 0xff, sizeof(struct nftl_oob));
|
||||
oob.b.Status = oob.b.Status1 = SECTOR_USED;
|
||||
MTD_WRITEECC(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
|
||||
512, &retlen, (char *)buffer, (char *)&oob, &nftl->oobinfo);
|
||||
/* need to write SECTOR_USED flags since they are not written in mtd_writeecc */
|
||||
|
||||
nftl_write(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
|
||||
512, &retlen, (char *)buffer, (char *)&oob);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_NFTL_RW */
|
||||
@ -651,20 +714,22 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
|
||||
char *buffer)
|
||||
{
|
||||
struct NFTLrecord *nftl = (void *)mbd;
|
||||
struct mtd_info *mtd = nftl->mbd.mtd;
|
||||
u16 lastgoodEUN;
|
||||
u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
|
||||
unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
|
||||
unsigned int status;
|
||||
unsigned int status;
|
||||
int silly = MAX_LOOPS;
|
||||
size_t retlen;
|
||||
struct nftl_bci bci;
|
||||
size_t retlen;
|
||||
struct nftl_bci bci;
|
||||
|
||||
lastgoodEUN = BLOCK_NIL;
|
||||
|
||||
if (thisEUN != BLOCK_NIL) {
|
||||
if (thisEUN != BLOCK_NIL) {
|
||||
while (thisEUN < nftl->nb_blocks) {
|
||||
if (MTD_READOOB(nftl->mbd.mtd, (thisEUN * nftl->EraseSize) + blockofs,
|
||||
8, &retlen, (char *)&bci) < 0)
|
||||
if (nftl_read_oob(mtd, (thisEUN * nftl->EraseSize) +
|
||||
blockofs, 8, &retlen,
|
||||
(char *)&bci) < 0)
|
||||
status = SECTOR_IGNORE;
|
||||
else
|
||||
status = bci.Status | bci.Status1;
|
||||
@ -694,7 +759,7 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
|
||||
}
|
||||
thisEUN = nftl->ReplUnitTable[thisEUN];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
the_end:
|
||||
if (lastgoodEUN == BLOCK_NIL) {
|
||||
@ -703,7 +768,9 @@ static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
|
||||
} else {
|
||||
loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
|
||||
size_t retlen;
|
||||
if (MTD_READ(nftl->mbd.mtd, ptr, 512, &retlen, buffer))
|
||||
int res = mtd->read(mtd, ptr, 512, &retlen, buffer);
|
||||
|
||||
if (res < 0 && res != -EUCLEAN)
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
|
@ -33,6 +33,11 @@
|
||||
|
||||
char nftlmountrev[]="$Revision: 1.41 $";
|
||||
|
||||
extern int nftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
size_t *retlen, uint8_t *buf);
|
||||
extern int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
|
||||
size_t *retlen, uint8_t *buf);
|
||||
|
||||
/* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the
|
||||
* various device information of the NFTL partition and Bad Unit Table. Update
|
||||
* the ReplUnitTable[] table accroding to the Bad Unit Table. ReplUnitTable[]
|
||||
@ -45,6 +50,7 @@ static int find_boot_record(struct NFTLrecord *nftl)
|
||||
size_t retlen;
|
||||
u8 buf[SECTORSIZE];
|
||||
struct NFTLMediaHeader *mh = &nftl->MediaHdr;
|
||||
struct mtd_info *mtd = nftl->mbd.mtd;
|
||||
unsigned int i;
|
||||
|
||||
/* Assume logical EraseSize == physical erasesize for starting the scan.
|
||||
@ -65,7 +71,8 @@ static int find_boot_record(struct NFTLrecord *nftl)
|
||||
|
||||
/* Check for ANAND header first. Then can whinge if it's found but later
|
||||
checks fail */
|
||||
ret = MTD_READ(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf);
|
||||
ret = mtd->read(mtd, block * nftl->EraseSize, SECTORSIZE,
|
||||
&retlen, buf);
|
||||
/* We ignore ret in case the ECC of the MediaHeader is invalid
|
||||
(which is apparently acceptable) */
|
||||
if (retlen != SECTORSIZE) {
|
||||
@ -90,8 +97,9 @@ static int find_boot_record(struct NFTLrecord *nftl)
|
||||
}
|
||||
|
||||
/* To be safer with BIOS, also use erase mark as discriminant */
|
||||
if ((ret = MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8,
|
||||
8, &retlen, (char *)&h1) < 0)) {
|
||||
if ((ret = nftl_read_oob(mtd, block * nftl->EraseSize +
|
||||
SECTORSIZE + 8, 8, &retlen,
|
||||
(char *)&h1) < 0)) {
|
||||
printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n",
|
||||
block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
|
||||
continue;
|
||||
@ -109,8 +117,8 @@ static int find_boot_record(struct NFTLrecord *nftl)
|
||||
}
|
||||
|
||||
/* Finally reread to check ECC */
|
||||
if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE,
|
||||
&retlen, buf, (char *)&oob, NULL) < 0)) {
|
||||
if ((ret = mtd->read(mtd, block * nftl->EraseSize, SECTORSIZE,
|
||||
&retlen, buf) < 0)) {
|
||||
printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n",
|
||||
block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
|
||||
continue;
|
||||
@ -228,9 +236,9 @@ device is already correct.
|
||||
The new DiskOnChip driver already scanned the bad block table. Just query it.
|
||||
if ((i & (SECTORSIZE - 1)) == 0) {
|
||||
/* read one sector for every SECTORSIZE of blocks */
|
||||
if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize +
|
||||
i + SECTORSIZE, SECTORSIZE, &retlen, buf,
|
||||
(char *)&oob, NULL)) < 0) {
|
||||
if ((ret = mtd->read(nftl->mbd.mtd, block * nftl->EraseSize +
|
||||
i + SECTORSIZE, SECTORSIZE, &retlen,
|
||||
buf)) < 0) {
|
||||
printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n",
|
||||
ret);
|
||||
kfree(nftl->ReplUnitTable);
|
||||
@ -268,18 +276,22 @@ static int memcmpb(void *a, int c, int n)
|
||||
static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int len,
|
||||
int check_oob)
|
||||
{
|
||||
int i;
|
||||
size_t retlen;
|
||||
u8 buf[SECTORSIZE + nftl->mbd.mtd->oobsize];
|
||||
struct mtd_info *mtd = nftl->mbd.mtd;
|
||||
size_t retlen;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i += SECTORSIZE) {
|
||||
if (MTD_READECC(nftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &nftl->oobinfo) < 0)
|
||||
if (mtd->read(mtd, address, SECTORSIZE, &retlen, buf))
|
||||
return -1;
|
||||
if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
|
||||
return -1;
|
||||
|
||||
if (check_oob) {
|
||||
if (memcmpb(buf + SECTORSIZE, 0xff, nftl->mbd.mtd->oobsize) != 0)
|
||||
if(nftl_read_oob(mtd, address, mtd->oobsize,
|
||||
&retlen, &buf[SECTORSIZE]) < 0)
|
||||
return -1;
|
||||
if (memcmpb(buf + SECTORSIZE, 0xff, mtd->oobsize) != 0)
|
||||
return -1;
|
||||
}
|
||||
address += SECTORSIZE;
|
||||
@ -301,10 +313,11 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block)
|
||||
unsigned int nb_erases, erase_mark;
|
||||
struct nftl_uci1 uci;
|
||||
struct erase_info *instr = &nftl->instr;
|
||||
struct mtd_info *mtd = nftl->mbd.mtd;
|
||||
|
||||
/* Read the Unit Control Information #1 for Wear-Leveling */
|
||||
if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8,
|
||||
8, &retlen, (char *)&uci) < 0)
|
||||
if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8,
|
||||
8, &retlen, (char *)&uci) < 0)
|
||||
goto default_uci1;
|
||||
|
||||
erase_mark = le16_to_cpu ((uci.EraseMark | uci.EraseMark1));
|
||||
@ -321,7 +334,7 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block)
|
||||
instr->mtd = nftl->mbd.mtd;
|
||||
instr->addr = block * nftl->EraseSize;
|
||||
instr->len = nftl->EraseSize;
|
||||
MTD_ERASE(nftl->mbd.mtd, instr);
|
||||
mtd->erase(mtd, instr);
|
||||
|
||||
if (instr->state == MTD_ERASE_FAILED) {
|
||||
printk("Error while formatting block %d\n", block);
|
||||
@ -343,8 +356,8 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block)
|
||||
goto fail;
|
||||
|
||||
uci.WearInfo = le32_to_cpu(nb_erases);
|
||||
if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
|
||||
&retlen, (char *)&uci) < 0)
|
||||
if (nftl_write_oob(mtd, block * nftl->EraseSize + SECTORSIZE +
|
||||
8, 8, &retlen, (char *)&uci) < 0)
|
||||
goto fail;
|
||||
return 0;
|
||||
fail:
|
||||
@ -365,6 +378,7 @@ fail:
|
||||
* case. */
|
||||
static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_block)
|
||||
{
|
||||
struct mtd_info *mtd = nftl->mbd.mtd;
|
||||
unsigned int block, i, status;
|
||||
struct nftl_bci bci;
|
||||
int sectors_per_block;
|
||||
@ -374,8 +388,9 @@ static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_b
|
||||
block = first_block;
|
||||
for (;;) {
|
||||
for (i = 0; i < sectors_per_block; i++) {
|
||||
if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i * SECTORSIZE,
|
||||
8, &retlen, (char *)&bci) < 0)
|
||||
if (nftl_read_oob(mtd,
|
||||
block * nftl->EraseSize + i * SECTORSIZE,
|
||||
8, &retlen, (char *)&bci) < 0)
|
||||
status = SECTOR_IGNORE;
|
||||
else
|
||||
status = bci.Status | bci.Status1;
|
||||
@ -394,9 +409,10 @@ static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_b
|
||||
/* sector not free actually : mark it as SECTOR_IGNORE */
|
||||
bci.Status = SECTOR_IGNORE;
|
||||
bci.Status1 = SECTOR_IGNORE;
|
||||
MTD_WRITEOOB(nftl->mbd.mtd,
|
||||
block * nftl->EraseSize + i * SECTORSIZE,
|
||||
8, &retlen, (char *)&bci);
|
||||
nftl_write_oob(mtd, block *
|
||||
nftl->EraseSize +
|
||||
i * SECTORSIZE, 8,
|
||||
&retlen, (char *)&bci);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -481,13 +497,14 @@ static void format_chain(struct NFTLrecord *nftl, unsigned int first_block)
|
||||
* 1. */
|
||||
static int check_and_mark_free_block(struct NFTLrecord *nftl, int block)
|
||||
{
|
||||
struct mtd_info *mtd = nftl->mbd.mtd;
|
||||
struct nftl_uci1 h1;
|
||||
unsigned int erase_mark;
|
||||
size_t retlen;
|
||||
|
||||
/* check erase mark. */
|
||||
if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
|
||||
&retlen, (char *)&h1) < 0)
|
||||
if (nftl_read_oob(mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
|
||||
&retlen, (char *)&h1) < 0)
|
||||
return -1;
|
||||
|
||||
erase_mark = le16_to_cpu ((h1.EraseMark | h1.EraseMark1));
|
||||
@ -501,8 +518,9 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block)
|
||||
h1.EraseMark = cpu_to_le16(ERASE_MARK);
|
||||
h1.EraseMark1 = cpu_to_le16(ERASE_MARK);
|
||||
h1.WearInfo = cpu_to_le32(0);
|
||||
if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
|
||||
&retlen, (char *)&h1) < 0)
|
||||
if (nftl_write_oob(mtd,
|
||||
block * nftl->EraseSize + SECTORSIZE + 8, 8,
|
||||
&retlen, (char *)&h1) < 0)
|
||||
return -1;
|
||||
} else {
|
||||
#if 0
|
||||
@ -513,8 +531,8 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block)
|
||||
SECTORSIZE, 0) != 0)
|
||||
return -1;
|
||||
|
||||
if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i,
|
||||
16, &retlen, buf) < 0)
|
||||
if (nftl_read_oob(mtd, block * nftl->EraseSize + i,
|
||||
16, &retlen, buf) < 0)
|
||||
return -1;
|
||||
if (i == SECTORSIZE) {
|
||||
/* skip erase mark */
|
||||
@ -540,11 +558,12 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block)
|
||||
*/
|
||||
static int get_fold_mark(struct NFTLrecord *nftl, unsigned int block)
|
||||
{
|
||||
struct mtd_info *mtd = nftl->mbd.mtd;
|
||||
struct nftl_uci2 uci;
|
||||
size_t retlen;
|
||||
|
||||
if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8,
|
||||
8, &retlen, (char *)&uci) < 0)
|
||||
if (nftl_read_oob(mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8,
|
||||
8, &retlen, (char *)&uci) < 0)
|
||||
return 0;
|
||||
|
||||
return le16_to_cpu((uci.FoldMark | uci.FoldMark1));
|
||||
@ -558,6 +577,7 @@ int NFTL_mount(struct NFTLrecord *s)
|
||||
int chain_length, do_format_chain;
|
||||
struct nftl_uci0 h0;
|
||||
struct nftl_uci1 h1;
|
||||
struct mtd_info *mtd = s->mbd.mtd;
|
||||
size_t retlen;
|
||||
|
||||
/* search for NFTL MediaHeader and Spare NFTL Media Header */
|
||||
@ -582,10 +602,13 @@ int NFTL_mount(struct NFTLrecord *s)
|
||||
|
||||
for (;;) {
|
||||
/* read the block header. If error, we format the chain */
|
||||
if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8, 8,
|
||||
&retlen, (char *)&h0) < 0 ||
|
||||
MTD_READOOB(s->mbd.mtd, block * s->EraseSize + SECTORSIZE + 8, 8,
|
||||
&retlen, (char *)&h1) < 0) {
|
||||
if (nftl_read_oob(mtd,
|
||||
block * s->EraseSize + 8, 8,
|
||||
&retlen, (char *)&h0) < 0 ||
|
||||
nftl_read_oob(mtd,
|
||||
block * s->EraseSize +
|
||||
SECTORSIZE + 8, 8,
|
||||
&retlen, (char *)&h1) < 0) {
|
||||
s->ReplUnitTable[block] = BLOCK_NIL;
|
||||
do_format_chain = 1;
|
||||
break;
|
||||
|
@ -29,6 +29,20 @@ config MTD_ONENAND_GENERIC
|
||||
help
|
||||
Support for OneNAND flash via platform device driver.
|
||||
|
||||
config MTD_ONENAND_OTP
|
||||
bool "OneNAND OTP Support"
|
||||
depends on MTD_ONENAND
|
||||
help
|
||||
One Block of the NAND Flash Array memory is reserved as
|
||||
a One-Time Programmable Block memory area.
|
||||
Also, 1st Block of NAND Flash Array can be used as OTP.
|
||||
|
||||
The OTP block can be read, programmed and locked using the same
|
||||
operations as any other NAND Flash Array memory block.
|
||||
OTP block cannot be erased.
|
||||
|
||||
OTP block is fully-guaranteed to be a valid block.
|
||||
|
||||
config MTD_ONENAND_SYNC_READ
|
||||
bool "OneNAND Sync. Burst Read Support"
|
||||
depends on ARCH_OMAP
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -17,6 +17,9 @@
|
||||
#include <linux/mtd/onenand.h>
|
||||
#include <linux/mtd/compatmac.h>
|
||||
|
||||
extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf);
|
||||
|
||||
/**
|
||||
* check_short_pattern - [GENERIC] check if a pattern is in the buffer
|
||||
* @param buf the buffer to search
|
||||
@ -87,13 +90,13 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
|
||||
|
||||
/* 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]);
|
||||
ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs,
|
||||
readlen, &retlen, &buf[0]);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (check_short_pattern(&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
|
||||
if (check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) {
|
||||
bbm->bbt[i >> 3] |= 0x03 << (i & 0x6);
|
||||
printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
|
||||
i >> 1, (unsigned int) from);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* $Id: redboot.c,v 1.19 2005/12/01 10:03:51 dwmw2 Exp $
|
||||
* $Id: redboot.c,v 1.21 2006/03/30 18:34:37 bjd Exp $
|
||||
*
|
||||
* Parse RedBoot-style Flash Image System (FIS) tables and
|
||||
* produce a Linux partition array to match.
|
||||
@ -15,14 +15,14 @@
|
||||
|
||||
struct fis_image_desc {
|
||||
unsigned char name[16]; // Null terminated name
|
||||
unsigned long flash_base; // Address within FLASH of image
|
||||
unsigned long mem_base; // Address in memory where it executes
|
||||
unsigned long size; // Length of image
|
||||
unsigned long entry_point; // Execution entry point
|
||||
unsigned long data_length; // Length of actual data
|
||||
unsigned char _pad[256-(16+7*sizeof(unsigned long))];
|
||||
unsigned long desc_cksum; // Checksum over image descriptor
|
||||
unsigned long file_cksum; // Checksum over image data
|
||||
uint32_t flash_base; // Address within FLASH of image
|
||||
uint32_t mem_base; // Address in memory where it executes
|
||||
uint32_t size; // Length of image
|
||||
uint32_t entry_point; // Execution entry point
|
||||
uint32_t data_length; // Length of actual data
|
||||
unsigned char _pad[256-(16+7*sizeof(uint32_t))];
|
||||
uint32_t desc_cksum; // Checksum over image descriptor
|
||||
uint32_t file_cksum; // Checksum over image data
|
||||
};
|
||||
|
||||
struct fis_list {
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (C) 2005 Sean Young <sean@mess.org>
|
||||
*
|
||||
* $Id: rfd_ftl.c,v 1.5 2005/11/07 11:14:21 gleixner Exp $
|
||||
* $Id: rfd_ftl.c,v 1.8 2006/01/15 12:51:44 sean Exp $
|
||||
*
|
||||
* This type of flash translation layer (FTL) is used by the Embedded BIOS
|
||||
* by General Software. It is known as the Resident Flash Disk (RFD), see:
|
||||
@ -61,6 +61,7 @@ struct block {
|
||||
BLOCK_OK,
|
||||
BLOCK_ERASING,
|
||||
BLOCK_ERASED,
|
||||
BLOCK_UNUSED,
|
||||
BLOCK_FAILED
|
||||
} state;
|
||||
int free_sectors;
|
||||
@ -99,10 +100,8 @@ static int build_block_map(struct partition *part, int block_no)
|
||||
block->offset = part->block_size * block_no;
|
||||
|
||||
if (le16_to_cpu(part->header_cache[0]) != RFD_MAGIC) {
|
||||
block->state = BLOCK_ERASED; /* assumption */
|
||||
block->free_sectors = part->data_sectors_per_block;
|
||||
part->reserved_block = block_no;
|
||||
return 1;
|
||||
block->state = BLOCK_UNUSED;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
block->state = BLOCK_OK;
|
||||
@ -124,7 +123,7 @@ static int build_block_map(struct partition *part, int block_no)
|
||||
entry = 0;
|
||||
|
||||
if (entry >= part->sector_count) {
|
||||
printk(KERN_NOTICE PREFIX
|
||||
printk(KERN_WARNING PREFIX
|
||||
"'%s': unit #%d: entry %d corrupt, "
|
||||
"sector %d out of range\n",
|
||||
part->mbd.mtd->name, block_no, i, entry);
|
||||
@ -132,7 +131,7 @@ static int build_block_map(struct partition *part, int block_no)
|
||||
}
|
||||
|
||||
if (part->sector_map[entry] != -1) {
|
||||
printk(KERN_NOTICE PREFIX
|
||||
printk(KERN_WARNING PREFIX
|
||||
"'%s': more than one entry for sector %d\n",
|
||||
part->mbd.mtd->name, entry);
|
||||
part->errors = 1;
|
||||
@ -167,7 +166,7 @@ static int scan_header(struct partition *part)
|
||||
/* each erase block has three bytes header, followed by the map */
|
||||
part->header_sectors_per_block =
|
||||
((HEADER_MAP_OFFSET + sectors_per_block) *
|
||||
sizeof(u16) + SECTOR_SIZE - 1) / SECTOR_SIZE;
|
||||
sizeof(u16) + SECTOR_SIZE - 1) / SECTOR_SIZE;
|
||||
|
||||
part->data_sectors_per_block = sectors_per_block -
|
||||
part->header_sectors_per_block;
|
||||
@ -226,7 +225,7 @@ static int scan_header(struct partition *part)
|
||||
}
|
||||
|
||||
if (part->reserved_block == -1) {
|
||||
printk(KERN_NOTICE PREFIX "'%s': no empty erase unit found\n",
|
||||
printk(KERN_WARNING PREFIX "'%s': no empty erase unit found\n",
|
||||
part->mbd.mtd->name);
|
||||
|
||||
part->errors = 1;
|
||||
@ -315,7 +314,7 @@ static void erase_callback(struct erase_info *erase)
|
||||
rc = -EIO;
|
||||
|
||||
if (rc) {
|
||||
printk(KERN_NOTICE PREFIX "'%s': unable to write RFD "
|
||||
printk(KERN_ERR PREFIX "'%s': unable to write RFD "
|
||||
"header at 0x%lx\n",
|
||||
part->mbd.mtd->name,
|
||||
part->blocks[i].offset);
|
||||
@ -348,7 +347,7 @@ static int erase_block(struct partition *part, int block)
|
||||
rc = part->mbd.mtd->erase(part->mbd.mtd, erase);
|
||||
|
||||
if (rc) {
|
||||
printk(KERN_WARNING PREFIX "erase of region %x,%x on '%s' "
|
||||
printk(KERN_ERR PREFIX "erase of region %x,%x on '%s' "
|
||||
"failed\n", erase->addr, erase->len,
|
||||
part->mbd.mtd->name);
|
||||
kfree(erase);
|
||||
@ -383,7 +382,7 @@ static int move_block_contents(struct partition *part, int block_no, u_long *old
|
||||
rc = -EIO;
|
||||
|
||||
if (rc) {
|
||||
printk(KERN_NOTICE PREFIX "error reading '%s' at "
|
||||
printk(KERN_ERR PREFIX "error reading '%s' at "
|
||||
"0x%lx\n", part->mbd.mtd->name,
|
||||
part->blocks[block_no].offset);
|
||||
|
||||
@ -423,7 +422,7 @@ static int move_block_contents(struct partition *part, int block_no, u_long *old
|
||||
rc = -EIO;
|
||||
|
||||
if (rc) {
|
||||
printk(KERN_NOTICE PREFIX "'%s': Unable to "
|
||||
printk(KERN_ERR PREFIX "'%s': Unable to "
|
||||
"read sector for relocation\n",
|
||||
part->mbd.mtd->name);
|
||||
|
||||
@ -520,7 +519,7 @@ static int reclaim_block(struct partition *part, u_long *old_sector)
|
||||
* because if we fill that one up first it'll have the most chance of having
|
||||
* the least live sectors at reclaim.
|
||||
*/
|
||||
static int find_free_block(const struct partition *part)
|
||||
static int find_free_block(struct partition *part)
|
||||
{
|
||||
int block, stop;
|
||||
|
||||
@ -533,6 +532,9 @@ static int find_free_block(const struct partition *part)
|
||||
block != part->reserved_block)
|
||||
return block;
|
||||
|
||||
if (part->blocks[block].state == BLOCK_UNUSED)
|
||||
erase_block(part, block);
|
||||
|
||||
if (++block >= part->total_blocks)
|
||||
block = 0;
|
||||
|
||||
@ -541,7 +543,7 @@ static int find_free_block(const struct partition *part)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int find_writeable_block(struct partition *part, u_long *old_sector)
|
||||
static int find_writable_block(struct partition *part, u_long *old_sector)
|
||||
{
|
||||
int rc, block;
|
||||
size_t retlen;
|
||||
@ -570,7 +572,7 @@ static int find_writeable_block(struct partition *part, u_long *old_sector)
|
||||
rc = -EIO;
|
||||
|
||||
if (rc) {
|
||||
printk(KERN_NOTICE PREFIX "'%s': unable to read header at "
|
||||
printk(KERN_ERR PREFIX "'%s': unable to read header at "
|
||||
"0x%lx\n", part->mbd.mtd->name,
|
||||
part->blocks[block].offset);
|
||||
goto err;
|
||||
@ -602,7 +604,7 @@ static int mark_sector_deleted(struct partition *part, u_long old_addr)
|
||||
rc = -EIO;
|
||||
|
||||
if (rc) {
|
||||
printk(KERN_WARNING PREFIX "error writing '%s' at "
|
||||
printk(KERN_ERR PREFIX "error writing '%s' at "
|
||||
"0x%lx\n", part->mbd.mtd->name, addr);
|
||||
if (rc)
|
||||
goto err;
|
||||
@ -652,7 +654,7 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf,
|
||||
if (part->current_block == -1 ||
|
||||
!part->blocks[part->current_block].free_sectors) {
|
||||
|
||||
rc = find_writeable_block(part, old_addr);
|
||||
rc = find_writable_block(part, old_addr);
|
||||
if (rc)
|
||||
goto err;
|
||||
}
|
||||
@ -675,7 +677,7 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf,
|
||||
rc = -EIO;
|
||||
|
||||
if (rc) {
|
||||
printk(KERN_WARNING PREFIX "error writing '%s' at 0x%lx\n",
|
||||
printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
|
||||
part->mbd.mtd->name, addr);
|
||||
if (rc)
|
||||
goto err;
|
||||
@ -695,7 +697,7 @@ static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf,
|
||||
rc = -EIO;
|
||||
|
||||
if (rc) {
|
||||
printk(KERN_WARNING PREFIX "error writing '%s' at 0x%lx\n",
|
||||
printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
|
||||
part->mbd.mtd->name, addr);
|
||||
if (rc)
|
||||
goto err;
|
||||
@ -776,7 +778,7 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
|
||||
part->block_size = block_size;
|
||||
else {
|
||||
if (!mtd->erasesize) {
|
||||
printk(KERN_NOTICE PREFIX "please provide block_size");
|
||||
printk(KERN_WARNING PREFIX "please provide block_size");
|
||||
return;
|
||||
}
|
||||
else
|
||||
@ -791,8 +793,8 @@ static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
|
||||
if (!(mtd->flags & MTD_WRITEABLE))
|
||||
part->mbd.readonly = 1;
|
||||
else if (part->errors) {
|
||||
printk(KERN_NOTICE PREFIX "'%s': errors found, "
|
||||
"setting read-only", mtd->name);
|
||||
printk(KERN_WARNING PREFIX "'%s': errors found, "
|
||||
"setting read-only\n", mtd->name);
|
||||
part->mbd.readonly = 1;
|
||||
}
|
||||
|
||||
|
38
fs/Kconfig
38
fs/Kconfig
@ -1101,6 +1101,44 @@ config JFFS2_SUMMARY
|
||||
|
||||
If unsure, say 'N'.
|
||||
|
||||
config JFFS2_FS_XATTR
|
||||
bool "JFFS2 XATTR support (EXPERIMENTAL)"
|
||||
depends on JFFS2_FS && EXPERIMENTAL && !JFFS2_FS_WRITEBUFFER
|
||||
default n
|
||||
help
|
||||
Extended attributes are name:value pairs associated with inodes by
|
||||
the kernel or by users (see the attr(5) manual page, or visit
|
||||
<http://acl.bestbits.at/> for details).
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config JFFS2_FS_POSIX_ACL
|
||||
bool "JFFS2 POSIX Access Control Lists"
|
||||
depends on JFFS2_FS_XATTR
|
||||
default y
|
||||
select FS_POSIX_ACL
|
||||
help
|
||||
Posix Access Control Lists (ACLs) support permissions for users and
|
||||
groups beyond the owner/group/world scheme.
|
||||
|
||||
To learn more about Access Control Lists, visit the Posix ACLs for
|
||||
Linux website <http://acl.bestbits.at/>.
|
||||
|
||||
If you don't know what Access Control Lists are, say N
|
||||
|
||||
config JFFS2_FS_SECURITY
|
||||
bool "JFFS2 Security Labels"
|
||||
depends on JFFS2_FS_XATTR
|
||||
default y
|
||||
help
|
||||
Security labels support alternative access control models
|
||||
implemented by security modules like SELinux. This option
|
||||
enables an extended attribute handler for file security
|
||||
labels in the jffs2 filesystem.
|
||||
|
||||
If you are not using a security module that requires using
|
||||
extended attributes for file security labels, say N.
|
||||
|
||||
config JFFS2_COMPRESSION_OPTIONS
|
||||
bool "Advanced compression options for JFFS2"
|
||||
depends on JFFS2_FS
|
||||
|
@ -247,7 +247,7 @@ flash_safe_read(struct mtd_info *mtd, loff_t from,
|
||||
D3(printk(KERN_NOTICE "flash_safe_read(%p, %08x, %p, %08x)\n",
|
||||
mtd, (unsigned int) from, buf, count));
|
||||
|
||||
res = MTD_READ(mtd, from, count, &retlen, buf);
|
||||
res = mtd->read(mtd, from, count, &retlen, buf);
|
||||
if (retlen != count) {
|
||||
panic("Didn't read all bytes in flash_safe_read(). Returned %d\n", res);
|
||||
}
|
||||
@ -262,7 +262,7 @@ flash_read_u32(struct mtd_info *mtd, loff_t from)
|
||||
__u32 ret;
|
||||
int res;
|
||||
|
||||
res = MTD_READ(mtd, from, 4, &retlen, (unsigned char *)&ret);
|
||||
res = mtd->read(mtd, from, 4, &retlen, (unsigned char *)&ret);
|
||||
if (retlen != 4) {
|
||||
printk("Didn't read all bytes in flash_read_u32(). Returned %d\n", res);
|
||||
return 0;
|
||||
@ -282,7 +282,7 @@ flash_safe_write(struct mtd_info *mtd, loff_t to,
|
||||
D3(printk(KERN_NOTICE "flash_safe_write(%p, %08x, %p, %08x)\n",
|
||||
mtd, (unsigned int) to, buf, count));
|
||||
|
||||
res = MTD_WRITE(mtd, to, count, &retlen, buf);
|
||||
res = mtd->write(mtd, to, count, &retlen, buf);
|
||||
if (retlen != count) {
|
||||
printk("Didn't write all bytes in flash_safe_write(). Returned %d\n", res);
|
||||
}
|
||||
@ -300,9 +300,9 @@ flash_safe_writev(struct mtd_info *mtd, const struct kvec *vecs,
|
||||
|
||||
D3(printk(KERN_NOTICE "flash_safe_writev(%p, %08x, %p)\n",
|
||||
mtd, (unsigned int) to, vecs));
|
||||
|
||||
|
||||
if (mtd->writev) {
|
||||
res = MTD_WRITEV(mtd, vecs, iovec_cnt, to, &retlen);
|
||||
res = mtd->writev(mtd, vecs, iovec_cnt, to, &retlen);
|
||||
return res ? res : retlen;
|
||||
}
|
||||
/* Not implemented writev. Repeatedly use write - on the not so
|
||||
@ -312,7 +312,8 @@ flash_safe_writev(struct mtd_info *mtd, const struct kvec *vecs,
|
||||
retlen=0;
|
||||
|
||||
for (i=0; !res && i<iovec_cnt; i++) {
|
||||
res = MTD_WRITE(mtd, to, vecs[i].iov_len, &retlen_a, vecs[i].iov_base);
|
||||
res = mtd->write(mtd, to, vecs[i].iov_len, &retlen_a,
|
||||
vecs[i].iov_base);
|
||||
if (retlen_a != vecs[i].iov_len) {
|
||||
printk("Didn't write all bytes in flash_safe_writev(). Returned %d\n", res);
|
||||
if (i != iovec_cnt-1)
|
||||
@ -393,7 +394,7 @@ flash_erase_region(struct mtd_info *mtd, loff_t start,
|
||||
set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
add_wait_queue(&wait_q, &wait);
|
||||
|
||||
if (MTD_ERASE(mtd, erase) < 0) {
|
||||
if (mtd->erase(mtd, erase) < 0) {
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(&wait_q, &wait);
|
||||
kfree(erase);
|
||||
|
@ -12,6 +12,9 @@ jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o
|
||||
jffs2-y += super.o debug.o
|
||||
|
||||
jffs2-$(CONFIG_JFFS2_FS_WRITEBUFFER) += wbuf.o
|
||||
jffs2-$(CONFIG_JFFS2_FS_XATTR) += xattr.o xattr_trusted.o xattr_user.o
|
||||
jffs2-$(CONFIG_JFFS2_FS_SECURITY) += security.o
|
||||
jffs2-$(CONFIG_JFFS2_FS_POSIX_ACL) += acl.o
|
||||
jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o
|
||||
jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o
|
||||
jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o
|
||||
|
@ -150,3 +150,24 @@ the buffer.
|
||||
|
||||
Ordering constraints:
|
||||
Lock wbuf_sem last, after the alloc_sem or and f->sem.
|
||||
|
||||
|
||||
c->xattr_sem
|
||||
------------
|
||||
|
||||
This read/write semaphore protects against concurrent access to the
|
||||
xattr related objects which include stuff in superblock and ic->xref.
|
||||
In read-only path, write-semaphore is too much exclusion. It's enough
|
||||
by read-semaphore. But you must hold write-semaphore when updating,
|
||||
creating or deleting any xattr related object.
|
||||
|
||||
Once xattr_sem released, there would be no assurance for the existence
|
||||
of those objects. Thus, a series of processes is often required to retry,
|
||||
when updating such a object is necessary under holding read semaphore.
|
||||
For example, do_jffs2_getxattr() holds read-semaphore to scan xref and
|
||||
xdatum at first. But it retries this process with holding write-semaphore
|
||||
after release read-semaphore, if it's necessary to load name/value pair
|
||||
from medium.
|
||||
|
||||
Ordering constraints:
|
||||
Lock xattr_sem last, after the alloc_sem.
|
||||
|
485
fs/jffs2/acl.c
Normal file
485
fs/jffs2/acl.c
Normal file
@ -0,0 +1,485 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2006 NEC Corporation
|
||||
*
|
||||
* Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/posix_acl_xattr.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
static size_t jffs2_acl_size(int count)
|
||||
{
|
||||
if (count <= 4) {
|
||||
return sizeof(struct jffs2_acl_header)
|
||||
+ count * sizeof(struct jffs2_acl_entry_short);
|
||||
} else {
|
||||
return sizeof(struct jffs2_acl_header)
|
||||
+ 4 * sizeof(struct jffs2_acl_entry_short)
|
||||
+ (count - 4) * sizeof(struct jffs2_acl_entry);
|
||||
}
|
||||
}
|
||||
|
||||
static int jffs2_acl_count(size_t size)
|
||||
{
|
||||
size_t s;
|
||||
|
||||
size -= sizeof(struct jffs2_acl_header);
|
||||
s = size - 4 * sizeof(struct jffs2_acl_entry_short);
|
||||
if (s < 0) {
|
||||
if (size % sizeof(struct jffs2_acl_entry_short))
|
||||
return -1;
|
||||
return size / sizeof(struct jffs2_acl_entry_short);
|
||||
} else {
|
||||
if (s % sizeof(struct jffs2_acl_entry))
|
||||
return -1;
|
||||
return s / sizeof(struct jffs2_acl_entry) + 4;
|
||||
}
|
||||
}
|
||||
|
||||
static struct posix_acl *jffs2_acl_from_medium(void *value, size_t size)
|
||||
{
|
||||
void *end = value + size;
|
||||
struct jffs2_acl_header *header = value;
|
||||
struct jffs2_acl_entry *entry;
|
||||
struct posix_acl *acl;
|
||||
uint32_t ver;
|
||||
int i, count;
|
||||
|
||||
if (!value)
|
||||
return NULL;
|
||||
if (size < sizeof(struct jffs2_acl_header))
|
||||
return ERR_PTR(-EINVAL);
|
||||
ver = je32_to_cpu(header->a_version);
|
||||
if (ver != JFFS2_ACL_VERSION) {
|
||||
JFFS2_WARNING("Invalid ACL version. (=%u)\n", ver);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
value += sizeof(struct jffs2_acl_header);
|
||||
count = jffs2_acl_count(size);
|
||||
if (count < 0)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (count == 0)
|
||||
return NULL;
|
||||
|
||||
acl = posix_acl_alloc(count, GFP_KERNEL);
|
||||
if (!acl)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for (i=0; i < count; i++) {
|
||||
entry = value;
|
||||
if (value + sizeof(struct jffs2_acl_entry_short) > end)
|
||||
goto fail;
|
||||
acl->a_entries[i].e_tag = je16_to_cpu(entry->e_tag);
|
||||
acl->a_entries[i].e_perm = je16_to_cpu(entry->e_perm);
|
||||
switch (acl->a_entries[i].e_tag) {
|
||||
case ACL_USER_OBJ:
|
||||
case ACL_GROUP_OBJ:
|
||||
case ACL_MASK:
|
||||
case ACL_OTHER:
|
||||
value += sizeof(struct jffs2_acl_entry_short);
|
||||
acl->a_entries[i].e_id = ACL_UNDEFINED_ID;
|
||||
break;
|
||||
|
||||
case ACL_USER:
|
||||
case ACL_GROUP:
|
||||
value += sizeof(struct jffs2_acl_entry);
|
||||
if (value > end)
|
||||
goto fail;
|
||||
acl->a_entries[i].e_id = je32_to_cpu(entry->e_id);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (value != end)
|
||||
goto fail;
|
||||
return acl;
|
||||
fail:
|
||||
posix_acl_release(acl);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static void *jffs2_acl_to_medium(const struct posix_acl *acl, size_t *size)
|
||||
{
|
||||
struct jffs2_acl_header *header;
|
||||
struct jffs2_acl_entry *entry;
|
||||
void *e;
|
||||
size_t i;
|
||||
|
||||
*size = jffs2_acl_size(acl->a_count);
|
||||
header = kmalloc(sizeof(*header) + acl->a_count * sizeof(*entry), GFP_KERNEL);
|
||||
if (!header)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
header->a_version = cpu_to_je32(JFFS2_ACL_VERSION);
|
||||
e = header + 1;
|
||||
for (i=0; i < acl->a_count; i++) {
|
||||
entry = e;
|
||||
entry->e_tag = cpu_to_je16(acl->a_entries[i].e_tag);
|
||||
entry->e_perm = cpu_to_je16(acl->a_entries[i].e_perm);
|
||||
switch(acl->a_entries[i].e_tag) {
|
||||
case ACL_USER:
|
||||
case ACL_GROUP:
|
||||
entry->e_id = cpu_to_je32(acl->a_entries[i].e_id);
|
||||
e += sizeof(struct jffs2_acl_entry);
|
||||
break;
|
||||
|
||||
case ACL_USER_OBJ:
|
||||
case ACL_GROUP_OBJ:
|
||||
case ACL_MASK:
|
||||
case ACL_OTHER:
|
||||
e += sizeof(struct jffs2_acl_entry_short);
|
||||
break;
|
||||
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
return header;
|
||||
fail:
|
||||
kfree(header);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
static struct posix_acl *jffs2_iget_acl(struct inode *inode, struct posix_acl **i_acl)
|
||||
{
|
||||
struct posix_acl *acl = JFFS2_ACL_NOT_CACHED;
|
||||
|
||||
spin_lock(&inode->i_lock);
|
||||
if (*i_acl != JFFS2_ACL_NOT_CACHED)
|
||||
acl = posix_acl_dup(*i_acl);
|
||||
spin_unlock(&inode->i_lock);
|
||||
return acl;
|
||||
}
|
||||
|
||||
static void jffs2_iset_acl(struct inode *inode, struct posix_acl **i_acl, struct posix_acl *acl)
|
||||
{
|
||||
spin_lock(&inode->i_lock);
|
||||
if (*i_acl != JFFS2_ACL_NOT_CACHED)
|
||||
posix_acl_release(*i_acl);
|
||||
*i_acl = posix_acl_dup(acl);
|
||||
spin_unlock(&inode->i_lock);
|
||||
}
|
||||
|
||||
static struct posix_acl *jffs2_get_acl(struct inode *inode, int type)
|
||||
{
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
|
||||
struct posix_acl *acl;
|
||||
char *value = NULL;
|
||||
int rc, xprefix;
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
acl = jffs2_iget_acl(inode, &f->i_acl_access);
|
||||
if (acl != JFFS2_ACL_NOT_CACHED)
|
||||
return acl;
|
||||
xprefix = JFFS2_XPREFIX_ACL_ACCESS;
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
acl = jffs2_iget_acl(inode, &f->i_acl_default);
|
||||
if (acl != JFFS2_ACL_NOT_CACHED)
|
||||
return acl;
|
||||
xprefix = JFFS2_XPREFIX_ACL_DEFAULT;
|
||||
break;
|
||||
default:
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
rc = do_jffs2_getxattr(inode, xprefix, "", NULL, 0);
|
||||
if (rc > 0) {
|
||||
value = kmalloc(rc, GFP_KERNEL);
|
||||
if (!value)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
rc = do_jffs2_getxattr(inode, xprefix, "", value, rc);
|
||||
}
|
||||
if (rc > 0) {
|
||||
acl = jffs2_acl_from_medium(value, rc);
|
||||
} else if (rc == -ENODATA || rc == -ENOSYS) {
|
||||
acl = NULL;
|
||||
} else {
|
||||
acl = ERR_PTR(rc);
|
||||
}
|
||||
if (value)
|
||||
kfree(value);
|
||||
if (!IS_ERR(acl)) {
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
jffs2_iset_acl(inode, &f->i_acl_access, acl);
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
jffs2_iset_acl(inode, &f->i_acl_default, acl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return acl;
|
||||
}
|
||||
|
||||
static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl)
|
||||
{
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
|
||||
size_t size = 0;
|
||||
char *value = NULL;
|
||||
int rc, xprefix;
|
||||
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
xprefix = JFFS2_XPREFIX_ACL_ACCESS;
|
||||
if (acl) {
|
||||
mode_t mode = inode->i_mode;
|
||||
rc = posix_acl_equiv_mode(acl, &mode);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
if (inode->i_mode != mode) {
|
||||
inode->i_mode = mode;
|
||||
jffs2_dirty_inode(inode);
|
||||
}
|
||||
if (rc == 0)
|
||||
acl = NULL;
|
||||
}
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
xprefix = JFFS2_XPREFIX_ACL_DEFAULT;
|
||||
if (!S_ISDIR(inode->i_mode))
|
||||
return acl ? -EACCES : 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (acl) {
|
||||
value = jffs2_acl_to_medium(acl, &size);
|
||||
if (IS_ERR(value))
|
||||
return PTR_ERR(value);
|
||||
}
|
||||
|
||||
rc = do_jffs2_setxattr(inode, xprefix, "", value, size, 0);
|
||||
if (value)
|
||||
kfree(value);
|
||||
if (!rc) {
|
||||
switch(type) {
|
||||
case ACL_TYPE_ACCESS:
|
||||
jffs2_iset_acl(inode, &f->i_acl_access, acl);
|
||||
break;
|
||||
case ACL_TYPE_DEFAULT:
|
||||
jffs2_iset_acl(inode, &f->i_acl_default, acl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int jffs2_check_acl(struct inode *inode, int mask)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
int rc;
|
||||
|
||||
acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (acl) {
|
||||
rc = posix_acl_permission(inode, acl, mask);
|
||||
posix_acl_release(acl);
|
||||
return rc;
|
||||
}
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
int jffs2_permission(struct inode *inode, int mask, struct nameidata *nd)
|
||||
{
|
||||
return generic_permission(inode, mask, jffs2_check_acl);
|
||||
}
|
||||
|
||||
int jffs2_init_acl(struct inode *inode, struct inode *dir)
|
||||
{
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
|
||||
struct posix_acl *acl = NULL, *clone;
|
||||
mode_t mode;
|
||||
int rc = 0;
|
||||
|
||||
f->i_acl_access = JFFS2_ACL_NOT_CACHED;
|
||||
f->i_acl_default = JFFS2_ACL_NOT_CACHED;
|
||||
if (!S_ISLNK(inode->i_mode)) {
|
||||
acl = jffs2_get_acl(dir, ACL_TYPE_DEFAULT);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (!acl)
|
||||
inode->i_mode &= ~current->fs->umask;
|
||||
}
|
||||
if (acl) {
|
||||
if (S_ISDIR(inode->i_mode)) {
|
||||
rc = jffs2_set_acl(inode, ACL_TYPE_DEFAULT, acl);
|
||||
if (rc)
|
||||
goto cleanup;
|
||||
}
|
||||
clone = posix_acl_clone(acl, GFP_KERNEL);
|
||||
rc = -ENOMEM;
|
||||
if (!clone)
|
||||
goto cleanup;
|
||||
mode = inode->i_mode;
|
||||
rc = posix_acl_create_masq(clone, &mode);
|
||||
if (rc >= 0) {
|
||||
inode->i_mode = mode;
|
||||
if (rc > 0)
|
||||
rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone);
|
||||
}
|
||||
posix_acl_release(clone);
|
||||
}
|
||||
cleanup:
|
||||
posix_acl_release(acl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void jffs2_clear_acl(struct inode *inode)
|
||||
{
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
|
||||
|
||||
if (f->i_acl_access && f->i_acl_access != JFFS2_ACL_NOT_CACHED) {
|
||||
posix_acl_release(f->i_acl_access);
|
||||
f->i_acl_access = JFFS2_ACL_NOT_CACHED;
|
||||
}
|
||||
if (f->i_acl_default && f->i_acl_default != JFFS2_ACL_NOT_CACHED) {
|
||||
posix_acl_release(f->i_acl_default);
|
||||
f->i_acl_default = JFFS2_ACL_NOT_CACHED;
|
||||
}
|
||||
}
|
||||
|
||||
int jffs2_acl_chmod(struct inode *inode)
|
||||
{
|
||||
struct posix_acl *acl, *clone;
|
||||
int rc;
|
||||
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
return -EOPNOTSUPP;
|
||||
acl = jffs2_get_acl(inode, ACL_TYPE_ACCESS);
|
||||
if (IS_ERR(acl) || !acl)
|
||||
return PTR_ERR(acl);
|
||||
clone = posix_acl_clone(acl, GFP_KERNEL);
|
||||
posix_acl_release(acl);
|
||||
if (!clone)
|
||||
return -ENOMEM;
|
||||
rc = posix_acl_chmod_masq(clone, inode->i_mode);
|
||||
if (!rc)
|
||||
rc = jffs2_set_acl(inode, ACL_TYPE_ACCESS, clone);
|
||||
posix_acl_release(clone);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static size_t jffs2_acl_access_listxattr(struct inode *inode, char *list, size_t list_size,
|
||||
const char *name, size_t name_len)
|
||||
{
|
||||
const int retlen = sizeof(POSIX_ACL_XATTR_ACCESS);
|
||||
|
||||
if (list && retlen <= list_size)
|
||||
strcpy(list, POSIX_ACL_XATTR_ACCESS);
|
||||
return retlen;
|
||||
}
|
||||
|
||||
static size_t jffs2_acl_default_listxattr(struct inode *inode, char *list, size_t list_size,
|
||||
const char *name, size_t name_len)
|
||||
{
|
||||
const int retlen = sizeof(POSIX_ACL_XATTR_DEFAULT);
|
||||
|
||||
if (list && retlen <= list_size)
|
||||
strcpy(list, POSIX_ACL_XATTR_DEFAULT);
|
||||
return retlen;
|
||||
}
|
||||
|
||||
static int jffs2_acl_getxattr(struct inode *inode, int type, void *buffer, size_t size)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
int rc;
|
||||
|
||||
acl = jffs2_get_acl(inode, type);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (!acl)
|
||||
return -ENODATA;
|
||||
rc = posix_acl_to_xattr(acl, buffer, size);
|
||||
posix_acl_release(acl);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int jffs2_acl_access_getxattr(struct inode *inode, const char *name, void *buffer, size_t size)
|
||||
{
|
||||
if (name[0] != '\0')
|
||||
return -EINVAL;
|
||||
return jffs2_acl_getxattr(inode, ACL_TYPE_ACCESS, buffer, size);
|
||||
}
|
||||
|
||||
static int jffs2_acl_default_getxattr(struct inode *inode, const char *name, void *buffer, size_t size)
|
||||
{
|
||||
if (name[0] != '\0')
|
||||
return -EINVAL;
|
||||
return jffs2_acl_getxattr(inode, ACL_TYPE_DEFAULT, buffer, size);
|
||||
}
|
||||
|
||||
static int jffs2_acl_setxattr(struct inode *inode, int type, const void *value, size_t size)
|
||||
{
|
||||
struct posix_acl *acl;
|
||||
int rc;
|
||||
|
||||
if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
|
||||
return -EPERM;
|
||||
|
||||
if (value) {
|
||||
acl = posix_acl_from_xattr(value, size);
|
||||
if (IS_ERR(acl))
|
||||
return PTR_ERR(acl);
|
||||
if (acl) {
|
||||
rc = posix_acl_valid(acl);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
acl = NULL;
|
||||
}
|
||||
rc = jffs2_set_acl(inode, type, acl);
|
||||
out:
|
||||
posix_acl_release(acl);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int jffs2_acl_access_setxattr(struct inode *inode, const char *name,
|
||||
const void *buffer, size_t size, int flags)
|
||||
{
|
||||
if (name[0] != '\0')
|
||||
return -EINVAL;
|
||||
return jffs2_acl_setxattr(inode, ACL_TYPE_ACCESS, buffer, size);
|
||||
}
|
||||
|
||||
static int jffs2_acl_default_setxattr(struct inode *inode, const char *name,
|
||||
const void *buffer, size_t size, int flags)
|
||||
{
|
||||
if (name[0] != '\0')
|
||||
return -EINVAL;
|
||||
return jffs2_acl_setxattr(inode, ACL_TYPE_DEFAULT, buffer, size);
|
||||
}
|
||||
|
||||
struct xattr_handler jffs2_acl_access_xattr_handler = {
|
||||
.prefix = POSIX_ACL_XATTR_ACCESS,
|
||||
.list = jffs2_acl_access_listxattr,
|
||||
.get = jffs2_acl_access_getxattr,
|
||||
.set = jffs2_acl_access_setxattr,
|
||||
};
|
||||
|
||||
struct xattr_handler jffs2_acl_default_xattr_handler = {
|
||||
.prefix = POSIX_ACL_XATTR_DEFAULT,
|
||||
.list = jffs2_acl_default_listxattr,
|
||||
.get = jffs2_acl_default_getxattr,
|
||||
.set = jffs2_acl_default_setxattr,
|
||||
};
|
45
fs/jffs2/acl.h
Normal file
45
fs/jffs2/acl.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2006 NEC Corporation
|
||||
*
|
||||
* Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
*/
|
||||
struct jffs2_acl_entry {
|
||||
jint16_t e_tag;
|
||||
jint16_t e_perm;
|
||||
jint32_t e_id;
|
||||
};
|
||||
|
||||
struct jffs2_acl_entry_short {
|
||||
jint16_t e_tag;
|
||||
jint16_t e_perm;
|
||||
};
|
||||
|
||||
struct jffs2_acl_header {
|
||||
jint32_t a_version;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_POSIX_ACL
|
||||
|
||||
#define JFFS2_ACL_NOT_CACHED ((void *)-1)
|
||||
|
||||
extern int jffs2_permission(struct inode *, int, struct nameidata *);
|
||||
extern int jffs2_acl_chmod(struct inode *);
|
||||
extern int jffs2_init_acl(struct inode *, struct inode *);
|
||||
extern void jffs2_clear_acl(struct inode *);
|
||||
|
||||
extern struct xattr_handler jffs2_acl_access_xattr_handler;
|
||||
extern struct xattr_handler jffs2_acl_default_xattr_handler;
|
||||
|
||||
#else
|
||||
|
||||
#define jffs2_permission NULL
|
||||
#define jffs2_acl_chmod(inode) (0)
|
||||
#define jffs2_init_acl(inode,dir) (0)
|
||||
#define jffs2_clear_acl(inode)
|
||||
|
||||
#endif /* CONFIG_JFFS2_FS_POSIX_ACL */
|
@ -160,6 +160,7 @@ static int jffs2_build_filesystem(struct jffs2_sb_info *c)
|
||||
ic->scan_dents = NULL;
|
||||
cond_resched();
|
||||
}
|
||||
jffs2_build_xattr_subsystem(c);
|
||||
c->flags &= ~JFFS2_SB_FLAG_BUILDING;
|
||||
|
||||
dbg_fsbuild("FS build complete\n");
|
||||
@ -178,6 +179,7 @@ exit:
|
||||
jffs2_free_full_dirent(fd);
|
||||
}
|
||||
}
|
||||
jffs2_clear_xattr_subsystem(c);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -412,7 +412,7 @@ void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
|
||||
kfree(comprbuf);
|
||||
}
|
||||
|
||||
int jffs2_compressors_init(void)
|
||||
int __init jffs2_compressors_init(void)
|
||||
{
|
||||
/* Registering compressors */
|
||||
#ifdef CONFIG_JFFS2_ZLIB
|
||||
|
@ -23,8 +23,8 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include <linux/jffs2_fs_i.h>
|
||||
#include <linux/jffs2_fs_sb.h>
|
||||
#include "jffs2_fs_i.h"
|
||||
#include "jffs2_fs_sb.h"
|
||||
#include "nodelist.h"
|
||||
|
||||
#define JFFS2_RUBINMIPS_PRIORITY 10
|
||||
|
@ -192,13 +192,13 @@ __jffs2_dbg_acct_paranoia_check_nolock(struct jffs2_sb_info *c,
|
||||
else
|
||||
my_dirty_size += totlen;
|
||||
|
||||
if ((!ref2->next_phys) != (ref2 == jeb->last_node)) {
|
||||
JFFS2_ERROR("node_ref for node at %#08x (mem %p) has next_phys at %#08x (mem %p), last_node is at %#08x (mem %p).\n",
|
||||
ref_offset(ref2), ref2, ref_offset(ref2->next_phys), ref2->next_phys,
|
||||
ref_offset(jeb->last_node), jeb->last_node);
|
||||
if ((!ref_next(ref2)) != (ref2 == jeb->last_node)) {
|
||||
JFFS2_ERROR("node_ref for node at %#08x (mem %p) has next at %#08x (mem %p), last_node is at %#08x (mem %p).\n",
|
||||
ref_offset(ref2), ref2, ref_offset(ref_next(ref2)), ref_next(ref2),
|
||||
ref_offset(jeb->last_node), jeb->last_node);
|
||||
goto error;
|
||||
}
|
||||
ref2 = ref2->next_phys;
|
||||
ref2 = ref_next(ref2);
|
||||
}
|
||||
|
||||
if (my_used_size != jeb->used_size) {
|
||||
@ -268,9 +268,9 @@ __jffs2_dbg_dump_node_refs_nolock(struct jffs2_sb_info *c,
|
||||
}
|
||||
|
||||
printk(JFFS2_DBG);
|
||||
for (ref = jeb->first_node; ; ref = ref->next_phys) {
|
||||
for (ref = jeb->first_node; ; ref = ref_next(ref)) {
|
||||
printk("%#08x(%#x)", ref_offset(ref), ref->__totlen);
|
||||
if (ref->next_phys)
|
||||
if (ref_next(ref))
|
||||
printk("->");
|
||||
else
|
||||
break;
|
||||
|
@ -171,6 +171,12 @@
|
||||
#define dbg_memalloc(fmt, ...)
|
||||
#endif
|
||||
|
||||
/* Watch the XATTR subsystem */
|
||||
#ifdef JFFS2_DBG_XATTR_MESSAGES
|
||||
#define dbg_xattr(fmt, ...) JFFS2_DEBUG(fmt, ##__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_xattr(fmt, ...)
|
||||
#endif
|
||||
|
||||
/* "Sanity" checks */
|
||||
void
|
||||
|
121
fs/jffs2/dir.c
121
fs/jffs2/dir.c
@ -17,8 +17,8 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include <linux/jffs2_fs_i.h>
|
||||
#include <linux/jffs2_fs_sb.h>
|
||||
#include "jffs2_fs_i.h"
|
||||
#include "jffs2_fs_sb.h"
|
||||
#include <linux/time.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
@ -57,7 +57,12 @@ struct inode_operations jffs2_dir_inode_operations =
|
||||
.rmdir = jffs2_rmdir,
|
||||
.mknod = jffs2_mknod,
|
||||
.rename = jffs2_rename,
|
||||
.permission = jffs2_permission,
|
||||
.setattr = jffs2_setattr,
|
||||
.setxattr = jffs2_setxattr,
|
||||
.getxattr = jffs2_getxattr,
|
||||
.listxattr = jffs2_listxattr,
|
||||
.removexattr = jffs2_removexattr
|
||||
};
|
||||
|
||||
/***********************************************************************/
|
||||
@ -78,6 +83,9 @@ static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target,
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_lookup()\n"));
|
||||
|
||||
if (target->d_name.len > JFFS2_MAX_NAME_LEN)
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
|
||||
dir_f = JFFS2_INODE_INFO(dir_i);
|
||||
c = JFFS2_SB_INFO(dir_i->i_sb);
|
||||
|
||||
@ -206,12 +214,15 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
|
||||
ret = jffs2_do_create(c, dir_f, f, ri,
|
||||
dentry->d_name.name, dentry->d_name.len);
|
||||
|
||||
if (ret) {
|
||||
make_bad_inode(inode);
|
||||
iput(inode);
|
||||
jffs2_free_raw_inode(ri);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
ret = jffs2_init_security(inode, dir_i);
|
||||
if (ret)
|
||||
goto fail;
|
||||
ret = jffs2_init_acl(inode, dir_i);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime));
|
||||
|
||||
@ -221,6 +232,12 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
|
||||
D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n",
|
||||
inode->i_ino, inode->i_mode, inode->i_nlink, f->inocache->nlink, inode->i_mapping->nrpages));
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
make_bad_inode(inode);
|
||||
iput(inode);
|
||||
jffs2_free_raw_inode(ri);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/***********************************************************************/
|
||||
@ -291,7 +308,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
|
||||
struct jffs2_full_dnode *fn;
|
||||
struct jffs2_full_dirent *fd;
|
||||
int namelen;
|
||||
uint32_t alloclen, phys_ofs;
|
||||
uint32_t alloclen;
|
||||
int ret, targetlen = strlen(target);
|
||||
|
||||
/* FIXME: If you care. We'd need to use frags for the target
|
||||
@ -310,8 +327,8 @@ 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) + targetlen, &phys_ofs, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
|
||||
ret = jffs2_reserve_space(c, sizeof(*ri) + targetlen, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
|
||||
|
||||
if (ret) {
|
||||
jffs2_free_raw_inode(ri);
|
||||
@ -339,7 +356,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
|
||||
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, targetlen, phys_ofs, ALLOC_NORMAL);
|
||||
fn = jffs2_write_dnode(c, f, ri, target, targetlen, ALLOC_NORMAL);
|
||||
|
||||
jffs2_free_raw_inode(ri);
|
||||
|
||||
@ -371,8 +388,20 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
|
||||
up(&f->sem);
|
||||
|
||||
jffs2_complete_reservation(c);
|
||||
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
|
||||
|
||||
ret = jffs2_init_security(inode, dir_i);
|
||||
if (ret) {
|
||||
jffs2_clear_inode(inode);
|
||||
return ret;
|
||||
}
|
||||
ret = jffs2_init_acl(inode, dir_i);
|
||||
if (ret) {
|
||||
jffs2_clear_inode(inode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
|
||||
if (ret) {
|
||||
/* Eep. */
|
||||
jffs2_clear_inode(inode);
|
||||
@ -404,7 +433,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
|
||||
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
|
||||
rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
|
||||
|
||||
fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
|
||||
fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, ALLOC_NORMAL);
|
||||
|
||||
if (IS_ERR(fd)) {
|
||||
/* dirent failed to write. Delete the inode normally
|
||||
@ -442,7 +471,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
|
||||
struct jffs2_full_dnode *fn;
|
||||
struct jffs2_full_dirent *fd;
|
||||
int namelen;
|
||||
uint32_t alloclen, phys_ofs;
|
||||
uint32_t alloclen;
|
||||
int ret;
|
||||
|
||||
mode |= S_IFDIR;
|
||||
@ -457,8 +486,8 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
|
||||
* Just the node will do for now, though
|
||||
*/
|
||||
namelen = dentry->d_name.len;
|
||||
ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL,
|
||||
JFFS2_SUMMARY_INODE_SIZE);
|
||||
ret = jffs2_reserve_space(c, sizeof(*ri), &alloclen, ALLOC_NORMAL,
|
||||
JFFS2_SUMMARY_INODE_SIZE);
|
||||
|
||||
if (ret) {
|
||||
jffs2_free_raw_inode(ri);
|
||||
@ -483,7 +512,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
|
||||
ri->data_crc = cpu_to_je32(0);
|
||||
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
|
||||
|
||||
fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
|
||||
fn = jffs2_write_dnode(c, f, ri, NULL, 0, ALLOC_NORMAL);
|
||||
|
||||
jffs2_free_raw_inode(ri);
|
||||
|
||||
@ -501,8 +530,20 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
|
||||
up(&f->sem);
|
||||
|
||||
jffs2_complete_reservation(c);
|
||||
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
|
||||
|
||||
ret = jffs2_init_security(inode, dir_i);
|
||||
if (ret) {
|
||||
jffs2_clear_inode(inode);
|
||||
return ret;
|
||||
}
|
||||
ret = jffs2_init_acl(inode, dir_i);
|
||||
if (ret) {
|
||||
jffs2_clear_inode(inode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
|
||||
if (ret) {
|
||||
/* Eep. */
|
||||
jffs2_clear_inode(inode);
|
||||
@ -534,7 +575,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode)
|
||||
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
|
||||
rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
|
||||
|
||||
fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
|
||||
fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, ALLOC_NORMAL);
|
||||
|
||||
if (IS_ERR(fd)) {
|
||||
/* dirent failed to write. Delete the inode normally
|
||||
@ -588,12 +629,12 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
|
||||
struct jffs2_full_dnode *fn;
|
||||
struct jffs2_full_dirent *fd;
|
||||
int namelen;
|
||||
jint16_t dev;
|
||||
union jffs2_device_node dev;
|
||||
int devlen = 0;
|
||||
uint32_t alloclen, phys_ofs;
|
||||
uint32_t alloclen;
|
||||
int ret;
|
||||
|
||||
if (!old_valid_dev(rdev))
|
||||
if (!new_valid_dev(rdev))
|
||||
return -EINVAL;
|
||||
|
||||
ri = jffs2_alloc_raw_inode();
|
||||
@ -602,17 +643,15 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
|
||||
|
||||
c = JFFS2_SB_INFO(dir_i->i_sb);
|
||||
|
||||
if (S_ISBLK(mode) || S_ISCHR(mode)) {
|
||||
dev = cpu_to_je16(old_encode_dev(rdev));
|
||||
devlen = sizeof(dev);
|
||||
}
|
||||
if (S_ISBLK(mode) || S_ISCHR(mode))
|
||||
devlen = jffs2_encode_dev(&dev, rdev);
|
||||
|
||||
/* Try to reserve enough space for both node and dirent.
|
||||
* Just the node will do for now, though
|
||||
*/
|
||||
namelen = dentry->d_name.len;
|
||||
ret = jffs2_reserve_space(c, sizeof(*ri) + devlen, &phys_ofs, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
|
||||
ret = jffs2_reserve_space(c, sizeof(*ri) + devlen, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
|
||||
|
||||
if (ret) {
|
||||
jffs2_free_raw_inode(ri);
|
||||
@ -639,7 +678,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
|
||||
ri->data_crc = cpu_to_je32(crc32(0, &dev, devlen));
|
||||
ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
|
||||
|
||||
fn = jffs2_write_dnode(c, f, ri, (char *)&dev, devlen, phys_ofs, ALLOC_NORMAL);
|
||||
fn = jffs2_write_dnode(c, f, ri, (char *)&dev, devlen, ALLOC_NORMAL);
|
||||
|
||||
jffs2_free_raw_inode(ri);
|
||||
|
||||
@ -657,8 +696,20 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
|
||||
up(&f->sem);
|
||||
|
||||
jffs2_complete_reservation(c);
|
||||
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
|
||||
|
||||
ret = jffs2_init_security(inode, dir_i);
|
||||
if (ret) {
|
||||
jffs2_clear_inode(inode);
|
||||
return ret;
|
||||
}
|
||||
ret = jffs2_init_acl(inode, dir_i);
|
||||
if (ret) {
|
||||
jffs2_clear_inode(inode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_DIRENT_SIZE(namelen));
|
||||
if (ret) {
|
||||
/* Eep. */
|
||||
jffs2_clear_inode(inode);
|
||||
@ -693,7 +744,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de
|
||||
rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
|
||||
rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
|
||||
|
||||
fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
|
||||
fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, ALLOC_NORMAL);
|
||||
|
||||
if (IS_ERR(fd)) {
|
||||
/* dirent failed to write. Delete the inode normally
|
||||
|
@ -30,7 +30,6 @@ static void jffs2_erase_callback(struct erase_info *);
|
||||
#endif
|
||||
static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t bad_offset);
|
||||
static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
|
||||
static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
|
||||
static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
|
||||
|
||||
static void jffs2_erase_block(struct jffs2_sb_info *c,
|
||||
@ -136,7 +135,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
|
||||
c->used_size -= jeb->used_size;
|
||||
c->dirty_size -= jeb->dirty_size;
|
||||
jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
|
||||
jffs2_free_all_node_refs(c, jeb);
|
||||
jffs2_free_jeb_node_refs(c, jeb);
|
||||
list_add(&jeb->list, &c->erasing_list);
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
@ -231,6 +230,7 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
|
||||
at the end of the linked list. Stash it and continue
|
||||
from the beginning of the list */
|
||||
ic = (struct jffs2_inode_cache *)(*prev);
|
||||
BUG_ON(ic->class != RAWNODE_CLASS_INODE_CACHE);
|
||||
prev = &ic->nodes;
|
||||
continue;
|
||||
}
|
||||
@ -283,22 +283,27 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c,
|
||||
jffs2_del_ino_cache(c, ic);
|
||||
}
|
||||
|
||||
static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
|
||||
void jffs2_free_jeb_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
struct jffs2_raw_node_ref *ref;
|
||||
struct jffs2_raw_node_ref *block, *ref;
|
||||
D1(printk(KERN_DEBUG "Freeing all node refs for eraseblock offset 0x%08x\n", jeb->offset));
|
||||
while(jeb->first_node) {
|
||||
ref = jeb->first_node;
|
||||
jeb->first_node = ref->next_phys;
|
||||
|
||||
/* Remove from the inode-list */
|
||||
if (ref->next_in_ino)
|
||||
block = ref = jeb->first_node;
|
||||
|
||||
while (ref) {
|
||||
if (ref->flash_offset == REF_LINK_NODE) {
|
||||
ref = ref->next_in_ino;
|
||||
jffs2_free_refblock(block);
|
||||
block = ref;
|
||||
continue;
|
||||
}
|
||||
if (ref->flash_offset != REF_EMPTY_NODE && ref->next_in_ino)
|
||||
jffs2_remove_node_refs_from_ino_list(c, ref, jeb);
|
||||
/* else it was a non-inode node or already removed, so don't bother */
|
||||
|
||||
jffs2_free_raw_node_ref(ref);
|
||||
ref++;
|
||||
}
|
||||
jeb->last_node = NULL;
|
||||
jeb->first_node = jeb->last_node = NULL;
|
||||
}
|
||||
|
||||
static int jffs2_block_check_erase(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t *bad_offset)
|
||||
@ -351,7 +356,6 @@ fail:
|
||||
|
||||
static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
struct jffs2_raw_node_ref *marker_ref = NULL;
|
||||
size_t retlen;
|
||||
int ret;
|
||||
uint32_t bad_offset;
|
||||
@ -373,12 +377,8 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
|
||||
goto filebad;
|
||||
}
|
||||
|
||||
jeb->first_node = jeb->last_node = NULL;
|
||||
/* Everything else got zeroed before the erase */
|
||||
jeb->free_size = c->sector_size;
|
||||
jeb->used_size = 0;
|
||||
jeb->dirty_size = 0;
|
||||
jeb->wasted_size = 0;
|
||||
|
||||
} else {
|
||||
|
||||
struct kvec vecs[1];
|
||||
@ -388,11 +388,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
|
||||
.totlen = cpu_to_je32(c->cleanmarker_size)
|
||||
};
|
||||
|
||||
marker_ref = jffs2_alloc_raw_node_ref();
|
||||
if (!marker_ref) {
|
||||
printk(KERN_WARNING "Failed to allocate raw node ref for clean marker. Refiling\n");
|
||||
goto refile;
|
||||
}
|
||||
jffs2_prealloc_raw_node_refs(c, jeb, 1);
|
||||
|
||||
marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4));
|
||||
|
||||
@ -408,21 +404,13 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb
|
||||
printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n",
|
||||
jeb->offset, sizeof(marker), retlen);
|
||||
|
||||
jffs2_free_raw_node_ref(marker_ref);
|
||||
goto filebad;
|
||||
}
|
||||
|
||||
marker_ref->next_in_ino = NULL;
|
||||
marker_ref->next_phys = NULL;
|
||||
marker_ref->flash_offset = jeb->offset | REF_NORMAL;
|
||||
marker_ref->__totlen = c->cleanmarker_size;
|
||||
|
||||
jeb->first_node = jeb->last_node = marker_ref;
|
||||
|
||||
jeb->free_size = c->sector_size - c->cleanmarker_size;
|
||||
jeb->used_size = c->cleanmarker_size;
|
||||
jeb->dirty_size = 0;
|
||||
jeb->wasted_size = 0;
|
||||
/* Everything else got zeroed before the erase */
|
||||
jeb->free_size = c->sector_size;
|
||||
/* FIXME Special case for cleanmarker in empty block */
|
||||
jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL, c->cleanmarker_size, NULL);
|
||||
}
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
@ -54,7 +54,12 @@ const struct file_operations jffs2_file_operations =
|
||||
|
||||
struct inode_operations jffs2_file_inode_operations =
|
||||
{
|
||||
.setattr = jffs2_setattr
|
||||
.permission = jffs2_permission,
|
||||
.setattr = jffs2_setattr,
|
||||
.setxattr = jffs2_setxattr,
|
||||
.getxattr = jffs2_getxattr,
|
||||
.listxattr = jffs2_listxattr,
|
||||
.removexattr = jffs2_removexattr
|
||||
};
|
||||
|
||||
struct address_space_operations jffs2_file_address_operations =
|
||||
@ -129,13 +134,13 @@ static int jffs2_prepare_write (struct file *filp, struct page *pg,
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
|
||||
struct jffs2_raw_inode ri;
|
||||
struct jffs2_full_dnode *fn;
|
||||
uint32_t phys_ofs, alloc_len;
|
||||
uint32_t alloc_len;
|
||||
|
||||
D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
|
||||
(unsigned int)inode->i_size, pageofs));
|
||||
|
||||
ret = jffs2_reserve_space(c, sizeof(ri), &phys_ofs, &alloc_len,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
|
||||
ret = jffs2_reserve_space(c, sizeof(ri), &alloc_len,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -161,7 +166,7 @@ static int jffs2_prepare_write (struct file *filp, struct page *pg,
|
||||
ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
|
||||
ri.data_crc = cpu_to_je32(0);
|
||||
|
||||
fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
|
||||
fn = jffs2_write_dnode(c, f, &ri, NULL, 0, ALLOC_NORMAL);
|
||||
|
||||
if (IS_ERR(fn)) {
|
||||
ret = PTR_ERR(fn);
|
||||
@ -215,12 +220,20 @@ static int jffs2_commit_write (struct file *filp, struct page *pg,
|
||||
D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n",
|
||||
inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags));
|
||||
|
||||
if (!start && end == PAGE_CACHE_SIZE) {
|
||||
/* We need to avoid deadlock with page_cache_read() in
|
||||
jffs2_garbage_collect_pass(). So we have to mark the
|
||||
page up to date, to prevent page_cache_read() from
|
||||
trying to re-lock it. */
|
||||
SetPageUptodate(pg);
|
||||
if (end == PAGE_CACHE_SIZE) {
|
||||
if (!start) {
|
||||
/* We need to avoid deadlock with page_cache_read() in
|
||||
jffs2_garbage_collect_pass(). So we have to mark the
|
||||
page up to date, to prevent page_cache_read() from
|
||||
trying to re-lock it. */
|
||||
SetPageUptodate(pg);
|
||||
} else {
|
||||
/* When writing out the end of a page, write out the
|
||||
_whole_ page. This helps to reduce the number of
|
||||
nodes in files which have many short writes, like
|
||||
syslog files. */
|
||||
start = aligned_start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ri = jffs2_alloc_raw_inode();
|
||||
|
@ -33,11 +33,11 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
|
||||
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
|
||||
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
|
||||
struct jffs2_raw_inode *ri;
|
||||
unsigned short dev;
|
||||
union jffs2_device_node dev;
|
||||
unsigned char *mdata = NULL;
|
||||
int mdatalen = 0;
|
||||
unsigned int ivalid;
|
||||
uint32_t phys_ofs, alloclen;
|
||||
uint32_t alloclen;
|
||||
int ret;
|
||||
D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
|
||||
ret = inode_change_ok(inode, iattr);
|
||||
@ -51,20 +51,24 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
|
||||
it out again with the appropriate data attached */
|
||||
if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
|
||||
/* For these, we don't actually need to read the old node */
|
||||
dev = old_encode_dev(inode->i_rdev);
|
||||
mdatalen = jffs2_encode_dev(&dev, inode->i_rdev);
|
||||
mdata = (char *)&dev;
|
||||
mdatalen = sizeof(dev);
|
||||
D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen));
|
||||
} else if (S_ISLNK(inode->i_mode)) {
|
||||
down(&f->sem);
|
||||
mdatalen = f->metadata->size;
|
||||
mdata = kmalloc(f->metadata->size, GFP_USER);
|
||||
if (!mdata)
|
||||
if (!mdata) {
|
||||
up(&f->sem);
|
||||
return -ENOMEM;
|
||||
}
|
||||
ret = jffs2_read_dnode(c, f, f->metadata, mdata, 0, mdatalen);
|
||||
if (ret) {
|
||||
up(&f->sem);
|
||||
kfree(mdata);
|
||||
return ret;
|
||||
}
|
||||
up(&f->sem);
|
||||
D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen));
|
||||
}
|
||||
|
||||
@ -75,8 +79,8 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
|
||||
ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &alloclen,
|
||||
ALLOC_NORMAL, JFFS2_SUMMARY_INODE_SIZE);
|
||||
if (ret) {
|
||||
jffs2_free_raw_inode(ri);
|
||||
if (S_ISLNK(inode->i_mode & S_IFMT))
|
||||
@ -127,7 +131,7 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
|
||||
else
|
||||
ri->data_crc = cpu_to_je32(0);
|
||||
|
||||
new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, phys_ofs, ALLOC_NORMAL);
|
||||
new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, ALLOC_NORMAL);
|
||||
if (S_ISLNK(inode->i_mode))
|
||||
kfree(mdata);
|
||||
|
||||
@ -180,7 +184,12 @@ static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
|
||||
|
||||
int jffs2_setattr(struct dentry *dentry, struct iattr *iattr)
|
||||
{
|
||||
return jffs2_do_setattr(dentry->d_inode, iattr);
|
||||
int rc;
|
||||
|
||||
rc = jffs2_do_setattr(dentry->d_inode, iattr);
|
||||
if (!rc && (iattr->ia_valid & ATTR_MODE))
|
||||
rc = jffs2_acl_chmod(dentry->d_inode);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int jffs2_statfs(struct super_block *sb, struct kstatfs *buf)
|
||||
@ -219,6 +228,7 @@ void jffs2_clear_inode (struct inode *inode)
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
|
||||
|
||||
jffs2_xattr_delete_inode(c, f->inocache);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
}
|
||||
|
||||
@ -227,6 +237,8 @@ void jffs2_read_inode (struct inode *inode)
|
||||
struct jffs2_inode_info *f;
|
||||
struct jffs2_sb_info *c;
|
||||
struct jffs2_raw_inode latest_node;
|
||||
union jffs2_device_node jdev;
|
||||
dev_t rdev = 0;
|
||||
int ret;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
|
||||
@ -258,7 +270,6 @@ void jffs2_read_inode (struct inode *inode)
|
||||
inode->i_blocks = (inode->i_size + 511) >> 9;
|
||||
|
||||
switch (inode->i_mode & S_IFMT) {
|
||||
jint16_t rdev;
|
||||
|
||||
case S_IFLNK:
|
||||
inode->i_op = &jffs2_symlink_inode_operations;
|
||||
@ -292,8 +303,16 @@ void jffs2_read_inode (struct inode *inode)
|
||||
case S_IFBLK:
|
||||
case S_IFCHR:
|
||||
/* Read the device numbers from the media */
|
||||
if (f->metadata->size != sizeof(jdev.old) &&
|
||||
f->metadata->size != sizeof(jdev.new)) {
|
||||
printk(KERN_NOTICE "Device node has strange size %d\n", f->metadata->size);
|
||||
up(&f->sem);
|
||||
jffs2_do_clear_inode(c, f);
|
||||
make_bad_inode(inode);
|
||||
return;
|
||||
}
|
||||
D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
|
||||
if (jffs2_read_dnode(c, f, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) {
|
||||
if (jffs2_read_dnode(c, f, f->metadata, (char *)&jdev, 0, f->metadata->size) < 0) {
|
||||
/* Eep */
|
||||
printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
|
||||
up(&f->sem);
|
||||
@ -301,12 +320,15 @@ void jffs2_read_inode (struct inode *inode)
|
||||
make_bad_inode(inode);
|
||||
return;
|
||||
}
|
||||
if (f->metadata->size == sizeof(jdev.old))
|
||||
rdev = old_decode_dev(je16_to_cpu(jdev.old));
|
||||
else
|
||||
rdev = new_decode_dev(je32_to_cpu(jdev.new));
|
||||
|
||||
case S_IFSOCK:
|
||||
case S_IFIFO:
|
||||
inode->i_op = &jffs2_file_inode_operations;
|
||||
init_special_inode(inode, inode->i_mode,
|
||||
old_decode_dev((je16_to_cpu(rdev))));
|
||||
init_special_inode(inode, inode->i_mode, rdev);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -492,6 +514,8 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
|
||||
}
|
||||
memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *));
|
||||
|
||||
jffs2_init_xattr_subsystem(c);
|
||||
|
||||
if ((ret = jffs2_do_mount_fs(c)))
|
||||
goto out_inohash;
|
||||
|
||||
@ -526,6 +550,7 @@ int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
|
||||
else
|
||||
kfree(c->blocks);
|
||||
out_inohash:
|
||||
jffs2_clear_xattr_subsystem(c);
|
||||
kfree(c->inocache_list);
|
||||
out_wbuf:
|
||||
jffs2_flash_cleanup(c);
|
||||
@ -639,13 +664,6 @@ static int jffs2_flash_setup(struct jffs2_sb_info *c) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* add setups for other bizarre flashes here... */
|
||||
if (jffs2_nor_ecc(c)) {
|
||||
ret = jffs2_nor_ecc_flash_setup(c);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* and Dataflash */
|
||||
if (jffs2_dataflash(c)) {
|
||||
ret = jffs2_dataflash_setup(c);
|
||||
@ -669,11 +687,6 @@ void jffs2_flash_cleanup(struct jffs2_sb_info *c) {
|
||||
jffs2_nand_flash_cleanup(c);
|
||||
}
|
||||
|
||||
/* add cleanups for other bizarre flashes here... */
|
||||
if (jffs2_nor_ecc(c)) {
|
||||
jffs2_nor_ecc_flash_cleanup(c);
|
||||
}
|
||||
|
||||
/* and DataFlash */
|
||||
if (jffs2_dataflash(c)) {
|
||||
jffs2_dataflash_cleanup(c);
|
||||
|
131
fs/jffs2/gc.c
131
fs/jffs2/gc.c
@ -125,6 +125,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
|
||||
struct jffs2_eraseblock *jeb;
|
||||
struct jffs2_raw_node_ref *raw;
|
||||
int ret = 0, inum, nlink;
|
||||
int xattr = 0;
|
||||
|
||||
if (down_interruptible(&c->alloc_sem))
|
||||
return -EINTR;
|
||||
@ -138,7 +139,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
|
||||
the node CRCs etc. Do it now. */
|
||||
|
||||
/* checked_ino is protected by the alloc_sem */
|
||||
if (c->checked_ino > c->highest_ino) {
|
||||
if (c->checked_ino > c->highest_ino && xattr) {
|
||||
printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n",
|
||||
c->unchecked_size);
|
||||
jffs2_dbg_dump_block_lists_nolock(c);
|
||||
@ -148,6 +149,9 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
|
||||
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
if (!xattr)
|
||||
xattr = jffs2_verify_xattr(c);
|
||||
|
||||
spin_lock(&c->inocache_lock);
|
||||
|
||||
ic = jffs2_get_ino_cache(c, c->checked_ino++);
|
||||
@ -181,6 +185,10 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
|
||||
and trigger the BUG() above while we haven't yet
|
||||
finished checking all its nodes */
|
||||
D1(printk(KERN_DEBUG "Waiting for ino #%u to finish reading\n", ic->ino));
|
||||
/* We need to come back again for the _same_ inode. We've
|
||||
made no progress in this case, but that should be OK */
|
||||
c->checked_ino--;
|
||||
|
||||
up(&c->alloc_sem);
|
||||
sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
|
||||
return 0;
|
||||
@ -231,7 +239,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
|
||||
|
||||
while(ref_obsolete(raw)) {
|
||||
D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw)));
|
||||
raw = raw->next_phys;
|
||||
raw = ref_next(raw);
|
||||
if (unlikely(!raw)) {
|
||||
printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n");
|
||||
printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n",
|
||||
@ -248,16 +256,37 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
|
||||
|
||||
if (!raw->next_in_ino) {
|
||||
/* Inode-less node. Clean marker, snapshot or something like that */
|
||||
/* FIXME: If it's something that needs to be copied, including something
|
||||
we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we should do so */
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
jffs2_mark_node_obsolete(c, raw);
|
||||
if (ref_flags(raw) == REF_PRISTINE) {
|
||||
/* It's an unknown node with JFFS2_FEATURE_RWCOMPAT_COPY */
|
||||
jffs2_garbage_collect_pristine(c, NULL, raw);
|
||||
} else {
|
||||
/* Just mark it obsolete */
|
||||
jffs2_mark_node_obsolete(c, raw);
|
||||
}
|
||||
up(&c->alloc_sem);
|
||||
goto eraseit_lock;
|
||||
}
|
||||
|
||||
ic = jffs2_raw_ref_to_ic(raw);
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
/* When 'ic' refers xattr_datum/xattr_ref, this node is GCed as xattr.
|
||||
* We can decide whether this node is inode or xattr by ic->class. */
|
||||
if (ic->class == RAWNODE_CLASS_XATTR_DATUM
|
||||
|| ic->class == RAWNODE_CLASS_XATTR_REF) {
|
||||
BUG_ON(raw->next_in_ino != (void *)ic);
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
if (ic->class == RAWNODE_CLASS_XATTR_DATUM) {
|
||||
ret = jffs2_garbage_collect_xattr_datum(c, (struct jffs2_xattr_datum *)ic);
|
||||
} else {
|
||||
ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic);
|
||||
}
|
||||
goto release_sem;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* We need to hold the inocache. Either the erase_completion_lock or
|
||||
the inocache_lock are sufficient; we trade down since the inocache_lock
|
||||
causes less contention. */
|
||||
@ -499,7 +528,6 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
|
||||
struct jffs2_raw_node_ref *raw)
|
||||
{
|
||||
union jffs2_node_union *node;
|
||||
struct jffs2_raw_node_ref *nraw;
|
||||
size_t retlen;
|
||||
int ret;
|
||||
uint32_t phys_ofs, alloclen;
|
||||
@ -508,15 +536,16 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
|
||||
|
||||
D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw)));
|
||||
|
||||
rawlen = ref_totlen(c, c->gcblock, raw);
|
||||
alloclen = rawlen = ref_totlen(c, c->gcblock, raw);
|
||||
|
||||
/* Ask for a small amount of space (or the totlen if smaller) because we
|
||||
don't want to force wastage of the end of a block if splitting would
|
||||
work. */
|
||||
ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) +
|
||||
JFFS2_MIN_DATA_LEN, rawlen), &phys_ofs, &alloclen, rawlen);
|
||||
/* this is not the exact summary size of it,
|
||||
it is only an upper estimation */
|
||||
if (ic && alloclen > sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN)
|
||||
alloclen = sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN;
|
||||
|
||||
ret = jffs2_reserve_space_gc(c, alloclen, &alloclen, rawlen);
|
||||
/* 'rawlen' is not the exact summary size; it is only an upper estimation */
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
@ -580,22 +609,17 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n",
|
||||
ref_offset(raw), je16_to_cpu(node->u.nodetype));
|
||||
goto bail;
|
||||
}
|
||||
|
||||
nraw = jffs2_alloc_raw_node_ref();
|
||||
if (!nraw) {
|
||||
ret = -ENOMEM;
|
||||
goto out_node;
|
||||
/* If it's inode-less, we don't _know_ what it is. Just copy it intact */
|
||||
if (ic) {
|
||||
printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n",
|
||||
ref_offset(raw), je16_to_cpu(node->u.nodetype));
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
|
||||
/* OK, all the CRCs are good; this node can just be copied as-is. */
|
||||
retry:
|
||||
nraw->flash_offset = phys_ofs;
|
||||
nraw->__totlen = rawlen;
|
||||
nraw->next_phys = NULL;
|
||||
phys_ofs = write_ofs(c);
|
||||
|
||||
ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node);
|
||||
|
||||
@ -603,17 +627,11 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
|
||||
printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
|
||||
rawlen, phys_ofs, ret, retlen);
|
||||
if (retlen) {
|
||||
/* Doesn't belong to any inode */
|
||||
nraw->next_in_ino = NULL;
|
||||
|
||||
nraw->flash_offset |= REF_OBSOLETE;
|
||||
jffs2_add_physical_node_ref(c, nraw);
|
||||
jffs2_mark_node_obsolete(c, nraw);
|
||||
jffs2_add_physical_node_ref(c, phys_ofs | REF_OBSOLETE, rawlen, NULL);
|
||||
} else {
|
||||
printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset);
|
||||
jffs2_free_raw_node_ref(nraw);
|
||||
printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", phys_ofs);
|
||||
}
|
||||
if (!retried && (nraw = jffs2_alloc_raw_node_ref())) {
|
||||
if (!retried) {
|
||||
/* Try to reallocate space and retry */
|
||||
uint32_t dummy;
|
||||
struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size];
|
||||
@ -625,7 +643,7 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
|
||||
jffs2_dbg_acct_sanity_check(c,jeb);
|
||||
jffs2_dbg_acct_paranoia_check(c, jeb);
|
||||
|
||||
ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy, rawlen);
|
||||
ret = jffs2_reserve_space_gc(c, rawlen, &dummy, rawlen);
|
||||
/* this is not the exact summary size of it,
|
||||
it is only an upper estimation */
|
||||
|
||||
@ -638,25 +656,13 @@ static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
|
||||
goto retry;
|
||||
}
|
||||
D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
|
||||
jffs2_free_raw_node_ref(nraw);
|
||||
}
|
||||
|
||||
jffs2_free_raw_node_ref(nraw);
|
||||
if (!ret)
|
||||
ret = -EIO;
|
||||
goto out_node;
|
||||
}
|
||||
nraw->flash_offset |= REF_PRISTINE;
|
||||
jffs2_add_physical_node_ref(c, nraw);
|
||||
|
||||
/* Link into per-inode list. This is safe because of the ic
|
||||
state being INO_STATE_GC. Note that if we're doing this
|
||||
for an inode which is in-core, the 'nraw' pointer is then
|
||||
going to be fetched from ic->nodes by our caller. */
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
nraw->next_in_ino = ic->nodes;
|
||||
ic->nodes = nraw;
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, rawlen, ic);
|
||||
|
||||
jffs2_mark_node_obsolete(c, raw);
|
||||
D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw)));
|
||||
@ -675,19 +681,16 @@ 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;
|
||||
union jffs2_device_node dev;
|
||||
char *mdata = NULL, mdatalen = 0;
|
||||
uint32_t alloclen, phys_ofs, ilen;
|
||||
uint32_t alloclen, ilen;
|
||||
int ret;
|
||||
|
||||
if (S_ISBLK(JFFS2_F_I_MODE(f)) ||
|
||||
S_ISCHR(JFFS2_F_I_MODE(f)) ) {
|
||||
/* For these, we don't actually need to read the old node */
|
||||
/* FIXME: for minor or major > 255. */
|
||||
dev = cpu_to_je16(((JFFS2_F_I_RDEV_MAJ(f) << 8) |
|
||||
JFFS2_F_I_RDEV_MIN(f)));
|
||||
mdatalen = jffs2_encode_dev(&dev, JFFS2_F_I_RDEV(f));
|
||||
mdata = (char *)&dev;
|
||||
mdatalen = sizeof(dev);
|
||||
D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen));
|
||||
} else if (S_ISLNK(JFFS2_F_I_MODE(f))) {
|
||||
mdatalen = fn->size;
|
||||
@ -706,7 +709,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
|
||||
|
||||
}
|
||||
|
||||
ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen,
|
||||
ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &alloclen,
|
||||
JFFS2_SUMMARY_INODE_SIZE);
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n",
|
||||
@ -744,7 +747,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_
|
||||
ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
|
||||
ri.data_crc = cpu_to_je32(crc32(0, mdata, mdatalen));
|
||||
|
||||
new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, ALLOC_GC);
|
||||
new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, ALLOC_GC);
|
||||
|
||||
if (IS_ERR(new_fn)) {
|
||||
printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
|
||||
@ -765,7 +768,7 @@ static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_er
|
||||
{
|
||||
struct jffs2_full_dirent *new_fd;
|
||||
struct jffs2_raw_dirent rd;
|
||||
uint32_t alloclen, phys_ofs;
|
||||
uint32_t alloclen;
|
||||
int ret;
|
||||
|
||||
rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
|
||||
@ -787,14 +790,14 @@ static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_er
|
||||
rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd)-8));
|
||||
rd.name_crc = cpu_to_je32(crc32(0, fd->name, rd.nsize));
|
||||
|
||||
ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &phys_ofs, &alloclen,
|
||||
ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &alloclen,
|
||||
JFFS2_SUMMARY_DIRENT_SIZE(rd.nsize));
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n",
|
||||
sizeof(rd)+rd.nsize, ret);
|
||||
return ret;
|
||||
}
|
||||
new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, phys_ofs, ALLOC_GC);
|
||||
new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, ALLOC_GC);
|
||||
|
||||
if (IS_ERR(new_fd)) {
|
||||
printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd));
|
||||
@ -922,7 +925,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, ilen;
|
||||
uint32_t alloclen, ilen;
|
||||
int ret;
|
||||
|
||||
D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n",
|
||||
@ -1001,14 +1004,14 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras
|
||||
ri.data_crc = cpu_to_je32(0);
|
||||
ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
|
||||
|
||||
ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen,
|
||||
JFFS2_SUMMARY_INODE_SIZE);
|
||||
ret = jffs2_reserve_space_gc(c, sizeof(ri), &alloclen,
|
||||
JFFS2_SUMMARY_INODE_SIZE);
|
||||
if (ret) {
|
||||
printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n",
|
||||
sizeof(ri), ret);
|
||||
return ret;
|
||||
}
|
||||
new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_GC);
|
||||
new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, ALLOC_GC);
|
||||
|
||||
if (IS_ERR(new_fn)) {
|
||||
printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn));
|
||||
@ -1070,7 +1073,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
|
||||
{
|
||||
struct jffs2_full_dnode *new_fn;
|
||||
struct jffs2_raw_inode ri;
|
||||
uint32_t alloclen, phys_ofs, offset, orig_end, orig_start;
|
||||
uint32_t alloclen, offset, orig_end, orig_start;
|
||||
int ret = 0;
|
||||
unsigned char *comprbuf = NULL, *writebuf;
|
||||
unsigned long pg;
|
||||
@ -1227,7 +1230,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
|
||||
uint32_t cdatalen;
|
||||
uint16_t comprtype = JFFS2_COMPR_NONE;
|
||||
|
||||
ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs,
|
||||
ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN,
|
||||
&alloclen, JFFS2_SUMMARY_INODE_SIZE);
|
||||
|
||||
if (ret) {
|
||||
@ -1264,7 +1267,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era
|
||||
ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
|
||||
ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
|
||||
|
||||
new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC);
|
||||
new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, ALLOC_GC);
|
||||
|
||||
jffs2_free_comprbuf(comprbuf, writebuf);
|
||||
|
||||
|
@ -1,3 +0,0 @@
|
||||
/* This file provides the bit-probabilities for the input file */
|
||||
#define BIT_DIVIDER 629
|
||||
static int bits[9] = { 179,167,183,165,159,198,178,119,}; /* ia32 .so files */
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/posix_acl.h>
|
||||
#include <asm/semaphore.h>
|
||||
|
||||
struct jffs2_inode_info {
|
||||
@ -45,6 +46,10 @@ struct jffs2_inode_info {
|
||||
struct inode vfs_inode;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef CONFIG_JFFS2_FS_POSIX_ACL
|
||||
struct posix_acl *i_acl_access;
|
||||
struct posix_acl *i_acl_default;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif /* _JFFS2_FS_I */
|
@ -100,6 +100,7 @@ struct jffs2_sb_info {
|
||||
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
|
||||
/* Write-behind buffer for NAND flash */
|
||||
unsigned char *wbuf;
|
||||
unsigned char *oobbuf;
|
||||
uint32_t wbuf_ofs;
|
||||
uint32_t wbuf_len;
|
||||
struct jffs2_inodirty *wbuf_inodes;
|
||||
@ -107,7 +108,7 @@ struct jffs2_sb_info {
|
||||
struct rw_semaphore wbuf_sem; /* Protects the write buffer */
|
||||
|
||||
/* Information about out-of-band area usage... */
|
||||
struct nand_oobinfo *oobinfo;
|
||||
struct nand_ecclayout *ecclayout;
|
||||
uint32_t badblock_pos;
|
||||
uint32_t fsdata_pos;
|
||||
uint32_t fsdata_len;
|
||||
@ -115,6 +116,16 @@ struct jffs2_sb_info {
|
||||
|
||||
struct jffs2_summary *summary; /* Summary information */
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
#define XATTRINDEX_HASHSIZE (57)
|
||||
uint32_t highest_xid;
|
||||
struct list_head xattrindex[XATTRINDEX_HASHSIZE];
|
||||
struct list_head xattr_unchecked;
|
||||
struct jffs2_xattr_ref *xref_temp;
|
||||
struct rw_semaphore xattr_sem;
|
||||
uint32_t xdatum_mem_usage;
|
||||
uint32_t xdatum_mem_threshold;
|
||||
#endif
|
||||
/* OS-private pointer for getting back to master superblock info */
|
||||
void *os_priv;
|
||||
};
|
@ -26,6 +26,10 @@ static kmem_cache_t *tmp_dnode_info_slab;
|
||||
static kmem_cache_t *raw_node_ref_slab;
|
||||
static kmem_cache_t *node_frag_slab;
|
||||
static kmem_cache_t *inode_cache_slab;
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
static kmem_cache_t *xattr_datum_cache;
|
||||
static kmem_cache_t *xattr_ref_cache;
|
||||
#endif
|
||||
|
||||
int __init jffs2_create_slab_caches(void)
|
||||
{
|
||||
@ -53,8 +57,8 @@ int __init jffs2_create_slab_caches(void)
|
||||
if (!tmp_dnode_info_slab)
|
||||
goto err;
|
||||
|
||||
raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref",
|
||||
sizeof(struct jffs2_raw_node_ref),
|
||||
raw_node_ref_slab = kmem_cache_create("jffs2_refblock",
|
||||
sizeof(struct jffs2_raw_node_ref) * (REFS_PER_BLOCK + 1),
|
||||
0, 0, NULL, NULL);
|
||||
if (!raw_node_ref_slab)
|
||||
goto err;
|
||||
@ -68,8 +72,24 @@ int __init jffs2_create_slab_caches(void)
|
||||
inode_cache_slab = kmem_cache_create("jffs2_inode_cache",
|
||||
sizeof(struct jffs2_inode_cache),
|
||||
0, 0, NULL, NULL);
|
||||
if (inode_cache_slab)
|
||||
return 0;
|
||||
if (!inode_cache_slab)
|
||||
goto err;
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
xattr_datum_cache = kmem_cache_create("jffs2_xattr_datum",
|
||||
sizeof(struct jffs2_xattr_datum),
|
||||
0, 0, NULL, NULL);
|
||||
if (!xattr_datum_cache)
|
||||
goto err;
|
||||
|
||||
xattr_ref_cache = kmem_cache_create("jffs2_xattr_ref",
|
||||
sizeof(struct jffs2_xattr_ref),
|
||||
0, 0, NULL, NULL);
|
||||
if (!xattr_ref_cache)
|
||||
goto err;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
err:
|
||||
jffs2_destroy_slab_caches();
|
||||
return -ENOMEM;
|
||||
@ -91,6 +111,12 @@ void jffs2_destroy_slab_caches(void)
|
||||
kmem_cache_destroy(node_frag_slab);
|
||||
if(inode_cache_slab)
|
||||
kmem_cache_destroy(inode_cache_slab);
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
if (xattr_datum_cache)
|
||||
kmem_cache_destroy(xattr_datum_cache);
|
||||
if (xattr_ref_cache)
|
||||
kmem_cache_destroy(xattr_ref_cache);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize)
|
||||
@ -164,15 +190,65 @@ void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *x)
|
||||
kmem_cache_free(tmp_dnode_info_slab, x);
|
||||
}
|
||||
|
||||
struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void)
|
||||
struct jffs2_raw_node_ref *jffs2_alloc_refblock(void)
|
||||
{
|
||||
struct jffs2_raw_node_ref *ret;
|
||||
|
||||
ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL);
|
||||
dbg_memalloc("%p\n", ret);
|
||||
if (ret) {
|
||||
int i = 0;
|
||||
for (i=0; i < REFS_PER_BLOCK; i++) {
|
||||
ret[i].flash_offset = REF_EMPTY_NODE;
|
||||
ret[i].next_in_ino = NULL;
|
||||
}
|
||||
ret[i].flash_offset = REF_LINK_NODE;
|
||||
ret[i].next_in_ino = NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *x)
|
||||
int jffs2_prealloc_raw_node_refs(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb, int nr)
|
||||
{
|
||||
struct jffs2_raw_node_ref **p, *ref;
|
||||
int i = nr;
|
||||
|
||||
dbg_memalloc("%d\n", nr);
|
||||
|
||||
p = &jeb->last_node;
|
||||
ref = *p;
|
||||
|
||||
dbg_memalloc("Reserving %d refs for block @0x%08x\n", nr, jeb->offset);
|
||||
|
||||
/* If jeb->last_node is really a valid node then skip over it */
|
||||
if (ref && ref->flash_offset != REF_EMPTY_NODE)
|
||||
ref++;
|
||||
|
||||
while (i) {
|
||||
if (!ref) {
|
||||
dbg_memalloc("Allocating new refblock linked from %p\n", p);
|
||||
ref = *p = jffs2_alloc_refblock();
|
||||
if (!ref)
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (ref->flash_offset == REF_LINK_NODE) {
|
||||
p = &ref->next_in_ino;
|
||||
ref = *p;
|
||||
continue;
|
||||
}
|
||||
i--;
|
||||
ref++;
|
||||
}
|
||||
jeb->allocated_refs = nr;
|
||||
|
||||
dbg_memalloc("Reserved %d refs for block @0x%08x, last_node is %p (%08x,%p)\n",
|
||||
nr, jeb->offset, jeb->last_node, jeb->last_node->flash_offset,
|
||||
jeb->last_node->next_in_ino);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void jffs2_free_refblock(struct jffs2_raw_node_ref *x)
|
||||
{
|
||||
dbg_memalloc("%p\n", x);
|
||||
kmem_cache_free(raw_node_ref_slab, x);
|
||||
@ -205,3 +281,40 @@ void jffs2_free_inode_cache(struct jffs2_inode_cache *x)
|
||||
dbg_memalloc("%p\n", x);
|
||||
kmem_cache_free(inode_cache_slab, x);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void)
|
||||
{
|
||||
struct jffs2_xattr_datum *xd;
|
||||
xd = kmem_cache_alloc(xattr_datum_cache, GFP_KERNEL);
|
||||
dbg_memalloc("%p\n", xd);
|
||||
|
||||
memset(xd, 0, sizeof(struct jffs2_xattr_datum));
|
||||
xd->class = RAWNODE_CLASS_XATTR_DATUM;
|
||||
INIT_LIST_HEAD(&xd->xindex);
|
||||
return xd;
|
||||
}
|
||||
|
||||
void jffs2_free_xattr_datum(struct jffs2_xattr_datum *xd)
|
||||
{
|
||||
dbg_memalloc("%p\n", xd);
|
||||
kmem_cache_free(xattr_datum_cache, xd);
|
||||
}
|
||||
|
||||
struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void)
|
||||
{
|
||||
struct jffs2_xattr_ref *ref;
|
||||
ref = kmem_cache_alloc(xattr_ref_cache, GFP_KERNEL);
|
||||
dbg_memalloc("%p\n", ref);
|
||||
|
||||
memset(ref, 0, sizeof(struct jffs2_xattr_ref));
|
||||
ref->class = RAWNODE_CLASS_XATTR_REF;
|
||||
return ref;
|
||||
}
|
||||
|
||||
void jffs2_free_xattr_ref(struct jffs2_xattr_ref *ref)
|
||||
{
|
||||
dbg_memalloc("%p\n", ref);
|
||||
kmem_cache_free(xattr_ref_cache, ref);
|
||||
}
|
||||
#endif
|
||||
|
@ -438,8 +438,7 @@ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info
|
||||
if (c->mtd->point) {
|
||||
err = c->mtd->point(c->mtd, ofs, len, &retlen, &buffer);
|
||||
if (!err && retlen < tn->csize) {
|
||||
JFFS2_WARNING("MTD point returned len too short: %zu "
|
||||
"instead of %u.\n", retlen, tn->csize);
|
||||
JFFS2_WARNING("MTD point returned len too short: %zu instead of %u.\n", retlen, tn->csize);
|
||||
c->mtd->unpoint(c->mtd, buffer, ofs, len);
|
||||
} else if (err)
|
||||
JFFS2_WARNING("MTD point failed: error code %d.\n", err);
|
||||
@ -462,8 +461,7 @@ static int check_node_data(struct jffs2_sb_info *c, struct jffs2_tmp_dnode_info
|
||||
}
|
||||
|
||||
if (retlen != len) {
|
||||
JFFS2_ERROR("short read at %#08x: %zd instead of %d.\n",
|
||||
ofs, retlen, len);
|
||||
JFFS2_ERROR("short read at %#08x: %zd instead of %d.\n", ofs, retlen, len);
|
||||
err = -EIO;
|
||||
goto free_out;
|
||||
}
|
||||
@ -940,6 +938,7 @@ void jffs2_free_ino_caches(struct jffs2_sb_info *c)
|
||||
this = c->inocache_list[i];
|
||||
while (this) {
|
||||
next = this->next;
|
||||
jffs2_xattr_free_inode(c, this);
|
||||
jffs2_free_inode_cache(this);
|
||||
this = next;
|
||||
}
|
||||
@ -954,9 +953,13 @@ void jffs2_free_raw_node_refs(struct jffs2_sb_info *c)
|
||||
|
||||
for (i=0; i<c->nr_blocks; i++) {
|
||||
this = c->blocks[i].first_node;
|
||||
while(this) {
|
||||
next = this->next_phys;
|
||||
jffs2_free_raw_node_ref(this);
|
||||
while (this) {
|
||||
if (this[REFS_PER_BLOCK].flash_offset == REF_LINK_NODE)
|
||||
next = this[REFS_PER_BLOCK].next_in_ino;
|
||||
else
|
||||
next = NULL;
|
||||
|
||||
jffs2_free_refblock(this);
|
||||
this = next;
|
||||
}
|
||||
c->blocks[i].first_node = c->blocks[i].last_node = NULL;
|
||||
@ -1047,3 +1050,169 @@ void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c)
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
|
||||
struct jffs2_raw_node_ref *jffs2_link_node_ref(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb,
|
||||
uint32_t ofs, uint32_t len,
|
||||
struct jffs2_inode_cache *ic)
|
||||
{
|
||||
struct jffs2_raw_node_ref *ref;
|
||||
|
||||
BUG_ON(!jeb->allocated_refs);
|
||||
jeb->allocated_refs--;
|
||||
|
||||
ref = jeb->last_node;
|
||||
|
||||
dbg_noderef("Last node at %p is (%08x,%p)\n", ref, ref->flash_offset,
|
||||
ref->next_in_ino);
|
||||
|
||||
while (ref->flash_offset != REF_EMPTY_NODE) {
|
||||
if (ref->flash_offset == REF_LINK_NODE)
|
||||
ref = ref->next_in_ino;
|
||||
else
|
||||
ref++;
|
||||
}
|
||||
|
||||
dbg_noderef("New ref is %p (%08x becomes %08x,%p) len 0x%x\n", ref,
|
||||
ref->flash_offset, ofs, ref->next_in_ino, len);
|
||||
|
||||
ref->flash_offset = ofs;
|
||||
|
||||
if (!jeb->first_node) {
|
||||
jeb->first_node = ref;
|
||||
BUG_ON(ref_offset(ref) != jeb->offset);
|
||||
} else if (unlikely(ref_offset(ref) != jeb->offset + c->sector_size - jeb->free_size)) {
|
||||
uint32_t last_len = ref_totlen(c, jeb, jeb->last_node);
|
||||
|
||||
JFFS2_ERROR("Adding new ref %p at (0x%08x-0x%08x) not immediately after previous (0x%08x-0x%08x)\n",
|
||||
ref, ref_offset(ref), ref_offset(ref)+len,
|
||||
ref_offset(jeb->last_node),
|
||||
ref_offset(jeb->last_node)+last_len);
|
||||
BUG();
|
||||
}
|
||||
jeb->last_node = ref;
|
||||
|
||||
if (ic) {
|
||||
ref->next_in_ino = ic->nodes;
|
||||
ic->nodes = ref;
|
||||
} else {
|
||||
ref->next_in_ino = NULL;
|
||||
}
|
||||
|
||||
switch(ref_flags(ref)) {
|
||||
case REF_UNCHECKED:
|
||||
c->unchecked_size += len;
|
||||
jeb->unchecked_size += len;
|
||||
break;
|
||||
|
||||
case REF_NORMAL:
|
||||
case REF_PRISTINE:
|
||||
c->used_size += len;
|
||||
jeb->used_size += len;
|
||||
break;
|
||||
|
||||
case REF_OBSOLETE:
|
||||
c->dirty_size += len;
|
||||
jeb->dirty_size += len;
|
||||
break;
|
||||
}
|
||||
c->free_size -= len;
|
||||
jeb->free_size -= len;
|
||||
|
||||
#ifdef TEST_TOTLEN
|
||||
/* Set (and test) __totlen field... for now */
|
||||
ref->__totlen = len;
|
||||
ref_totlen(c, jeb, ref);
|
||||
#endif
|
||||
return ref;
|
||||
}
|
||||
|
||||
/* No locking, no reservation of 'ref'. Do not use on a live file system */
|
||||
int jffs2_scan_dirty_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
uint32_t size)
|
||||
{
|
||||
if (!size)
|
||||
return 0;
|
||||
if (unlikely(size > jeb->free_size)) {
|
||||
printk(KERN_CRIT "Dirty space 0x%x larger then free_size 0x%x (wasted 0x%x)\n",
|
||||
size, jeb->free_size, jeb->wasted_size);
|
||||
BUG();
|
||||
}
|
||||
/* REF_EMPTY_NODE is !obsolete, so that works OK */
|
||||
if (jeb->last_node && ref_obsolete(jeb->last_node)) {
|
||||
#ifdef TEST_TOTLEN
|
||||
jeb->last_node->__totlen += size;
|
||||
#endif
|
||||
c->dirty_size += size;
|
||||
c->free_size -= size;
|
||||
jeb->dirty_size += size;
|
||||
jeb->free_size -= size;
|
||||
} else {
|
||||
uint32_t ofs = jeb->offset + c->sector_size - jeb->free_size;
|
||||
ofs |= REF_OBSOLETE;
|
||||
|
||||
jffs2_link_node_ref(c, jeb, ofs, size, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Calculate totlen from surrounding nodes or eraseblock */
|
||||
static inline uint32_t __ref_totlen(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_node_ref *ref)
|
||||
{
|
||||
uint32_t ref_end;
|
||||
struct jffs2_raw_node_ref *next_ref = ref_next(ref);
|
||||
|
||||
if (next_ref)
|
||||
ref_end = ref_offset(next_ref);
|
||||
else {
|
||||
if (!jeb)
|
||||
jeb = &c->blocks[ref->flash_offset / c->sector_size];
|
||||
|
||||
/* Last node in block. Use free_space */
|
||||
if (unlikely(ref != jeb->last_node)) {
|
||||
printk(KERN_CRIT "ref %p @0x%08x is not jeb->last_node (%p @0x%08x)\n",
|
||||
ref, ref_offset(ref), jeb->last_node, jeb->last_node?ref_offset(jeb->last_node):0);
|
||||
BUG();
|
||||
}
|
||||
ref_end = jeb->offset + c->sector_size - jeb->free_size;
|
||||
}
|
||||
return ref_end - ref_offset(ref);
|
||||
}
|
||||
|
||||
uint32_t __jffs2_ref_totlen(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_node_ref *ref)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
ret = __ref_totlen(c, jeb, ref);
|
||||
|
||||
#ifdef TEST_TOTLEN
|
||||
if (unlikely(ret != ref->__totlen)) {
|
||||
if (!jeb)
|
||||
jeb = &c->blocks[ref->flash_offset / c->sector_size];
|
||||
|
||||
printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n",
|
||||
ref, ref_offset(ref), ref_offset(ref)+ref->__totlen,
|
||||
ret, ref->__totlen);
|
||||
if (ref_next(ref)) {
|
||||
printk(KERN_CRIT "next %p (0x%08x-0x%08x)\n", ref_next(ref), ref_offset(ref_next(ref)),
|
||||
ref_offset(ref_next(ref))+ref->__totlen);
|
||||
} else
|
||||
printk(KERN_CRIT "No next ref. jeb->last_node is %p\n", jeb->last_node);
|
||||
|
||||
printk(KERN_CRIT "jeb->wasted_size %x, dirty_size %x, used_size %x, free_size %x\n", jeb->wasted_size, jeb->dirty_size, jeb->used_size, jeb->free_size);
|
||||
|
||||
#if defined(JFFS2_DBG_DUMPS) || defined(JFFS2_DBG_PARANOIA_CHECKS)
|
||||
__jffs2_dbg_dump_node_refs_nolock(c, jeb);
|
||||
#endif
|
||||
|
||||
WARN_ON(1);
|
||||
|
||||
ret = ref->__totlen;
|
||||
}
|
||||
#endif /* TEST_TOTLEN */
|
||||
return ret;
|
||||
}
|
||||
|
@ -18,8 +18,10 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include <linux/jffs2_fs_sb.h>
|
||||
#include <linux/jffs2_fs_i.h>
|
||||
#include "jffs2_fs_sb.h"
|
||||
#include "jffs2_fs_i.h"
|
||||
#include "xattr.h"
|
||||
#include "acl.h"
|
||||
#include "summary.h"
|
||||
|
||||
#ifdef __ECOS
|
||||
@ -75,14 +77,50 @@
|
||||
struct jffs2_raw_node_ref
|
||||
{
|
||||
struct jffs2_raw_node_ref *next_in_ino; /* Points to the next raw_node_ref
|
||||
for this inode. If this is the last, it points to the inode_cache
|
||||
for this inode instead. The inode_cache will have NULL in the first
|
||||
word so you know when you've got there :) */
|
||||
struct jffs2_raw_node_ref *next_phys;
|
||||
for this object. If this _is_ the last, it points to the inode_cache,
|
||||
xattr_ref or xattr_datum instead. The common part of those structures
|
||||
has NULL in the first word. See jffs2_raw_ref_to_ic() below */
|
||||
uint32_t flash_offset;
|
||||
#define TEST_TOTLEN
|
||||
#ifdef TEST_TOTLEN
|
||||
uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */
|
||||
#endif
|
||||
};
|
||||
|
||||
#define REF_LINK_NODE ((int32_t)-1)
|
||||
#define REF_EMPTY_NODE ((int32_t)-2)
|
||||
|
||||
/* Use blocks of about 256 bytes */
|
||||
#define REFS_PER_BLOCK ((255/sizeof(struct jffs2_raw_node_ref))-1)
|
||||
|
||||
static inline struct jffs2_raw_node_ref *ref_next(struct jffs2_raw_node_ref *ref)
|
||||
{
|
||||
ref++;
|
||||
|
||||
/* Link to another block of refs */
|
||||
if (ref->flash_offset == REF_LINK_NODE) {
|
||||
ref = ref->next_in_ino;
|
||||
if (!ref)
|
||||
return ref;
|
||||
}
|
||||
|
||||
/* End of chain */
|
||||
if (ref->flash_offset == REF_EMPTY_NODE)
|
||||
return NULL;
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw)
|
||||
{
|
||||
while(raw->next_in_ino)
|
||||
raw = raw->next_in_ino;
|
||||
|
||||
/* NB. This can be a jffs2_xattr_datum or jffs2_xattr_ref and
|
||||
not actually a jffs2_inode_cache. Check ->class */
|
||||
return ((struct jffs2_inode_cache *)raw);
|
||||
}
|
||||
|
||||
/* flash_offset & 3 always has to be zero, because nodes are
|
||||
always aligned at 4 bytes. So we have a couple of extra bits
|
||||
to play with, which indicate the node's status; see below: */
|
||||
@ -95,6 +133,11 @@ struct jffs2_raw_node_ref
|
||||
#define ref_obsolete(ref) (((ref)->flash_offset & 3) == REF_OBSOLETE)
|
||||
#define mark_ref_normal(ref) do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0)
|
||||
|
||||
/* NB: REF_PRISTINE for an inode-less node (ref->next_in_ino == NULL) indicates
|
||||
it is an unknown node of type JFFS2_NODETYPE_RWCOMPAT_COPY, so it'll get
|
||||
copied. If you need to do anything different to GC inode-less nodes, then
|
||||
you need to modify gc.c accordingly. */
|
||||
|
||||
/* For each inode in the filesystem, we need to keep a record of
|
||||
nlink, because it would be a PITA to scan the whole directory tree
|
||||
at read_inode() time to calculate it, and to keep sufficient information
|
||||
@ -103,15 +146,27 @@ struct jffs2_raw_node_ref
|
||||
a pointer to the first physical node which is part of this inode, too.
|
||||
*/
|
||||
struct jffs2_inode_cache {
|
||||
/* First part of structure is shared with other objects which
|
||||
can terminate the raw node refs' next_in_ino list -- which
|
||||
currently struct jffs2_xattr_datum and struct jffs2_xattr_ref. */
|
||||
|
||||
struct jffs2_full_dirent *scan_dents; /* Used during scan to hold
|
||||
temporary lists of dirents, and later must be set to
|
||||
NULL to mark the end of the raw_node_ref->next_in_ino
|
||||
chain. */
|
||||
struct jffs2_inode_cache *next;
|
||||
struct jffs2_raw_node_ref *nodes;
|
||||
uint8_t class; /* It's used for identification */
|
||||
|
||||
/* end of shared structure */
|
||||
|
||||
uint8_t flags;
|
||||
uint16_t state;
|
||||
uint32_t ino;
|
||||
struct jffs2_inode_cache *next;
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
struct jffs2_xattr_ref *xref;
|
||||
#endif
|
||||
int nlink;
|
||||
int state;
|
||||
};
|
||||
|
||||
/* Inode states for 'state' above. We need the 'GC' state to prevent
|
||||
@ -125,8 +180,16 @@ struct jffs2_inode_cache {
|
||||
#define INO_STATE_READING 5 /* In read_inode() */
|
||||
#define INO_STATE_CLEARING 6 /* In clear_inode() */
|
||||
|
||||
#define INO_FLAGS_XATTR_CHECKED 0x01 /* has no duplicate xattr_ref */
|
||||
|
||||
#define RAWNODE_CLASS_INODE_CACHE 0
|
||||
#define RAWNODE_CLASS_XATTR_DATUM 1
|
||||
#define RAWNODE_CLASS_XATTR_REF 2
|
||||
|
||||
#define INOCACHE_HASHSIZE 128
|
||||
|
||||
#define write_ofs(c) ((c)->nextblock->offset + (c)->sector_size - (c)->nextblock->free_size)
|
||||
|
||||
/*
|
||||
Larger representation of a raw node, kept in-core only when the
|
||||
struct inode for this particular ino is instantiated.
|
||||
@ -192,6 +255,7 @@ struct jffs2_eraseblock
|
||||
uint32_t wasted_size;
|
||||
uint32_t free_size; /* Note that sector_size - free_size
|
||||
is the address of the first free space */
|
||||
uint32_t allocated_refs;
|
||||
struct jffs2_raw_node_ref *first_node;
|
||||
struct jffs2_raw_node_ref *last_node;
|
||||
|
||||
@ -203,57 +267,7 @@ static inline int jffs2_blocks_use_vmalloc(struct jffs2_sb_info *c)
|
||||
return ((c->flash_size / c->sector_size) * sizeof (struct jffs2_eraseblock)) > (128 * 1024);
|
||||
}
|
||||
|
||||
/* Calculate totlen from surrounding nodes or eraseblock */
|
||||
static inline uint32_t __ref_totlen(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_node_ref *ref)
|
||||
{
|
||||
uint32_t ref_end;
|
||||
|
||||
if (ref->next_phys)
|
||||
ref_end = ref_offset(ref->next_phys);
|
||||
else {
|
||||
if (!jeb)
|
||||
jeb = &c->blocks[ref->flash_offset / c->sector_size];
|
||||
|
||||
/* Last node in block. Use free_space */
|
||||
BUG_ON(ref != jeb->last_node);
|
||||
ref_end = jeb->offset + c->sector_size - jeb->free_size;
|
||||
}
|
||||
return ref_end - ref_offset(ref);
|
||||
}
|
||||
|
||||
static inline uint32_t ref_totlen(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_node_ref *ref)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
#if CONFIG_JFFS2_FS_DEBUG > 0
|
||||
if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) {
|
||||
printk(KERN_CRIT "ref_totlen called with wrong block -- at 0x%08x instead of 0x%08x; ref 0x%08x\n",
|
||||
jeb->offset, c->blocks[ref->flash_offset / c->sector_size].offset, ref_offset(ref));
|
||||
BUG();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 1
|
||||
ret = ref->__totlen;
|
||||
#else
|
||||
/* This doesn't actually work yet */
|
||||
ret = __ref_totlen(c, jeb, ref);
|
||||
if (ret != ref->__totlen) {
|
||||
printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n",
|
||||
ref, ref_offset(ref), ref_offset(ref)+ref->__totlen,
|
||||
ret, ref->__totlen);
|
||||
if (!jeb)
|
||||
jeb = &c->blocks[ref->flash_offset / c->sector_size];
|
||||
jffs2_dbg_dump_node_refs_nolock(c, jeb);
|
||||
BUG();
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
#define ref_totlen(a, b, c) __jffs2_ref_totlen((a), (b), (c))
|
||||
|
||||
#define ALLOC_NORMAL 0 /* Normal allocation */
|
||||
#define ALLOC_DELETION 1 /* Deletion node. Best to allow it */
|
||||
@ -268,13 +282,15 @@ static inline uint32_t ref_totlen(struct jffs2_sb_info *c,
|
||||
|
||||
#define PAD(x) (((x)+3)&~3)
|
||||
|
||||
static inline struct jffs2_inode_cache *jffs2_raw_ref_to_ic(struct jffs2_raw_node_ref *raw)
|
||||
static inline int jffs2_encode_dev(union jffs2_device_node *jdev, dev_t rdev)
|
||||
{
|
||||
while(raw->next_in_ino) {
|
||||
raw = raw->next_in_ino;
|
||||
if (old_valid_dev(rdev)) {
|
||||
jdev->old = cpu_to_je16(old_encode_dev(rdev));
|
||||
return sizeof(jdev->old);
|
||||
} else {
|
||||
jdev->new = cpu_to_je32(new_encode_dev(rdev));
|
||||
return sizeof(jdev->new);
|
||||
}
|
||||
|
||||
return ((struct jffs2_inode_cache *)raw);
|
||||
}
|
||||
|
||||
static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
|
||||
@ -324,28 +340,44 @@ void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *t
|
||||
int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn);
|
||||
void jffs2_truncate_fragtree (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size);
|
||||
int jffs2_add_older_frag_to_fragtree(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_tmp_dnode_info *tn);
|
||||
struct jffs2_raw_node_ref *jffs2_link_node_ref(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb,
|
||||
uint32_t ofs, uint32_t len,
|
||||
struct jffs2_inode_cache *ic);
|
||||
extern uint32_t __jffs2_ref_totlen(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_node_ref *ref);
|
||||
|
||||
/* nodemgmt.c */
|
||||
int jffs2_thread_should_wake(struct jffs2_sb_info *c);
|
||||
int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs,
|
||||
int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
|
||||
uint32_t *len, int prio, uint32_t sumsize);
|
||||
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs,
|
||||
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize,
|
||||
uint32_t *len, uint32_t sumsize);
|
||||
int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new);
|
||||
struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info *c,
|
||||
uint32_t ofs, uint32_t len,
|
||||
struct jffs2_inode_cache *ic);
|
||||
void jffs2_complete_reservation(struct jffs2_sb_info *c);
|
||||
void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw);
|
||||
|
||||
/* write.c */
|
||||
int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri);
|
||||
|
||||
struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode);
|
||||
struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode);
|
||||
struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
||||
struct jffs2_raw_inode *ri, const unsigned char *data,
|
||||
uint32_t datalen, int alloc_mode);
|
||||
struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
||||
struct jffs2_raw_dirent *rd, const unsigned char *name,
|
||||
uint32_t namelen, int alloc_mode);
|
||||
int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
|
||||
struct jffs2_raw_inode *ri, unsigned char *buf,
|
||||
uint32_t offset, uint32_t writelen, uint32_t *retlen);
|
||||
int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen);
|
||||
int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f, uint32_t time);
|
||||
int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen, uint32_t time);
|
||||
int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f,
|
||||
struct jffs2_raw_inode *ri, const char *name, int namelen);
|
||||
int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name,
|
||||
int namelen, struct jffs2_inode_info *dead_f, uint32_t time);
|
||||
int jffs2_do_link(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino,
|
||||
uint8_t type, const char *name, int namelen, uint32_t time);
|
||||
|
||||
|
||||
/* readinode.c */
|
||||
@ -368,12 +400,19 @@ struct jffs2_raw_inode *jffs2_alloc_raw_inode(void);
|
||||
void jffs2_free_raw_inode(struct jffs2_raw_inode *);
|
||||
struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void);
|
||||
void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *);
|
||||
struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void);
|
||||
void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *);
|
||||
int jffs2_prealloc_raw_node_refs(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb, int nr);
|
||||
void jffs2_free_refblock(struct jffs2_raw_node_ref *);
|
||||
struct jffs2_node_frag *jffs2_alloc_node_frag(void);
|
||||
void jffs2_free_node_frag(struct jffs2_node_frag *);
|
||||
struct jffs2_inode_cache *jffs2_alloc_inode_cache(void);
|
||||
void jffs2_free_inode_cache(struct jffs2_inode_cache *);
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void);
|
||||
void jffs2_free_xattr_datum(struct jffs2_xattr_datum *);
|
||||
struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void);
|
||||
void jffs2_free_xattr_ref(struct jffs2_xattr_ref *);
|
||||
#endif
|
||||
|
||||
/* gc.c */
|
||||
int jffs2_garbage_collect_pass(struct jffs2_sb_info *c);
|
||||
@ -393,12 +432,14 @@ int jffs2_fill_scan_buf(struct jffs2_sb_info *c, void *buf,
|
||||
uint32_t ofs, uint32_t len);
|
||||
struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino);
|
||||
int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
|
||||
int jffs2_scan_dirty_space(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, uint32_t size);
|
||||
|
||||
/* build.c */
|
||||
int jffs2_do_mount_fs(struct jffs2_sb_info *c);
|
||||
|
||||
/* erase.c */
|
||||
void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count);
|
||||
void jffs2_free_jeb_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
|
||||
/* wbuf.c */
|
||||
|
@ -23,13 +23,12 @@
|
||||
* jffs2_reserve_space - request physical space to write nodes to flash
|
||||
* @c: superblock info
|
||||
* @minsize: Minimum acceptable size of allocation
|
||||
* @ofs: Returned value of node offset
|
||||
* @len: Returned value of allocation length
|
||||
* @prio: Allocation type - ALLOC_{NORMAL,DELETION}
|
||||
*
|
||||
* Requests a block of physical space on the flash. Returns zero for success
|
||||
* and puts 'ofs' and 'len' into the appriopriate place, or returns -ENOSPC
|
||||
* or other error if appropriate.
|
||||
* and puts 'len' into the appropriate place, or returns -ENOSPC or other
|
||||
* error if appropriate. Doesn't return len since that's
|
||||
*
|
||||
* If it returns zero, jffs2_reserve_space() also downs the per-filesystem
|
||||
* allocation semaphore, to prevent more than one allocation from being
|
||||
@ -40,9 +39,9 @@
|
||||
*/
|
||||
|
||||
static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
|
||||
uint32_t *ofs, uint32_t *len, uint32_t sumsize);
|
||||
uint32_t *len, uint32_t sumsize);
|
||||
|
||||
int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs,
|
||||
int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
|
||||
uint32_t *len, int prio, uint32_t sumsize)
|
||||
{
|
||||
int ret = -EAGAIN;
|
||||
@ -132,19 +131,21 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
}
|
||||
|
||||
ret = jffs2_do_reserve_space(c, minsize, ofs, len, sumsize);
|
||||
ret = jffs2_do_reserve_space(c, minsize, len, sumsize);
|
||||
if (ret) {
|
||||
D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret));
|
||||
}
|
||||
}
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
if (!ret)
|
||||
ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
|
||||
if (ret)
|
||||
up(&c->alloc_sem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs,
|
||||
uint32_t *len, uint32_t sumsize)
|
||||
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize,
|
||||
uint32_t *len, uint32_t sumsize)
|
||||
{
|
||||
int ret = -EAGAIN;
|
||||
minsize = PAD(minsize);
|
||||
@ -153,12 +154,15 @@ int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
while(ret == -EAGAIN) {
|
||||
ret = jffs2_do_reserve_space(c, minsize, ofs, len, sumsize);
|
||||
ret = jffs2_do_reserve_space(c, minsize, len, sumsize);
|
||||
if (ret) {
|
||||
D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
|
||||
}
|
||||
}
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
if (!ret)
|
||||
ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -259,10 +263,11 @@ static int jffs2_find_nextblock(struct jffs2_sb_info *c)
|
||||
}
|
||||
|
||||
/* Called with alloc sem _and_ erase_completion_lock */
|
||||
static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, uint32_t sumsize)
|
||||
static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
|
||||
uint32_t *len, uint32_t sumsize)
|
||||
{
|
||||
struct jffs2_eraseblock *jeb = c->nextblock;
|
||||
uint32_t reserved_size; /* for summary information at the end of the jeb */
|
||||
uint32_t reserved_size; /* for summary information at the end of the jeb */
|
||||
int ret;
|
||||
|
||||
restart:
|
||||
@ -312,6 +317,8 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uin
|
||||
}
|
||||
} else {
|
||||
if (jeb && minsize > jeb->free_size) {
|
||||
uint32_t waste;
|
||||
|
||||
/* Skip the end of this block and file it as having some dirty space */
|
||||
/* If there's a pending write to it, flush now */
|
||||
|
||||
@ -324,10 +331,26 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uin
|
||||
goto restart;
|
||||
}
|
||||
|
||||
c->wasted_size += jeb->free_size;
|
||||
c->free_size -= jeb->free_size;
|
||||
jeb->wasted_size += jeb->free_size;
|
||||
jeb->free_size = 0;
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
ret = jffs2_prealloc_raw_node_refs(c, jeb, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* Just lock it again and continue. Nothing much can change because
|
||||
we hold c->alloc_sem anyway. In fact, it's not entirely clear why
|
||||
we hold c->erase_completion_lock in the majority of this function...
|
||||
but that's a question for another (more caffeine-rich) day. */
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
waste = jeb->free_size;
|
||||
jffs2_link_node_ref(c, jeb,
|
||||
(jeb->offset + c->sector_size - waste) | REF_OBSOLETE,
|
||||
waste, NULL);
|
||||
/* FIXME: that made it count as dirty. Convert to wasted */
|
||||
jeb->dirty_size -= waste;
|
||||
c->dirty_size -= waste;
|
||||
jeb->wasted_size += waste;
|
||||
c->wasted_size += waste;
|
||||
|
||||
jffs2_close_nextblock(c, jeb);
|
||||
jeb = NULL;
|
||||
@ -349,7 +372,6 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uin
|
||||
}
|
||||
/* OK, jeb (==c->nextblock) is now pointing at a block which definitely has
|
||||
enough space */
|
||||
*ofs = jeb->offset + (c->sector_size - jeb->free_size);
|
||||
*len = jeb->free_size - reserved_size;
|
||||
|
||||
if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size &&
|
||||
@ -365,7 +387,8 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uin
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
}
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs));
|
||||
D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n",
|
||||
*len, jeb->offset + (c->sector_size - jeb->free_size)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -374,7 +397,6 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uin
|
||||
* @c: superblock info
|
||||
* @new: new node reference to add
|
||||
* @len: length of this physical node
|
||||
* @dirty: dirty flag for new node
|
||||
*
|
||||
* Should only be used to report nodes for which space has been allocated
|
||||
* by jffs2_reserve_space.
|
||||
@ -382,42 +404,30 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uin
|
||||
* Must be called with the alloc_sem held.
|
||||
*/
|
||||
|
||||
int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new)
|
||||
struct jffs2_raw_node_ref *jffs2_add_physical_node_ref(struct jffs2_sb_info *c,
|
||||
uint32_t ofs, uint32_t len,
|
||||
struct jffs2_inode_cache *ic)
|
||||
{
|
||||
struct jffs2_eraseblock *jeb;
|
||||
uint32_t len;
|
||||
struct jffs2_raw_node_ref *new;
|
||||
|
||||
jeb = &c->blocks[new->flash_offset / c->sector_size];
|
||||
len = ref_totlen(c, jeb, new);
|
||||
jeb = &c->blocks[ofs / c->sector_size];
|
||||
|
||||
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));
|
||||
D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n",
|
||||
ofs & ~3, ofs & 3, len));
|
||||
#if 1
|
||||
/* 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))) {
|
||||
/* Allow non-obsolete nodes only to be added at the end of c->nextblock,
|
||||
if c->nextblock is set. Note that wbuf.c will file obsolete nodes
|
||||
even after refiling c->nextblock */
|
||||
if ((c->nextblock || ((ofs & 3) != REF_OBSOLETE))
|
||||
&& (jeb != c->nextblock || (ofs & ~3) != 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;
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
#endif
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
if (!jeb->first_node)
|
||||
jeb->first_node = new;
|
||||
if (jeb->last_node)
|
||||
jeb->last_node->next_phys = new;
|
||||
jeb->last_node = new;
|
||||
|
||||
jeb->free_size -= len;
|
||||
c->free_size -= len;
|
||||
if (ref_obsolete(new)) {
|
||||
jeb->dirty_size += len;
|
||||
c->dirty_size += len;
|
||||
} else {
|
||||
jeb->used_size += len;
|
||||
c->used_size += len;
|
||||
}
|
||||
new = jffs2_link_node_ref(c, jeb, ofs, len, ic);
|
||||
|
||||
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 */
|
||||
@ -438,7 +448,7 @@ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_r
|
||||
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
return 0;
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
@ -470,8 +480,9 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
|
||||
struct jffs2_unknown_node n;
|
||||
int ret, addedsize;
|
||||
size_t retlen;
|
||||
uint32_t freed_len;
|
||||
|
||||
if(!ref) {
|
||||
if(unlikely(!ref)) {
|
||||
printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n");
|
||||
return;
|
||||
}
|
||||
@ -499,32 +510,34 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
freed_len = ref_totlen(c, jeb, ref);
|
||||
|
||||
if (ref_flags(ref) == REF_UNCHECKED) {
|
||||
D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) {
|
||||
D1(if (unlikely(jeb->unchecked_size < freed_len)) {
|
||||
printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n",
|
||||
ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
|
||||
freed_len, blocknr, ref->flash_offset, jeb->used_size);
|
||||
BUG();
|
||||
})
|
||||
D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
|
||||
jeb->unchecked_size -= ref_totlen(c, jeb, ref);
|
||||
c->unchecked_size -= ref_totlen(c, jeb, ref);
|
||||
D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), freed_len));
|
||||
jeb->unchecked_size -= freed_len;
|
||||
c->unchecked_size -= freed_len;
|
||||
} else {
|
||||
D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) {
|
||||
D1(if (unlikely(jeb->used_size < freed_len)) {
|
||||
printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
|
||||
ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
|
||||
freed_len, blocknr, ref->flash_offset, jeb->used_size);
|
||||
BUG();
|
||||
})
|
||||
D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %#x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
|
||||
jeb->used_size -= ref_totlen(c, jeb, ref);
|
||||
c->used_size -= ref_totlen(c, jeb, ref);
|
||||
D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %#x: ", ref_offset(ref), freed_len));
|
||||
jeb->used_size -= freed_len;
|
||||
c->used_size -= freed_len;
|
||||
}
|
||||
|
||||
// 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(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);
|
||||
if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + freed_len)) && jeb != c->nextblock) {
|
||||
D1(printk("Dirtying\n"));
|
||||
addedsize = freed_len;
|
||||
jeb->dirty_size += freed_len;
|
||||
c->dirty_size += freed_len;
|
||||
|
||||
/* Convert wasted space to dirty, if not a bad block */
|
||||
if (jeb->wasted_size) {
|
||||
@ -543,10 +556,10 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
|
||||
}
|
||||
}
|
||||
} else {
|
||||
D1(printk(KERN_DEBUG "Wasting\n"));
|
||||
D1(printk("Wasting\n"));
|
||||
addedsize = 0;
|
||||
jeb->wasted_size += ref_totlen(c, jeb, ref);
|
||||
c->wasted_size += ref_totlen(c, jeb, ref);
|
||||
jeb->wasted_size += freed_len;
|
||||
c->wasted_size += freed_len;
|
||||
}
|
||||
ref->flash_offset = ref_offset(ref) | REF_OBSOLETE;
|
||||
|
||||
@ -622,7 +635,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
|
||||
/* The erase_free_sem is locked, and has been since before we marked the node obsolete
|
||||
and potentially put its eraseblock onto the erase_pending_list. Thus, we know that
|
||||
the block hasn't _already_ been erased, and that 'ref' itself hasn't been freed yet
|
||||
by jffs2_free_all_node_refs() in erase.c. Which is nice. */
|
||||
by jffs2_free_jeb_node_refs() in erase.c. Which is nice. */
|
||||
|
||||
D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref)));
|
||||
ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n);
|
||||
@ -634,8 +647,8 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
|
||||
printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
|
||||
goto out_erase_sem;
|
||||
}
|
||||
if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) {
|
||||
printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref));
|
||||
if (PAD(je32_to_cpu(n.totlen)) != PAD(freed_len)) {
|
||||
printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), freed_len);
|
||||
goto out_erase_sem;
|
||||
}
|
||||
if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) {
|
||||
@ -671,6 +684,10 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
ic = jffs2_raw_ref_to_ic(ref);
|
||||
/* It seems we should never call jffs2_mark_node_obsolete() for
|
||||
XATTR nodes.... yet. Make sure we notice if/when we change
|
||||
that :) */
|
||||
BUG_ON(ic->class != RAWNODE_CLASS_INODE_CACHE);
|
||||
for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino))
|
||||
;
|
||||
|
||||
@ -683,51 +700,6 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
}
|
||||
|
||||
|
||||
/* Merge with the next node in the physical list, if there is one
|
||||
and if it's also obsolete and if it doesn't belong to any inode */
|
||||
if (ref->next_phys && ref_obsolete(ref->next_phys) &&
|
||||
!ref->next_phys->next_in_ino) {
|
||||
struct jffs2_raw_node_ref *n = ref->next_phys;
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
ref->__totlen += n->__totlen;
|
||||
ref->next_phys = n->next_phys;
|
||||
if (jeb->last_node == n) jeb->last_node = ref;
|
||||
if (jeb->gc_node == n) {
|
||||
/* gc will be happy continuing gc on this node */
|
||||
jeb->gc_node=ref;
|
||||
}
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
jffs2_free_raw_node_ref(n);
|
||||
}
|
||||
|
||||
/* Also merge with the previous node in the list, if there is one
|
||||
and that one is obsolete */
|
||||
if (ref != jeb->first_node ) {
|
||||
struct jffs2_raw_node_ref *p = jeb->first_node;
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
while (p->next_phys != ref)
|
||||
p = p->next_phys;
|
||||
|
||||
if (ref_obsolete(p) && !ref->next_in_ino) {
|
||||
p->__totlen += ref->__totlen;
|
||||
if (jeb->last_node == ref) {
|
||||
jeb->last_node = p;
|
||||
}
|
||||
if (jeb->gc_node == ref) {
|
||||
/* gc will be happy continuing gc on this node */
|
||||
jeb->gc_node=p;
|
||||
}
|
||||
p->next_phys = ref->next_phys;
|
||||
jffs2_free_raw_node_ref(ref);
|
||||
}
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
}
|
||||
out_erase_sem:
|
||||
up(&c->erase_free_sem);
|
||||
}
|
||||
|
@ -31,9 +31,7 @@ struct kvec;
|
||||
#define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode)
|
||||
#define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid)
|
||||
#define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid)
|
||||
|
||||
#define JFFS2_F_I_RDEV_MIN(f) (iminor(OFNI_EDONI_2SFFJ(f)))
|
||||
#define JFFS2_F_I_RDEV_MAJ(f) (imajor(OFNI_EDONI_2SFFJ(f)))
|
||||
#define JFFS2_F_I_RDEV(f) (OFNI_EDONI_2SFFJ(f)->i_rdev)
|
||||
|
||||
#define ITIME(sec) ((struct timespec){sec, 0})
|
||||
#define I_SEC(tv) ((tv).tv_sec)
|
||||
@ -60,6 +58,10 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
|
||||
f->target = NULL;
|
||||
f->flags = 0;
|
||||
f->usercompr = 0;
|
||||
#ifdef CONFIG_JFFS2_FS_POSIX_ACL
|
||||
f->i_acl_access = JFFS2_ACL_NOT_CACHED;
|
||||
f->i_acl_default = JFFS2_ACL_NOT_CACHED;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -90,13 +92,10 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
|
||||
#define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e)
|
||||
#define jffs2_wbuf_timeout NULL
|
||||
#define jffs2_wbuf_process NULL
|
||||
#define jffs2_nor_ecc(c) (0)
|
||||
#define jffs2_dataflash(c) (0)
|
||||
#define jffs2_nor_wbuf_flash(c) (0)
|
||||
#define jffs2_nor_ecc_flash_setup(c) (0)
|
||||
#define jffs2_nor_ecc_flash_cleanup(c) do {} while (0)
|
||||
#define jffs2_dataflash_setup(c) (0)
|
||||
#define jffs2_dataflash_cleanup(c) do {} while (0)
|
||||
#define jffs2_nor_wbuf_flash(c) (0)
|
||||
#define jffs2_nor_wbuf_flash_setup(c) (0)
|
||||
#define jffs2_nor_wbuf_flash_cleanup(c) do {} while (0)
|
||||
|
||||
@ -107,9 +106,7 @@ static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
|
||||
#ifdef CONFIG_JFFS2_SUMMARY
|
||||
#define jffs2_can_mark_obsolete(c) (0)
|
||||
#else
|
||||
#define jffs2_can_mark_obsolete(c) \
|
||||
((c->mtd->type == MTD_NORFLASH && !(c->mtd->flags & (MTD_ECC|MTD_PROGRAM_REGIONS))) || \
|
||||
c->mtd->type == MTD_RAM)
|
||||
#define jffs2_can_mark_obsolete(c) (c->mtd->flags & (MTD_BIT_WRITEABLE))
|
||||
#endif
|
||||
|
||||
#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH)
|
||||
@ -133,15 +130,11 @@ 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);
|
||||
|
||||
#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);
|
||||
|
||||
#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);
|
||||
|
||||
#define jffs2_nor_wbuf_flash(c) (c->mtd->type == MTD_NORFLASH && (c->mtd->flags & MTD_PROGRAM_REGIONS))
|
||||
#define jffs2_nor_wbuf_flash(c) (c->mtd->type == MTD_NORFLASH && ! (c->mtd->flags & MTD_BIT_WRITEABLE))
|
||||
int jffs2_nor_wbuf_flash_setup(struct jffs2_sb_info *c);
|
||||
void jffs2_nor_wbuf_flash_cleanup(struct jffs2_sb_info *c);
|
||||
|
||||
|
@ -116,19 +116,42 @@ static inline int read_direntry(struct jffs2_sb_info *c, struct jffs2_raw_node_r
|
||||
uint32_t *latest_mctime, uint32_t *mctime_ver)
|
||||
{
|
||||
struct jffs2_full_dirent *fd;
|
||||
uint32_t crc;
|
||||
|
||||
/* The direntry nodes are checked during the flash scanning */
|
||||
BUG_ON(ref_flags(ref) == REF_UNCHECKED);
|
||||
/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
|
||||
BUG_ON(ref_obsolete(ref));
|
||||
|
||||
/* Sanity check */
|
||||
if (unlikely(PAD((rd->nsize + sizeof(*rd))) != PAD(je32_to_cpu(rd->totlen)))) {
|
||||
JFFS2_ERROR("illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n",
|
||||
ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen));
|
||||
crc = crc32(0, rd, sizeof(*rd) - 8);
|
||||
if (unlikely(crc != je32_to_cpu(rd->node_crc))) {
|
||||
JFFS2_NOTICE("header CRC failed on dirent node at %#08x: read %#08x, calculated %#08x\n",
|
||||
ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* If we've never checked the CRCs on this node, check them now */
|
||||
if (ref_flags(ref) == REF_UNCHECKED) {
|
||||
struct jffs2_eraseblock *jeb;
|
||||
int len;
|
||||
|
||||
/* Sanity check */
|
||||
if (unlikely(PAD((rd->nsize + sizeof(*rd))) != PAD(je32_to_cpu(rd->totlen)))) {
|
||||
JFFS2_ERROR("illegal nsize in node at %#08x: nsize %#02x, totlen %#04x\n",
|
||||
ref_offset(ref), rd->nsize, je32_to_cpu(rd->totlen));
|
||||
return 1;
|
||||
}
|
||||
|
||||
jeb = &c->blocks[ref->flash_offset / c->sector_size];
|
||||
len = ref_totlen(c, jeb, ref);
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
jeb->used_size += len;
|
||||
jeb->unchecked_size -= len;
|
||||
c->used_size += len;
|
||||
c->unchecked_size -= len;
|
||||
ref->flash_offset = ref_offset(ref) | REF_PRISTINE;
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
}
|
||||
|
||||
fd = jffs2_alloc_full_dirent(rd->nsize + 1);
|
||||
if (unlikely(!fd))
|
||||
return -ENOMEM;
|
||||
@ -198,13 +221,21 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
|
||||
struct jffs2_tmp_dnode_info *tn;
|
||||
uint32_t len, csize;
|
||||
int ret = 1;
|
||||
uint32_t crc;
|
||||
|
||||
/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
|
||||
BUG_ON(ref_obsolete(ref));
|
||||
|
||||
crc = crc32(0, rd, sizeof(*rd) - 8);
|
||||
if (unlikely(crc != je32_to_cpu(rd->node_crc))) {
|
||||
JFFS2_NOTICE("node CRC failed on dnode at %#08x: read %#08x, calculated %#08x\n",
|
||||
ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
tn = jffs2_alloc_tmp_dnode_info();
|
||||
if (!tn) {
|
||||
JFFS2_ERROR("failed to allocate tn (%d bytes).\n", sizeof(*tn));
|
||||
JFFS2_ERROR("failed to allocate tn (%zu bytes).\n", sizeof(*tn));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -213,14 +244,6 @@ static inline int read_dnode(struct jffs2_sb_info *c, struct jffs2_raw_node_ref
|
||||
|
||||
/* If we've never checked the CRCs on this node, check them now */
|
||||
if (ref_flags(ref) == REF_UNCHECKED) {
|
||||
uint32_t crc;
|
||||
|
||||
crc = crc32(0, rd, sizeof(*rd) - 8);
|
||||
if (unlikely(crc != je32_to_cpu(rd->node_crc))) {
|
||||
JFFS2_NOTICE("header CRC failed on node at %#08x: read %#08x, calculated %#08x\n",
|
||||
ref_offset(ref), je32_to_cpu(rd->node_crc), crc);
|
||||
goto free_out;
|
||||
}
|
||||
|
||||
/* Sanity checks */
|
||||
if (unlikely(je32_to_cpu(rd->offset) > je32_to_cpu(rd->isize)) ||
|
||||
@ -343,7 +366,7 @@ free_out:
|
||||
* Helper function for jffs2_get_inode_nodes().
|
||||
* It is called every time an unknown node is found.
|
||||
*
|
||||
* Returns: 0 on succes;
|
||||
* Returns: 0 on success;
|
||||
* 1 if the node should be marked obsolete;
|
||||
* negative error code on failure.
|
||||
*/
|
||||
@ -354,37 +377,30 @@ static inline int read_unknown(struct jffs2_sb_info *c, struct jffs2_raw_node_re
|
||||
|
||||
un->nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(un->nodetype));
|
||||
|
||||
if (crc32(0, un, sizeof(struct jffs2_unknown_node) - 4) != je32_to_cpu(un->hdr_crc)) {
|
||||
/* Hmmm. This should have been caught at scan time. */
|
||||
JFFS2_NOTICE("node header CRC failed at %#08x. But it must have been OK earlier.\n", ref_offset(ref));
|
||||
jffs2_dbg_dump_node(c, ref_offset(ref));
|
||||
switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) {
|
||||
|
||||
case JFFS2_FEATURE_INCOMPAT:
|
||||
JFFS2_ERROR("unknown INCOMPAT nodetype %#04X at %#08x\n",
|
||||
je16_to_cpu(un->nodetype), ref_offset(ref));
|
||||
/* EEP */
|
||||
BUG();
|
||||
break;
|
||||
|
||||
case JFFS2_FEATURE_ROCOMPAT:
|
||||
JFFS2_ERROR("unknown ROCOMPAT nodetype %#04X at %#08x\n",
|
||||
je16_to_cpu(un->nodetype), ref_offset(ref));
|
||||
BUG_ON(!(c->flags & JFFS2_SB_FLAG_RO));
|
||||
break;
|
||||
|
||||
case JFFS2_FEATURE_RWCOMPAT_COPY:
|
||||
JFFS2_NOTICE("unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n",
|
||||
je16_to_cpu(un->nodetype), ref_offset(ref));
|
||||
break;
|
||||
|
||||
case JFFS2_FEATURE_RWCOMPAT_DELETE:
|
||||
JFFS2_NOTICE("unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n",
|
||||
je16_to_cpu(un->nodetype), ref_offset(ref));
|
||||
return 1;
|
||||
} else {
|
||||
switch(je16_to_cpu(un->nodetype) & JFFS2_COMPAT_MASK) {
|
||||
|
||||
case JFFS2_FEATURE_INCOMPAT:
|
||||
JFFS2_ERROR("unknown INCOMPAT nodetype %#04X at %#08x\n",
|
||||
je16_to_cpu(un->nodetype), ref_offset(ref));
|
||||
/* EEP */
|
||||
BUG();
|
||||
break;
|
||||
|
||||
case JFFS2_FEATURE_ROCOMPAT:
|
||||
JFFS2_ERROR("unknown ROCOMPAT nodetype %#04X at %#08x\n",
|
||||
je16_to_cpu(un->nodetype), ref_offset(ref));
|
||||
BUG_ON(!(c->flags & JFFS2_SB_FLAG_RO));
|
||||
break;
|
||||
|
||||
case JFFS2_FEATURE_RWCOMPAT_COPY:
|
||||
JFFS2_NOTICE("unknown RWCOMPAT_COPY nodetype %#04X at %#08x\n",
|
||||
je16_to_cpu(un->nodetype), ref_offset(ref));
|
||||
break;
|
||||
|
||||
case JFFS2_FEATURE_RWCOMPAT_DELETE:
|
||||
JFFS2_NOTICE("unknown RWCOMPAT_DELETE nodetype %#04X at %#08x\n",
|
||||
je16_to_cpu(un->nodetype), ref_offset(ref));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -434,7 +450,7 @@ static int read_more(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref,
|
||||
}
|
||||
|
||||
if (retlen < len) {
|
||||
JFFS2_ERROR("short read at %#08x: %d instead of %d.\n",
|
||||
JFFS2_ERROR("short read at %#08x: %zu instead of %d.\n",
|
||||
offs, retlen, len);
|
||||
return -EIO;
|
||||
}
|
||||
@ -542,13 +558,25 @@ static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_inf
|
||||
}
|
||||
|
||||
if (retlen < len) {
|
||||
JFFS2_ERROR("short read at %#08x: %d instead of %d.\n", ref_offset(ref), retlen, len);
|
||||
JFFS2_ERROR("short read at %#08x: %zu instead of %d.\n", ref_offset(ref), retlen, len);
|
||||
err = -EIO;
|
||||
goto free_out;
|
||||
}
|
||||
|
||||
node = (union jffs2_node_union *)bufstart;
|
||||
|
||||
/* No need to mask in the valid bit; it shouldn't be invalid */
|
||||
if (je32_to_cpu(node->u.hdr_crc) != crc32(0, node, sizeof(node->u)-4)) {
|
||||
JFFS2_NOTICE("Node header CRC failed at %#08x. {%04x,%04x,%08x,%08x}\n",
|
||||
ref_offset(ref), je16_to_cpu(node->u.magic),
|
||||
je16_to_cpu(node->u.nodetype),
|
||||
je32_to_cpu(node->u.totlen),
|
||||
je32_to_cpu(node->u.hdr_crc));
|
||||
jffs2_dbg_dump_node(c, ref_offset(ref));
|
||||
jffs2_mark_node_obsolete(c, ref);
|
||||
goto cont;
|
||||
}
|
||||
|
||||
switch (je16_to_cpu(node->u.nodetype)) {
|
||||
|
||||
case JFFS2_NODETYPE_DIRENT:
|
||||
@ -606,6 +634,7 @@ static int jffs2_get_inode_nodes(struct jffs2_sb_info *c, struct jffs2_inode_inf
|
||||
goto free_out;
|
||||
|
||||
}
|
||||
cont:
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
}
|
||||
|
||||
|
444
fs/jffs2/scan.c
444
fs/jffs2/scan.c
@ -65,6 +65,28 @@ static inline uint32_t EMPTY_SCAN_SIZE(uint32_t sector_size) {
|
||||
return DEFAULT_EMPTY_SCAN_SIZE;
|
||||
}
|
||||
|
||||
static int file_dirty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((ret = jffs2_prealloc_raw_node_refs(c, jeb, 1)))
|
||||
return ret;
|
||||
if ((ret = jffs2_scan_dirty_space(c, jeb, jeb->free_size)))
|
||||
return ret;
|
||||
/* Turned wasted size into dirty, since we apparently
|
||||
think it's recoverable now. */
|
||||
jeb->dirty_size += jeb->wasted_size;
|
||||
c->dirty_size += jeb->wasted_size;
|
||||
c->wasted_size -= jeb->wasted_size;
|
||||
jeb->wasted_size = 0;
|
||||
if (VERYDIRTY(c, jeb->dirty_size)) {
|
||||
list_add(&jeb->list, &c->very_dirty_list);
|
||||
} else {
|
||||
list_add(&jeb->list, &c->dirty_list);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int jffs2_scan_medium(struct jffs2_sb_info *c)
|
||||
{
|
||||
int i, ret;
|
||||
@ -170,34 +192,20 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
|
||||
(!c->nextblock || c->nextblock->free_size < jeb->free_size)) {
|
||||
/* Better candidate for the next writes to go to */
|
||||
if (c->nextblock) {
|
||||
c->nextblock->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size;
|
||||
c->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size;
|
||||
c->free_size -= c->nextblock->free_size;
|
||||
c->wasted_size -= c->nextblock->wasted_size;
|
||||
c->nextblock->free_size = c->nextblock->wasted_size = 0;
|
||||
if (VERYDIRTY(c, c->nextblock->dirty_size)) {
|
||||
list_add(&c->nextblock->list, &c->very_dirty_list);
|
||||
} else {
|
||||
list_add(&c->nextblock->list, &c->dirty_list);
|
||||
}
|
||||
ret = file_dirty(c, c->nextblock);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* deleting summary information of the old nextblock */
|
||||
jffs2_sum_reset_collected(c->summary);
|
||||
}
|
||||
/* update collected summary infromation for the current nextblock */
|
||||
/* update collected summary information for the current nextblock */
|
||||
jffs2_sum_move_collected(c, s);
|
||||
D1(printk(KERN_DEBUG "jffs2_scan_medium(): new nextblock = 0x%08x\n", jeb->offset));
|
||||
c->nextblock = jeb;
|
||||
} else {
|
||||
jeb->dirty_size += jeb->free_size + jeb->wasted_size;
|
||||
c->dirty_size += jeb->free_size + jeb->wasted_size;
|
||||
c->free_size -= jeb->free_size;
|
||||
c->wasted_size -= jeb->wasted_size;
|
||||
jeb->free_size = jeb->wasted_size = 0;
|
||||
if (VERYDIRTY(c, jeb->dirty_size)) {
|
||||
list_add(&jeb->list, &c->very_dirty_list);
|
||||
} else {
|
||||
list_add(&jeb->list, &c->dirty_list);
|
||||
}
|
||||
ret = file_dirty(c, jeb);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -222,9 +230,6 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
|
||||
}
|
||||
}
|
||||
|
||||
if (jffs2_sum_active() && s)
|
||||
kfree(s);
|
||||
|
||||
/* Nextblock dirty is always seen as wasted, because we cannot recycle it now */
|
||||
if (c->nextblock && (c->nextblock->dirty_size)) {
|
||||
c->nextblock->wasted_size += c->nextblock->dirty_size;
|
||||
@ -242,11 +247,8 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n",
|
||||
skip));
|
||||
c->nextblock->wasted_size += skip;
|
||||
c->wasted_size += skip;
|
||||
|
||||
c->nextblock->free_size -= skip;
|
||||
c->free_size -= skip;
|
||||
jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
|
||||
jffs2_scan_dirty_space(c, c->nextblock, skip);
|
||||
}
|
||||
#endif
|
||||
if (c->nr_erasing_blocks) {
|
||||
@ -266,6 +268,9 @@ int jffs2_scan_medium(struct jffs2_sb_info *c)
|
||||
else
|
||||
c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
|
||||
#endif
|
||||
if (s)
|
||||
kfree(s);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -290,7 +295,7 @@ int jffs2_fill_scan_buf (struct jffs2_sb_info *c, void *buf,
|
||||
int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
|
||||
{
|
||||
if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size
|
||||
&& (!jeb->first_node || !jeb->first_node->next_phys) )
|
||||
&& (!jeb->first_node || !ref_next(jeb->first_node)) )
|
||||
return BLK_STATE_CLEANMARKER;
|
||||
|
||||
/* move blocks with max 4 byte dirty space to cleanlist */
|
||||
@ -306,11 +311,119 @@ int jffs2_scan_classify_jeb(struct jffs2_sb_info *c, struct jffs2_eraseblock *je
|
||||
return BLK_STATE_ALLDIRTY;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
static int jffs2_scan_xattr_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_xattr *rx, uint32_t ofs,
|
||||
struct jffs2_summary *s)
|
||||
{
|
||||
struct jffs2_xattr_datum *xd;
|
||||
uint32_t totlen, crc;
|
||||
int err;
|
||||
|
||||
crc = crc32(0, rx, sizeof(struct jffs2_raw_xattr) - 4);
|
||||
if (crc != je32_to_cpu(rx->node_crc)) {
|
||||
if (je32_to_cpu(rx->node_crc) != 0xffffffff)
|
||||
JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
|
||||
ofs, je32_to_cpu(rx->node_crc), crc);
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(rx->totlen))))
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
totlen = PAD(sizeof(*rx) + rx->name_len + 1 + je16_to_cpu(rx->value_len));
|
||||
if (totlen != je32_to_cpu(rx->totlen)) {
|
||||
JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%u\n",
|
||||
ofs, je32_to_cpu(rx->totlen), totlen);
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(rx->totlen))))
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
xd = jffs2_setup_xattr_datum(c, je32_to_cpu(rx->xid), je32_to_cpu(rx->version));
|
||||
if (IS_ERR(xd)) {
|
||||
if (PTR_ERR(xd) == -EEXIST) {
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rx->totlen)))))
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
return PTR_ERR(xd);
|
||||
}
|
||||
xd->xprefix = rx->xprefix;
|
||||
xd->name_len = rx->name_len;
|
||||
xd->value_len = je16_to_cpu(rx->value_len);
|
||||
xd->data_crc = je32_to_cpu(rx->data_crc);
|
||||
|
||||
xd->node = jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, totlen, NULL);
|
||||
/* FIXME */ xd->node->next_in_ino = (void *)xd;
|
||||
|
||||
if (jffs2_sum_active())
|
||||
jffs2_sum_add_xattr_mem(s, rx, ofs - jeb->offset);
|
||||
dbg_xattr("scaning xdatum at %#08x (xid=%u, version=%u)\n",
|
||||
ofs, xd->xid, xd->version);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jffs2_scan_xref_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_xref *rr, uint32_t ofs,
|
||||
struct jffs2_summary *s)
|
||||
{
|
||||
struct jffs2_xattr_ref *ref;
|
||||
uint32_t crc;
|
||||
int err;
|
||||
|
||||
crc = crc32(0, rr, sizeof(*rr) - 4);
|
||||
if (crc != je32_to_cpu(rr->node_crc)) {
|
||||
if (je32_to_cpu(rr->node_crc) != 0xffffffff)
|
||||
JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
|
||||
ofs, je32_to_cpu(rr->node_crc), crc);
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rr->totlen)))))
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (PAD(sizeof(struct jffs2_raw_xref)) != je32_to_cpu(rr->totlen)) {
|
||||
JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%zd\n",
|
||||
ofs, je32_to_cpu(rr->totlen),
|
||||
PAD(sizeof(struct jffs2_raw_xref)));
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(rr->totlen))))
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ref = jffs2_alloc_xattr_ref();
|
||||
if (!ref)
|
||||
return -ENOMEM;
|
||||
|
||||
/* BEFORE jffs2_build_xattr_subsystem() called,
|
||||
* ref->xid is used to store 32bit xid, xd is not used
|
||||
* ref->ino is used to store 32bit inode-number, ic is not used
|
||||
* Thoes variables are declared as union, thus using those
|
||||
* are exclusive. In a similar way, ref->next is temporarily
|
||||
* used to chain all xattr_ref object. It's re-chained to
|
||||
* jffs2_inode_cache in jffs2_build_xattr_subsystem() correctly.
|
||||
*/
|
||||
ref->ino = je32_to_cpu(rr->ino);
|
||||
ref->xid = je32_to_cpu(rr->xid);
|
||||
ref->next = c->xref_temp;
|
||||
c->xref_temp = ref;
|
||||
|
||||
ref->node = jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(rr->totlen)), NULL);
|
||||
/* FIXME */ ref->node->next_in_ino = (void *)ref;
|
||||
|
||||
if (jffs2_sum_active())
|
||||
jffs2_sum_add_xref_mem(s, rr, ofs - jeb->offset);
|
||||
dbg_xattr("scan xref at %#08x (xid=%u, ino=%u)\n",
|
||||
ofs, ref->xid, ref->ino);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Called with 'buf_size == 0' if buf is in fact a pointer _directly_ into
|
||||
the flash, XIP-style */
|
||||
static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) {
|
||||
unsigned char *buf, uint32_t buf_size, struct jffs2_summary *s) {
|
||||
struct jffs2_unknown_node *node;
|
||||
struct jffs2_unknown_node crcnode;
|
||||
struct jffs2_sum_marker *sm;
|
||||
uint32_t ofs, prevofs;
|
||||
uint32_t hdr_crc, buf_ofs, buf_len;
|
||||
int err;
|
||||
@ -344,44 +457,75 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
|
||||
#endif
|
||||
|
||||
if (jffs2_sum_active()) {
|
||||
sm = kmalloc(sizeof(struct jffs2_sum_marker), GFP_KERNEL);
|
||||
if (!sm) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = jffs2_fill_scan_buf(c, (unsigned char *) sm, jeb->offset + c->sector_size -
|
||||
sizeof(struct jffs2_sum_marker), sizeof(struct jffs2_sum_marker));
|
||||
if (err) {
|
||||
kfree(sm);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC ) {
|
||||
err = jffs2_sum_scan_sumnode(c, jeb, je32_to_cpu(sm->offset), &pseudo_random);
|
||||
if (err) {
|
||||
kfree(sm);
|
||||
return err;
|
||||
struct jffs2_sum_marker *sm;
|
||||
void *sumptr = NULL;
|
||||
uint32_t sumlen;
|
||||
|
||||
if (!buf_size) {
|
||||
/* XIP case. Just look, point at the summary if it's there */
|
||||
sm = (void *)buf + c->sector_size - sizeof(*sm);
|
||||
if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC) {
|
||||
sumptr = buf + je32_to_cpu(sm->offset);
|
||||
sumlen = c->sector_size - je32_to_cpu(sm->offset);
|
||||
}
|
||||
} else {
|
||||
/* If NAND flash, read a whole page of it. Else just the end */
|
||||
if (c->wbuf_pagesize)
|
||||
buf_len = c->wbuf_pagesize;
|
||||
else
|
||||
buf_len = sizeof(*sm);
|
||||
|
||||
/* Read as much as we want into the _end_ of the preallocated buffer */
|
||||
err = jffs2_fill_scan_buf(c, buf + buf_size - buf_len,
|
||||
jeb->offset + c->sector_size - buf_len,
|
||||
buf_len);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
sm = (void *)buf + buf_size - sizeof(*sm);
|
||||
if (je32_to_cpu(sm->magic) == JFFS2_SUM_MAGIC) {
|
||||
sumlen = c->sector_size - je32_to_cpu(sm->offset);
|
||||
sumptr = buf + buf_size - sumlen;
|
||||
|
||||
/* Now, make sure the summary itself is available */
|
||||
if (sumlen > buf_size) {
|
||||
/* Need to kmalloc for this. */
|
||||
sumptr = kmalloc(sumlen, GFP_KERNEL);
|
||||
if (!sumptr)
|
||||
return -ENOMEM;
|
||||
memcpy(sumptr + sumlen - buf_len, buf + buf_size - buf_len, buf_len);
|
||||
}
|
||||
if (buf_len < sumlen) {
|
||||
/* Need to read more so that the entire summary node is present */
|
||||
err = jffs2_fill_scan_buf(c, sumptr,
|
||||
jeb->offset + c->sector_size - sumlen,
|
||||
sumlen - buf_len);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
kfree(sm);
|
||||
if (sumptr) {
|
||||
err = jffs2_sum_scan_sumnode(c, jeb, sumptr, sumlen, &pseudo_random);
|
||||
|
||||
ofs = jeb->offset;
|
||||
prevofs = jeb->offset - 1;
|
||||
if (buf_size && sumlen > buf_size)
|
||||
kfree(sumptr);
|
||||
/* If it returns with a real error, bail.
|
||||
If it returns positive, that's a block classification
|
||||
(i.e. BLK_STATE_xxx) so return that too.
|
||||
If it returns zero, fall through to full scan. */
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
buf_ofs = jeb->offset;
|
||||
|
||||
if (!buf_size) {
|
||||
/* This is the XIP case -- we're reading _directly_ from the flash chip */
|
||||
buf_len = c->sector_size;
|
||||
|
||||
if (jffs2_sum_active()) {
|
||||
/* must reread because of summary test */
|
||||
err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
} else {
|
||||
buf_len = EMPTY_SCAN_SIZE(c->sector_size);
|
||||
err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
|
||||
@ -418,7 +562,10 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo
|
||||
if (ofs) {
|
||||
D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset,
|
||||
jeb->offset + ofs));
|
||||
DIRTY_SPACE(ofs);
|
||||
if ((err = jffs2_prealloc_raw_node_refs(c, jeb, 1)))
|
||||
return err;
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, ofs)))
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Now ofs is a complete physical flash offset as it always was... */
|
||||
@ -433,6 +580,11 @@ scan_more:
|
||||
|
||||
jffs2_dbg_acct_paranoia_check_nolock(c, jeb);
|
||||
|
||||
/* Make sure there are node refs available for use */
|
||||
err = jffs2_prealloc_raw_node_refs(c, jeb, 2);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
cond_resched();
|
||||
|
||||
if (ofs & 3) {
|
||||
@ -442,7 +594,8 @@ scan_more:
|
||||
}
|
||||
if (ofs == prevofs) {
|
||||
printk(KERN_WARNING "ofs 0x%08x has already been seen. Skipping\n", ofs);
|
||||
DIRTY_SPACE(4);
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
|
||||
return err;
|
||||
ofs += 4;
|
||||
continue;
|
||||
}
|
||||
@ -451,7 +604,8 @@ scan_more:
|
||||
if (jeb->offset + c->sector_size < ofs + sizeof(*node)) {
|
||||
D1(printk(KERN_DEBUG "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n", sizeof(struct jffs2_unknown_node),
|
||||
jeb->offset, c->sector_size, ofs, sizeof(*node)));
|
||||
DIRTY_SPACE((jeb->offset + c->sector_size)-ofs);
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, (jeb->offset + c->sector_size)-ofs)))
|
||||
return err;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -481,7 +635,8 @@ scan_more:
|
||||
if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) {
|
||||
printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n",
|
||||
empty_start, ofs);
|
||||
DIRTY_SPACE(ofs-empty_start);
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, ofs-empty_start)))
|
||||
return err;
|
||||
goto scan_more;
|
||||
}
|
||||
|
||||
@ -494,7 +649,7 @@ 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_phys) {
|
||||
c->cleanmarker_size && !jeb->dirty_size && !ref_next(jeb->first_node)) {
|
||||
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;
|
||||
}
|
||||
@ -518,20 +673,23 @@ scan_more:
|
||||
|
||||
if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) {
|
||||
printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs);
|
||||
DIRTY_SPACE(4);
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
|
||||
return err;
|
||||
ofs += 4;
|
||||
continue;
|
||||
}
|
||||
if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) {
|
||||
D1(printk(KERN_DEBUG "Dirty bitmask at 0x%08x\n", ofs));
|
||||
DIRTY_SPACE(4);
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
|
||||
return err;
|
||||
ofs += 4;
|
||||
continue;
|
||||
}
|
||||
if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) {
|
||||
printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs);
|
||||
printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n");
|
||||
DIRTY_SPACE(4);
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
|
||||
return err;
|
||||
ofs += 4;
|
||||
continue;
|
||||
}
|
||||
@ -540,7 +698,8 @@ scan_more:
|
||||
noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n",
|
||||
JFFS2_MAGIC_BITMASK, ofs,
|
||||
je16_to_cpu(node->magic));
|
||||
DIRTY_SPACE(4);
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
|
||||
return err;
|
||||
ofs += 4;
|
||||
continue;
|
||||
}
|
||||
@ -557,7 +716,8 @@ scan_more:
|
||||
je32_to_cpu(node->totlen),
|
||||
je32_to_cpu(node->hdr_crc),
|
||||
hdr_crc);
|
||||
DIRTY_SPACE(4);
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
|
||||
return err;
|
||||
ofs += 4;
|
||||
continue;
|
||||
}
|
||||
@ -568,7 +728,8 @@ scan_more:
|
||||
printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n",
|
||||
ofs, je32_to_cpu(node->totlen));
|
||||
printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n");
|
||||
DIRTY_SPACE(4);
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, 4)))
|
||||
return err;
|
||||
ofs += 4;
|
||||
continue;
|
||||
}
|
||||
@ -576,7 +737,8 @@ scan_more:
|
||||
if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) {
|
||||
/* Wheee. This is an obsoleted node */
|
||||
D2(printk(KERN_DEBUG "Node at 0x%08x is obsolete. Skipping\n", ofs));
|
||||
DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
|
||||
return err;
|
||||
ofs += PAD(je32_to_cpu(node->totlen));
|
||||
continue;
|
||||
}
|
||||
@ -614,30 +776,59 @@ scan_more:
|
||||
ofs += PAD(je32_to_cpu(node->totlen));
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
case JFFS2_NODETYPE_XATTR:
|
||||
if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
|
||||
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
|
||||
D1(printk(KERN_DEBUG "Fewer than %d bytes (xattr node)"
|
||||
" left to end of buf. Reading 0x%x at 0x%08x\n",
|
||||
je32_to_cpu(node->totlen), buf_len, ofs));
|
||||
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
|
||||
if (err)
|
||||
return err;
|
||||
buf_ofs = ofs;
|
||||
node = (void *)buf;
|
||||
}
|
||||
err = jffs2_scan_xattr_node(c, jeb, (void *)node, ofs, s);
|
||||
if (err)
|
||||
return err;
|
||||
ofs += PAD(je32_to_cpu(node->totlen));
|
||||
break;
|
||||
case JFFS2_NODETYPE_XREF:
|
||||
if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
|
||||
buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
|
||||
D1(printk(KERN_DEBUG "Fewer than %d bytes (xref node)"
|
||||
" left to end of buf. Reading 0x%x at 0x%08x\n",
|
||||
je32_to_cpu(node->totlen), buf_len, ofs));
|
||||
err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
|
||||
if (err)
|
||||
return err;
|
||||
buf_ofs = ofs;
|
||||
node = (void *)buf;
|
||||
}
|
||||
err = jffs2_scan_xref_node(c, jeb, (void *)node, ofs, s);
|
||||
if (err)
|
||||
return err;
|
||||
ofs += PAD(je32_to_cpu(node->totlen));
|
||||
break;
|
||||
#endif /* CONFIG_JFFS2_FS_XATTR */
|
||||
|
||||
case JFFS2_NODETYPE_CLEANMARKER:
|
||||
D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs));
|
||||
if (je32_to_cpu(node->totlen) != c->cleanmarker_size) {
|
||||
printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n",
|
||||
ofs, je32_to_cpu(node->totlen), c->cleanmarker_size);
|
||||
DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(sizeof(struct jffs2_unknown_node)))))
|
||||
return err;
|
||||
ofs += PAD(sizeof(struct jffs2_unknown_node));
|
||||
} else if (jeb->first_node) {
|
||||
printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset);
|
||||
DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(sizeof(struct jffs2_unknown_node)))))
|
||||
return err;
|
||||
ofs += PAD(sizeof(struct jffs2_unknown_node));
|
||||
} else {
|
||||
struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref();
|
||||
if (!marker_ref) {
|
||||
printk(KERN_NOTICE "Failed to allocate node ref for clean marker\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
marker_ref->next_in_ino = NULL;
|
||||
marker_ref->next_phys = NULL;
|
||||
marker_ref->flash_offset = ofs | REF_NORMAL;
|
||||
marker_ref->__totlen = c->cleanmarker_size;
|
||||
jeb->first_node = jeb->last_node = marker_ref;
|
||||
jffs2_link_node_ref(c, jeb, ofs | REF_NORMAL, c->cleanmarker_size, NULL);
|
||||
|
||||
USED_SPACE(PAD(c->cleanmarker_size));
|
||||
ofs += PAD(c->cleanmarker_size);
|
||||
}
|
||||
break;
|
||||
@ -645,7 +836,8 @@ scan_more:
|
||||
case JFFS2_NODETYPE_PADDING:
|
||||
if (jffs2_sum_active())
|
||||
jffs2_sum_add_padding_mem(s, je32_to_cpu(node->totlen));
|
||||
DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
|
||||
return err;
|
||||
ofs += PAD(je32_to_cpu(node->totlen));
|
||||
break;
|
||||
|
||||
@ -656,7 +848,8 @@ scan_more:
|
||||
c->flags |= JFFS2_SB_FLAG_RO;
|
||||
if (!(jffs2_is_readonly(c)))
|
||||
return -EROFS;
|
||||
DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
|
||||
return err;
|
||||
ofs += PAD(je32_to_cpu(node->totlen));
|
||||
break;
|
||||
|
||||
@ -666,15 +859,21 @@ scan_more:
|
||||
|
||||
case JFFS2_FEATURE_RWCOMPAT_DELETE:
|
||||
D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
|
||||
DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(node->totlen)))))
|
||||
return err;
|
||||
ofs += PAD(je32_to_cpu(node->totlen));
|
||||
break;
|
||||
|
||||
case JFFS2_FEATURE_RWCOMPAT_COPY:
|
||||
case JFFS2_FEATURE_RWCOMPAT_COPY: {
|
||||
D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
|
||||
USED_SPACE(PAD(je32_to_cpu(node->totlen)));
|
||||
|
||||
jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(node->totlen)), NULL);
|
||||
|
||||
/* We can't summarise nodes we don't grok */
|
||||
jffs2_sum_disable_collecting(s);
|
||||
ofs += PAD(je32_to_cpu(node->totlen));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -687,9 +886,9 @@ scan_more:
|
||||
}
|
||||
}
|
||||
|
||||
D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x\n", jeb->offset,
|
||||
jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size));
|
||||
|
||||
D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x, wasted 0x%08x\n",
|
||||
jeb->offset,jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size, jeb->wasted_size));
|
||||
|
||||
/* mark_node_obsolete can add to wasted !! */
|
||||
if (jeb->wasted_size) {
|
||||
jeb->dirty_size += jeb->wasted_size;
|
||||
@ -730,9 +929,9 @@ struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uin
|
||||
static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_inode *ri, uint32_t ofs, struct jffs2_summary *s)
|
||||
{
|
||||
struct jffs2_raw_node_ref *raw;
|
||||
struct jffs2_inode_cache *ic;
|
||||
uint32_t ino = je32_to_cpu(ri->ino);
|
||||
int err;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs));
|
||||
|
||||
@ -745,12 +944,6 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc
|
||||
Which means that the _full_ amount of time to get to proper write mode with GC
|
||||
operational may actually be _longer_ than before. Sucks to be me. */
|
||||
|
||||
raw = jffs2_alloc_raw_node_ref();
|
||||
if (!raw) {
|
||||
printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of node reference failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ic = jffs2_get_ino_cache(c, ino);
|
||||
if (!ic) {
|
||||
/* Inocache get failed. Either we read a bogus ino# or it's just genuinely the
|
||||
@ -762,30 +955,17 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc
|
||||
printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
|
||||
ofs, je32_to_cpu(ri->node_crc), crc);
|
||||
/* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
|
||||
DIRTY_SPACE(PAD(je32_to_cpu(ri->totlen)));
|
||||
jffs2_free_raw_node_ref(raw);
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(ri->totlen)))))
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
ic = jffs2_scan_make_ino_cache(c, ino);
|
||||
if (!ic) {
|
||||
jffs2_free_raw_node_ref(raw);
|
||||
if (!ic)
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wheee. It worked */
|
||||
|
||||
raw->flash_offset = ofs | REF_UNCHECKED;
|
||||
raw->__totlen = PAD(je32_to_cpu(ri->totlen));
|
||||
raw->next_phys = NULL;
|
||||
raw->next_in_ino = ic->nodes;
|
||||
|
||||
ic->nodes = raw;
|
||||
if (!jeb->first_node)
|
||||
jeb->first_node = raw;
|
||||
if (jeb->last_node)
|
||||
jeb->last_node->next_phys = raw;
|
||||
jeb->last_node = raw;
|
||||
jffs2_link_node_ref(c, jeb, ofs | REF_UNCHECKED, PAD(je32_to_cpu(ri->totlen)), ic);
|
||||
|
||||
D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n",
|
||||
je32_to_cpu(ri->ino), je32_to_cpu(ri->version),
|
||||
@ -794,8 +974,6 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc
|
||||
|
||||
pseudo_random += je32_to_cpu(ri->version);
|
||||
|
||||
UNCHECKED_SPACE(PAD(je32_to_cpu(ri->totlen)));
|
||||
|
||||
if (jffs2_sum_active()) {
|
||||
jffs2_sum_add_inode_mem(s, ri, ofs - jeb->offset);
|
||||
}
|
||||
@ -806,10 +984,10 @@ static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_erasebloc
|
||||
static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_dirent *rd, uint32_t ofs, struct jffs2_summary *s)
|
||||
{
|
||||
struct jffs2_raw_node_ref *raw;
|
||||
struct jffs2_full_dirent *fd;
|
||||
struct jffs2_inode_cache *ic;
|
||||
uint32_t crc;
|
||||
int err;
|
||||
|
||||
D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs));
|
||||
|
||||
@ -821,7 +999,8 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
|
||||
printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
|
||||
ofs, je32_to_cpu(rd->node_crc), crc);
|
||||
/* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
|
||||
DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen)));
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rd->totlen)))))
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -842,40 +1021,23 @@ static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblo
|
||||
jffs2_free_full_dirent(fd);
|
||||
/* FIXME: Why do we believe totlen? */
|
||||
/* We believe totlen because the CRC on the node _header_ was OK, just the name failed. */
|
||||
DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen)));
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rd->totlen)))))
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
raw = jffs2_alloc_raw_node_ref();
|
||||
if (!raw) {
|
||||
jffs2_free_full_dirent(fd);
|
||||
printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino));
|
||||
if (!ic) {
|
||||
jffs2_free_full_dirent(fd);
|
||||
jffs2_free_raw_node_ref(raw);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
raw->__totlen = PAD(je32_to_cpu(rd->totlen));
|
||||
raw->flash_offset = ofs | REF_PRISTINE;
|
||||
raw->next_phys = NULL;
|
||||
raw->next_in_ino = ic->nodes;
|
||||
ic->nodes = raw;
|
||||
if (!jeb->first_node)
|
||||
jeb->first_node = raw;
|
||||
if (jeb->last_node)
|
||||
jeb->last_node->next_phys = raw;
|
||||
jeb->last_node = raw;
|
||||
fd->raw = jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(rd->totlen)), ic);
|
||||
|
||||
fd->raw = raw;
|
||||
fd->next = NULL;
|
||||
fd->version = je32_to_cpu(rd->version);
|
||||
fd->ino = je32_to_cpu(rd->ino);
|
||||
fd->nhash = full_name_hash(fd->name, rd->nsize);
|
||||
fd->type = rd->type;
|
||||
USED_SPACE(PAD(je32_to_cpu(rd->totlen)));
|
||||
jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
|
||||
|
||||
if (jffs2_sum_active()) {
|
||||
|
82
fs/jffs2/security.c
Normal file
82
fs/jffs2/security.c
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* JFFS2 -- Journalling Flash File System, Version 2.
|
||||
*
|
||||
* Copyright (C) 2006 NEC Corporation
|
||||
*
|
||||
* Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/jffs2.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/security.h>
|
||||
#include "nodelist.h"
|
||||
|
||||
/* ---- Initial Security Label Attachment -------------- */
|
||||
int jffs2_init_security(struct inode *inode, struct inode *dir)
|
||||
{
|
||||
int rc;
|
||||
size_t len;
|
||||
void *value;
|
||||
char *name;
|
||||
|
||||
rc = security_inode_init_security(inode, dir, &name, &value, &len);
|
||||
if (rc) {
|
||||
if (rc == -EOPNOTSUPP)
|
||||
return 0;
|
||||
return rc;
|
||||
}
|
||||
rc = do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, value, len, 0);
|
||||
|
||||
kfree(name);
|
||||
kfree(value);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* ---- XATTR Handler for "security.*" ----------------- */
|
||||
static int jffs2_security_getxattr(struct inode *inode, const char *name,
|
||||
void *buffer, size_t size)
|
||||
{
|
||||
if (!strcmp(name, ""))
|
||||
return -EINVAL;
|
||||
|
||||
return do_jffs2_getxattr(inode, JFFS2_XPREFIX_SECURITY, name, buffer, size);
|
||||
}
|
||||
|
||||
static int jffs2_security_setxattr(struct inode *inode, const char *name, const void *buffer,
|
||||
size_t size, int flags)
|
||||
{
|
||||
if (!strcmp(name, ""))
|
||||
return -EINVAL;
|
||||
|
||||
return do_jffs2_setxattr(inode, JFFS2_XPREFIX_SECURITY, name, buffer, size, flags);
|
||||
}
|
||||
|
||||
static size_t jffs2_security_listxattr(struct inode *inode, char *list, size_t list_size,
|
||||
const char *name, size_t name_len)
|
||||
{
|
||||
size_t retlen = XATTR_SECURITY_PREFIX_LEN + name_len + 1;
|
||||
|
||||
if (list && retlen <= list_size) {
|
||||
strcpy(list, XATTR_SECURITY_PREFIX);
|
||||
strcpy(list + XATTR_SECURITY_PREFIX_LEN, name);
|
||||
}
|
||||
|
||||
return retlen;
|
||||
}
|
||||
|
||||
struct xattr_handler jffs2_security_xattr_handler = {
|
||||
.prefix = XATTR_SECURITY_PREFIX,
|
||||
.list = jffs2_security_listxattr,
|
||||
.set = jffs2_security_setxattr,
|
||||
.get = jffs2_security_getxattr
|
||||
};
|
@ -5,6 +5,7 @@
|
||||
* Zoltan Sogor <weth@inf.u-szeged.hu>,
|
||||
* Patrik Kluba <pajko@halom.u-szeged.hu>,
|
||||
* University of Szeged, Hungary
|
||||
* 2005 KaiGai Kohei <kaigai@ak.jp.nec.com>
|
||||
*
|
||||
* For licensing information, see the file 'LICENCE' in this directory.
|
||||
*
|
||||
@ -81,6 +82,19 @@ static int jffs2_sum_add_mem(struct jffs2_summary *s, union jffs2_sum_mem *item)
|
||||
dbg_summary("dirent (%u) added to summary\n",
|
||||
je32_to_cpu(item->d.ino));
|
||||
break;
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
case JFFS2_NODETYPE_XATTR:
|
||||
s->sum_size += JFFS2_SUMMARY_XATTR_SIZE;
|
||||
s->sum_num++;
|
||||
dbg_summary("xattr (xid=%u, version=%u) added to summary\n",
|
||||
je32_to_cpu(item->x.xid), je32_to_cpu(item->x.version));
|
||||
break;
|
||||
case JFFS2_NODETYPE_XREF:
|
||||
s->sum_size += JFFS2_SUMMARY_XREF_SIZE;
|
||||
s->sum_num++;
|
||||
dbg_summary("xref added to summary\n");
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
JFFS2_WARNING("UNKNOWN node type %u\n",
|
||||
je16_to_cpu(item->u.nodetype));
|
||||
@ -141,6 +155,40 @@ int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *r
|
||||
return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
int jffs2_sum_add_xattr_mem(struct jffs2_summary *s, struct jffs2_raw_xattr *rx, uint32_t ofs)
|
||||
{
|
||||
struct jffs2_sum_xattr_mem *temp;
|
||||
|
||||
temp = kmalloc(sizeof(struct jffs2_sum_xattr_mem), GFP_KERNEL);
|
||||
if (!temp)
|
||||
return -ENOMEM;
|
||||
|
||||
temp->nodetype = rx->nodetype;
|
||||
temp->xid = rx->xid;
|
||||
temp->version = rx->version;
|
||||
temp->offset = cpu_to_je32(ofs);
|
||||
temp->totlen = rx->totlen;
|
||||
temp->next = NULL;
|
||||
|
||||
return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
|
||||
}
|
||||
|
||||
int jffs2_sum_add_xref_mem(struct jffs2_summary *s, struct jffs2_raw_xref *rr, uint32_t ofs)
|
||||
{
|
||||
struct jffs2_sum_xref_mem *temp;
|
||||
|
||||
temp = kmalloc(sizeof(struct jffs2_sum_xref_mem), GFP_KERNEL);
|
||||
if (!temp)
|
||||
return -ENOMEM;
|
||||
|
||||
temp->nodetype = rr->nodetype;
|
||||
temp->offset = cpu_to_je32(ofs);
|
||||
temp->next = NULL;
|
||||
|
||||
return jffs2_sum_add_mem(s, (union jffs2_sum_mem *)temp);
|
||||
}
|
||||
#endif
|
||||
/* Cleanup every collected summary information */
|
||||
|
||||
static void jffs2_sum_clean_collected(struct jffs2_summary *s)
|
||||
@ -259,7 +307,40 @@ int jffs2_sum_add_kvec(struct jffs2_sb_info *c, const struct kvec *invecs,
|
||||
|
||||
return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
|
||||
}
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
case JFFS2_NODETYPE_XATTR: {
|
||||
struct jffs2_sum_xattr_mem *temp;
|
||||
if (je32_to_cpu(node->x.version) == 0xffffffff)
|
||||
return 0;
|
||||
temp = kmalloc(sizeof(struct jffs2_sum_xattr_mem), GFP_KERNEL);
|
||||
if (!temp)
|
||||
goto no_mem;
|
||||
|
||||
temp->nodetype = node->x.nodetype;
|
||||
temp->xid = node->x.xid;
|
||||
temp->version = node->x.version;
|
||||
temp->totlen = node->x.totlen;
|
||||
temp->offset = cpu_to_je32(ofs);
|
||||
temp->next = NULL;
|
||||
|
||||
return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
|
||||
}
|
||||
case JFFS2_NODETYPE_XREF: {
|
||||
struct jffs2_sum_xref_mem *temp;
|
||||
|
||||
if (je32_to_cpu(node->r.ino) == 0xffffffff
|
||||
&& je32_to_cpu(node->r.xid) == 0xffffffff)
|
||||
return 0;
|
||||
temp = kmalloc(sizeof(struct jffs2_sum_xref_mem), GFP_KERNEL);
|
||||
if (!temp)
|
||||
goto no_mem;
|
||||
temp->nodetype = node->r.nodetype;
|
||||
temp->offset = cpu_to_je32(ofs);
|
||||
temp->next = NULL;
|
||||
|
||||
return jffs2_sum_add_mem(c->summary, (union jffs2_sum_mem *)temp);
|
||||
}
|
||||
#endif
|
||||
case JFFS2_NODETYPE_PADDING:
|
||||
dbg_summary("node PADDING\n");
|
||||
c->summary->sum_padded += je32_to_cpu(node->u.totlen);
|
||||
@ -288,23 +369,41 @@ no_mem:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static struct jffs2_raw_node_ref *sum_link_node_ref(struct jffs2_sb_info *c,
|
||||
struct jffs2_eraseblock *jeb,
|
||||
uint32_t ofs, uint32_t len,
|
||||
struct jffs2_inode_cache *ic)
|
||||
{
|
||||
/* If there was a gap, mark it dirty */
|
||||
if ((ofs & ~3) > c->sector_size - jeb->free_size) {
|
||||
/* Ew. Summary doesn't actually tell us explicitly about dirty space */
|
||||
jffs2_scan_dirty_space(c, jeb, (ofs & ~3) - (c->sector_size - jeb->free_size));
|
||||
}
|
||||
|
||||
return jffs2_link_node_ref(c, jeb, jeb->offset + ofs, len, ic);
|
||||
}
|
||||
|
||||
/* Process the stored summary information - helper function for jffs2_sum_scan_sumnode() */
|
||||
|
||||
static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
struct jffs2_raw_summary *summary, uint32_t *pseudo_random)
|
||||
{
|
||||
struct jffs2_raw_node_ref *raw;
|
||||
struct jffs2_inode_cache *ic;
|
||||
struct jffs2_full_dirent *fd;
|
||||
void *sp;
|
||||
int i, ino;
|
||||
int err;
|
||||
|
||||
sp = summary->sum;
|
||||
|
||||
for (i=0; i<je32_to_cpu(summary->sum_num); i++) {
|
||||
dbg_summary("processing summary index %d\n", i);
|
||||
|
||||
/* Make sure there's a spare ref for dirty space */
|
||||
err = jffs2_prealloc_raw_node_refs(c, jeb, 2);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype)) {
|
||||
case JFFS2_NODETYPE_INODE: {
|
||||
struct jffs2_sum_inode_flash *spi;
|
||||
@ -312,39 +411,21 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras
|
||||
|
||||
ino = je32_to_cpu(spi->inode);
|
||||
|
||||
dbg_summary("Inode at 0x%08x\n",
|
||||
jeb->offset + je32_to_cpu(spi->offset));
|
||||
|
||||
raw = jffs2_alloc_raw_node_ref();
|
||||
if (!raw) {
|
||||
JFFS2_NOTICE("allocation of node reference failed\n");
|
||||
kfree(summary);
|
||||
return -ENOMEM;
|
||||
}
|
||||
dbg_summary("Inode at 0x%08x-0x%08x\n",
|
||||
jeb->offset + je32_to_cpu(spi->offset),
|
||||
jeb->offset + je32_to_cpu(spi->offset) + je32_to_cpu(spi->totlen));
|
||||
|
||||
ic = jffs2_scan_make_ino_cache(c, ino);
|
||||
if (!ic) {
|
||||
JFFS2_NOTICE("scan_make_ino_cache failed\n");
|
||||
jffs2_free_raw_node_ref(raw);
|
||||
kfree(summary);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
raw->flash_offset = (jeb->offset + je32_to_cpu(spi->offset)) | REF_UNCHECKED;
|
||||
raw->__totlen = PAD(je32_to_cpu(spi->totlen));
|
||||
raw->next_phys = NULL;
|
||||
raw->next_in_ino = ic->nodes;
|
||||
sum_link_node_ref(c, jeb, je32_to_cpu(spi->offset) | REF_UNCHECKED,
|
||||
PAD(je32_to_cpu(spi->totlen)), ic);
|
||||
|
||||
ic->nodes = raw;
|
||||
if (!jeb->first_node)
|
||||
jeb->first_node = raw;
|
||||
if (jeb->last_node)
|
||||
jeb->last_node->next_phys = raw;
|
||||
jeb->last_node = raw;
|
||||
*pseudo_random += je32_to_cpu(spi->version);
|
||||
|
||||
UNCHECKED_SPACE(PAD(je32_to_cpu(spi->totlen)));
|
||||
|
||||
sp += JFFS2_SUMMARY_INODE_SIZE;
|
||||
|
||||
break;
|
||||
@ -354,52 +435,33 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras
|
||||
struct jffs2_sum_dirent_flash *spd;
|
||||
spd = sp;
|
||||
|
||||
dbg_summary("Dirent at 0x%08x\n",
|
||||
jeb->offset + je32_to_cpu(spd->offset));
|
||||
dbg_summary("Dirent at 0x%08x-0x%08x\n",
|
||||
jeb->offset + je32_to_cpu(spd->offset),
|
||||
jeb->offset + je32_to_cpu(spd->offset) + je32_to_cpu(spd->totlen));
|
||||
|
||||
|
||||
fd = jffs2_alloc_full_dirent(spd->nsize+1);
|
||||
if (!fd) {
|
||||
kfree(summary);
|
||||
if (!fd)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(&fd->name, spd->name, spd->nsize);
|
||||
fd->name[spd->nsize] = 0;
|
||||
|
||||
raw = jffs2_alloc_raw_node_ref();
|
||||
if (!raw) {
|
||||
jffs2_free_full_dirent(fd);
|
||||
JFFS2_NOTICE("allocation of node reference failed\n");
|
||||
kfree(summary);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(spd->pino));
|
||||
if (!ic) {
|
||||
jffs2_free_full_dirent(fd);
|
||||
jffs2_free_raw_node_ref(raw);
|
||||
kfree(summary);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
raw->__totlen = PAD(je32_to_cpu(spd->totlen));
|
||||
raw->flash_offset = (jeb->offset + je32_to_cpu(spd->offset)) | REF_PRISTINE;
|
||||
raw->next_phys = NULL;
|
||||
raw->next_in_ino = ic->nodes;
|
||||
ic->nodes = raw;
|
||||
if (!jeb->first_node)
|
||||
jeb->first_node = raw;
|
||||
if (jeb->last_node)
|
||||
jeb->last_node->next_phys = raw;
|
||||
jeb->last_node = raw;
|
||||
fd->raw = sum_link_node_ref(c, jeb, je32_to_cpu(spd->offset) | REF_UNCHECKED,
|
||||
PAD(je32_to_cpu(spd->totlen)), ic);
|
||||
|
||||
fd->raw = raw;
|
||||
fd->next = NULL;
|
||||
fd->version = je32_to_cpu(spd->version);
|
||||
fd->ino = je32_to_cpu(spd->ino);
|
||||
fd->nhash = full_name_hash(fd->name, spd->nsize);
|
||||
fd->type = spd->type;
|
||||
USED_SPACE(PAD(je32_to_cpu(spd->totlen)));
|
||||
|
||||
jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
|
||||
|
||||
*pseudo_random += je32_to_cpu(spd->version);
|
||||
@ -408,48 +470,105 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras
|
||||
|
||||
break;
|
||||
}
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
case JFFS2_NODETYPE_XATTR: {
|
||||
struct jffs2_xattr_datum *xd;
|
||||
struct jffs2_sum_xattr_flash *spx;
|
||||
|
||||
spx = (struct jffs2_sum_xattr_flash *)sp;
|
||||
dbg_summary("xattr at %#08x-%#08x (xid=%u, version=%u)\n",
|
||||
jeb->offset + je32_to_cpu(spx->offset),
|
||||
jeb->offset + je32_to_cpu(spx->offset) + je32_to_cpu(spx->totlen),
|
||||
je32_to_cpu(spx->xid), je32_to_cpu(spx->version));
|
||||
|
||||
xd = jffs2_setup_xattr_datum(c, je32_to_cpu(spx->xid),
|
||||
je32_to_cpu(spx->version));
|
||||
if (IS_ERR(xd)) {
|
||||
if (PTR_ERR(xd) == -EEXIST) {
|
||||
/* a newer version of xd exists */
|
||||
if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(spx->totlen))))
|
||||
return err;
|
||||
sp += JFFS2_SUMMARY_XATTR_SIZE;
|
||||
break;
|
||||
}
|
||||
JFFS2_NOTICE("allocation of xattr_datum failed\n");
|
||||
return PTR_ERR(xd);
|
||||
}
|
||||
|
||||
xd->node = sum_link_node_ref(c, jeb, je32_to_cpu(spx->offset) | REF_UNCHECKED,
|
||||
PAD(je32_to_cpu(spx->totlen)), NULL);
|
||||
/* FIXME */ xd->node->next_in_ino = (void *)xd;
|
||||
|
||||
*pseudo_random += je32_to_cpu(spx->xid);
|
||||
sp += JFFS2_SUMMARY_XATTR_SIZE;
|
||||
|
||||
break;
|
||||
}
|
||||
case JFFS2_NODETYPE_XREF: {
|
||||
struct jffs2_xattr_ref *ref;
|
||||
struct jffs2_sum_xref_flash *spr;
|
||||
|
||||
spr = (struct jffs2_sum_xref_flash *)sp;
|
||||
dbg_summary("xref at %#08x-%#08x\n",
|
||||
jeb->offset + je32_to_cpu(spr->offset),
|
||||
jeb->offset + je32_to_cpu(spr->offset) +
|
||||
(uint32_t)PAD(sizeof(struct jffs2_raw_xref)));
|
||||
|
||||
ref = jffs2_alloc_xattr_ref();
|
||||
if (!ref) {
|
||||
JFFS2_NOTICE("allocation of xattr_datum failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
ref->ino = 0xfffffffe;
|
||||
ref->xid = 0xfffffffd;
|
||||
ref->next = c->xref_temp;
|
||||
c->xref_temp = ref;
|
||||
|
||||
ref->node = sum_link_node_ref(c, jeb, je32_to_cpu(spr->offset) | REF_UNCHECKED,
|
||||
PAD(sizeof(struct jffs2_raw_xref)), NULL);
|
||||
/* FIXME */ ref->node->next_in_ino = (void *)ref;
|
||||
|
||||
*pseudo_random += ref->node->flash_offset;
|
||||
sp += JFFS2_SUMMARY_XREF_SIZE;
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default : {
|
||||
JFFS2_WARNING("Unsupported node type found in summary! Exiting...");
|
||||
kfree(summary);
|
||||
return -EIO;
|
||||
uint16_t nodetype = je16_to_cpu(((struct jffs2_sum_unknown_flash *)sp)->nodetype);
|
||||
JFFS2_WARNING("Unsupported node type %x found in summary! Exiting...\n", nodetype);
|
||||
if ((nodetype & JFFS2_COMPAT_MASK) == JFFS2_FEATURE_INCOMPAT)
|
||||
return -EIO;
|
||||
|
||||
/* For compatible node types, just fall back to the full scan */
|
||||
c->wasted_size -= jeb->wasted_size;
|
||||
c->free_size += c->sector_size - jeb->free_size;
|
||||
c->used_size -= jeb->used_size;
|
||||
c->dirty_size -= jeb->dirty_size;
|
||||
jeb->wasted_size = jeb->used_size = jeb->dirty_size = 0;
|
||||
jeb->free_size = c->sector_size;
|
||||
|
||||
jffs2_free_jeb_node_refs(c, jeb);
|
||||
return -ENOTRECOVERABLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kfree(summary);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Process the summary node - called from jffs2_scan_eraseblock() */
|
||||
|
||||
int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
uint32_t ofs, uint32_t *pseudo_random)
|
||||
struct jffs2_raw_summary *summary, uint32_t sumsize,
|
||||
uint32_t *pseudo_random)
|
||||
{
|
||||
struct jffs2_unknown_node crcnode;
|
||||
struct jffs2_raw_node_ref *cache_ref;
|
||||
struct jffs2_raw_summary *summary;
|
||||
int ret, sumsize;
|
||||
int ret, ofs;
|
||||
uint32_t crc;
|
||||
|
||||
sumsize = c->sector_size - ofs;
|
||||
ofs += jeb->offset;
|
||||
ofs = c->sector_size - sumsize;
|
||||
|
||||
dbg_summary("summary found for 0x%08x at 0x%08x (0x%x bytes)\n",
|
||||
jeb->offset, ofs, sumsize);
|
||||
|
||||
summary = kmalloc(sumsize, GFP_KERNEL);
|
||||
|
||||
if (!summary) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = jffs2_fill_scan_buf(c, (unsigned char *)summary, ofs, sumsize);
|
||||
|
||||
if (ret) {
|
||||
kfree(summary);
|
||||
return ret;
|
||||
}
|
||||
jeb->offset, jeb->offset + ofs, sumsize);
|
||||
|
||||
/* OK, now check for node validity and CRC */
|
||||
crcnode.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
|
||||
@ -486,67 +605,50 @@ int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb
|
||||
|
||||
dbg_summary("Summary : CLEANMARKER node \n");
|
||||
|
||||
ret = jffs2_prealloc_raw_node_refs(c, jeb, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (je32_to_cpu(summary->cln_mkr) != c->cleanmarker_size) {
|
||||
dbg_summary("CLEANMARKER node has totlen 0x%x != normal 0x%x\n",
|
||||
je32_to_cpu(summary->cln_mkr), c->cleanmarker_size);
|
||||
UNCHECKED_SPACE(PAD(je32_to_cpu(summary->cln_mkr)));
|
||||
if ((ret = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(summary->cln_mkr)))))
|
||||
return ret;
|
||||
} else if (jeb->first_node) {
|
||||
dbg_summary("CLEANMARKER node not first node in block "
|
||||
"(0x%08x)\n", jeb->offset);
|
||||
UNCHECKED_SPACE(PAD(je32_to_cpu(summary->cln_mkr)));
|
||||
if ((ret = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(summary->cln_mkr)))))
|
||||
return ret;
|
||||
} else {
|
||||
struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref();
|
||||
|
||||
if (!marker_ref) {
|
||||
JFFS2_NOTICE("Failed to allocate node ref for clean marker\n");
|
||||
kfree(summary);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
marker_ref->next_in_ino = NULL;
|
||||
marker_ref->next_phys = NULL;
|
||||
marker_ref->flash_offset = jeb->offset | REF_NORMAL;
|
||||
marker_ref->__totlen = je32_to_cpu(summary->cln_mkr);
|
||||
jeb->first_node = jeb->last_node = marker_ref;
|
||||
|
||||
USED_SPACE( PAD(je32_to_cpu(summary->cln_mkr)) );
|
||||
jffs2_link_node_ref(c, jeb, jeb->offset | REF_NORMAL,
|
||||
je32_to_cpu(summary->cln_mkr), NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (je32_to_cpu(summary->padded)) {
|
||||
DIRTY_SPACE(je32_to_cpu(summary->padded));
|
||||
}
|
||||
|
||||
ret = jffs2_sum_process_sum_data(c, jeb, summary, pseudo_random);
|
||||
/* -ENOTRECOVERABLE isn't a fatal error -- it means we should do a full
|
||||
scan of this eraseblock. So return zero */
|
||||
if (ret == -ENOTRECOVERABLE)
|
||||
return 0;
|
||||
if (ret)
|
||||
return ret; /* real error */
|
||||
|
||||
/* for PARANOIA_CHECK */
|
||||
ret = jffs2_prealloc_raw_node_refs(c, jeb, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* for PARANOIA_CHECK */
|
||||
cache_ref = jffs2_alloc_raw_node_ref();
|
||||
sum_link_node_ref(c, jeb, ofs | REF_NORMAL, sumsize, NULL);
|
||||
|
||||
if (!cache_ref) {
|
||||
JFFS2_NOTICE("Failed to allocate node ref for cache\n");
|
||||
return -ENOMEM;
|
||||
if (unlikely(jeb->free_size)) {
|
||||
JFFS2_WARNING("Free size 0x%x bytes in eraseblock @0x%08x with summary?\n",
|
||||
jeb->free_size, jeb->offset);
|
||||
jeb->wasted_size += jeb->free_size;
|
||||
c->wasted_size += jeb->free_size;
|
||||
c->free_size -= jeb->free_size;
|
||||
jeb->free_size = 0;
|
||||
}
|
||||
|
||||
cache_ref->next_in_ino = NULL;
|
||||
cache_ref->next_phys = NULL;
|
||||
cache_ref->flash_offset = ofs | REF_NORMAL;
|
||||
cache_ref->__totlen = sumsize;
|
||||
|
||||
if (!jeb->first_node)
|
||||
jeb->first_node = cache_ref;
|
||||
if (jeb->last_node)
|
||||
jeb->last_node->next_phys = cache_ref;
|
||||
jeb->last_node = cache_ref;
|
||||
|
||||
USED_SPACE(sumsize);
|
||||
|
||||
jeb->wasted_size += jeb->free_size;
|
||||
c->wasted_size += jeb->free_size;
|
||||
c->free_size -= jeb->free_size;
|
||||
jeb->free_size = 0;
|
||||
|
||||
return jffs2_scan_classify_jeb(c, jeb);
|
||||
|
||||
crc_err:
|
||||
@ -564,6 +666,7 @@ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock
|
||||
union jffs2_sum_mem *temp;
|
||||
struct jffs2_sum_marker *sm;
|
||||
struct kvec vecs[2];
|
||||
uint32_t sum_ofs;
|
||||
void *wpage;
|
||||
int ret;
|
||||
size_t retlen;
|
||||
@ -581,16 +684,17 @@ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock
|
||||
wpage = c->summary->sum_buf;
|
||||
|
||||
while (c->summary->sum_num) {
|
||||
temp = c->summary->sum_list_head;
|
||||
|
||||
switch (je16_to_cpu(c->summary->sum_list_head->u.nodetype)) {
|
||||
switch (je16_to_cpu(temp->u.nodetype)) {
|
||||
case JFFS2_NODETYPE_INODE: {
|
||||
struct jffs2_sum_inode_flash *sino_ptr = wpage;
|
||||
|
||||
sino_ptr->nodetype = c->summary->sum_list_head->i.nodetype;
|
||||
sino_ptr->inode = c->summary->sum_list_head->i.inode;
|
||||
sino_ptr->version = c->summary->sum_list_head->i.version;
|
||||
sino_ptr->offset = c->summary->sum_list_head->i.offset;
|
||||
sino_ptr->totlen = c->summary->sum_list_head->i.totlen;
|
||||
sino_ptr->nodetype = temp->i.nodetype;
|
||||
sino_ptr->inode = temp->i.inode;
|
||||
sino_ptr->version = temp->i.version;
|
||||
sino_ptr->offset = temp->i.offset;
|
||||
sino_ptr->totlen = temp->i.totlen;
|
||||
|
||||
wpage += JFFS2_SUMMARY_INODE_SIZE;
|
||||
|
||||
@ -600,30 +704,60 @@ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock
|
||||
case JFFS2_NODETYPE_DIRENT: {
|
||||
struct jffs2_sum_dirent_flash *sdrnt_ptr = wpage;
|
||||
|
||||
sdrnt_ptr->nodetype = c->summary->sum_list_head->d.nodetype;
|
||||
sdrnt_ptr->totlen = c->summary->sum_list_head->d.totlen;
|
||||
sdrnt_ptr->offset = c->summary->sum_list_head->d.offset;
|
||||
sdrnt_ptr->pino = c->summary->sum_list_head->d.pino;
|
||||
sdrnt_ptr->version = c->summary->sum_list_head->d.version;
|
||||
sdrnt_ptr->ino = c->summary->sum_list_head->d.ino;
|
||||
sdrnt_ptr->nsize = c->summary->sum_list_head->d.nsize;
|
||||
sdrnt_ptr->type = c->summary->sum_list_head->d.type;
|
||||
sdrnt_ptr->nodetype = temp->d.nodetype;
|
||||
sdrnt_ptr->totlen = temp->d.totlen;
|
||||
sdrnt_ptr->offset = temp->d.offset;
|
||||
sdrnt_ptr->pino = temp->d.pino;
|
||||
sdrnt_ptr->version = temp->d.version;
|
||||
sdrnt_ptr->ino = temp->d.ino;
|
||||
sdrnt_ptr->nsize = temp->d.nsize;
|
||||
sdrnt_ptr->type = temp->d.type;
|
||||
|
||||
memcpy(sdrnt_ptr->name, c->summary->sum_list_head->d.name,
|
||||
c->summary->sum_list_head->d.nsize);
|
||||
memcpy(sdrnt_ptr->name, temp->d.name,
|
||||
temp->d.nsize);
|
||||
|
||||
wpage += JFFS2_SUMMARY_DIRENT_SIZE(c->summary->sum_list_head->d.nsize);
|
||||
wpage += JFFS2_SUMMARY_DIRENT_SIZE(temp->d.nsize);
|
||||
|
||||
break;
|
||||
}
|
||||
#ifdef CONFIG_JFFS2_FS_XATTR
|
||||
case JFFS2_NODETYPE_XATTR: {
|
||||
struct jffs2_sum_xattr_flash *sxattr_ptr = wpage;
|
||||
|
||||
temp = c->summary->sum_list_head;
|
||||
sxattr_ptr->nodetype = temp->x.nodetype;
|
||||
sxattr_ptr->xid = temp->x.xid;
|
||||
sxattr_ptr->version = temp->x.version;
|
||||
sxattr_ptr->offset = temp->x.offset;
|
||||
sxattr_ptr->totlen = temp->x.totlen;
|
||||
|
||||
wpage += JFFS2_SUMMARY_XATTR_SIZE;
|
||||
break;
|
||||
}
|
||||
case JFFS2_NODETYPE_XREF: {
|
||||
struct jffs2_sum_xref_flash *sxref_ptr = wpage;
|
||||
|
||||
temp = c->summary->sum_list_head;
|
||||
sxref_ptr->nodetype = temp->r.nodetype;
|
||||
sxref_ptr->offset = temp->r.offset;
|
||||
|
||||
wpage += JFFS2_SUMMARY_XREF_SIZE;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default : {
|
||||
BUG(); /* unknown node in summary information */
|
||||
if ((je16_to_cpu(temp->u.nodetype) & JFFS2_COMPAT_MASK)
|
||||
== JFFS2_FEATURE_RWCOMPAT_COPY) {
|
||||
dbg_summary("Writing unknown RWCOMPAT_COPY node type %x\n",
|
||||
je16_to_cpu(temp->u.nodetype));
|
||||
jffs2_sum_disable_collecting(c->summary);
|
||||
} else {
|
||||
BUG(); /* unknown node in summary information */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
temp = c->summary->sum_list_head;
|
||||
c->summary->sum_list_head = c->summary->sum_list_head->u.next;
|
||||
c->summary->sum_list_head = temp->u.next;
|
||||
kfree(temp);
|
||||
|
||||
c->summary->sum_num--;
|
||||
@ -645,25 +779,34 @@ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock
|
||||
vecs[1].iov_base = c->summary->sum_buf;
|
||||
vecs[1].iov_len = datasize;
|
||||
|
||||
sum_ofs = jeb->offset + c->sector_size - jeb->free_size;
|
||||
|
||||
dbg_summary("JFFS2: writing out data to flash to pos : 0x%08x\n",
|
||||
jeb->offset + c->sector_size - jeb->free_size);
|
||||
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
ret = jffs2_flash_writev(c, vecs, 2, jeb->offset + c->sector_size -
|
||||
jeb->free_size, &retlen, 0);
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
sum_ofs);
|
||||
|
||||
ret = jffs2_flash_writev(c, vecs, 2, sum_ofs, &retlen, 0);
|
||||
|
||||
if (ret || (retlen != infosize)) {
|
||||
JFFS2_WARNING("Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n",
|
||||
infosize, jeb->offset + c->sector_size - jeb->free_size, ret, retlen);
|
||||
|
||||
JFFS2_WARNING("Write of %u bytes at 0x%08x failed. returned %d, retlen %zd\n",
|
||||
infosize, sum_ofs, ret, retlen);
|
||||
|
||||
if (retlen) {
|
||||
/* Waste remaining space */
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
jffs2_link_node_ref(c, jeb, sum_ofs | REF_OBSOLETE, infosize, NULL);
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
}
|
||||
|
||||
c->summary->sum_size = JFFS2_SUMMARY_NOSUM_SIZE;
|
||||
WASTED_SPACE(infosize);
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
jffs2_link_node_ref(c, jeb, sum_ofs | REF_NORMAL, infosize, NULL);
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -671,13 +814,16 @@ static int jffs2_sum_write_data(struct jffs2_sb_info *c, struct jffs2_eraseblock
|
||||
|
||||
int jffs2_sum_write_sumnode(struct jffs2_sb_info *c)
|
||||
{
|
||||
struct jffs2_raw_node_ref *summary_ref;
|
||||
int datasize, infosize, padsize, ret;
|
||||
int datasize, infosize, padsize;
|
||||
struct jffs2_eraseblock *jeb;
|
||||
int ret;
|
||||
|
||||
dbg_summary("called\n");
|
||||
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
|
||||
jeb = c->nextblock;
|
||||
jffs2_prealloc_raw_node_refs(c, jeb, 1);
|
||||
|
||||
if (!c->summary->sum_num || !c->summary->sum_list_head) {
|
||||
JFFS2_WARNING("Empty summary info!!!\n");
|
||||
@ -696,35 +842,11 @@ int jffs2_sum_write_sumnode(struct jffs2_sb_info *c)
|
||||
jffs2_sum_disable_collecting(c->summary);
|
||||
|
||||
JFFS2_WARNING("Not enough space for summary, padsize = %d\n", padsize);
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = jffs2_sum_write_data(c, jeb, infosize, datasize, padsize);
|
||||
if (ret)
|
||||
return 0; /* can't write out summary, block is marked as NOSUM_SIZE */
|
||||
|
||||
/* for ACCT_PARANOIA_CHECK */
|
||||
spin_unlock(&c->erase_completion_lock);
|
||||
summary_ref = jffs2_alloc_raw_node_ref();
|
||||
spin_lock(&c->erase_completion_lock);
|
||||
|
||||
if (!summary_ref) {
|
||||
JFFS2_NOTICE("Failed to allocate node ref for summary\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
summary_ref->next_in_ino = NULL;
|
||||
summary_ref->next_phys = NULL;
|
||||
summary_ref->flash_offset = (jeb->offset + c->sector_size - jeb->free_size) | REF_NORMAL;
|
||||
summary_ref->__totlen = infosize;
|
||||
|
||||
if (!jeb->first_node)
|
||||
jeb->first_node = summary_ref;
|
||||
if (jeb->last_node)
|
||||
jeb->last_node->next_phys = summary_ref;
|
||||
jeb->last_node = summary_ref;
|
||||
|
||||
USED_SPACE(infosize);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
@ -18,23 +18,6 @@
|
||||
#include <linux/uio.h>
|
||||
#include <linux/jffs2.h>
|
||||
|
||||
#define DIRTY_SPACE(x) do { typeof(x) _x = (x); \
|
||||
c->free_size -= _x; c->dirty_size += _x; \
|
||||
jeb->free_size -= _x ; jeb->dirty_size += _x; \
|
||||
}while(0)
|
||||
#define USED_SPACE(x) do { typeof(x) _x = (x); \
|
||||
c->free_size -= _x; c->used_size += _x; \
|
||||
jeb->free_size -= _x ; jeb->used_size += _x; \
|
||||
}while(0)
|
||||
#define WASTED_SPACE(x) do { typeof(x) _x = (x); \
|
||||
c->free_size -= _x; c->wasted_size += _x; \
|
||||
jeb->free_size -= _x ; jeb->wasted_size += _x; \
|
||||
}while(0)
|
||||
#define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \
|
||||
c->free_size -= _x; c->unchecked_size += _x; \
|
||||
jeb->free_size -= _x ; jeb->unchecked_size += _x; \
|
||||
}while(0)
|
||||
|
||||
#define BLK_STATE_ALLFF 0
|
||||
#define BLK_STATE_CLEAN 1
|
||||
#define BLK_STATE_PARTDIRTY 2
|
||||
@ -45,6 +28,8 @@
|
||||
#define JFFS2_SUMMARY_NOSUM_SIZE 0xffffffff
|
||||
#define JFFS2_SUMMARY_INODE_SIZE (sizeof(struct jffs2_sum_inode_flash))
|
||||
#define JFFS2_SUMMARY_DIRENT_SIZE(x) (sizeof(struct jffs2_sum_dirent_flash) + (x))
|
||||
#define JFFS2_SUMMARY_XATTR_SIZE (sizeof(struct jffs2_sum_xattr_flash))
|
||||
#define JFFS2_SUMMARY_XREF_SIZE (sizeof(struct jffs2_sum_xref_flash))
|
||||
|
||||
/* Summary structures used on flash */
|
||||
|
||||
@ -75,11 +60,28 @@ struct jffs2_sum_dirent_flash
|
||||
uint8_t name[0]; /* dirent name */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct jffs2_sum_xattr_flash
|
||||
{
|
||||
jint16_t nodetype; /* == JFFS2_NODETYPE_XATR */
|
||||
jint32_t xid; /* xattr identifier */
|
||||
jint32_t version; /* version number */
|
||||
jint32_t offset; /* offset on jeb */
|
||||
jint32_t totlen; /* node length */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct jffs2_sum_xref_flash
|
||||
{
|
||||
jint16_t nodetype; /* == JFFS2_NODETYPE_XREF */
|
||||
jint32_t offset; /* offset on jeb */
|
||||
} __attribute__((packed));
|
||||
|
||||
union jffs2_sum_flash
|
||||
{
|
||||
struct jffs2_sum_unknown_flash u;
|
||||
struct jffs2_sum_inode_flash i;
|
||||
struct jffs2_sum_dirent_flash d;
|
||||
struct jffs2_sum_xattr_flash x;
|
||||
struct jffs2_sum_xref_flash r;
|
||||
};
|
||||
|
||||
/* Summary structures used in the memory */
|
||||
@ -114,11 +116,30 @@ struct jffs2_sum_dirent_mem
|
||||
uint8_t name[0]; /* dirent name */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct jffs2_sum_xattr_mem
|
||||
{
|
||||
union jffs2_sum_mem *next;
|
||||
jint16_t nodetype;
|
||||
jint32_t xid;
|
||||
jint32_t version;
|
||||
jint32_t offset;
|
||||
jint32_t totlen;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct jffs2_sum_xref_mem
|
||||
{
|
||||
union jffs2_sum_mem *next;
|
||||
jint16_t nodetype;
|
||||
jint32_t offset;
|
||||
} __attribute__((packed));
|
||||
|
||||
union jffs2_sum_mem
|
||||
{
|
||||
struct jffs2_sum_unknown_mem u;
|
||||
struct jffs2_sum_inode_mem i;
|
||||
struct jffs2_sum_dirent_mem d;
|
||||
struct jffs2_sum_xattr_mem x;
|
||||
struct jffs2_sum_xref_mem r;
|
||||
};
|
||||
|
||||
/* Summary related information stored in superblock */
|
||||
@ -159,8 +180,11 @@ int jffs2_sum_write_sumnode(struct jffs2_sb_info *c);
|
||||
int jffs2_sum_add_padding_mem(struct jffs2_summary *s, uint32_t size);
|
||||
int jffs2_sum_add_inode_mem(struct jffs2_summary *s, struct jffs2_raw_inode *ri, uint32_t ofs);
|
||||
int jffs2_sum_add_dirent_mem(struct jffs2_summary *s, struct jffs2_raw_dirent *rd, uint32_t ofs);
|
||||
int jffs2_sum_add_xattr_mem(struct jffs2_summary *s, struct jffs2_raw_xattr *rx, uint32_t ofs);
|
||||
int jffs2_sum_add_xref_mem(struct jffs2_summary *s, struct jffs2_raw_xref *rr, uint32_t ofs);
|
||||
int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
|
||||
uint32_t ofs, uint32_t *pseudo_random);
|
||||
struct jffs2_raw_summary *summary, uint32_t sumlen,
|
||||
uint32_t *pseudo_random);
|
||||
|
||||
#else /* SUMMARY DISABLED */
|
||||
|
||||
@ -176,7 +200,9 @@ int jffs2_sum_scan_sumnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb
|
||||
#define jffs2_sum_add_padding_mem(a,b)
|
||||
#define jffs2_sum_add_inode_mem(a,b,c)
|
||||
#define jffs2_sum_add_dirent_mem(a,b,c)
|
||||
#define jffs2_sum_scan_sumnode(a,b,c,d) (0)
|
||||
#define jffs2_sum_add_xattr_mem(a,b,c)
|
||||
#define jffs2_sum_add_xref_mem(a,b,c)
|
||||
#define jffs2_sum_scan_sumnode(a,b,c,d,e) (0)
|
||||
|
||||
#endif /* CONFIG_JFFS2_SUMMARY */
|
||||
|
||||
|
@ -151,7 +151,10 @@ static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type,
|
||||
|
||||
sb->s_op = &jffs2_super_operations;
|
||||
sb->s_flags = flags | MS_NOATIME;
|
||||
|
||||
sb->s_xattr = jffs2_xattr_handlers;
|
||||
#ifdef CONFIG_JFFS2_FS_POSIX_ACL
|
||||
sb->s_flags |= MS_POSIXACL;
|
||||
#endif
|
||||
ret = jffs2_do_fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
|
||||
|
||||
if (ret) {
|
||||
@ -293,6 +296,7 @@ static void jffs2_put_super (struct super_block *sb)
|
||||
kfree(c->blocks);
|
||||
jffs2_flash_cleanup(c);
|
||||
kfree(c->inocache_list);
|
||||
jffs2_clear_xattr_subsystem(c);
|
||||
if (c->mtd->sync)
|
||||
c->mtd->sync(c->mtd);
|
||||
|
||||
@ -320,6 +324,18 @@ static int __init init_jffs2_fs(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Paranoia checks for on-medium structures. If we ask GCC
|
||||
to pack them with __attribute__((packed)) then it _also_
|
||||
assumes that they're not aligned -- so it emits crappy
|
||||
code on some architectures. Ideally we want an attribute
|
||||
which means just 'no padding', without the alignment
|
||||
thing. But GCC doesn't have that -- we have to just
|
||||
hope the structs are the right sizes, instead. */
|
||||
BUG_ON(sizeof(struct jffs2_unknown_node) != 12);
|
||||
BUG_ON(sizeof(struct jffs2_raw_dirent) != 40);
|
||||
BUG_ON(sizeof(struct jffs2_raw_inode) != 68);
|
||||
BUG_ON(sizeof(struct jffs2_raw_summary) != 32);
|
||||
|
||||
printk(KERN_INFO "JFFS2 version 2.2."
|
||||
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
|
||||
" (NAND)"
|
||||
@ -327,7 +343,7 @@ static int __init init_jffs2_fs(void)
|
||||
#ifdef CONFIG_JFFS2_SUMMARY
|
||||
" (SUMMARY) "
|
||||
#endif
|
||||
" (C) 2001-2003 Red Hat, Inc.\n");
|
||||
" (C) 2001-2006 Red Hat, Inc.\n");
|
||||
|
||||
jffs2_inode_cachep = kmem_cache_create("jffs2_i",
|
||||
sizeof(struct jffs2_inode_info),
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user